@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
@@ -0,0 +1,266 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.28;
3
+
4
+ import "forge-std/Test.sol";
5
+
6
+ import {IZoraHookRegistry} from "../src/interfaces/IZoraHookRegistry.sol";
7
+ import {ZoraHookRegistry} from "../src/hook-registry/ZoraHookRegistry.sol";
8
+
9
+ contract MockHook {
10
+ function contractVersion() public pure returns (string memory) {
11
+ return "0.0.0";
12
+ }
13
+ }
14
+
15
+ contract ZoraHookRegistryTest is Test {
16
+ uint256 internal forkId;
17
+ address internal owner;
18
+
19
+ ZoraHookRegistry internal zoraHookRegistry;
20
+ MockHook internal mockHook;
21
+
22
+ function setUp() public {
23
+ forkId = vm.createSelectFork("base", 34509280);
24
+ owner = makeAddr("owner");
25
+
26
+ address[] memory initialOwners = new address[](1);
27
+ initialOwners[0] = owner;
28
+
29
+ zoraHookRegistry = new ZoraHookRegistry();
30
+ zoraHookRegistry.initialize(initialOwners);
31
+
32
+ mockHook = new MockHook();
33
+ }
34
+
35
+ function test_register_hooks() public {
36
+ address[] memory hooks = new address[](1);
37
+ string[] memory tags = new string[](1);
38
+
39
+ hooks[0] = address(mockHook);
40
+ tags[0] = "ZoraHook";
41
+
42
+ vm.prank(owner);
43
+ zoraHookRegistry.registerHooks(hooks, tags);
44
+
45
+ assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), true);
46
+
47
+ address[] memory addrs = zoraHookRegistry.getHookAddresses();
48
+ assertEq(addrs.length, 1);
49
+ assertEq(addrs[0], hooks[0]);
50
+
51
+ assertEq(zoraHookRegistry.getHookTag(hooks[0]), tags[0]);
52
+
53
+ IZoraHookRegistry.ZoraHook[] memory zoraHooks = zoraHookRegistry.getHooks();
54
+ assertEq(zoraHooks.length, 1);
55
+ assertEq(zoraHooks[0].hook, hooks[0]);
56
+ assertEq(zoraHooks[0].tag, "ZoraHook");
57
+ assertEq(zoraHooks[0].version, "0.0.0");
58
+ }
59
+
60
+ function test_revert_register_hooks_only_owner() public {
61
+ address[] memory hooks = new address[](1);
62
+ string[] memory tags = new string[](1);
63
+
64
+ hooks[0] = address(mockHook);
65
+ tags[0] = "ZoraHook";
66
+
67
+ vm.expectRevert(abi.encodeWithSignature("OnlyOwner()"));
68
+ vm.prank(makeAddr("notOwner"));
69
+ zoraHookRegistry.registerHooks(hooks, tags);
70
+ }
71
+
72
+ function test_revert_register_hooks_array_length_mismatch() public {
73
+ address[] memory hooks = new address[](2);
74
+ string[] memory tags = new string[](1);
75
+
76
+ hooks[0] = address(mockHook);
77
+ hooks[1] = makeAddr("anotherHook");
78
+ tags[0] = "Tag0";
79
+
80
+ vm.prank(owner);
81
+ vm.expectRevert(abi.encodeWithSignature("ArrayLengthMismatch()"));
82
+ zoraHookRegistry.registerHooks(hooks, tags);
83
+ }
84
+
85
+ function test_register_duplicate_is_idempotent() public {
86
+ address[] memory hooks = new address[](1);
87
+ string[] memory tags = new string[](1);
88
+
89
+ hooks[0] = address(mockHook);
90
+ tags[0] = "ZoraHook";
91
+
92
+ vm.startPrank(owner);
93
+ zoraHookRegistry.registerHooks(hooks, tags);
94
+ zoraHookRegistry.registerHooks(hooks, tags);
95
+ vm.stopPrank();
96
+
97
+ assertEq(zoraHookRegistry.getHookAddresses().length, 1);
98
+ assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), true);
99
+ }
100
+
101
+ function test_remove_hooks() public {
102
+ address[] memory hooks = new address[](1);
103
+ string[] memory tags = new string[](1);
104
+
105
+ hooks[0] = address(mockHook);
106
+ tags[0] = "ZoraHook";
107
+
108
+ vm.prank(owner);
109
+ zoraHookRegistry.registerHooks(hooks, tags);
110
+
111
+ vm.prank(owner);
112
+ zoraHookRegistry.removeHooks(hooks);
113
+
114
+ assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), false);
115
+ assertEq(zoraHookRegistry.getHookAddresses().length, 0);
116
+ assertEq(zoraHookRegistry.getHookTag(hooks[0]), "");
117
+ }
118
+
119
+ function test_revert_remove_hooks_only_owner() public {
120
+ address[] memory hooks = new address[](1);
121
+ string[] memory tags = new string[](1);
122
+
123
+ hooks[0] = address(mockHook);
124
+ tags[0] = "ZoraHook";
125
+
126
+ vm.prank(owner);
127
+ zoraHookRegistry.registerHooks(hooks, tags);
128
+
129
+ vm.expectRevert(abi.encodeWithSignature("OnlyOwner()"));
130
+ vm.prank(makeAddr("notOwner"));
131
+ zoraHookRegistry.removeHooks(hooks);
132
+ }
133
+
134
+ function test_remove_unregistered_noop() public {
135
+ address[] memory hooks = new address[](1);
136
+ hooks[0] = makeAddr("unregistered");
137
+
138
+ uint256 beforeLen = zoraHookRegistry.getHookAddresses().length;
139
+
140
+ vm.prank(owner);
141
+ zoraHookRegistry.removeHooks(hooks);
142
+
143
+ assertEq(zoraHookRegistry.getHookAddresses().length, beforeLen);
144
+ assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), false);
145
+ }
146
+
147
+ function test_emit_register_hooks_event() public {
148
+ address[] memory hooks = new address[](1);
149
+ string[] memory tags = new string[](1);
150
+
151
+ hooks[0] = address(mockHook);
152
+ tags[0] = "ZoraHook";
153
+
154
+ vm.expectEmit(true, true, true, false, address(zoraHookRegistry));
155
+ emit IZoraHookRegistry.ZoraHookRegistered(hooks[0], tags[0], "0.0.0");
156
+
157
+ vm.prank(owner);
158
+ zoraHookRegistry.registerHooks(hooks, tags);
159
+ }
160
+
161
+ function test_emit_remove_hooks_event() public {
162
+ address[] memory hooks = new address[](1);
163
+ string[] memory tags = new string[](1);
164
+
165
+ hooks[0] = address(mockHook);
166
+ tags[0] = "ZoraHook";
167
+
168
+ vm.prank(owner);
169
+ zoraHookRegistry.registerHooks(hooks, tags);
170
+
171
+ vm.expectEmit(true, true, true, false, address(zoraHookRegistry));
172
+ emit IZoraHookRegistry.ZoraHookRemoved(hooks[0], tags[0], "0.0.0");
173
+
174
+ vm.prank(owner);
175
+ zoraHookRegistry.removeHooks(hooks);
176
+ }
177
+
178
+ function test_hook_version() public {
179
+ address[] memory hooks = new address[](1);
180
+ string[] memory tags = new string[](1);
181
+
182
+ hooks[0] = 0x81542dC43Aff247eff4a0eceFC286A2973aE1040;
183
+ tags[0] = "CONTENT";
184
+
185
+ vm.prank(owner);
186
+ zoraHookRegistry.registerHooks(hooks, tags);
187
+
188
+ assertEq(zoraHookRegistry.getHookVersion(hooks[0]), "1.1.1");
189
+ }
190
+
191
+ function test_hook_version_not_found() public {
192
+ address[] memory hooks = new address[](1);
193
+ string[] memory tags = new string[](1);
194
+
195
+ hooks[0] = 0xA1eBdD5cA6470Bbd67114331387f2dDa7bfad040;
196
+ tags[0] = "CONTENT";
197
+
198
+ vm.prank(owner);
199
+ zoraHookRegistry.registerHooks(hooks, tags);
200
+
201
+ assertEq(zoraHookRegistry.getHookVersion(hooks[0]), "");
202
+ }
203
+
204
+ function test_is_registered_hook_false_when_never_registered() public view {
205
+ assertEq(zoraHookRegistry.isRegisteredHook(address(this)), false);
206
+ }
207
+
208
+ function test_get_hook_addresses_multiple_and_remove_middle() public {
209
+ address a = address(mockHook);
210
+ address b = address(new MockHook());
211
+ address c = address(new MockHook());
212
+
213
+ address[] memory hooks = new address[](3);
214
+ string[] memory tags = new string[](3);
215
+ hooks[0] = a;
216
+ tags[0] = "A";
217
+ hooks[1] = b;
218
+ tags[1] = "B";
219
+ hooks[2] = c;
220
+ tags[2] = "C";
221
+
222
+ vm.prank(owner);
223
+ zoraHookRegistry.registerHooks(hooks, tags);
224
+
225
+ address[] memory removeB = new address[](1);
226
+ removeB[0] = b;
227
+
228
+ vm.prank(owner);
229
+ zoraHookRegistry.removeHooks(removeB);
230
+
231
+ address[] memory addrs = zoraHookRegistry.getHookAddresses();
232
+ assertEq(addrs.length, 2);
233
+
234
+ bool hasA;
235
+ bool hasC;
236
+ for (uint256 i = 0; i < addrs.length; i++) {
237
+ if (addrs[i] == a) hasA = true;
238
+ if (addrs[i] == c) hasC = true;
239
+ assertTrue(addrs[i] != b);
240
+ }
241
+ assertTrue(hasA);
242
+ assertTrue(hasC);
243
+
244
+ assertEq(zoraHookRegistry.getHookTag(a), "A");
245
+ assertEq(zoraHookRegistry.getHookTag(b), "");
246
+ assertEq(zoraHookRegistry.getHookTag(c), "C");
247
+
248
+ IZoraHookRegistry.ZoraHook[] memory full = zoraHookRegistry.getHooks();
249
+ assertEq(full.length, 2);
250
+
251
+ bool okA;
252
+ bool okC;
253
+ for (uint256 i = 0; i < full.length; i++) {
254
+ if (full[i].hook == a) {
255
+ assertEq(full[i].tag, "A");
256
+ assertEq(full[i].version, "0.0.0");
257
+ okA = true;
258
+ } else if (full[i].hook == c) {
259
+ assertEq(full[i].tag, "C");
260
+ assertEq(full[i].version, "0.0.0");
261
+ okC = true;
262
+ }
263
+ }
264
+ assertTrue(okA && okC);
265
+ }
266
+ }
@@ -24,10 +24,9 @@ import {IUniswapV3Factory} from "../../src/interfaces/IUniswapV3Factory.sol";
24
24
  import {IUniswapV3Pool} from "../../src/interfaces/IUniswapV3Pool.sol";
