@zoralabs/coins 2.4.0 → 2.5.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 (129) hide show
  1. package/.abi-stability +923 -0
  2. package/.turbo/turbo-build$colon$js.log +110 -116
  3. package/CHANGELOG.md +25 -0
  4. package/abis/Address.json +0 -16
  5. package/abis/BaseCoin.json +18 -0
  6. package/abis/BuySupplyWithSwapRouterHook.json +0 -27
  7. package/abis/BuySupplyWithV4SwapHook.json +0 -32
  8. package/abis/Clones.json +1 -1
  9. package/abis/CoinDopplerMultiCurve.json +109 -0
  10. package/abis/ContentCoin.json +18 -0
  11. package/abis/Create2.json +0 -21
  12. package/abis/CreatorCoin.json +18 -0
  13. package/abis/ERC1967Proxy.json +1 -1
  14. package/abis/ERC1967Utils.json +0 -45
  15. package/abis/{UpgradeCoinImpl.json → Errors.json} +14 -10
  16. package/abis/{MockERC20.json → IERC1363.json} +134 -104
  17. package/abis/IERC1967.json +47 -0
  18. package/abis/IERC20.json +0 -36
  19. package/abis/IHasCreationInfo.json +20 -0
  20. package/abis/IProtocolRewards.json +0 -258
  21. package/abis/{Script.json → ISupportsLimitOrderFill.json} +2 -2
  22. package/abis/IZoraLimitOrderBookCoinsInterface.json +67 -0
  23. package/abis/IZoraV4CoinHook.json +10 -0
  24. package/abis/ProxyShim.json +15 -16
  25. package/abis/SafeCast.json +51 -0
  26. package/abis/{AddressConstants.json → SafeCast160.json} +1 -1
  27. package/abis/Strings.json +10 -0
  28. package/abis/UUPSUpgradeable.json +1 -1
  29. package/abis/V3ToV4SwapLib.json +28 -0
  30. package/abis/ZoraFactory.json +1 -1
  31. package/abis/ZoraFactoryImpl.json +22 -6
  32. package/abis/ZoraV4CoinHook.json +20 -48
  33. package/dist/index.cjs +980 -43
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.js +978 -41
  36. package/dist/index.js.map +1 -1
  37. package/dist/wagmiGenerated.d.ts +1501 -76
  38. package/dist/wagmiGenerated.d.ts.map +1 -1
  39. package/package/wagmiGenerated.ts +981 -44
  40. package/package.json +11 -9
  41. package/remappings.txt +2 -1
  42. package/src/BaseCoin.sol +32 -2
  43. package/src/ZoraFactoryImpl.sol +8 -0
  44. package/src/deployment/ForkedCoinsAddresses.sol +54 -0
  45. package/src/hooks/ZoraV4CoinHook.sol +131 -20
  46. package/src/hooks/deployment/BuySupplyWithV4SwapHook.sol +20 -142
  47. package/src/interfaces/IHasCreationInfo.sol +12 -0
  48. package/src/interfaces/ISupportsLimitOrderFill.sol +11 -0
  49. package/src/interfaces/IZoraLimitOrderBookCoinsInterface.sol +21 -0
  50. package/src/interfaces/IZoraV4CoinHook.sol +6 -0
  51. package/src/libs/CoinConstants.sol +22 -0
  52. package/src/libs/CoinDopplerMultiCurve.sol +1 -1
  53. package/src/libs/CoinRewardsV4.sol +0 -1
  54. package/src/libs/CoinSetup.sol +7 -1
  55. package/src/libs/HooksDeployment.sol +20 -8
  56. package/src/libs/UniV4SwapHelper.sol +35 -0
  57. package/src/libs/V3ToV4SwapLib.sol +265 -0
  58. package/src/version/ContractVersionBase.sol +1 -1
  59. package/test/BuySupplyWithV4SwapHook.t.sol +4 -3
  60. package/test/Coin.t.sol +7 -1
  61. package/test/CoinUniV4.t.sol +6 -1
  62. package/test/ContentCoinRewards.t.sol +6 -1
  63. package/test/CreatorCoin.t.sol +3 -1
  64. package/test/CreatorCoinRewards.t.sol +4 -1
  65. package/test/Factory.t.sol +20 -7
  66. package/test/HooksDeployment.t.sol +16 -3
  67. package/test/LaunchFee.t.sol +286 -0
  68. package/test/LiquidityMigration.t.sol +52 -44
  69. package/test/MultiOwnable.t.sol +2 -1
  70. package/test/Upgrades.t.sol +110 -81
  71. package/test/V4Liquidity.t.sol +1 -1
  72. package/test/mocks/MockSwapRouter.sol +33 -0
  73. package/test/mocks/MockZoraLimitOrderBook.sol +14 -0
  74. package/test/utils/BaseTest.sol +14 -448
  75. package/test/utils/FeeEstimatorHook.sol +6 -2
  76. package/test/utils/V4TestSetup.sol +595 -0
  77. package/wagmi.config.ts +1 -1
  78. package/abis/BaseTest.json +0 -718
  79. package/abis/DeterministicDeployerAndCaller.json +0 -315
  80. package/abis/DeterministicUUPSProxyDeployer.json +0 -167
  81. package/abis/EIP712.json +0 -67
  82. package/abis/ERC20.json +0 -310
  83. package/abis/FeeEstimatorHook.json +0 -1938
  84. package/abis/IERC721.json +0 -287
  85. package/abis/IERC721Enumerable.json +0 -343
  86. package/abis/IERC721Metadata.json +0 -332
  87. package/abis/IERC721TokenReceiver.json +0 -36
  88. package/abis/IImmutableCreate2Factory.json +0 -93
  89. package/abis/IMulticall3.json +0 -440
  90. package/abis/ISafe.json +0 -15
  91. package/abis/ISymbol.json +0 -15
  92. package/abis/IUniswapV4Router04.json +0 -484
  93. package/abis/IUniversalRouter.json +0 -61
  94. package/abis/IV4Quoter.json +0 -310
  95. package/abis/ImmutableCreate2FactoryUtils.json +0 -15
  96. package/abis/LibString.json +0 -7
  97. package/abis/Math.json +0 -7
  98. package/abis/MockAirlock.json +0 -39
  99. package/abis/MockERC721.json +0 -350
  100. package/abis/ProtocolRewards.json +0 -494
  101. package/abis/ShortStrings.json +0 -18
  102. package/abis/SimpleERC20.json +0 -326
  103. package/abis/StdAssertions.json +0 -379
  104. package/abis/StdInvariant.json +0 -180
  105. package/abis/Test.json +0 -570
  106. package/abis/VmContractHelper235.json +0 -233
  107. package/abis/VmContractHelper242.json +0 -233
  108. package/abis/stdError.json +0 -119
  109. package/abis/stdStorageSafe.json +0 -52
  110. package/addresses/8453.json +0 -13
  111. package/addresses/84532.json +0 -10
  112. package/deterministicConfig/deployerAndCaller.json +0 -5
  113. package/deterministicConfig/zoraFactory.json +0 -8
  114. package/script/Deploy.s.sol +0 -23
  115. package/script/DeployAutoSwapper.s.sol +0 -30
  116. package/script/DeployDevFactory.s.sol +0 -21
  117. package/script/DeployPostDeploymentHooks.s.sol +0 -20
  118. package/script/DeployTrustedMsgSenderLookup.s.sol +0 -20
  119. package/script/DeployUpgradeGate.s.sol +0 -21
  120. package/script/GenerateDeterministicParams.s.sol +0 -43
  121. package/script/PrintRegisterUpgradePath.s.sol +0 -28
  122. package/script/PrintUpgradeCommand.s.sol +0 -13
  123. package/script/TestBackingCoinSwap.s.sol +0 -144
  124. package/script/TestV4Swap.s.sol +0 -133
  125. package/script/UpgradeCoinImpl.sol +0 -23
  126. package/script/UpgradeFactoryImpl.s.sol +0 -28
  127. package/script/UpgradeHooks.s.sol +0 -23
  128. package/src/deployment/CoinsDeployerBase.sol +0 -297
  129. /package/{test → src}/utils/ProxyShim.sol +0 -0
