@zoralabs/coins 0.9.0 → 1.0.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 (124) hide show
  1. package/.turbo/turbo-build.log +131 -114
  2. package/CHANGELOG.md +40 -0
  3. package/abis/BaseCoin.json +26 -118
  4. package/abis/BaseTest.json +47 -0
  5. package/abis/Coin.json +171 -63
  6. package/abis/CoinDopplerMultiCurve.json +38 -0
  7. package/abis/CoinRewardsV4.json +54 -0
  8. package/abis/CoinTest.json +53 -20
  9. package/abis/CoinUniV4Test.json +1053 -0
  10. package/abis/CoinV4.json +234 -211
  11. package/abis/DeployScript.json +47 -0
  12. package/abis/DeployedCoinVersionLookup.json +21 -0
  13. package/abis/DeployedCoinVersionLookupTest.json +716 -0
  14. package/abis/DifferentNamespaceVersionLookup.json +39 -0
  15. package/abis/DopplerUniswapV3Test.json +49 -93
  16. package/abis/ERC20.json +310 -0
  17. package/abis/FactoryTest.json +85 -7
  18. package/abis/FeeEstimatorHook.json +1528 -0
  19. package/abis/HooksDeployment.json +23 -0
  20. package/abis/HooksTest.json +47 -0
  21. package/abis/ICoin.json +40 -71
  22. package/abis/ICoinV3.json +879 -0
  23. package/abis/ICoinV4.json +915 -0
  24. package/abis/IDeployedCoinVersionLookup.json +21 -0
  25. package/abis/IERC721.json +36 -36
  26. package/abis/IHasPoolKey.json +42 -0
  27. package/abis/IHasRewardsRecipients.json +54 -0
  28. package/abis/IHasSwapPath.json +60 -0
  29. package/abis/IMsgSender.json +15 -0
  30. package/abis/IPoolConfigEncoding.json +46 -0
  31. package/abis/ISwapPathRouter.json +92 -0
  32. package/abis/IUniversalRouter.json +61 -0
  33. package/abis/IUnlockCallback.json +21 -0
  34. package/abis/IV4Quoter.json +310 -0
  35. package/abis/IZoraFactory.json +191 -11
  36. package/abis/IZoraV4CoinHook.json +348 -4
  37. package/abis/MockERC20.json +21 -0
  38. package/abis/MultiOwnableTest.json +47 -0
  39. package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
  40. package/abis/PrintUpgradeCommand.json +9 -0
  41. package/abis/ProxyShim.json +24 -0
  42. package/abis/StateLibrary.json +80 -0
  43. package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
  44. package/abis/TestV4Swap.json +9 -0
  45. package/abis/UpgradeCoinImpl.json +47 -0
  46. package/abis/UpgradesTest.json +67 -0
  47. package/abis/Vm.json +1482 -111
  48. package/abis/VmSafe.json +856 -32
  49. package/abis/ZoraFactoryImpl.json +339 -1
  50. package/abis/ZoraV4CoinHook.json +455 -5
  51. package/addresses/8453.json +8 -4
  52. package/addresses/84532.json +8 -4
  53. package/dist/index.cjs +1920 -169
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.js +1916 -169
  56. package/dist/index.js.map +1 -1
  57. package/dist/wagmiGenerated.d.ts +2599 -183
  58. package/dist/wagmiGenerated.d.ts.map +1 -1
  59. package/package/wagmiGenerated.ts +1928 -165
  60. package/package.json +8 -3
  61. package/remappings.txt +6 -1
  62. package/script/CoinsDeployerBase.sol +74 -11
  63. package/script/DeployDevFactory.s.sol +21 -0
  64. package/script/PrintUpgradeCommand.s.sol +13 -0
  65. package/script/Simulate.s.sol +1 -10
  66. package/script/TestBackingCoinSwap.s.sol +146 -0
  67. package/script/TestV4Swap.s.sol +136 -0
  68. package/script/UpgradeFactoryImpl.s.sol +1 -1
  69. package/src/BaseCoin.sol +176 -0
  70. package/src/Coin.sol +87 -202
  71. package/src/CoinV4.sol +121 -0
  72. package/src/ZoraFactoryImpl.sol +208 -36
  73. package/src/hooks/ZoraV4CoinHook.sol +195 -0
  74. package/src/hooks/{BaseCoinDeployHook.sol → deployment/BaseCoinDeployHook.sol} +3 -3
  75. package/src/hooks/{BuySupplyWithSwapRouterHook.sol → deployment/BuySupplyWithSwapRouterHook.sol} +7 -5
  76. package/src/interfaces/ICoin.sol +31 -39
  77. package/src/interfaces/ICoinV3.sol +71 -0
  78. package/src/interfaces/ICoinV4.sol +69 -0
  79. package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
  80. package/src/interfaces/IMsgSender.sol +9 -0
  81. package/src/interfaces/IPoolConfigEncoding.sol +14 -0
  82. package/src/interfaces/ISwapPathRouter.sol +14 -0
  83. package/src/interfaces/IZoraFactory.sol +65 -27
  84. package/src/interfaces/IZoraV4CoinHook.sol +116 -0
  85. package/src/libs/CoinCommon.sol +15 -0
  86. package/src/libs/CoinConfigurationVersions.sol +116 -1
  87. package/src/libs/CoinConstants.sol +5 -0
  88. package/src/libs/CoinDopplerMultiCurve.sol +134 -0
  89. package/src/libs/CoinDopplerUniV3.sol +19 -171
  90. package/src/libs/CoinRewards.sol +195 -0
  91. package/src/libs/CoinRewardsV4.sol +180 -0
  92. package/src/libs/CoinSetup.sol +57 -0
  93. package/src/libs/CoinSetupV3.sol +6 -67
  94. package/src/libs/DopplerMath.sol +156 -0
  95. package/src/libs/HooksDeployment.sol +84 -0
  96. package/src/libs/MarketConstants.sol +4 -0
  97. package/src/libs/PoolStateReader.sol +22 -0
  98. package/src/libs/UniV3BuySell.sol +74 -292
  99. package/src/libs/UniV4SwapHelper.sol +65 -0
  100. package/src/libs/UniV4SwapToCurrency.sol +109 -0
  101. package/src/libs/V4Liquidity.sol +129 -0
  102. package/src/types/PoolConfiguration.sol +15 -0
  103. package/src/utils/DeployedCoinVersionLookup.sol +52 -0
  104. package/src/version/ContractVersionBase.sol +1 -1
  105. package/test/Coin.t.sol +78 -88
  106. package/test/CoinDopplerUniV3.t.sol +32 -171
  107. package/test/CoinUniV4.t.sol +752 -0
  108. package/test/{Hooks.t.sol → DeploymentHooks.t.sol} +2 -6
  109. package/test/Factory.t.sol +80 -47
  110. package/test/MultiOwnable.t.sol +6 -3
  111. package/test/Upgrades.t.sol +6 -5
  112. package/test/mocks/MockERC20.sol +12 -0
  113. package/test/utils/BaseTest.sol +106 -56
  114. package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
  115. package/test/utils/FeeEstimatorHook.sol +84 -0
  116. package/test/utils/ProxyShim.sol +17 -0
  117. package/wagmi.config.ts +4 -0
  118. package/.env +0 -1
  119. package/.turbo/turbo-update-contract-version.log +0 -22
  120. package/abis/CoinSetupV3.json +0 -7
  121. package/abis/HookDeployer.json +0 -68
  122. package/abis/IHookDeployer.json +0 -42
  123. package/src/libs/CoinLegacy.sol +0 -48
  124. package/src/libs/CoinLegacyMarket.sol +0 -182
