@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.
Files changed (107) hide show
  1. package/.turbo/turbo-build$colon$js.log +152 -0
  2. package/CHANGELOG.md +93 -0
  3. package/README.md +4 -0
  4. package/abis/BaseCoin.json +26 -5
  5. package/abis/BaseTest.json +2 -7
  6. package/abis/ContentCoin.json +26 -5
  7. package/abis/CreatorCoin.json +30 -9
  8. package/abis/FeeEstimatorHook.json +94 -6
  9. package/abis/ICoin.json +26 -0
  10. package/abis/ICoinV3.json +26 -0
  11. package/abis/ICreatorCoin.json +39 -0
  12. package/abis/IERC721.json +36 -36
  13. package/abis/IHasCoinType.json +15 -0
  14. package/abis/IHasTotalSupplyForPositions.json +15 -0
  15. package/abis/{LiquidityMigrationReceiver.json → IUpgradeableDestinationV4HookWithUpdateableFee.json} +10 -18
  16. package/abis/IZoraFactory.json +121 -0
  17. package/abis/IZoraHookRegistry.json +188 -0
  18. package/abis/VmContractHelper226.json +233 -0
  19. package/abis/ZoraFactoryImpl.json +101 -6
  20. package/abis/ZoraHookRegistry.json +375 -0
  21. package/abis/{CreatorCoinHook.json → ZoraV4CoinHook.json} +95 -2
  22. package/addresses/8453.json +6 -5
  23. package/audits/report-cantinacode-zora-0827.pdf +3498 -4
  24. package/dist/index.cjs +93 -13
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +93 -13
  27. package/dist/index.js.map +1 -1
  28. package/dist/wagmiGenerated.d.ts +144 -22
  29. package/dist/wagmiGenerated.d.ts.map +1 -1
  30. package/foundry.toml +4 -1
  31. package/package/wagmiGenerated.ts +93 -13
  32. package/package.json +6 -4
  33. package/script/PrintRegisterUpgradePath.s.sol +0 -7
  34. package/script/TestBackingCoinSwap.s.sol +0 -3
  35. package/script/TestV4Swap.s.sol +0 -3
  36. package/script/UpgradeFactoryImpl.s.sol +1 -1
  37. package/src/BaseCoin.sol +19 -24
  38. package/src/ContentCoin.sol +11 -2
  39. package/src/CreatorCoin.sol +34 -15
  40. package/src/ZoraFactoryImpl.sol +163 -92
  41. package/src/deployment/CoinsDeployerBase.sol +24 -58
  42. package/src/hook-registry/ZoraHookRegistry.sol +97 -0
  43. package/src/hooks/{BaseZoraV4CoinHook.sol → ZoraV4CoinHook.sol} +77 -15
  44. package/src/interfaces/ICoin.sol +19 -1
  45. package/src/interfaces/ICreatorCoin.sol +4 -0
  46. package/src/interfaces/IUpgradeableV4Hook.sol +18 -0
  47. package/src/interfaces/IZoraFactory.sol +51 -10
  48. package/src/interfaces/IZoraHookRegistry.sol +47 -0
  49. package/src/libs/CoinConstants.sol +43 -32
  50. package/src/libs/CoinDopplerMultiCurve.sol +11 -11
  51. package/src/libs/CoinRewardsV4.sol +68 -37
  52. package/src/libs/CoinSetup.sol +2 -9
  53. package/src/libs/DopplerMath.sol +2 -2
  54. package/src/libs/HooksDeployment.sol +13 -65
  55. package/src/libs/V4Liquidity.sol +109 -15
  56. package/src/version/ContractVersionBase.sol +1 -1
  57. package/test/Coin.t.sol +5 -5
  58. package/test/CoinRewardsV4.t.sol +33 -0
  59. package/test/CoinUniV4.t.sol +32 -30
  60. package/test/ContentCoinRewards.t.sol +363 -0
  61. package/test/CreatorCoin.t.sol +53 -29
  62. package/test/CreatorCoinRewards.t.sol +375 -0
  63. package/test/DeploymentHooks.t.sol +64 -12
  64. package/test/Factory.t.sol +24 -7
  65. package/test/HooksDeployment.t.sol +4 -4
  66. package/test/LiquidityMigration.t.sol +149 -16
  67. package/test/Upgrades.t.sol +44 -48
  68. package/test/V4Liquidity.t.sol +178 -0
  69. package/test/ZoraHookRegistry.t.sol +266 -0
  70. package/test/utils/BaseTest.sol +25 -43
  71. package/test/utils/FeeEstimatorHook.sol +4 -6
  72. package/test/utils/RewardTestHelpers.sol +106 -0
  73. package/.turbo/turbo-build.log +0 -199
  74. package/abis/AutoSwapperTest.json +0 -618
  75. package/abis/BadImpl.json +0 -15
  76. package/abis/BaseZoraV4CoinHook.json +0 -1664
  77. package/abis/CoinConstants.json +0 -158
  78. package/abis/CoinRewardsV4.json +0 -67
  79. package/abis/CoinTest.json +0 -819
  80. package/abis/CoinUniV4Test.json +0 -1128
  81. package/abis/ContentCoinHook.json +0 -1733
  82. package/abis/CreatorCoinTest.json +0 -887
  83. package/abis/Deploy.json +0 -9
  84. package/abis/DeployHooks.json +0 -9
  85. package/abis/DeployScript.json +0 -35
  86. package/abis/DeployedCoinVersionLookupTest.json +0 -740
  87. package/abis/DifferentNamespaceVersionLookup.json +0 -39
  88. package/abis/FactoryTest.json +0 -748
  89. package/abis/FakeHookNoInterface.json +0 -21
  90. package/abis/GenerateDeterministicParams.json +0 -9
  91. package/abis/HooksDeploymentTest.json +0 -645
  92. package/abis/HooksTest.json +0 -709
  93. package/abis/InvalidLiquidityMigrationReceiver.json +0 -21
  94. package/abis/LiquidityMigrationTest.json +0 -889
  95. package/abis/MockBadFactory.json +0 -15
  96. package/abis/MultiOwnableTest.json +0 -766
  97. package/abis/PrintUpgradeCommand.json +0 -9
  98. package/abis/TestDeployedCoinVersionLookupImplementation.json +0 -39
  99. package/abis/TestV4Swap.json +0 -9
  100. package/abis/UpgradeFactoryImpl.json +0 -9
  101. package/abis/UpgradeHooks.json +0 -35
  102. package/abis/UpgradesTest.json +0 -723
  103. package/src/hooks/ContentCoinHook.sol +0 -27
  104. package/src/hooks/CreatorCoinHook.sol +0 -27
  105. package/src/libs/CreatorCoinConstants.sol +0 -16
  106. package/src/libs/CreatorCoinRewards.sol +0 -34
  107. package/src/libs/MarketConstants.sol +0 -15