25
25
  import {IProtocolRewards} from "../../src/interfaces/IProtocolRewards.sol";
26
26
  import {ProtocolRewards} from "../utils/ProtocolRewards.sol";
27
- import {MarketConstants} from "../../src/libs/MarketConstants.sol";
28
27
  import {CoinConfigurationVersions} from "../../src/libs/CoinConfigurationVersions.sol";
29
28
  import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
30
- import {ContentCoinHook} from "../../src/hooks/ContentCoinHook.sol";
29
+ import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
31
30
  import {HooksDeployment} from "../../src/libs/HooksDeployment.sol";
32
31
  import {CoinConstants} from "../../src/libs/CoinConstants.sol";
33
32
  import {ProxyShim} from "./ProxyShim.sol";
@@ -41,15 +40,19 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
41
40
  import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
42
41
  import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
43
42
  import {CreatorCoin} from "../../src/CreatorCoin.sol";
44
- import {CreatorCoinHook} from "../../src/hooks/CreatorCoinHook.sol";
45
43
  import {ContractAddresses} from "./ContractAddresses.sol";
46
44
  import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
47
45
  import {HookUpgradeGate} from "../../src/hooks/HookUpgradeGate.sol";
46
+ import {ZoraHookRegistry} from "../../src/hook-registry/ZoraHookRegistry.sol";
48
47
 