@@ -2,14 +2,14 @@
2
2
  pragma solidity ^0.8.13;
3
3
 
4
4
  import {BaseTest} from "./utils/BaseTest.sol";
5
- import {BuySupplyWithSwapRouterHook} from "../src/hooks/BuySupplyWithSwapRouterHook.sol";
5
+ import {BuySupplyWithSwapRouterHook} from "../src/hooks/deployment/BuySupplyWithSwapRouterHook.sol";
6
6
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7
7
  import {IUniswapV3Pool} from "../src/interfaces/IUniswapV3Pool.sol";
8
8
  import {Coin} from "../src/Coin.sol";
9
9
  import {CoinConfigurationVersions} from "../src/libs/CoinConfigurationVersions.sol";
10
10
  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
11
11
  import {ICoin} from "../src/interfaces/ICoin.sol";
12
- import {IHasAfterCoinDeploy} from "../src/hooks/BaseCoinDeployHook.sol";
12
+ import {IHasAfterCoinDeploy} from "../src/hooks/deployment/BaseCoinDeployHook.sol";
13
13
  import {IZoraFactory} from "../src/interfaces/IZoraFactory.sol";
14
14
  import {ISwapRouter} from "../src/interfaces/ISwapRouter.sol";
15
15
  import {CoinConstants} from "../src/libs/CoinConstants.sol";
