@zoralabs/coins 2.1.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$js.log +152 -0
- package/CHANGELOG.md +93 -0
- package/README.md +4 -0
- package/abis/BaseCoin.json +26 -5
- package/abis/BaseTest.json +2 -7
- package/abis/ContentCoin.json +26 -5
- package/abis/CreatorCoin.json +30 -9
- package/abis/FeeEstimatorHook.json +94 -6
- package/abis/ICoin.json +26 -0
- package/abis/ICoinV3.json +26 -0
- package/abis/ICreatorCoin.json +39 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasCoinType.json +15 -0
- package/abis/IHasTotalSupplyForPositions.json +15 -0
- package/abis/{LiquidityMigrationReceiver.json → IUpgradeableDestinationV4HookWithUpdateableFee.json} +10 -18
- package/abis/IZoraFactory.json +121 -0
- package/abis/IZoraHookRegistry.json +188 -0
- package/abis/VmContractHelper226.json +233 -0
- package/abis/ZoraFactoryImpl.json +101 -6
- package/abis/ZoraHookRegistry.json +375 -0
- package/abis/{CreatorCoinHook.json → ZoraV4CoinHook.json} +95 -2
- package/addresses/8453.json +6 -5
- package/audits/report-cantinacode-zora-0827.pdf +3498 -4
- package/dist/index.cjs +93 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -13
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +144 -22
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/foundry.toml +4 -1
- package/package/wagmiGenerated.ts +93 -13
- package/package.json +6 -4
- package/script/PrintRegisterUpgradePath.s.sol +0 -7
- package/script/TestBackingCoinSwap.s.sol +0 -3
- package/script/TestV4Swap.s.sol +0 -3
- package/script/UpgradeFactoryImpl.s.sol +1 -1
- package/src/BaseCoin.sol +19 -24
- package/src/ContentCoin.sol +11 -2
- package/src/CreatorCoin.sol +34 -15
- package/src/ZoraFactoryImpl.sol +163 -92
- package/src/deployment/CoinsDeployerBase.sol +24 -58
- package/src/hook-registry/ZoraHookRegistry.sol +97 -0
- package/src/hooks/{BaseZoraV4CoinHook.sol → ZoraV4CoinHook.sol} +77 -15
- package/src/interfaces/ICoin.sol +19 -1
- package/src/interfaces/ICreatorCoin.sol +4 -0
- package/src/interfaces/IUpgradeableV4Hook.sol +18 -0
- package/src/interfaces/IZoraFactory.sol +51 -10
- package/src/interfaces/IZoraHookRegistry.sol +47 -0
- package/src/libs/CoinConstants.sol +43 -32
- package/src/libs/CoinDopplerMultiCurve.sol +11 -11
- package/src/libs/CoinRewardsV4.sol +68 -37
- package/src/libs/CoinSetup.sol +2 -9
- package/src/libs/DopplerMath.sol +2 -2
- package/src/libs/HooksDeployment.sol +13 -65
- package/src/libs/V4Liquidity.sol +109 -15
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +5 -5
- package/test/CoinRewardsV4.t.sol +33 -0
- package/test/CoinUniV4.t.sol +32 -30
- package/test/ContentCoinRewards.t.sol +363 -0
- package/test/CreatorCoin.t.sol +53 -29
- package/test/CreatorCoinRewards.t.sol +375 -0
- package/test/DeploymentHooks.t.sol +64 -12
- package/test/Factory.t.sol +24 -7
- package/test/HooksDeployment.t.sol +4 -4
- package/test/LiquidityMigration.t.sol +149 -16
- package/test/Upgrades.t.sol +44 -48
- package/test/V4Liquidity.t.sol +178 -0
- package/test/ZoraHookRegistry.t.sol +266 -0
- package/test/utils/BaseTest.sol +25 -43
- package/test/utils/FeeEstimatorHook.sol +4 -6
- package/test/utils/RewardTestHelpers.sol +106 -0
- package/.turbo/turbo-build.log +0 -199
- package/abis/AutoSwapperTest.json +0 -618
- package/abis/BadImpl.json +0 -15
- package/abis/BaseZoraV4CoinHook.json +0 -1664
- package/abis/CoinConstants.json +0 -158
- package/abis/CoinRewardsV4.json +0 -67
- package/abis/CoinTest.json +0 -819
- package/abis/CoinUniV4Test.json +0 -1128
- package/abis/ContentCoinHook.json +0 -1733
- package/abis/CreatorCoinTest.json +0 -887
- package/abis/Deploy.json +0 -9
- package/abis/DeployHooks.json +0 -9
- package/abis/DeployScript.json +0 -35
- package/abis/DeployedCoinVersionLookupTest.json +0 -740
- package/abis/DifferentNamespaceVersionLookup.json +0 -39
- package/abis/FactoryTest.json +0 -748
- package/abis/FakeHookNoInterface.json +0 -21
- package/abis/GenerateDeterministicParams.json +0 -9
- package/abis/HooksDeploymentTest.json +0 -645
- package/abis/HooksTest.json +0 -709
- package/abis/InvalidLiquidityMigrationReceiver.json +0 -21
- package/abis/LiquidityMigrationTest.json +0 -889
- package/abis/MockBadFactory.json +0 -15
- package/abis/MultiOwnableTest.json +0 -766
- package/abis/PrintUpgradeCommand.json +0 -9
- package/abis/TestDeployedCoinVersionLookupImplementation.json +0 -39
- package/abis/TestV4Swap.json +0 -9
- package/abis/UpgradeFactoryImpl.json +0 -9
- package/abis/UpgradeHooks.json +0 -35
- package/abis/UpgradesTest.json +0 -723
- package/src/hooks/ContentCoinHook.sol +0 -27
- package/src/hooks/CreatorCoinHook.sol +0 -27
- package/src/libs/CreatorCoinConstants.sol +0 -16
- package/src/libs/CreatorCoinRewards.sol +0 -34
- package/src/libs/MarketConstants.sol +0 -15
package/src/libs/V4Liquidity.sol
CHANGED
|
@@ -28,8 +28,9 @@ import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
|
|
28
28
|
import {Position} from "@uniswap/v4-core/src/libraries/Position.sol";
|
|
29
29
|
import {BurnedPosition, Delta, MigratedLiquidityResult, IUpgradeableV4Hook} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
30
30
|
import {PoolStateReader} from "../libs/PoolStateReader.sol";
|
|
31
|
-
import {IUpgradeableDestinationV4Hook} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
31
|
+
import {IUpgradeableDestinationV4Hook, IUpgradeableDestinationV4HookWithUpdateableFee} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
32
32
|
import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
|
|
33
|
+
import {IZoraV4CoinHook} from "../interfaces/IZoraV4CoinHook.sol";
|
|
33
34
|
|
|
34
35
|
// command = 1; mint
|
|
35
36
|
struct MintCallbackData {
|
|
@@ -71,7 +72,7 @@ library V4Liquidity {
|
|
|
71
72
|
address coin,
|
|
72
73
|
address newHook,
|
|
73
74
|
bytes calldata additionalData
|
|
74
|
-
) internal returns (PoolKey memory) {
|
|
75
|
+
) internal returns (PoolKey memory newPoolKey) {
|
|
75
76
|
bytes memory data = abi.encode(
|
|
76
77
|
BURN_ALL_POSITIONS_CALLBACK_ID,
|
|
77
78
|
abi.encode(BurnAllPositionsCallbackData({poolKey: poolKey, positions: positions, coin: coin, newHook: newHook}))
|
|
@@ -82,19 +83,36 @@ library V4Liquidity {
|
|
|
82
83
|
|
|
83
84
|
MigratedLiquidityResult memory migratedLiquidityResult = abi.decode(result, (MigratedLiquidityResult));
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
newPoolKey.currency0 = poolKey.currency0;
|
|
87
|
+
newPoolKey.currency1 = poolKey.currency1;
|
|
88
|
+
newPoolKey.hooks = IHooks(newHook);
|
|
89
|
+
|
|
90
|
+
// Check if new hook supports the new interface first, then fall back to old interface
|
|
91
|
+
if (IERC165(newHook).supportsInterface(type(IUpgradeableDestinationV4HookWithUpdateableFee).interfaceId)) {
|
|
92
|
+
// Use new interface with fee updates
|
|
93
|
+
(uint24 fee, int24 tickSpacing) = IUpgradeableDestinationV4HookWithUpdateableFee(address(newHook)).initializeFromMigrationWithUpdateableFee(
|
|
94
|
+
poolKey,
|
|
95
|
+
coin,
|
|
96
|
+
migratedLiquidityResult.sqrtPriceX96,
|
|
97
|
+
migratedLiquidityResult.burnedPositions,
|
|
98
|
+
additionalData
|
|
99
|
+
);
|
|
100
|
+
newPoolKey.fee = fee;
|
|
101
|
+
newPoolKey.tickSpacing = tickSpacing;
|
|
102
|
+
} else {
|
|
103
|
+
// Fall back to old interface for backward compatibility
|
|
104
|
+
require(IERC165(newHook).supportsInterface(type(IUpgradeableDestinationV4Hook).interfaceId), IUpgradeableV4Hook.InvalidNewHook(newHook));
|
|
105
|
+
IUpgradeableDestinationV4Hook(address(newHook)).initializeFromMigration(
|
|
106
|
+
poolKey,
|
|
107
|
+
coin,
|
|
108
|
+
migratedLiquidityResult.sqrtPriceX96,
|
|
109
|
+
migratedLiquidityResult.burnedPositions,
|
|
110
|
+
additionalData
|
|
111
|
+
);
|
|
112
|
+
// Keep existing fee and tick spacing when using old interface
|
|
113
|
+
newPoolKey.fee = poolKey.fee;
|
|
114
|
+
newPoolKey.tickSpacing = poolKey.tickSpacing;
|
|
115
|
+
}
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
/// @notice Handles the callback from the pool manager. Called by the hook upon unlock.
|
|
@@ -138,6 +156,41 @@ library V4Liquidity {
|
|
|
138
156
|
return abi.encode(result);
|
|
139
157
|
}
|
|
140
158
|
|
|
159
|
+
function dedupePositions(LpPosition[] memory positions) internal pure returns (LpPosition[] memory dedupedPositions) {
|
|
160
|
+
// Upper bound: no more than input length
|
|
161
|
+
dedupedPositions = new LpPosition[](positions.length);
|
|
162
|
+
uint outLen = 0;
|
|
163
|
+
|
|
164
|
+
// O(n²) approach: for each position, check if it already exists in output
|
|
165
|
+
// This is acceptable since position arrays are typically small (< 100 positions)
|
|
166
|
+
|
|
167
|
+
for (uint i = 0; i < positions.length; i++) {
|
|
168
|
+
int24 t0 = positions[i].tickLower;
|
|
169
|
+
int24 t1 = positions[i].tickUpper;
|
|
170
|
+
uint128 v = positions[i].liquidity;
|
|
171
|
+
|
|
172
|
+
bool duplicate = false;
|
|
173
|
+
for (uint j = 0; j < outLen; j++) {
|
|
174
|
+
LpPosition memory dedupedPosition = dedupedPositions[j];
|
|
175
|
+
if (dedupedPosition.tickLower == t0 && dedupedPosition.tickUpper == t1) {
|
|
176
|
+
dedupedPosition.liquidity += v;
|
|
177
|
+
duplicate = true;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!duplicate) {
|
|
183
|
+
dedupedPositions[outLen] = LpPosition({tickLower: t0, tickUpper: t1, liquidity: v});
|
|
184
|
+
outLen++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Shrink to exact size by overwriting length field on the array
|
|
189
|
+
assembly {
|
|
190
|
+
mstore(dedupedPositions, outLen)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
141
194
|
function generatePositionsFromMigratedLiquidity(
|
|
142
195
|
uint160 sqrtPriceX96,
|
|
143
196
|
BurnedPosition[] calldata migratedLiquidity
|
|
@@ -167,6 +220,12 @@ library V4Liquidity {
|
|
|
167
220
|
continue;
|
|
168
221
|
}
|
|
169
222
|
|
|
223
|
+
// skip lps with no fees to collect
|
|
224
|
+
(uint256 feeGrowthInside0DeltaX128, uint256 feeGrowthInside1DeltaX128) = getFeeGrowth(poolManager, poolKey, positions[i]);
|
|
225
|
+
if (feeGrowthInside0DeltaX128 == 0 && feeGrowthInside1DeltaX128 == 0) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
170
229
|
params = ModifyLiquidityParams({
|
|
171
230
|
tickLower: positions[i].tickLower,
|
|
172
231
|
tickUpper: positions[i].tickUpper,
|
|
@@ -192,6 +251,17 @@ library V4Liquidity {
|
|
|
192
251
|
for (uint256 i; i < positions.length; i++) {
|
|
193
252
|
uint128 liquidity = getLiquidity(poolManager, address(this), poolKey, positions[i].tickLower, positions[i].tickUpper);
|
|
194
253
|
|
|
254
|
+
// Skip positions that have no liquidity to avoid CannotUpdateEmptyPosition error
|
|
255
|
+
if (liquidity == 0) {
|
|
256
|
+
burnedPositions[i] = BurnedPosition({
|
|
257
|
+
tickLower: positions[i].tickLower,
|
|
258
|
+
tickUpper: positions[i].tickUpper,
|
|
259
|
+
amount0Received: 0,
|
|
260
|
+
amount1Received: 0
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
195
265
|
ModifyLiquidityParams memory params = ModifyLiquidityParams({
|
|
196
266
|
tickLower: positions[i].tickLower,
|
|
197
267
|
tickUpper: positions[i].tickUpper,
|
|
@@ -221,6 +291,30 @@ library V4Liquidity {
|
|
|
221
291
|
liquidity = StateLibrary.getPositionLiquidity(poolManager, poolKey.toId(), positionId);
|
|
222
292
|
}
|
|
223
293
|
|
|
294
|
+
function getFeeGrowth(
|
|
295
|
+
IPoolManager poolManager,
|
|
296
|
+
PoolKey memory poolKey,
|
|
297
|
+
LpPosition memory position
|
|
298
|
+
) private view returns (uint256 feeGrowthInside0DeltaX128, uint256 feeGrowthInside1DeltaX128) {
|
|
299
|
+
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128) = StateLibrary.getPositionInfo(
|
|
300
|
+
poolManager,
|
|
301
|
+
poolKey.toId(),
|
|
302
|
+
address(this),
|
|
303
|
+
position.tickLower,
|
|
304
|
+
position.tickUpper,
|
|
305
|
+
bytes32(0)
|
|
306
|
+
);
|
|
307
|
+
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = StateLibrary.getFeeGrowthInside(
|
|
308
|
+
poolManager,
|
|
309
|
+
poolKey.toId(),
|
|
310
|
+
position.tickLower,
|
|
311
|
+
position.tickUpper
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
feeGrowthInside0DeltaX128 = feeGrowthInside0X128 - feeGrowthInside0LastX128;
|
|
315
|
+
feeGrowthInside1DeltaX128 = feeGrowthInside1X128 - feeGrowthInside1LastX128;
|
|
316
|
+
}
|
|
317
|
+
|
|
224
318
|
function mintPositions(IPoolManager poolManager, PoolKey memory poolKey, LpPosition[] memory positions) internal returns (int128 amount0, int128 amount1) {
|
|
225
319
|
ModifyLiquidityParams memory params;
|
|
226
320
|
uint256 numPositions = positions.length;
|
|
@@ -9,6 +9,6 @@ import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersion
|
|
|
9
9
|
contract ContractVersionBase is IVersionedContract {
|
|
10
10
|
/// @notice The version of the contract
|
|
11
11
|
function contractVersion() external pure override returns (string memory) {
|
|
12
|
-
return "2.
|
|
12
|
+
return "2.3.0";
|
|
13
13
|
}
|
|
14
14
|
}
|
package/test/Coin.t.sol
CHANGED
|
@@ -37,16 +37,16 @@ contract CoinTest is BaseTest {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function test_supply_constants() public {
|
|
40
|
-
assertEq(CoinConstants.MAX_TOTAL_SUPPLY, CoinConstants.
|
|
40
|
+
assertEq(CoinConstants.MAX_TOTAL_SUPPLY, CoinConstants.CONTENT_COIN_MARKET_SUPPLY + CoinConstants.CONTENT_COIN_INITIAL_CREATOR_SUPPLY);
|
|
41
41
|
|
|
42
42
|
assertEq(CoinConstants.MAX_TOTAL_SUPPLY, 1_000_000_000e18);
|
|
43
|
-
assertEq(CoinConstants.
|
|
44
|
-
assertEq(CoinConstants.
|
|
43
|
+
assertEq(CoinConstants.CONTENT_COIN_MARKET_SUPPLY, 990_000_000e18);
|
|
44
|
+
assertEq(CoinConstants.CONTENT_COIN_INITIAL_CREATOR_SUPPLY, 10_000_000e18);
|
|
45
45
|
|
|
46
46
|
_deployV4Coin();
|
|
47
47
|
assertEq(coinV4.totalSupply(), CoinConstants.MAX_TOTAL_SUPPLY);
|
|
48
|
-
assertEq(coinV4.balanceOf(coinV4.payoutRecipient()), CoinConstants.
|
|
49
|
-
assertApproxEqAbs(coinV4.balanceOf(address(coinV4.poolManager())), CoinConstants.
|
|
48
|
+
assertEq(coinV4.balanceOf(coinV4.payoutRecipient()), CoinConstants.CONTENT_COIN_INITIAL_CREATOR_SUPPLY);
|
|
49
|
+
assertApproxEqAbs(coinV4.balanceOf(address(coinV4.poolManager())), CoinConstants.CONTENT_COIN_MARKET_SUPPLY, 1e18);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function test_initialize_validation() public {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import "forge-std/Test.sol";
|
|
5
|
+
import {CoinRewardsV4} from "../src/libs/CoinRewardsV4.sol";
|
|
6
|
+
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
|
|
7
|
+
|
|
8
|
+
contract CoinRewardsV4Test is Test {
|
|
9
|
+
function test_convertDeltaToPositiveUint128_success_with_valid_positive_values() public pure {
|
|
10
|
+
// Test with small positive value
|
|
11
|
+
int256 smallDelta = 1000;
|
|
12
|
+
uint128 result = CoinRewardsV4.convertDeltaToPositiveUint128(smallDelta);
|
|
13
|
+
assertEq(result, uint128(uint256(smallDelta)));
|
|
14
|
+
|
|
15
|
+
// Test with large but valid positive value (within uint128 range)
|
|
16
|
+
int256 largeDelta = int256(uint256(type(uint128).max));
|
|
17
|
+
uint128 result2 = CoinRewardsV4.convertDeltaToPositiveUint128(largeDelta);
|
|
18
|
+
assertEq(result2, type(uint128).max);
|
|
19
|
+
|
|
20
|
+
// Test with zero
|
|
21
|
+
int256 zeroDelta = 0;
|
|
22
|
+
uint128 result3 = CoinRewardsV4.convertDeltaToPositiveUint128(zeroDelta);
|
|
23
|
+
assertEq(result3, 0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// forge-config: default.allow_internal_expect_revert = true
|
|
27
|
+
function test_convertDeltaToPositiveUint128_edge_cases_and_reverts(int8 difference) public {
|
|
28
|
+
if (difference < 0) {
|
|
29
|
+
vm.expectRevert(SafeCast.SafeCastOverflow.selector);
|
|
30
|
+
}
|
|
31
|
+
CoinRewardsV4.convertDeltaToPositiveUint128(difference);
|
|
32
|
+
}
|
|
33
|
+
}
|
package/test/CoinUniV4.t.sol
CHANGED
|
@@ -16,6 +16,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
16
16
|
import {LpPosition} from "../src/types/LpPosition.sol";
|
|
17
17
|
import {CoinCommon} from "../src/libs/CoinCommon.sol";
|
|
18
18
|
import {IZoraV4CoinHook} from "../src/interfaces/IZoraV4CoinHook.sol";
|
|
19
|
+
import {CoinConstants} from "../src/libs/CoinConstants.sol";
|
|
19
20
|
import {IMsgSender} from "../src/interfaces/IMsgSender.sol";
|
|
20
21
|
import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
|
|
21
22
|
import {toBalanceDelta, BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
|
|
@@ -57,13 +58,13 @@ contract CoinUniV4Test is BaseTest {
|
|
|
57
58
|
/// and then reverting the state after the swap
|
|
58
59
|
function _estimateLpFees(bytes memory commands, bytes[] memory inputs) internal returns (FeeEstimatorHook.FeeEstimatorState memory feeState) {
|
|
59
60
|
uint256 snapshot = vm.snapshot();
|
|
60
|
-
_deployFeeEstimatorHook(
|
|
61
|
+
_deployFeeEstimatorHook(address(hook));
|
|
61
62
|
|
|
62
63
|
// Execute the swap
|
|
63
64
|
uint256 deadline = block.timestamp + 20;
|
|
64
65
|
router.execute(commands, inputs, deadline);
|
|
65
66
|
|
|
66
|
-
feeState = FeeEstimatorHook(payable(address(
|
|
67
|
+
feeState = FeeEstimatorHook(payable(address(hook))).getFeeState();
|
|
67
68
|
|
|
68
69
|
vm.revertToState(snapshot);
|
|
69
70
|
}
|
|
@@ -74,14 +75,14 @@ contract CoinUniV4Test is BaseTest {
|
|
|
74
75
|
bytes[] memory inputs
|
|
75
76
|
) internal returns (BalanceDelta delta, SwapParams memory swapParams, uint160 sqrtPriceX96) {
|
|
76
77
|
uint256 snapshot = vm.snapshot();
|
|
77
|
-
_deployFeeEstimatorHook(
|
|
78
|
+
_deployFeeEstimatorHook(address(hook));
|
|
78
79
|
|
|
79
80
|
// Execute the swap
|
|
80
81
|
uint256 deadline = block.timestamp + 20;
|
|
81
82
|
router.execute(commands, inputs, deadline);
|
|
82
83
|
|
|
83
|
-
delta = FeeEstimatorHook(payable(address(
|
|
84
|
-
swapParams = FeeEstimatorHook(payable(address(
|
|
84
|
+
delta = FeeEstimatorHook(payable(address(hook))).getFeeState().lastDelta;
|
|
85
|
+
swapParams = FeeEstimatorHook(payable(address(hook))).getFeeState().lastSwapParams;
|
|
85
86
|
|
|
86
87
|
sqrtPriceX96 = PoolStateReader.getSqrtPriceX96(coinV4.getPoolKey(), poolManager);
|
|
87
88
|
|
|
@@ -98,15 +99,15 @@ contract CoinUniV4Test is BaseTest {
|
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
function test_deployContentCoin_verifyTotalSupplyAllocation() public {
|
|
103
|
+
address currency = address(mockERC20A);
|
|
104
|
+
_deployV4Coin(currency);
|
|
105
|
+
|
|
106
|
+
// Verify total supply equals maximum allowed
|
|
107
|
+
assertEq(coinV4.totalSupply(), CoinConstants.MAX_TOTAL_SUPPLY, "total supply");
|
|
108
|
+
assertApproxEqAbs(coinV4.balanceOf(address(coinV4.poolManager())), CoinConstants.CONTENT_COIN_MARKET_SUPPLY, 1000, "pool launch supply");
|
|
109
|
+
assertEq(coinV4.balanceOf(coinV4.payoutRecipient()), CoinConstants.CONTENT_COIN_INITIAL_CREATOR_SUPPLY, "creator launch reward");
|
|
110
|
+
}
|
|
110
111
|
|
|
111
112
|
function test_estimateLpFees() public {
|
|
112
113
|
address currency = address(mockERC20A);
|
|
@@ -161,17 +162,18 @@ contract CoinUniV4Test is BaseTest {
|
|
|
161
162
|
FeeEstimatorHook.FeeEstimatorState memory newFeeState = _estimateLpFees(commands, inputs);
|
|
162
163
|
|
|
163
164
|
uint128 newFeeCoin = isCoinToken0 ? newFeeState.fees0 : newFeeState.fees1;
|
|
164
|
-
uint128 newFeeCurrency = isCoinToken0 ? newFeeState.fees1 : newFeeState.fees0;
|
|
165
|
+
// uint128 newFeeCurrency = isCoinToken0 ? newFeeState.fees1 : newFeeState.fees0;
|
|
165
166
|
|
|
166
167
|
assertGt(newFeeCoin, 0, "fee coin on second swap should be greater than 0");
|
|
167
168
|
// assertGt(newFeeCurrency, 0, "fee currency on second swap should be greater than 0"); // TODO confirm what this should be -- prev was assertEq(0) and test passed but error message was asserting greater than 0
|
|
168
169
|
assertGt(newFeeState.afterSwapCurrencyAmount, 0, "after swap fee currency on second swap should be greater than 0");
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
|
|
172
|
-
uint256 public constant
|
|
173
|
-
uint256 public constant
|
|
174
|
-
uint256 public constant
|
|
172
|
+
// Use the same constants as CoinRewardsV4.sol for consistency
|
|
173
|
+
uint256 public constant CREATOR_REWARD_BPS = 6250; // 62.5% of market rewards (0.50% of total 1% fee)
|
|
174
|
+
uint256 public constant CREATE_REFERRAL_REWARD_BPS = 2500; // 25% of market rewards (0.20% of total 1% fee)
|
|
175
|
+
uint256 public constant TRADE_REFERRAL_REWARD_BPS = 500; // 5% of market rewards (0.04% of total 1% fee)
|
|
176
|
+
uint256 public constant DOPPLER_REWARD_BPS = 125; // 1.25% of market rewards (0.01% of total 1% fee)
|
|
175
177
|
|
|
176
178
|
struct Rewards {
|
|
177
179
|
uint256 backing;
|
|
@@ -228,8 +230,8 @@ contract CoinUniV4Test is BaseTest {
|
|
|
228
230
|
return rewards;
|
|
229
231
|
}
|
|
230
232
|
|
|
231
|
-
function test_distributesMarketRewards(
|
|
232
|
-
|
|
233
|
+
function test_distributesMarketRewards(bool hasCreateReferral, bool hasTradeReferral) public {
|
|
234
|
+
uint64 amountIn = 2 ether;
|
|
233
235
|
address currency = address(mockERC20A);
|
|
234
236
|
address createReferral = hasCreateReferral ? makeAddr("createReferral") : address(0);
|
|
235
237
|
address tradeReferral = hasTradeReferral ? makeAddr("tradeReferral") : address(0);
|
|
@@ -282,15 +284,15 @@ contract CoinUniV4Test is BaseTest {
|
|
|
282
284
|
}
|
|
283
285
|
assertEq(coinV4.balanceOf(coinV4.protocolRewardRecipient()), 0, "protocol reward coin");
|
|
284
286
|
|
|
285
|
-
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.payoutRecipient()), totalRewards.backing,
|
|
286
|
-
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.dopplerFeeRecipient()), totalRewards.doppler,
|
|
287
|
+
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.payoutRecipient()), totalRewards.backing, 5000, "backing reward currency");
|
|
288
|
+
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.dopplerFeeRecipient()), totalRewards.doppler, 5000, "doppler reward currency");
|
|
287
289
|
if (hasCreateReferral) {
|
|
288
|
-
assertApproxEqAbs(mockERC20A.balanceOf(createReferral), totalRewards.createReferral,
|
|
290
|
+
assertApproxEqAbs(mockERC20A.balanceOf(createReferral), totalRewards.createReferral, 5000, "create referral reward currency");
|
|
289
291
|
}
|
|
290
292
|
if (hasTradeReferral) {
|
|
291
|
-
assertApproxEqAbs(mockERC20A.balanceOf(tradeReferral), totalRewards.tradeReferral,
|
|
293
|
+
assertApproxEqAbs(mockERC20A.balanceOf(tradeReferral), totalRewards.tradeReferral, 5000, "trade referral reward currency");
|
|
292
294
|
}
|
|
293
|
-
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.protocolRewardRecipient()), totalRewards.protocol,
|
|
295
|
+
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.protocolRewardRecipient()), totalRewards.protocol, 5000, "protocol reward currency");
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
function test_distributesMarketRewardsInEth() public {
|
|
@@ -349,8 +351,8 @@ contract CoinUniV4Test is BaseTest {
|
|
|
349
351
|
assertGt(trader.balance, 0, "trader should have received ETH back");
|
|
350
352
|
}
|
|
351
353
|
|
|
352
|
-
function test_swap_emitsCoinMarketRewardsV4(
|
|
353
|
-
|
|
354
|
+
function test_swap_emitsCoinMarketRewardsV4() public {
|
|
355
|
+
uint64 amountIn = 1 ether;
|
|
354
356
|
address currency = address(mockERC20A);
|
|
355
357
|
address createReferral = makeAddr("createReferral");
|
|
356
358
|
address tradeReferral = makeAddr("tradeReferral");
|
|
@@ -588,7 +590,7 @@ contract CoinUniV4Test is BaseTest {
|
|
|
588
590
|
currency1: isCoinToken0 ? Currency.wrap(address(notACoin)) : Currency.wrap(address(coinV4)),
|
|
589
591
|
fee: 3000,
|
|
590
592
|
tickSpacing: 60,
|
|
591
|
-
hooks: IHooks(address(
|
|
593
|
+
hooks: IHooks(address(hook))
|
|
592
594
|
});
|
|
593
595
|
|
|
594
596
|
// We need to prank the call to come from the non-coin contract
|
|
@@ -598,7 +600,7 @@ contract CoinUniV4Test is BaseTest {
|
|
|
598
600
|
vm.expectRevert(
|
|
599
601
|
abi.encodeWithSelector(
|
|
600
602
|
CustomRevert.WrappedError.selector,
|
|
601
|
-
address(
|
|
603
|
+
address(hook),
|
|
602
604
|
IHooks.afterInitialize.selector,
|
|
603
605
|
abi.encodeWithSelector(IZoraV4CoinHook.NotACoin.selector, address(notACoin)),
|
|
604
606
|
abi.encodeWithSelector(Hooks.HookCallFailed.selector)
|