@@ -0,0 +1,595 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import "forge-std/Test.sol";
5
+
6
+ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
7
+ import {IWETH} from "../../src/interfaces/IWETH.sol";
8
+ import {IAirlock} from "../../src/interfaces/IAirlock.sol";
9
+ import {INonfungiblePositionManager} from "../../src/interfaces/INonfungiblePositionManager.sol";
10
+ import {ISwapRouter} from "../../src/interfaces/ISwapRouter.sol";
11
+ import {IUniswapV3Factory} from "../../src/interfaces/IUniswapV3Factory.sol";
12
+ import {IProtocolRewards} from "../../src/interfaces/IProtocolRewards.sol";
13
+ import {ProtocolRewards} from "./ProtocolRewards.sol";
14
+ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
15
+ import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
16
+ import {HooksDeployment} from "../../src/libs/HooksDeployment.sol";
17
+ import {ProxyShim} from "../../src/utils/ProxyShim.sol";
18
+ import {ICoin} from "../../src/interfaces/ICoin.sol";
19
+ import {UniV4SwapHelper} from "../../src/libs/UniV4SwapHelper.sol";
20
+ import {IPermit2} from "permit2/src/interfaces/IPermit2.sol";
21
+ import {IUniversalRouter} from "@uniswap/universal-router/contracts/interfaces/IUniversalRouter.sol";
22
+ import {IV4Quoter} from "@uniswap/v4-periphery/src/interfaces/IV4Quoter.sol";
23
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
24
+ import {ContractAddresses} from "./ContractAddresses.sol";
25
+ import {HookUpgradeGate} from "../../src/hooks/HookUpgradeGate.sol";
26
+ import {ZoraHookRegistry} from "../../src/hook-registry/ZoraHookRegistry.sol";
27
+ import {IZoraFactory} from "../../src/interfaces/IZoraFactory.sol";
28
+ import {ZoraFactoryImpl} from "../../src/ZoraFactoryImpl.sol";
29
+ import {ZoraFactory} from "../../src/proxy/ZoraFactory.sol";
30
+ import {ContentCoin} from "../../src/ContentCoin.sol";
31
+ import {CreatorCoin} from "../../src/CreatorCoin.sol";
32
+ import {CoinConfigurationVersions} from "../../src/libs/CoinConfigurationVersions.sol";
33
+ import {CoinConstants} from "../../src/libs/CoinConstants.sol";
34
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
35
+ import {MockZoraLimitOrderBook} from "../mocks/MockZoraLimitOrderBook.sol";
36
+ import {ITrustedMsgSenderProviderLookup} from "../../src/interfaces/ITrustedMsgSenderProviderLookup.sol";
37
+ import {TrustedSenderTestHelper} from "./TrustedSenderTestHelper.sol";
38
+
39
+ // Hookmate imports for non-forked testing
40
+ import {V4PoolManagerDeployer} from "./hookmate/artifacts/V4PoolManager.sol";
41
+ import {V4QuoterDeployer} from "./hookmate/artifacts/V4Quoter.sol";
42
+ import {Permit2Deployer} from "./hookmate/artifacts/Permit2.sol";
43
+ import {DeployHelper} from "./hookmate/artifacts/DeployHelper.sol";
44
+ import {AddressConstants} from "./hookmate/constants/AddressConstants.sol";
45
+ import {UniversalRouterDeployer, RouterParameters} from "./hookmate/artifacts/UniversalRouter.sol";
46
+ import {MockAirlock} from "../mocks/MockAirlock.sol";
47
+ import {MockSwapRouter} from "../mocks/MockSwapRouter.sol";
48
+ import {SimpleERC20} from "../mocks/SimpleERC20.sol";
49
+
50
+ /**
51
+ * @title V4TestSetup
52
+ * @notice Shared base test contract for Uniswap V4 infrastructure setup
53
+ * @dev This contract provides common setup and utilities for both coins and limit-orders test suites.
54
+ * It includes fork management, V4 infrastructure deployment, and helper functions.
55
+ */
56
+ contract V4TestSetup is Test, ContractAddresses {
57
+ using stdStorage for StdStorage;
58
+
59
+ // Constants
60
+ int24 internal constant USDC_TICK_LOWER = 57200;
61
+ int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER;
62
+ int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
63
+ uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
64
+ uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
65
+ string internal constant DEFAULT_NAME = "Testcoin";
66
+ string internal constant DEFAULT_SYMBOL = "TEST";
67
+
68
+ struct Users {
69
+ address factoryOwner;
70
+ address feeRecipient;
71
+ address creator;
72
+ address platformReferrer;
73
+ address buyer;
74
+ address seller;
75
+ address coinRecipient;
76
+ address tradeReferrer;
77
+ address dopplerRecipient;
78
+ }
79
+
80
+ // Fork management
81
+ uint256 internal forkId;
82
+
83
+ // Tokens
84
+ IERC20Metadata internal zoraToken;
85
+ IERC20Metadata internal usdc;
86
+ IWETH internal weth;
87
+
88
+ // Protocol contracts
89
+ ProtocolRewards internal protocolRewards;
90
+ IUniswapV3Factory internal v3Factory;
91
+ INonfungiblePositionManager internal nonfungiblePositionManager;
92
+ ISwapRouter internal swapRouter;
93
+ IAirlock internal airlock;
94
+
95
+ // Uniswap V4 infrastructure
96
+ IPermit2 internal permit2;
97
+ IUniversalRouter internal router;
98
+ IPoolManager internal poolManager;
99
+ IV4Quoter internal quoter;
100
+
101
+ // Zora protocol contracts
102
+ ContentCoin internal coinV4Impl;
103
+ CreatorCoin internal creatorCoinImpl;
104
+ ZoraFactoryImpl internal factoryImpl;
105
+ IZoraFactory internal factory;
106
+ ZoraV4CoinHook internal hook;
107
+ HookUpgradeGate internal hookUpgradeGate;
108
+ ZoraHookRegistry internal zoraHookRegistry;
109
+ MockZoraLimitOrderBook internal mockZoraLimitOrderBook;
110
+
111
+ // Deployed coins (for convenience in tests)
112
+ ContentCoin internal coinV4;
113
+
114
+ // Test users
115
+ Users internal users;
116
+
117
+ // ============================================
118
+ // Setup Functions
119
+ // ============================================
120
+ // Note: No setUp() function - inheriting contracts must implement their own
121
+ function _setUpWithBlockNumber(uint256 forkBlockNumber) internal {
122
+ mockZoraLimitOrderBook = new MockZoraLimitOrderBook();
123
+ _setUpWithBlockNumber(forkBlockNumber, address(mockZoraLimitOrderBook));
124
+ }
125
+
126
+ function _setUpWithBlockNumber(uint256 forkBlockNumber, address _limitOrderBook) internal {
127
+ forkId = vm.createSelectFork("base", forkBlockNumber);
128
+
129
+ weth = IWETH(WETH_ADDRESS);
130
+ usdc = IERC20Metadata(USDC_ADDRESS);
131
+ zoraToken = IERC20Metadata(ZORA_TOKEN_ADDRESS);
132
+ v3Factory = IUniswapV3Factory(V3_FACTORY);
133
+ nonfungiblePositionManager = INonfungiblePositionManager(NONFUNGIBLE_POSITION_MANAGER);
134
+ swapRouter = ISwapRouter(SWAP_ROUTER);
135
+ airlock = IAirlock(DOPPLER_AIRLOCK);
136
+ protocolRewards = new ProtocolRewards();
137
+ permit2 = IPermit2(V4_PERMIT2);
138
+ router = IUniversalRouter(UNIVERSAL_ROUTER);
139
+ poolManager = IPoolManager(V4_POOL_MANAGER);
140
+ quoter = IV4Quoter(V4_QUOTER);
141
+ users = Users({
142
+ factoryOwner: makeAddr("factoryOwner"),
143
+ feeRecipient: makeAddr("feeRecipient"),
144
+ creator: makeAddr("creator"),
145
+ platformReferrer: makeAddr("platformReferrer"),
146
+ buyer: makeAddr("buyer"),
147
+ seller: makeAddr("seller"),
148
+ coinRecipient: makeAddr("coinRecipient"),
149
+ tradeReferrer: makeAddr("tradeReferrer"),
150
+ dopplerRecipient: makeAddr("dopplerRecipient")
151
+ });
152
+
153
+ ProxyShim mockUpgradeableImpl = new ProxyShim();
154
+ factory = IZoraFactory(address(new ZoraFactory(address(mockUpgradeableImpl))));
155
+
156
+ hookUpgradeGate = new HookUpgradeGate(users.factoryOwner);
157
+
158
+ zoraHookRegistry = new ZoraHookRegistry();
159
+
160
+ address[] memory initialOwners = new address[](2);
161
+ initialOwners[0] = users.factoryOwner;
162
+ initialOwners[1] = address(factory);
163
+ zoraHookRegistry.initialize(initialOwners);
164
+
165
+ _deployHooks(_limitOrderBook);
166
+
167
+ coinV4Impl = new ContentCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
168
+
169
+ creatorCoinImpl = new CreatorCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
170
+
171
+ factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
172
+ UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
173
+ factory = IZoraFactory(address(factory));
174
+
175
+ ZoraFactoryImpl(address(factory)).initialize(users.factoryOwner);
176
+
177
+ vm.label(address(factory), "ZORA_FACTORY");
178
+ vm.label(address(protocolRewards), "PROTOCOL_REWARDS");
179
+ vm.label(address(nonfungiblePositionManager), "NONFUNGIBLE_POSITION_MANAGER");
180
+ vm.label(address(v3Factory), "V3_FACTORY");
181
+ vm.label(address(swapRouter), "SWAP_ROUTER");
182
+ vm.label(address(weth), "WETH");
183
+ vm.label(address(usdc), "USDC");
184
+ vm.label(address(airlock), "AIRLOCK");
185
+ vm.label(address(zoraToken), "$ZORA");
186
+ vm.label(address(V4_POOL_MANAGER), "V4_POOL_MANAGER");
187
+ vm.label(address(V4_POSITION_MANAGER), "V4_POSITION_MANAGER");
188
+ vm.label(address(V4_QUOTER), "V4_QUOTER");
189
+ vm.label(address(V4_PERMIT2), "V4_PERMIT2");
190
+ vm.label(address(UNIVERSAL_ROUTER), "UNIVERSAL_ROUTER");
191
+ vm.label(address(hook), "HOOK");
192
+ vm.label(address(mockZoraLimitOrderBook), "LIMIT_ORDER_BOOK");
193
+ }
194
+
195
+ function _setUpNonForked() internal {
196
+ mockZoraLimitOrderBook = new MockZoraLimitOrderBook();
197
+ _setUpNonForked(address(mockZoraLimitOrderBook));
198
+ }
199
+
200
+ function _setUpNonForked(address limitOrderBook) internal {
201
+ // Initialize users first
202
+ users = Users({
203
+ factoryOwner: makeAddr("factoryOwner"),
204
+ feeRecipient: makeAddr("feeRecipient"),
205
+ creator: makeAddr("creator"),
206
+ platformReferrer: makeAddr("platformReferrer"),
207
+ buyer: makeAddr("buyer"),
208
+ seller: makeAddr("seller"),
209
+ coinRecipient: makeAddr("coinRecipient"),
210
+ tradeReferrer: makeAddr("tradeReferrer"),
211
+ dopplerRecipient: makeAddr("dopplerRecipient")
212
+ });
213
+
214
+ // Deploy mock airlock with the dopplerRecipient as owner (for doppler rewards)
215
+ MockAirlock mockAirlock = new MockAirlock(users.dopplerRecipient);
216
+
217
+ // Deploy V4 infrastructure using hookmate
218
+ _deployV4InfrastructureNonForked();
219
+
220
+ // Deploy mock ZORA token at the correct address
221
+ deployCodeTo("SimpleERC20.sol:SimpleERC20", abi.encode("ZORA", "$ZORA"), ZORA_TOKEN_ADDRESS);
222
+ zoraToken = IERC20Metadata(ZORA_TOKEN_ADDRESS);
223
+
224
+ // Fund the pool manager with ZORA tokens
225
+ deal(address(zoraToken), address(poolManager), 1_000_000_000e18);
226
+
227
+ // Deploy protocol rewards
228
+ protocolRewards = new ProtocolRewards();
229
+
230
+ // Deploy factory proxy
231
+ ProxyShim mockUpgradeableImpl = new ProxyShim();
232
+ factory = IZoraFactory(address(new ZoraFactory(address(mockUpgradeableImpl))));
233
+
234
+ // Deploy hook upgrade gate
235
+ hookUpgradeGate = new HookUpgradeGate(users.factoryOwner);
236
+
237
+ // Deploy zora hook registry
238
+ zoraHookRegistry = new ZoraHookRegistry();
239
+ address[] memory initialOwners = new address[](2);
240
+ initialOwners[0] = users.factoryOwner;
241
+ initialOwners[1] = address(factory);
242
+ zoraHookRegistry.initialize(initialOwners);
243
+
244
+ // Deploy limit order book
245
+ mockZoraLimitOrderBook = new MockZoraLimitOrderBook();
246
+
247
+ // Deploy hooks for non-forked environment
248
+ _deployHooksNonForked(limitOrderBook);
249
+
250
+ // Deploy coin implementations
251
+ coinV4Impl = new ContentCoin(users.feeRecipient, address(protocolRewards), poolManager, address(mockAirlock));
252
+ creatorCoinImpl = new CreatorCoin(users.feeRecipient, address(protocolRewards), poolManager, address(mockAirlock));
253
+
254
+ // Deploy and initialize factory implementation
255
+ factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
256
+ UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
257
+ ZoraFactoryImpl(address(factory)).initialize(users.factoryOwner);
258
+
259
+ // Deploy mock V3 swap router for non-forked tests
260
+ swapRouter = ISwapRouter(address(new MockSwapRouter()));
261
+
262
+ // Labels for easier debugging
263
+ vm.label(address(factory), "ZORA_FACTORY");
264
+ vm.label(address(protocolRewards), "PROTOCOL_REWARDS");
265
+ vm.label(address(poolManager), "V4_POOL_MANAGER");
266
+ vm.label(address(permit2), "V4_PERMIT2");
267
+ vm.label(address(router), "UNIVERSAL_ROUTER");
268
+ vm.label(address(hook), "HOOK");
269
+ vm.label(address(mockAirlock), "MOCK_AIRLOCK");
270
+ vm.label(address(mockZoraLimitOrderBook), "LIMIT_ORDER_BOOK");
271
+ vm.label(address(swapRouter), "MOCK_SWAP_ROUTER");
272
+ }
273
+
274
+ // ============================================
275
+ // V4 Infrastructure Deployment (Non-Forked)
276
+ // ============================================
277
+
278
+ function _deployV4InfrastructureNonForked() internal {
279
+ // Deploy Permit2 to canonical address
280
+ _deployPermit2NonForked();
281
+
282
+ // Deploy PoolManager
283
+ _deployPoolManagerNonForked();
284
+
285
+ // Deploy Quoter
286
+ _deployQuoterNonForked();
287
+
288
+ // Deploy Universal Router
289
+ _deployUniversalRouterNonForked();
290
+ }
291
+
292
+ function _deployPermit2NonForked() internal {
293
+ address permit2Address = AddressConstants.getPermit2Address();
294
+
295
+ if (permit2Address.code.length > 0) {
296
+ // Permit2 is already deployed
297
+ } else {
298
+ address tempDeployAddress = address(Permit2Deployer.deploy());
299
+ vm.etch(permit2Address, tempDeployAddress.code);
300
+ }
301
+
302
+ permit2 = IPermit2(permit2Address);
303
+ }
304
+
305
+ function _deployPoolManagerNonForked() internal {
306
+ if (block.chainid == 31337) {
307
+ poolManager = IPoolManager(address(V4PoolManagerDeployer.deploy(address(0x4444))));
308
+ } else {
309
+ poolManager = IPoolManager(AddressConstants.getPoolManagerAddress(block.chainid));
310
+ }
311
+
312
+ deal(address(poolManager), 10000 ether);
313
+ }
314
+
315
+ function _deployQuoterNonForked() internal {
316
+ quoter = IV4Quoter(V4QuoterDeployer.deploy(address(poolManager)));
317
+ }
318
+
319
+ function _deployUniversalRouterNonForked() internal {
320
+ RouterParameters memory params = RouterParameters({
321
+ permit2: address(permit2),
322
+ weth9: address(0),
323
+ v2Factory: address(0),
324
+ v3Factory: address(0),
325
+ pairInitCodeHash: bytes32(0),
326
+ poolInitCodeHash: bytes32(0),
327
+ v4PoolManager: address(poolManager),
328
+ v3NFTPositionManager: address(0),
329
+ v4PositionManager: address(0)
330
+ });
331
+ router = IUniversalRouter(UniversalRouterDeployer.deploy(params));
332
+ }
333
+
334
+ // ============================================
335
+ // Hook Deployment
336
+ // ============================================
337
+
338
+ function getSalt(ITrustedMsgSenderProviderLookup trustedMsgSenderLookup, address limitOrderBook) public returns (bytes32 hookSalt) {
339
+ address deployer = address(this);
340
+
341
+ (, hookSalt) = HooksDeployment.mineForCoinSalt(
342
+ deployer,
343
+ V4_POOL_MANAGER,
344
+ address(factory),
345
+ trustedMsgSenderLookup,
346
+ address(hookUpgradeGate),
347
+ limitOrderBook,
348
+ address(zoraHookRegistry)
349
+ );
350
+ }
351
+
352
+ function _deployHooks(address limitOrderBook) internal {
353
+ address[] memory trustedMessageSenders = new address[](2);
354
+ trustedMessageSenders[0] = UNIVERSAL_ROUTER;
355
+ trustedMessageSenders[1] = V4_POSITION_MANAGER;
356
+
357
+ ITrustedMsgSenderProviderLookup trustedMsgSenderLookup = TrustedSenderTestHelper.deployTrustedMessageSender(users.factoryOwner, trustedMessageSenders);
358
+
359
+ bytes32 hookSalt = getSalt(trustedMsgSenderLookup, limitOrderBook);
360
+
361
+ hook = ZoraV4CoinHook(
362
+ payable(
363
+ address(
364
+ HooksDeployment.deployHookWithSalt(
365
+ HooksDeployment.makeHookCreationCode(
366
+ V4_POOL_MANAGER,
367
+ address(factory),
368
+ trustedMsgSenderLookup,
369
+ address(hookUpgradeGate),
370
+ limitOrderBook,
371
+ address(zoraHookRegistry)
372
+ ),
373
+ hookSalt
374
+ )
375
+ )
376
+ )
377
+ );
378
+
379
+ address[] memory hooks = new address[](1);
380
+ hooks[0] = address(hook);
381
+ string[] memory tags = new string[](1);
382
+ tags[0] = "CoinHook";
383
+ vm.prank(users.factoryOwner);
384
+ zoraHookRegistry.registerHooks(hooks, tags);
385
+ }
386
+
387
+ function _deployHooksNonForked(address limitOrderBook) internal {
388
+ address[] memory trustedMessageSenders = new address[](1);
389
+ trustedMessageSenders[0] = address(router);
390
+
391
+ ITrustedMsgSenderProviderLookup trustedMsgSenderLookup = TrustedSenderTestHelper.deployTrustedMessageSender(users.factoryOwner, trustedMessageSenders);
392
+
393
+ // Use proper salt mining for hook deployment
394
+ address deployer = address(this);
395
+ (, bytes32 salt) = HooksDeployment.mineForCoinSalt(
396
+ deployer,
397
+ address(poolManager),
398
+ address(factory),
399
+ trustedMsgSenderLookup,
400
+ address(hookUpgradeGate),
401
+ limitOrderBook,
402
+ address(zoraHookRegistry)
403
+ );
404
+
405
+ bytes memory hookCreationCode = HooksDeployment.makeHookCreationCode(
406
+ address(poolManager),
407
+ address(factory),
408
+ trustedMsgSenderLookup,
409
+ address(hookUpgradeGate),
410
+ limitOrderBook,
411
+ address(zoraHookRegistry)
412
+ );
413
+
414
+ hook = ZoraV4CoinHook(payable(DeployHelper.deploy(hookCreationCode, salt)));
415
+
416
+ address[] memory hooks = new address[](1);
417
+ hooks[0] = address(hook);
418
+ string[] memory tags = new string[](1);
419
+ tags[0] = "CoinHook";
420
+ vm.prank(users.factoryOwner);
421
+ zoraHookRegistry.registerHooks(hooks, tags);
422
+ }
423
+
424
+ function _deployFeeEstimatorHook(address hooks) internal {
425
+ // Deploy a new lookup with the same trusted senders
426
+ address[] memory trustedMessageSenders = new address[](2);
427
+ trustedMessageSenders[0] = UNIVERSAL_ROUTER;
428
+ trustedMessageSenders[1] = V4_POSITION_MANAGER;
429
+ ITrustedMsgSenderProviderLookup newLookup = TrustedSenderTestHelper.deployTrustedMessageSender(users.factoryOwner, trustedMessageSenders);
430
+
431
+ deployCodeTo(
432
+ "FeeEstimatorHook.sol",
433
+ abi.encode(address(poolManager), address(factory), newLookup, hookUpgradeGate, _getLimitOrderBookAddress(), address(zoraHookRegistry)),
434
+ hooks
435
+ );
436
+ }
437
+
438
+ function _getLimitOrderBookAddress() internal view virtual returns (address) {
439
+ return address(mockZoraLimitOrderBook);
440
+ }
441
+
442
+ // ============================================
443
+ // Coin Deployment Helpers
444
+ // ============================================
445
+
446
+ function _defaultPoolConfig(address currency) internal pure returns (bytes memory) {
447
+ return CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(currency);
448
+ }
449
+
450
+ function _deployV4Coin(address currency) internal returns (ICoin) {
451
+ bytes32 salt = keccak256(abi.encode(bytes("randomSalt")));
452
+ return _deployV4Coin(currency, address(0), salt);
453
+ }
454
+
455
+ function _deployV4Coin(address currency, address createReferral, bytes32 salt) internal returns (ICoin) {
456
+ address[] memory owners = new address[](1);
457
+ owners[0] = users.creator;
458
+
459
+ bytes memory poolConfig = _defaultPoolConfig(currency);
460
+
461
+ vm.prank(users.creator);
462
+ (address coinAddress, ) = factory.deploy(
463
+ users.creator,
464
+ owners,
465
+ "https://test.com",
466
+ DEFAULT_NAME,
467
+ DEFAULT_SYMBOL,
468
+ poolConfig,
469
+ createReferral,
470
+ address(0),
471
+ bytes(""),
472
+ salt
473
+ );
474
+
475
+ coinV4 = ContentCoin(payable(coinAddress));
476
+ return coinV4;
477
+ }
478
+
479
+ function _deployV4Coin() internal returns (ICoin) {
480
+ // deploy with eth and no referral
481
+ return _deployV4Coin(address(0), address(0), bytes32(0));
482
+ }
483
+
484
+ function _deployCoinUSDCPair() internal {
485
+ bytes memory poolConfig_ = _defaultPoolConfig(USDC_ADDRESS);
486
+ vm.prank(users.creator);
487
+ (address coinAddress, ) = factory.deploy(
488
+ users.creator,
489
+ _getDefaultOwners(),
490
+ "https://test.com",
491
+ "Testcoin",
492
+ "TEST",
493
+ poolConfig_,
494
+ users.platformReferrer,
495
+ 0
496
+ );
497
+
498
+ vm.label(coinAddress, "COIN");
499
+ }
500
+
501
+ // ============================================
502
+ // Swap Helpers
503
+ // ============================================
504
+
505
+ function _swapSomeCurrencyForCoin(ICoin _coin, address currency, uint128 amountIn, address trader) internal {
506
+ _swapSomeCurrencyForCoin(_coin.getPoolKey(), _coin, currency, amountIn, trader);
507
+ }
508
+
509
+ function _swapSomeCurrencyForCoin(PoolKey memory poolKey, ICoin _coin, address currency, uint128 amountIn, address trader) internal {
510
+ uint128 minAmountOut = uint128(0);
511
+
512
+ (bytes memory commands, bytes[] memory inputs) = UniV4SwapHelper.buildExactInputSingleSwapCommand(
513
+ currency,
514
+ amountIn,
515
+ address(_coin),
516
+ minAmountOut,
517
+ poolKey,
518
+ bytes("")
519
+ );
520
+
521
+ vm.startPrank(trader);
522
+ if (currency != address(0)) {
523
+ UniV4SwapHelper.approveTokenWithPermit2(permit2, address(router), currency, amountIn, uint48(block.timestamp + 1 days));
524
+ }
525
+
526
+ uint256 value = currency == address(0) ? amountIn : 0;
527
+
528
+ // Execute the swap
529
+ uint256 deadline = block.timestamp + 20;
530
+ router.execute{value: value}(commands, inputs, deadline);
531
+
532
+ vm.stopPrank();
533
+ }
534
+
535
+ function _swapSomeCoinForCurrency(ICoin _coin, address currency, uint128 amountIn, address trader) internal {
536
+ uint128 minAmountOut = uint128(0);
537
+
538
+ (bytes memory commands, bytes[] memory inputs) = UniV4SwapHelper.buildExactInputSingleSwapCommand(
539
+ address(_coin),
540
+ amountIn,
541
+ currency,
542
+ minAmountOut,
543
+ _coin.getPoolKey(),
544
+ bytes("")
545
+ );
546
+
547
+ vm.startPrank(trader);
548
+ UniV4SwapHelper.approveTokenWithPermit2(permit2, address(router), address(_coin), amountIn, uint48(block.timestamp + 1 days));
549
+
550
+ // Execute the swap
551
+ uint256 deadline = block.timestamp + 20;
552
+ router.execute(commands, inputs, deadline);
553
+
554
+ vm.stopPrank();
555
+ }
556
+
557
+ // ============================================
558
+ // Common Helper Functions
559
+ // ============================================
560
+
561
+ function _calculateExpectedFee(uint256 ethAmount) internal pure returns (uint256) {
562
+ uint256 feeBps = 100; // 1%
563
+ return (ethAmount * feeBps) / 10_000;
564
+ }
565
+
566
+ function dealUSDC(address to, uint256 numUSDC) internal returns (uint256) {
567
+ uint256 amount = numUSDC * 1e6;
568
+ deal(address(usdc), to, amount);
569
+ return amount;
570
+ }
571
+
572
+ function _getDefaultOwners() internal view returns (address[] memory owners) {
573
+ owners = new address[](1);
574
+ owners[0] = users.creator;
575
+ }
576
+
577
+ function dopplerFeeRecipient() internal view returns (address) {
578
+ return airlock.owner();
579
+ }
580
+
581
+ function _generatePoolConfig(address currency_) internal pure returns (bytes memory) {
582
+ return CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(currency_);
583
+ }
584
+
585
+ function _generatePoolConfig(
586
+ uint8 version_,
587
+ address currency_,
588
+ int24 tickLower_,
589
+ int24 tickUpper_,
590
+ uint16 numDiscoveryPositions_,
591
+ uint256 maxDiscoverySupplyShare_
592
+ ) internal pure returns (bytes memory) {
593
+ return abi.encode(version_, currency_, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_);
594
+ }
595
+ }
package/wagmi.config.ts CHANGED
@@ -15,10 +15,10 @@ export default defineConfig({
15
15
  "ZoraFactoryImpl",
16
16
  "IUniswapV3Pool",
17
17
  "IPoolConfigEncoding",
18
- "IUniversalRouter",
19
18
  "IPermit2",
20
19
  "AutoSwapper",
21
20
  "BuySupplyWithV4SwapHook",
21
+ "ZoraV4CoinHook",
22
22
  ].map((contractName) => `${contractName}.json`),
23
23
  }),
24
24
  ],