@@ -22,10 +22,6 @@ contract FakeHookNoInterface {
22
22
  }
23
23
 
24
24
  contract HooksTest is BaseTest {
25
- int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = -777000;
26
- int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = 222000;
27
- uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = 10; // will be 11 total with tail position
28
- uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = 0.495e18; // half of the 990m total pool supply
29
25
  address constant zora = 0x1111111111166b7FE7bd91427724B487980aFc69;
30
26
 
31
27
  function _generateDefaultPoolConfig(address currency) internal pure returns (bytes memory) {
@@ -10,8 +10,9 @@ contract FactoryTest is BaseTest {
10
10
  }
11
11
 
12
12
  function test_constructor() public view {
13
- assertEq(factory.coinImpl(), address(coinImpl));
14
- assertEq(factory.owner(), users.factoryOwner);
13
+ assertEq(ZoraFactoryImpl(address(factory)).coinImpl(), address(coinV3Impl));
14
+ assertEq(ZoraFactoryImpl(address(factory)).owner(), users.factoryOwner);
15
+ assertEq(ZoraFactoryImpl(address(factory)).coinV4Impl(), address(coinV4Impl));
15
16
  }
16
17
 
17
18
  function test_deploy_no_eth() public {
@@ -24,9 +25,8 @@ contract FactoryTest is BaseTest {
24
25
  "https://test2.com",
25
26
  "Test2 Token",
26
27
  "TEST2",
28
+ _generatePoolConfig(address(weth)),
27
29
  users.platformReferrer,
28
- address(weth),
29
- MarketConstants.LP_TICK_LOWER_WETH,
30
30
  0
31
31
  );
32
32
  coin = Coin(payable(coinAddress));
@@ -73,9 +73,8 @@ contract FactoryTest is BaseTest {
73
73
  "https://test2.com",
74
74
  "Test2 Token",
75
75
  "TEST2",
76
+ _generatePoolConfig(address(weth)),
76
77
  users.platformReferrer,
77
- address(weth),
78
- MarketConstants.LP_TICK_LOWER_WETH,
79
78
  initialOrderSize
80
79
  );
81
80
  coin = Coin(payable(coinAddress));
@@ -119,9 +118,8 @@ contract FactoryTest is BaseTest {
119
118
  "https://test2.com",
120
119
  "Test2 Token",
121
120
  "TEST2",
121
+ _generatePoolConfig(address(weth)),
122
122
  users.platformReferrer,
123
- address(weth),
124
- MarketConstants.LP_TICK_LOWER_WETH,
125
123
  initialOrderSize
126
124
  );
127
125
  }
@@ -139,9 +137,8 @@ contract FactoryTest is BaseTest {
139
137
  "https://test2.com",
140
138
  "Test2 Token",
141
139
  "TEST2",
140
+ _generatePoolConfig(address(weth)),
142
141
  users.platformReferrer,
143
- address(weth),
144
- MarketConstants.LP_TICK_LOWER_WETH,
145
142
  orderSize
146
143
  );
147
144
  coin = Coin(payable(coinAddress));
@@ -160,9 +157,8 @@ contract FactoryTest is BaseTest {
160
157
  "https://testcoinusdcpair.com",
161
158
  "Testcoinusdcpair",
162
159
  "TESTCOINUSDCPAIR",
160
+ _generatePoolConfig(USDC_ADDRESS),
163
161
  users.platformReferrer,
164
- USDC_ADDRESS,
165
- USDC_TICK_LOWER,
166
162
  0
167
163
  );
168
164
  coin = Coin(payable(coinAddress));
@@ -193,9 +189,8 @@ contract FactoryTest is BaseTest {
193
189
  "https://testcoinusdcpair.com",
194
190
  "Testcoinusdcpair",
195
191
  "TESTCOINUSDCPAIR",
192
+ _generatePoolConfig(USDC_ADDRESS),
196
193
  users.platformReferrer,
197
- USDC_ADDRESS,
198
- USDC_TICK_LOWER,
199
194
  orderSize
200
195
  );
201
196
  coin = Coin(payable(coinAddress));
@@ -218,9 +213,8 @@ contract FactoryTest is BaseTest {
218
213
  "https://testcoinusdcpair.com",
219
214
  "Testcoinusdcpair",
220
215
  "TESTCOINUSDCPAIR",
216
+ _generatePoolConfig(USDC_ADDRESS),
221
217
  users.platformReferrer,
222
- USDC_ADDRESS,
223
- USDC_TICK_LOWER,
224
218
  0
225
219
  );
226
220
  }
@@ -235,9 +229,8 @@ contract FactoryTest is BaseTest {
235
229
  "https://testcoinusdcpair.com",
236
230
  "Testcoinusdcpair",
237
231
  "TESTCOINUSDCPAIR",
232
+ _generatePoolConfig(USDC_ADDRESS),
238
233
  users.platformReferrer,
239
- USDC_ADDRESS,
240
- USDC_TICK_LOWER,
241
234
  0
242
235
  );
243
236
  }