49
48
  contract BaseTest is Test, ContractAddresses {
50
49
  using stdStorage for StdStorage;
51
50
 
52
51
  int24 internal constant USDC_TICK_LOWER = 57200;
52
+ int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER;
53
+ int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
54
+ uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
55
+ uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
53
56
 
54
57
  struct Users {
55
58
  address factoryOwner;
@@ -85,13 +88,9 @@ contract BaseTest is Test, ContractAddresses {
85
88
  CreatorCoin internal creatorCoinImpl;
86
89
  ZoraFactoryImpl internal factoryImpl;
87
90
  IZoraFactory internal factory;
88
- ContentCoinHook internal contentCoinHook;
89
- CreatorCoinHook internal creatorCoinHook;
91
+ ZoraV4CoinHook internal hook;
90
92
  HookUpgradeGate internal hookUpgradeGate;
91
- int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER;
92
- int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
93
- uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
94
- uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
93
+ ZoraHookRegistry internal zoraHookRegistry;
95
94
 
96
95
  function _defaultPoolConfig(address currency) internal pure returns (bytes memory) {
97
96
  return CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(currency);
@@ -203,28 +202,14 @@ contract BaseTest is Test, ContractAddresses {
203
202
  vm.stopPrank();
204
203
  }
205
204
 
206
- function _deployFeeEstimatorHook(uint256 initialSupplyForPositions, address hooks) internal {
207
- deployCodeTo("FeeEstimatorHook.sol", abi.encode(V4_POOL_MANAGER, address(factory), hookUpgradeGate, initialSupplyForPositions), hooks);
205
+ function _deployFeeEstimatorHook(address hooks) internal {
206
+ deployCodeTo("FeeEstimatorHook.sol", abi.encode(V4_POOL_MANAGER, address(factory), hookUpgradeGate), hooks);
208
207
  }
209
208
 
210
- function getSalts(address[] memory trustedMessageSenders) public returns (bytes32 contentCoinSalt, bytes32 creatorCoinSalt) {
209
+ function getSalt(address[] memory trustedMessageSenders) public returns (bytes32 hookSalt) {
211
210
  address deployer = address(this);
212
211
 
213
- (, contentCoinSalt) = HooksDeployment.mineForContentCoinSalt(
214
- deployer,
215
- V4_POOL_MANAGER,
216
- address(factory),
217
- trustedMessageSenders,
218
- address(hookUpgradeGate)
219
- );
220
-
221
- (, creatorCoinSalt) = HooksDeployment.mineForCreatorCoinSalt(
222
- deployer,
223
- V4_POOL_MANAGER,
224
- address(factory),
225
- trustedMessageSenders,
226
- address(hookUpgradeGate)
227
- );
212
+ (, hookSalt) = HooksDeployment.mineForCoinSalt(deployer, V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate));
228
213
  }
229
214
 
230
215
  function _deployHooks() internal {
@@ -232,24 +217,14 @@ contract BaseTest is Test, ContractAddresses {
232
217
  trustedMessageSenders[0] = UNIVERSAL_ROUTER;
233
218
  trustedMessageSenders[1] = V4_POSITION_MANAGER;
234
219
 
235
- (bytes32 contentCoinSalt, bytes32 creatorCoinSalt) = getSalts(trustedMessageSenders);
220
+ bytes32 hookSalt = getSalt(trustedMessageSenders);
236
221
 
237
- contentCoinHook = ContentCoinHook(
238
- payable(
239
- address(
240
- HooksDeployment.deployHookWithSalt(
241
- HooksDeployment.contentCoinCreationCode(V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate)),
242
- contentCoinSalt
243
- )
244
- )
245
- )
246
- );
247
- creatorCoinHook = CreatorCoinHook(
222
+ hook = ZoraV4CoinHook(
248
223
  payable(
249
224
  address(
250
225
  HooksDeployment.deployHookWithSalt(
251
- HooksDeployment.creatorCoinHookCreationCode(V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate)),
252
- creatorCoinSalt
226
+ HooksDeployment.makeHookCreationCode(V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate)),
227
+ hookSalt
253
228
  )
254
229
  )
255
230
  )
@@ -291,13 +266,20 @@ contract BaseTest is Test, ContractAddresses {
291
266
 
292
267
  hookUpgradeGate = new HookUpgradeGate(users.factoryOwner);
293
268
 
269
+ zoraHookRegistry = new ZoraHookRegistry();
270
+
271
+ address[] memory initialOwners = new address[](2);
272
+ initialOwners[0] = users.factoryOwner;
273
+ initialOwners[1] = address(factory);
274
+ zoraHookRegistry.initialize(initialOwners);
275
+
294
276
  _deployHooks();
295
277
 
296
278
  coinV4Impl = new ContentCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
297
279
 
298
280
  creatorCoinImpl = new CreatorCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
299
281
 
300
- factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook));
282
+ factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
301
283
  UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
302
284
  factory = IZoraFactory(address(factory));
303
285
 
@@ -317,7 +299,7 @@ contract BaseTest is Test, ContractAddresses {
317
299
  vm.label(address(V4_QUOTER), "V4_QUOTER");
318
300
  vm.label(address(V4_PERMIT2), "V4_PERMIT2");
319
301
  vm.label(address(UNIVERSAL_ROUTER), "UNIVERSAL_ROUTER");
320
- vm.label(address(creatorCoinHook), "CREATOR_COIN_HOOK");
302
+ vm.label(address(hook), "HOOK");
321
303
  }
322
304
 
323
305
  struct TradeRewards {
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.13;
3
3
 
4
- import {ContentCoinHook} from "../../src/hooks/ContentCoinHook.sol";
4
+ import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
5
5
  import {IPoolManager, PoolKey} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
6
6
  import {IDeployedCoinVersionLookup} from "../../src/interfaces/IDeployedCoinVersionLookup.sol";
7
7
  import {IHasRewardsRecipients} from "../../src/interfaces/IHasRewardsRecipients.sol";
@@ -15,11 +15,10 @@ import {ICoin, IHasSwapPath} from "../../src/interfaces/ICoin.sol";
15
15
  import {UniV4SwapToCurrency} from "../../src/libs/UniV4SwapToCurrency.sol";
16
16
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
17
17
  import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
18
- import {BaseZoraV4CoinHook} from "../../src/hooks/BaseZoraV4CoinHook.sol";
19
18
  import {IHooksUpgradeGate} from "../../src/interfaces/IHooksUpgradeGate.sol";
20
19
 
21
20
  /// @dev Test util - meant to be able to etched where a normal zora hook is, to gather the fees from swaps but not distribute them
22
- contract FeeEstimatorHook is BaseZoraV4CoinHook {
21
+ contract FeeEstimatorHook is ZoraV4CoinHook {
23
22
  struct FeeEstimatorState {
24
23
  uint128 fees0;
25
24
  uint128 fees1;
@@ -34,9 +33,8 @@ contract FeeEstimatorHook is BaseZoraV4CoinHook {
34
33
  constructor(
35
34
  IPoolManager _poolManager,
36
35
  IDeployedCoinVersionLookup _coinVersionLookup,
37
- IHooksUpgradeGate upgradeGate,
38
- uint256 initialSupplyForPositions
39
- ) BaseZoraV4CoinHook(_poolManager, _coinVersionLookup, new address[](0), upgradeGate, initialSupplyForPositions) {}
36
+ IHooksUpgradeGate upgradeGate
37
+ ) ZoraV4CoinHook(_poolManager, _coinVersionLookup, new address[](0), upgradeGate) {}
40
38
 
41
39
  FeeEstimatorState public feeState;
42
40
 
@@ -0,0 +1,106 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
5
+ import {FeeEstimatorHook} from "./FeeEstimatorHook.sol";
6
+ import {CoinConstants} from "../../src/libs/CoinConstants.sol";
7
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8
+ import {Vm} from "forge-std/Vm.sol";
9
+
10
+ // Shared struct to track balance changes for reward recipients
11
+ struct RewardBalances {
12
+ uint256 creator;
13
+ uint256 platformReferrer;
14
+ uint256 tradeReferrer;
15
+ uint256 protocol;
16
+ uint256 doppler;
17
+ }
18
+
19
+ library RewardTestHelpers {
20
+ Vm private constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
21
+
22
+ // Helper function to calculate reward based on BPS
23
+ function calculateReward(uint256 total, uint256 bps) internal pure returns (uint256) {
24
+ return (total * bps) / 10_000;
25
+ }
26
+
27
+ function calculateExpectedRewards(
28
+ uint256 marketRewards,
29
+ bool hasPlatformReferrer,
30
+ bool hasTradeReferrer
31
+ ) internal pure returns (RewardBalances memory rewards) {
32
+ rewards.creator = calculateReward(marketRewards, CoinConstants.CREATOR_REWARD_BPS);
33
+ rewards.platformReferrer = hasPlatformReferrer ? calculateReward(marketRewards, CoinConstants.CREATE_REFERRAL_REWARD_BPS) : 0;
34
+ rewards.tradeReferrer = hasTradeReferrer ? calculateReward(marketRewards, CoinConstants.TRADE_REFERRAL_REWARD_BPS) : 0;
35
+ rewards.doppler = calculateReward(marketRewards, CoinConstants.DOPPLER_REWARD_BPS);
36
+ rewards.protocol = marketRewards - rewards.creator - rewards.platformReferrer - rewards.tradeReferrer - rewards.doppler;
37
+ }
38
+
39
+ // Helper function to assert reward distributions match expected RewardBalances
40
+ function assertRewardsApproxEqRel(RewardBalances memory actual, RewardBalances memory expected) internal pure {
41
+ vm.assertApproxEqRel(actual.creator, expected.creator, 0.25e18, "Creator reward amount");
42
+ vm.assertApproxEqRel(actual.platformReferrer, expected.platformReferrer, 0.25e18, "Platform referrer reward amount");
43
+ vm.assertApproxEqRel(actual.tradeReferrer, expected.tradeReferrer, 0.25e18, "Trade referrer reward amount");
44
+ vm.assertApproxEqRel(actual.doppler, expected.doppler, 0.25e18, "Doppler reward amount");
45
+ vm.assertApproxEqRel(actual.protocol, expected.protocol, 0.25e18, "Protocol reward amount");
46
+ }
47
+
48
+ // Helper function to assert with higher tolerance for complex conversions
49
+ function assertRewardsApproxEqRelWithTolerance(RewardBalances memory actual, RewardBalances memory expected, uint256 tolerance) internal pure {
50
+ vm.assertApproxEqRel(actual.creator, expected.creator, tolerance, "Creator reward amount");
51
+ vm.assertApproxEqRel(actual.platformReferrer, expected.platformReferrer, tolerance, "Platform referrer reward amount");
52
+ vm.assertApproxEqRel(actual.tradeReferrer, expected.tradeReferrer, tolerance, "Trade referrer reward amount");
53
+ vm.assertApproxEqRel(actual.doppler, expected.doppler, tolerance, "Doppler reward amount");
54
+ vm.assertApproxEqRel(actual.protocol, expected.protocol, tolerance, "Protocol reward amount");
55
+ }
56
+
57
+ // Generic function to record token balances for all reward recipients
58
+ function recordBalances(
59
+ IERC20 token,
60
+ address creator,
61
+ address platformReferrer,
62
+ address tradeReferrer,
63
+ address protocolRecipient,
64
+ address dopplerRecipient
65
+ ) internal view returns (RewardBalances memory balances) {
66
+ balances.creator = token.balanceOf(creator);
67
+ balances.platformReferrer = token.balanceOf(platformReferrer);
68
+ balances.tradeReferrer = token.balanceOf(tradeReferrer);
69
+ balances.protocol = token.balanceOf(protocolRecipient);
70
+ balances.doppler = token.balanceOf(dopplerRecipient);
71
+ }
72
+
73
+ // Helper function to calculate reward deltas between two balance snapshots
74
+ function calculateRewardDeltas(
75
+ RewardBalances memory initialBalances,
76
+ RewardBalances memory finalBalances
77
+ ) internal pure returns (RewardBalances memory deltas) {
78
+ deltas.creator = finalBalances.creator - initialBalances.creator;
79
+ deltas.platformReferrer = finalBalances.platformReferrer - initialBalances.platformReferrer;
80
+ deltas.tradeReferrer = finalBalances.tradeReferrer - initialBalances.tradeReferrer;
81
+ deltas.protocol = finalBalances.protocol - initialBalances.protocol;
82
+ deltas.doppler = finalBalances.doppler - initialBalances.doppler;
83
+ }
84
+
85
+ // Helper function to sum total rewards
86
+ function getTotalRewards(RewardBalances memory rewards) internal pure returns (uint256) {
87
+ return rewards.creator + rewards.platformReferrer + rewards.tradeReferrer + rewards.protocol + rewards.doppler;
88
+ }
89
+
90
+ // Helper function to calculate reward deltas after trade for any token
91
+ function calculateTokenRewardDeltas(
92
+ RewardBalances memory initialBalances,
93
+ IERC20 token,
94
+ address creator,
95
+ address platformReferrer,
96
+ address tradeReferrer,
97
+ address protocolRecipient,
98
+ address dopplerRecipient
99
+ ) internal view returns (RewardBalances memory deltas) {
100
+ deltas.creator = token.balanceOf(creator) - initialBalances.creator;
101
+ deltas.platformReferrer = token.balanceOf(platformReferrer) - initialBalances.platformReferrer;
102
+ deltas.tradeReferrer = token.balanceOf(tradeReferrer) - initialBalances.tradeReferrer;
103
+ deltas.protocol = token.balanceOf(protocolRecipient) - initialBalances.protocol;
104
+ deltas.doppler = token.balanceOf(dopplerRecipient) - initialBalances.doppler;
105
+ }
106
+ }