@@ -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
- // Check if new hook supports the upgradeable destination interface
86
- require(IERC165(newHook).supportsInterface(type(IUpgradeableDestinationV4Hook).interfaceId), IUpgradeableV4Hook.InvalidNewHook(newHook));
87
- // Initialize new hook with migration data
88
- IUpgradeableDestinationV4Hook(address(newHook)).initializeFromMigration(
89
- poolKey,
90
- coin,
91
- migratedLiquidityResult.sqrtPriceX96,
92
- migratedLiquidityResult.burnedPositions,
93
- additionalData
94
- );
95
-
96
- return
97
- PoolKey({currency0: poolKey.currency0, currency1: poolKey.currency1, fee: poolKey.fee, tickSpacing: poolKey.tickSpacing, hooks: IHooks(newHook)});
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.1.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.POOL_LAUNCH_SUPPLY + CoinConstants.CREATOR_LAUNCH_REWARD);
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.POOL_LAUNCH_SUPPLY, 990_000_000e18);
44
- assertEq(CoinConstants.CREATOR_LAUNCH_REWARD, 10_000_000e18);
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.CREATOR_LAUNCH_REWARD);
49
- assertApproxEqAbs(coinV4.balanceOf(address(coinV4.poolManager())), CoinConstants.POOL_LAUNCH_SUPPLY, 1e18);
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
+ }
@@ -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(CoinConstants.POOL_LAUNCH_SUPPLY, address(contentCoinHook));
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(contentCoinHook))).getFeeState();
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(CoinConstants.POOL_LAUNCH_SUPPLY, address(contentCoinHook));
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(contentCoinHook))).getFeeState().lastDelta;
84
- swapParams = FeeEstimatorHook(payable(address(contentCoinHook))).getFeeState().lastSwapParams;
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
- // function test_setupZeroAddressForHooks() public {
102
- // vm.expectRevert(ICoin.AddressZero.selector);
103
- // new ContentCoin({
104
- // protocolRewardRecipient_: address(0x1234),
105
- // protocolRewards_: address(0x1234),
106
- // poolManager_: IPoolManager(address(0x1234)),
107
- // airlock_: address(0x1234)
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
- uint256 public constant CREATOR_REWARD_BPS = 5000;
172
- uint256 public constant CREATE_REFERRAL_REWARD_BPS = 1500;
173
- uint256 public constant TRADE_REFERRAL_REWARD_BPS = 1500;
174
- uint256 public constant DOPPLER_REWARD_BPS = 500;
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(uint64 amountIn, bool hasCreateReferral, bool hasTradeReferral) public {
232
- vm.assume(amountIn > 0.00001 ether);
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, 10, "backing reward currency");
286
- assertApproxEqAbs(mockERC20A.balanceOf(coinV4.dopplerFeeRecipient()), totalRewards.doppler, 10, "doppler reward currency");
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, 10, "create referral reward currency");
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, 10, "trade referral reward currency");
293
+ assertApproxEqAbs(mockERC20A.balanceOf(tradeReferral), totalRewards.tradeReferral, 5000, "trade referral reward currency");
292
294
  }
293
- assertApproxEqAbs(mockERC20A.balanceOf(coinV4.protocolRewardRecipient()), totalRewards.protocol, 10, "protocol reward currency");
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(uint64 amountIn) public {
353
- vm.assume(amountIn > 0.00001 ether);
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(contentCoinHook))
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(contentCoinHook),
603
+ address(hook),
602
604
  IHooks.afterInitialize.selector,
603
605
  abi.encodeWithSelector(IZoraV4CoinHook.NotACoin.selector, address(notACoin)),
604
606
  abi.encodeWithSelector(Hooks.HookCallFailed.selector)