@@ -252,9 +245,8 @@ contract FactoryTest is BaseTest {
252
245
  "https://testcoinusdcpair.com",
253
246
  "Testcoinusdcpair",
254
247
  "TESTCOINUSDCPAIR",
248
+ _generatePoolConfig(USDC_ADDRESS),
255
249
  address(0),
256
- USDC_ADDRESS,
257
- USDC_TICK_LOWER,
258
250
  0
259
251
  );
260
252
 
@@ -263,24 +255,6 @@ contract FactoryTest is BaseTest {
263
255
  assertEq(coin.platformReferrer(), coin.protocolRewardRecipient(), "platformReferrer");
264
256
  }
265
257
 
266
- function test_revert_deploy_with_invalid_currency_tick() public {
267
- address[] memory owners = new address[](1);
268
- owners[0] = users.creator;
269
-
270
- vm.expectRevert(abi.encodeWithSelector(ICoin.InvalidWethLowerTick.selector));
271
- factory.deploy(
272
- users.creator,
273
- owners,
274
- "https://testcoin.com",
275
- "Testcoin",
276
- "TESTCOIN",
277
- users.platformReferrer,
278
- address(0),
279
- MarketConstants.LP_TICK_LOWER_WETH + 1,
280
- 0
281
- );
282
- }
283
-
284
258
  function test_deploy_with_usdc_revert_invalid_eth_transfer() public {
285
259
  address[] memory owners = new address[](1);
286
260
  owners[0] = users.creator;
@@ -298,9 +272,8 @@ contract FactoryTest is BaseTest {
298
272
  "https://testcoinusdcpair.com",
299
273
  "Testcoinusdcpair",
300
274
  "TESTCOINUSDCPAIR",
275
+ _generatePoolConfig(USDC_ADDRESS),
301
276
  users.platformReferrer,
302
- USDC_ADDRESS,
303
- USDC_TICK_LOWER,
304
277
  0
305
278
  );
306
279
  }
@@ -315,9 +288,8 @@ contract FactoryTest is BaseTest {
315
288
  "https://test.com",
316
289
  "Test Token",
317
290
  "TEST",
291
+ _generatePoolConfig(address(weth)),
318
292
  users.platformReferrer,
319
- address(weth),
320
- MarketConstants.LP_TICK_LOWER_WETH,
321
293
  0
322
294
  );
323
295
  coin = Coin(payable(coinAddress));
@@ -326,10 +298,10 @@ contract FactoryTest is BaseTest {
326
298
  }
327
299
 
328
300
  function test_upgrade() public {
329
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinImpl));
301
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV3Impl), address(coinV4Impl));
330
302
 
331
303
  vm.prank(users.factoryOwner);
332
- factory.upgradeToAndCall(address(newImpl), "");
304
+ ZoraFactoryImpl(address(factory)).upgradeToAndCall(address(newImpl), "");
333
305
 
334
306
  assertEq(factory.implementation(), address(newImpl), "implementation");
335
307
  }
@@ -343,14 +315,75 @@ contract FactoryTest is BaseTest {
343
315
 
344
316
  vm.prank(users.factoryOwner);
345
317
  vm.expectRevert(abi.encodeWithSelector(ERC1967Utils.ERC1967InvalidImplementation.selector, address(newImpl)));
346
- factory.upgradeToAndCall(address(newImpl), "");
318
+ ZoraFactoryImpl(address(factory)).upgradeToAndCall(address(newImpl), "");
347
319
  }
348
320
 
349
321
  function test_revert_invalid_owner() public {
350
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinImpl));
322
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV3Impl), address(coinV4Impl));
351
323
 
352
324
  vm.prank(users.creator);
353
325
  vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, users.creator));
354
- factory.upgradeToAndCall(address(newImpl), "");
326
+ ZoraFactoryImpl(address(factory)).upgradeToAndCall(address(newImpl), "");
327
+ }
328
+
329
+ function test_coinAddress_canBePredicted(
330
+ bool msgSenderChanged,
331
+ bool saltChanged,
332
+ bool poolConfigChanged,
333
+ bool platformReferrerChanged,
334
+ bool nameChanged,
335
+ bool symbolChanged
336
+ ) public {
337
+ address[] memory owners = new address[](1);
338
+ owners[0] = users.creator;
339
+
340
+ address payoutRecipient = users.creator;
341
+
342
+ bytes32 salt = keccak256(abi.encode(bytes("randomSalt")));
343
+
344
+ address msgSender = makeAddr("msgSender");
345
+
346
+ string memory uri = "https://test.com";
347
+ string memory name = "Testcoin";
348
+ string memory symbol = "TEST";
349
+
350
+ address platformReferrer = users.platformReferrer;
351
+
352
+ bytes memory poolConfig = CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(address(weth));
353
+ bytes memory poolConfigForGettingAddress = poolConfigChanged ? CoinConfigurationVersions.defaultDopplerUniV3(address(weth)) : poolConfig;
354
+
355
+ address expectedCoinAddress = factory.coinAddress(msgSender, name, symbol, poolConfigForGettingAddress, platformReferrer, salt);
356
+
357
+ if (msgSenderChanged) {
358
+ msgSender = makeAddr("msgSender2");
359
+ }
360
+
361
+ if (saltChanged) {
362
+ salt = keccak256(abi.encode(bytes("randomSalt2")));
363
+ }
364
+
365
+ if (platformReferrerChanged) {
366
+ platformReferrer = makeAddr("platformReferrer2");
367
+ }
368
+
369
+ if (nameChanged) {
370
+ name = "Testcoin2";
371
+ }
372
+
373
+ if (symbolChanged) {
374
+ symbol = "TEST2";
375
+ }
376
+
377
+ // now deploy the coin
378
+ vm.prank(msgSender);
379
+ (address coinAddress, ) = factory.deploy(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, address(0), bytes(""), salt);
380
+
381
+ bool addressShouldMismatch = msgSenderChanged || saltChanged || poolConfigChanged || platformReferrerChanged || nameChanged || symbolChanged;
382
+
383
+ if (addressShouldMismatch) {
384
+ assertNotEq(coinAddress, expectedCoinAddress, "coinAddress should mismatch");
385
+ } else {
386
+ assertEq(coinAddress, expectedCoinAddress, "coinAddress should match");
387
+ }
355
388
  }
356
389
  }
@@ -135,22 +135,25 @@ contract MultiOwnableTest is BaseTest {
135
135
 
136
136
  function test_revert_init_with_zero_owners() public {
137
137
  address[] memory emptyOwners = new address[](0);
138
+ bytes memory poolConfig_ = _generatePoolConfig(address(weth));
138
139
  vm.expectRevert(MultiOwnable.OneOwnerRequired.selector);
139
- factory.deploy(users.creator, emptyOwners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(0), 0, 0);
140
+ factory.deploy(users.creator, emptyOwners, "https://test.com", "Test Token", "TEST", poolConfig_, users.platformReferrer, 0);
140
141
  }
141
142
 
142
143
  function test_revert_init_with_zero_address() public {
143
144
  address[] memory owners = new address[](1);
144
145
  owners[0] = address(0);
146
+ bytes memory poolConfig_ = _generatePoolConfig(address(weth));
145
147
  vm.expectRevert(MultiOwnable.OwnerCannotBeAddressZero.selector);
146
- factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(0), 0, 0);
148
+ factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", poolConfig_, users.platformReferrer, 0);
147
149
  }
148
150
 
149
151
  function test_revert_init_with_duplicate_owner() public {
150
152
  address[] memory owners = new address[](2);
151
153
  owners[0] = users.creator;
152
154
  owners[1] = users.creator;
155
+ bytes memory poolConfig_ = _generatePoolConfig(address(weth));
153
156
  vm.expectRevert(MultiOwnable.AlreadyOwner.selector);
154
- factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(0), 0, 0);
157
+ factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", poolConfig_, users.platformReferrer, 0);
155
158
  }
156
159
  }
@@ -3,6 +3,7 @@ pragma solidity ^0.8.13;
3
3
  import {Test} from "forge-std/Test.sol";
4
4
  import {IZoraFactory} from "../src/interfaces/IZoraFactory.sol";
5
5
  import {ZoraFactoryImpl} from "../src/ZoraFactoryImpl.sol";
6
+ import {BaseTest} from "./utils/BaseTest.sol";
6
7
 
7
8
  contract BadImpl {
8
9
  function contractName() public pure returns (string memory) {
@@ -10,7 +11,7 @@ contract BadImpl {
10
11
  }
11
12
  }
12
13
 
13
- contract UpgradesTest is Test {
14
+ contract UpgradesTest is BaseTest {
14
15
  ZoraFactoryImpl public factoryProxy;
15
16
 
16
17
  function test_canUpgradeFromVersionWithoutContractName() public {
@@ -19,7 +20,7 @@ contract UpgradesTest is Test {
19
20
 
20
21
  factoryProxy = ZoraFactoryImpl(0x777777751622c0d3258f214F9DF38E35BF45baF3);
21
22
 
22
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(factoryProxy.implementation());
23
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(factoryProxy.coinImpl(), address(coinV4Impl));
23
24
 
24
25
  vm.prank(factoryProxy.owner());
25
26
  factoryProxy.upgradeToAndCall(address(newImpl), "");
@@ -34,7 +35,7 @@ contract UpgradesTest is Test {
34
35
 
35
36
  factoryProxy = ZoraFactoryImpl(0x777777751622c0d3258f214F9DF38E35BF45baF3);
36
37
 
37
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(factoryProxy.implementation());
38
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(factoryProxy.coinImpl(), address(coinV4Impl));
38
39
 
39
40
  vm.prank(factoryProxy.owner());
40
41
  factoryProxy.upgradeToAndCall(address(newImpl), "");
@@ -52,12 +53,12 @@ contract UpgradesTest is Test {
52
53
 
53
54
  factoryProxy = ZoraFactoryImpl(0x777777751622c0d3258f214F9DF38E35BF45baF3);
54
55
 
55
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(factoryProxy.implementation());
56
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(factoryProxy.coinImpl(), address(coinV4Impl));
56
57
 
57
58
  vm.prank(factoryProxy.owner());
58
59
  factoryProxy.upgradeToAndCall(address(newImpl), "");
59
60
 
60
- ZoraFactoryImpl newImpl2 = new ZoraFactoryImpl(factoryProxy.implementation());
61
+ ZoraFactoryImpl newImpl2 = new ZoraFactoryImpl(factoryProxy.coinImpl(), factoryProxy.coinV4Impl());
61
62
 
62
63
  vm.prank(factoryProxy.owner());
63
64
  factoryProxy.upgradeToAndCall(address(newImpl2), "");
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5
+
6
+ contract MockERC20 is ERC20 {
7
+ constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
8
+
9
+ function mint(address to, uint256 amount) external {
10
+ _mint(to, amount);
11
+ }
12
+ }
@@ -8,10 +8,12 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
8
8
  import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
9
9
  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
10
10
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
11
-
11
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
12
+ import {IZoraFactory} from "../../src/interfaces/IZoraFactory.sol";
12
13
  import {ZoraFactoryImpl} from "../../src/ZoraFactoryImpl.sol";
13
14
  import {ZoraFactory} from "../../src/proxy/ZoraFactory.sol";
14
15
  import {Coin} from "../../src/Coin.sol";
16
+ import {CoinV4} from "../../src/CoinV4.sol";
15
17
  import {MultiOwnable} from "../../src/utils/MultiOwnable.sol";
16
18
  import {ICoin} from "../../src/interfaces/ICoin.sol";
17
19
  import {IERC7572} from "../../src/interfaces/IERC7572.sol";
@@ -24,6 +26,12 @@ import {IUniswapV3Pool} from "../../src/interfaces/IUniswapV3Pool.sol";
24
26
  import {IProtocolRewards} from "../../src/interfaces/IProtocolRewards.sol";
25
27
  import {ProtocolRewards} from "../utils/ProtocolRewards.sol";
26
28
  import {MarketConstants} from "../../src/libs/MarketConstants.sol";
29
+ import {CoinConfigurationVersions} from "../../src/libs/CoinConfigurationVersions.sol";
30
+ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
31
+ import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
32
+ import {HooksDeployment} from "../../src/libs/HooksDeployment.sol";
33
+ import {CoinConstants} from "../../src/libs/CoinConstants.sol";
34
+ import {ProxyShim} from "./ProxyShim.sol";
27
35
 
28
36
  contract BaseTest is Test {
29
37
  using stdStorage for StdStorage;
@@ -35,6 +43,11 @@ contract BaseTest is Test {
35
43
  address internal constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
36
44
  address internal constant DOPPLER_AIRLOCK = 0x660eAaEdEBc968f8f3694354FA8EC0b4c5Ba8D12;
37
45
  address internal constant USDC_ADDRESS = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
46
+ address internal constant V4_POOL_MANAGER = 0x498581fF718922c3f8e6A244956aF099B2652b2b;
47
+ address internal constant V4_POSITION_MANAGER = 0x7C5f5A4bBd8fD63184577525326123B519429bDc;
48
+ address internal constant V4_PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
49
+ address internal constant V4_QUOTER = 0x0d5e0F971ED27FBfF6c2837bf31316121532048D;
50
+ address internal constant UNIVERSAL_ROUTER = 0x6fF5693b99212Da76ad316178A184AB56D299b43;
38
51
  int24 internal constant USDC_TICK_LOWER = 57200;
39
52
 
40
53
  struct Users {
@@ -54,15 +67,79 @@ contract BaseTest is Test {
54
67
  ProtocolRewards internal protocolRewards;
55
68
  IUniswapV3Factory internal v3Factory;
56
69
  INonfungiblePositionManager internal nonfungiblePositionManager;
70
+
57
71
  ISwapRouter internal swapRouter;
58
72
  IAirlock internal airlock;
59
73
  Users internal users;
60
74
 
61
- Coin internal coinImpl;
75
+ Coin internal coinV3Impl;
76
+ CoinV4 internal coinV4Impl;
62
77
  ZoraFactoryImpl internal factoryImpl;
63
- ZoraFactoryImpl internal factory;
78
+ IZoraFactory internal factory;
79
+ ZoraV4CoinHook internal zoraV4CoinHook;
64
80
  Coin internal coin;
81
+
65
82
  IUniswapV3Pool internal pool;
83
+ int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER;
84
+ int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
85
+ uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
86
+ uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
87
+
88
+ function _deployCoin() internal {
89
+ bytes memory poolConfig_ = _generatePoolConfig(
90
+ CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
91
+ address(weth),
92
+ DEFAULT_DISCOVERY_TICK_LOWER,
93
+ DEFAULT_DISCOVERY_TICK_UPPER,
94
+ DEFAULT_NUM_DISCOVERY_POSITIONS,
95
+ DEFAULT_DISCOVERY_SUPPLY_SHARE
96
+ );
97
+ vm.prank(users.creator);
98
+ (address coinAddress, ) = factory.deploy(
99
+ users.creator,
100
+ _getDefaultOwners(),
101
+ "https://test.com",
102
+ "Testcoin",
103
+ "TEST",
104
+ poolConfig_,
105
+ users.platformReferrer,
106
+ 0
107
+ );
108
+
109
+ coin = Coin(payable(coinAddress));
110
+ pool = IUniswapV3Pool(coin.poolAddress());
111
+
112
+ vm.label(address(coin), "COIN");
113
+ vm.label(address(pool), "POOL");
114
+ }
115
+
116
+ function _deployCoinUSDCPair() internal {
117
+ bytes memory poolConfig_ = _generatePoolConfig(
118
+ CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
119
+ USDC_ADDRESS,
120
+ DEFAULT_DISCOVERY_TICK_LOWER,
121
+ DEFAULT_DISCOVERY_TICK_UPPER,
122
+ DEFAULT_NUM_DISCOVERY_POSITIONS,
123
+ DEFAULT_DISCOVERY_SUPPLY_SHARE
124
+ );
125
+ vm.prank(users.creator);
126
+ (address coinAddress, ) = factory.deploy(
127
+ users.creator,
128
+ _getDefaultOwners(),
129
+ "https://test.com",
130
+ "Testcoin",
131
+ "TEST",
132
+ poolConfig_,
133
+ users.platformReferrer,
134
+ 0
135
+ );
136
+
137
+ coin = Coin(payable(coinAddress));
138
+ pool = IUniswapV3Pool(coin.poolAddress());
139
+
140
+ vm.label(address(coin), "COIN");
141
+ vm.label(address(pool), "POOL");
142
+ }
66
143
 
67
144
  function setUp() public virtual {
68
145
  setUpWithBlockNumber(28415528);
@@ -78,7 +155,6 @@ contract BaseTest is Test {
78
155
  swapRouter = ISwapRouter(SWAP_ROUTER);
79
156
  airlock = IAirlock(DOPPLER_AIRLOCK);
80
157
  protocolRewards = new ProtocolRewards();
81
-
82
158
  users = Users({
83
159
  factoryOwner: makeAddr("factoryOwner"),
84
160
  feeRecipient: makeAddr("feeRecipient"),
@@ -90,11 +166,21 @@ contract BaseTest is Test {
90
166
  tradeReferrer: makeAddr("tradeReferrer")
91
167
  });
92
168
 
93
- coinImpl = new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, V3_FACTORY, SWAP_ROUTER, DOPPLER_AIRLOCK);
94
- factoryImpl = new ZoraFactoryImpl(address(coinImpl));
95
- factory = ZoraFactoryImpl(address(new ZoraFactory(address(factoryImpl))));
169
+ address[] memory trustedMessageSenders = new address[](2);
170
+ trustedMessageSenders[0] = UNIVERSAL_ROUTER;
171
+ trustedMessageSenders[1] = V4_POSITION_MANAGER;
96
172
 
97
- ZoraFactoryImpl(factory).initialize(users.factoryOwner);
173
+ ProxyShim mockUpgradeableImpl = new ProxyShim();
174
+ factory = IZoraFactory(address(new ZoraFactory(address(mockUpgradeableImpl))));
175
+ zoraV4CoinHook = ZoraV4CoinHook(address(HooksDeployment.deployZoraV4CoinHookFromContract(V4_POOL_MANAGER, address(factory), trustedMessageSenders)));
176
+ coinV3Impl = new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, V3_FACTORY, SWAP_ROUTER, DOPPLER_AIRLOCK);
177
+ coinV4Impl = new CoinV4(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK, zoraV4CoinHook);
178
+ factoryImpl = new ZoraFactoryImpl(address(coinV3Impl), address(coinV4Impl));
179
+ UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
180
+ factory = IZoraFactory(address(factory));
181
+ // factory = ZoraFactoryImpl(address(new ZoraFactory(address(factoryImpl))));
182
+
183
+ ZoraFactoryImpl(address(factory)).initialize(users.factoryOwner);
98
184
 
99
185
  vm.label(address(factory), "ZORA_FACTORY");
100
186
  vm.label(address(protocolRewards), "PROTOCOL_REWARDS");
@@ -120,54 +206,6 @@ contract BaseTest is Test {
120
206
  uint256 protocol;
121
207
  }
122
208
 
123
- function _deployCoin() internal {
124
- address[] memory owners = new address[](1);
125
- owners[0] = users.creator;
126
-
127
- vm.prank(users.creator);
128
- (address coinAddress, ) = factory.deploy(
129
- users.creator,
130
- owners,
131
- "https://test.com",
132
- "Testcoin",
133
- "TEST",
134
- users.platformReferrer,
135
- address(weth),
136
- MarketConstants.LP_TICK_LOWER_WETH,
137
- 0
138
- );
139
-
140
- coin = Coin(payable(coinAddress));
141
- pool = IUniswapV3Pool(coin.poolAddress());
142
-
143
- vm.label(address(coin), "COIN");
144
- vm.label(address(pool), "POOL");
145
- }
146
-
147
- function _deployCoinUSDCPair() internal {
148
- address[] memory owners = new address[](1);
149
- owners[0] = users.creator;
150
-
151
- vm.prank(users.creator);
152
- (address coinAddress, ) = factory.deploy(
153
- users.creator,
154
- owners,
155
- "https://testusdccoin.com",
156
- "Testusdccoin",
157
- "TESTUSDCCOIN",
158
- users.platformReferrer,
159
- USDC_ADDRESS,
160
- USDC_TICK_LOWER,
161
- 0
162
- );
163
-
164
- coin = Coin(payable(coinAddress));
165
- pool = IUniswapV3Pool(coin.poolAddress());
166
-
167
- vm.label(address(coin), "COIN");
168
- vm.label(address(pool), "POOL");
169
- }
170
-
171
209
  function _calculateTradeRewards(uint256 ethAmount) internal pure returns (TradeRewards memory) {
172
210
  return
173
211
  TradeRewards({
@@ -207,6 +245,18 @@ contract BaseTest is Test {
207
245
  return airlock.owner();
208
246
  }
209
247
 
248
+ function _generatePoolConfig(address currency_) internal pure returns (bytes memory) {
249
+ return
250
+ _generatePoolConfig(
251
+ CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
252
+ currency_,
253
+ DEFAULT_DISCOVERY_TICK_LOWER,
254
+ DEFAULT_DISCOVERY_TICK_UPPER,
255
+ DEFAULT_NUM_DISCOVERY_POSITIONS,
256
+ DEFAULT_DISCOVERY_SUPPLY_SHARE
257
+ );
258
+ }
259
+
210
260
  function _generatePoolConfig(
211
261
  uint8 version_,
212
262
  address currency_,