@zoralabs/coins 0.9.0 → 1.0.1

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 (131) hide show
  1. package/.turbo/turbo-build.log +179 -114
  2. package/CHANGELOG.md +46 -0
  3. package/abis/BaseCoin.json +26 -118
  4. package/abis/BaseTest.json +47 -0
  5. package/abis/BuySupplyWithSwapRouterHook.json +40 -0
  6. package/abis/Coin.json +171 -63
  7. package/abis/CoinDopplerMultiCurve.json +38 -0
  8. package/abis/CoinRewardsV4.json +54 -0
  9. package/abis/CoinTest.json +53 -20
  10. package/abis/CoinUniV4Test.json +1091 -0
  11. package/abis/CoinV4.json +234 -211
  12. package/abis/DeployScript.json +47 -0
  13. package/abis/DeployedCoinVersionLookup.json +21 -0
  14. package/abis/DeployedCoinVersionLookupTest.json +716 -0
  15. package/abis/DifferentNamespaceVersionLookup.json +39 -0
  16. package/abis/DopplerUniswapV3Test.json +49 -93
  17. package/abis/ERC20.json +310 -0
  18. package/abis/FactoryTest.json +85 -7
  19. package/abis/FeeEstimatorHook.json +1515 -0
  20. package/abis/HooksDeployment.json +23 -0
  21. package/abis/HooksTest.json +60 -0
  22. package/abis/ICoin.json +40 -71
  23. package/abis/ICoinV3.json +879 -0
  24. package/abis/ICoinV4.json +915 -0
  25. package/abis/IDeployedCoinVersionLookup.json +21 -0
  26. package/abis/IERC721.json +36 -36
  27. package/abis/IHasPoolKey.json +42 -0
  28. package/abis/IHasRewardsRecipients.json +54 -0
  29. package/abis/IHasSwapPath.json +60 -0
  30. package/abis/IMsgSender.json +15 -0
  31. package/abis/IPoolConfigEncoding.json +46 -0
  32. package/abis/ISwapPathRouter.json +92 -0
  33. package/abis/IUniversalRouter.json +61 -0
  34. package/abis/IUnlockCallback.json +21 -0
  35. package/abis/IV4Quoter.json +310 -0
  36. package/abis/IZoraFactory.json +210 -11
  37. package/abis/IZoraV4CoinHook.json +348 -4
  38. package/abis/MockERC20.json +21 -0
  39. package/abis/MultiOwnableTest.json +47 -0
  40. package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
  41. package/abis/PrintUpgradeCommand.json +9 -0
  42. package/abis/ProxyShim.json +24 -0
  43. package/abis/StateLibrary.json +80 -0
  44. package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
  45. package/abis/TestV4Swap.json +9 -0
  46. package/abis/UpgradeCoinImpl.json +47 -0
  47. package/abis/UpgradesTest.json +81 -0
  48. package/abis/Vm.json +1482 -111
  49. package/abis/VmSafe.json +856 -32
  50. package/abis/ZoraFactoryImpl.json +339 -1
  51. package/abis/ZoraV4CoinHook.json +442 -5
  52. package/addresses/8453.json +7 -4
  53. package/addresses/84532.json +8 -5
  54. package/addresses/dev/8453.json +10 -0
  55. package/dist/index.cjs +1932 -167
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.js +1928 -167
  58. package/dist/index.js.map +1 -1
  59. package/dist/wagmiGenerated.d.ts +2606 -160
  60. package/dist/wagmiGenerated.d.ts.map +1 -1
  61. package/foundry.toml +1 -0
  62. package/package/wagmiGenerated.ts +1941 -164
  63. package/package.json +8 -3
  64. package/remappings.txt +6 -1
  65. package/script/Deploy.s.sol +1 -1
  66. package/script/DeployDevFactory.s.sol +21 -0
  67. package/script/DeployHooks.s.sol +1 -1
  68. package/script/PrintUpgradeCommand.s.sol +13 -0
  69. package/script/Simulate.s.sol +1 -10
  70. package/script/TestBackingCoinSwap.s.sol +147 -0
  71. package/script/TestV4Swap.s.sol +136 -0
  72. package/script/UpgradeCoinImpl.sol +2 -2
  73. package/script/UpgradeFactoryImpl.s.sol +2 -2
  74. package/src/BaseCoin.sol +190 -0
  75. package/src/Coin.sol +87 -202
  76. package/src/CoinV4.sol +121 -0
  77. package/src/ZoraFactoryImpl.sol +208 -36
  78. package/{script → src/deployment}/CoinsDeployerBase.sol +111 -17
  79. package/src/hooks/ZoraV4CoinHook.sol +212 -0
  80. package/src/hooks/{BaseCoinDeployHook.sol → deployment/BaseCoinDeployHook.sol} +3 -3
  81. package/src/hooks/deployment/BuySupplyWithSwapRouterHook.sol +140 -0
  82. package/src/interfaces/ICoin.sol +31 -39
  83. package/src/interfaces/ICoinV3.sol +71 -0
  84. package/src/interfaces/ICoinV4.sol +69 -0
  85. package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
  86. package/src/interfaces/IMsgSender.sol +9 -0
  87. package/src/interfaces/IPoolConfigEncoding.sol +14 -0
  88. package/src/interfaces/ISwapPathRouter.sol +14 -0
  89. package/src/interfaces/IZoraFactory.sol +67 -28
  90. package/src/interfaces/IZoraV4CoinHook.sol +116 -0
  91. package/src/libs/CoinCommon.sol +15 -0
  92. package/src/libs/CoinConfigurationVersions.sol +116 -1
  93. package/src/libs/CoinConstants.sol +5 -0
  94. package/src/libs/CoinDopplerMultiCurve.sol +134 -0
  95. package/src/libs/CoinDopplerUniV3.sol +19 -171
  96. package/src/libs/CoinRewards.sol +195 -0
  97. package/src/libs/CoinRewardsV4.sol +179 -0
  98. package/src/libs/CoinSetup.sol +57 -0
  99. package/src/libs/CoinSetupV3.sol +6 -67
  100. package/src/libs/DopplerMath.sol +156 -0
  101. package/src/libs/HooksDeployment.sol +128 -0
  102. package/src/libs/MarketConstants.sol +4 -0
  103. package/src/libs/PoolStateReader.sol +22 -0
  104. package/src/libs/UniV3BuySell.sol +74 -292
  105. package/src/libs/UniV4SwapHelper.sol +65 -0
  106. package/src/libs/UniV4SwapToCurrency.sol +109 -0
  107. package/src/libs/V4Liquidity.sol +122 -0
  108. package/src/types/PoolConfiguration.sol +15 -0
  109. package/src/utils/DeployedCoinVersionLookup.sol +52 -0
  110. package/src/version/ContractVersionBase.sol +1 -1
  111. package/test/Coin.t.sol +78 -88
  112. package/test/CoinDopplerUniV3.t.sol +32 -171
  113. package/test/CoinUniV4.t.sol +777 -0
  114. package/test/{Hooks.t.sol → DeploymentHooks.t.sol} +53 -16
  115. package/test/Factory.t.sol +80 -47
  116. package/test/MultiOwnable.t.sol +6 -3
  117. package/test/Upgrades.t.sol +97 -5
  118. package/test/mocks/MockERC20.sol +12 -0
  119. package/test/utils/BaseTest.sol +162 -57
  120. package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
  121. package/test/utils/FeeEstimatorHook.sol +84 -0
  122. package/test/utils/ProxyShim.sol +17 -0
  123. package/wagmi.config.ts +4 -0
  124. package/.env +0 -1
  125. package/.turbo/turbo-update-contract-version.log +0 -22
  126. package/abis/CoinSetupV3.json +0 -7
  127. package/abis/HookDeployer.json +0 -68
  128. package/abis/IHookDeployer.json +0 -42
  129. package/src/hooks/BuySupplyWithSwapRouterHook.sol +0 -78
  130. package/src/libs/CoinLegacy.sol +0 -48
  131. package/src/libs/CoinLegacyMarket.sol +0 -182
@@ -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,17 +26,35 @@ 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";
35
+ import {ICoinV4} from "../../src/interfaces/ICoinV4.sol";
36
+ import {UniV4SwapHelper} from "../../src/libs/UniV4SwapHelper.sol";
37
+ import {IPermit2} from "permit2/src/interfaces/IPermit2.sol";
38
+ import {IUniversalRouter} from "@uniswap/universal-router/contracts/interfaces/IUniversalRouter.sol";
39
+ import {Commands} from "@uniswap/universal-router/contracts/libraries/Commands.sol";
40
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
41
+ import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
42
+ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
27
43
 
28
44
  contract BaseTest is Test {
29
45
  using stdStorage for StdStorage;
30
46
 
31
- address internal constant PROTOCOL_REWARDS = 0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B;
32
47
  address internal constant WETH_ADDRESS = 0x4200000000000000000000000000000000000006;
33
48
  address internal constant V3_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
34
49
  address internal constant NONFUNGIBLE_POSITION_MANAGER = 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1;
35
50
  address internal constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
36
51
  address internal constant DOPPLER_AIRLOCK = 0x660eAaEdEBc968f8f3694354FA8EC0b4c5Ba8D12;
37
52
  address internal constant USDC_ADDRESS = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
53
+ address internal constant V4_POOL_MANAGER = 0x498581fF718922c3f8e6A244956aF099B2652b2b;
54
+ address internal constant V4_POSITION_MANAGER = 0x7C5f5A4bBd8fD63184577525326123B519429bDc;
55
+ address internal constant V4_PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
56
+ address internal constant V4_QUOTER = 0x0d5e0F971ED27FBfF6c2837bf31316121532048D;
57
+ address internal constant UNIVERSAL_ROUTER = 0x6fF5693b99212Da76ad316178A184AB56D299b43;
38
58
  int24 internal constant USDC_TICK_LOWER = 57200;
39
59
 
40
60
  struct Users {
@@ -54,15 +74,125 @@ contract BaseTest is Test {
54
74
  ProtocolRewards internal protocolRewards;
55
75
  IUniswapV3Factory internal v3Factory;
56
76
  INonfungiblePositionManager internal nonfungiblePositionManager;
77
+ IPermit2 internal permit2;
78
+ IUniversalRouter internal router;
79
+
57
80
  ISwapRouter internal swapRouter;
58
81
  IAirlock internal airlock;
59
82
  Users internal users;
60
83
 
61
- Coin internal coinImpl;
84
+ Coin internal coinV3Impl;
85
+ CoinV4 internal coinV4Impl;
62
86
  ZoraFactoryImpl internal factoryImpl;
63
- ZoraFactoryImpl internal factory;
87
+ IZoraFactory internal factory;
88
+ ZoraV4CoinHook internal zoraV4CoinHook;
64
89
  Coin internal coin;
90
+
65
91
  IUniswapV3Pool internal pool;
92
+ int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER;
93
+ int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
94
+ uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
95
+ uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
96
+
97
+ function _deployCoin() internal {
98
+ bytes memory poolConfig_ = _generatePoolConfig(
99
+ CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
100
+ address(weth),
101
+ DEFAULT_DISCOVERY_TICK_LOWER,
102
+ DEFAULT_DISCOVERY_TICK_UPPER,
103
+ DEFAULT_NUM_DISCOVERY_POSITIONS,
104
+ DEFAULT_DISCOVERY_SUPPLY_SHARE
105
+ );
106
+ vm.prank(users.creator);
107
+ (address coinAddress, ) = factory.deploy(
108
+ users.creator,
109
+ _getDefaultOwners(),
110
+ "https://test.com",
111
+ "Testcoin",
112
+ "TEST",
113
+ poolConfig_,
114
+ users.platformReferrer,
115
+ 0
116
+ );
117
+
118
+ coin = Coin(payable(coinAddress));
119
+ pool = IUniswapV3Pool(coin.poolAddress());
120
+
121
+ vm.label(address(coin), "COIN");
122
+ vm.label(address(pool), "POOL");
123
+ }
124
+
125
+ function _deployCoinUSDCPair() internal {
126
+ bytes memory poolConfig_ = _generatePoolConfig(
127
+ CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
128
+ USDC_ADDRESS,
129
+ DEFAULT_DISCOVERY_TICK_LOWER,
130
+ DEFAULT_DISCOVERY_TICK_UPPER,
131
+ DEFAULT_NUM_DISCOVERY_POSITIONS,
132
+ DEFAULT_DISCOVERY_SUPPLY_SHARE
133
+ );
134
+ vm.prank(users.creator);
135
+ (address coinAddress, ) = factory.deploy(
136
+ users.creator,
137
+ _getDefaultOwners(),
138
+ "https://test.com",
139
+ "Testcoin",
140
+ "TEST",
141
+ poolConfig_,
142
+ users.platformReferrer,
143
+ 0
144
+ );
145
+
146
+ coin = Coin(payable(coinAddress));
147
+ pool = IUniswapV3Pool(coin.poolAddress());
148
+
149
+ vm.label(address(coin), "COIN");
150
+ vm.label(address(pool), "POOL");
151
+ }
152
+
153
+ function _swapSomeCurrencyForCoin(ICoinV4 _coin, address currency, uint128 amountIn, address trader) internal {
154
+ uint128 minAmountOut = uint128(0);
155
+
156
+ (bytes memory commands, bytes[] memory inputs) = UniV4SwapHelper.buildExactInputSingleSwapCommand(
157
+ currency,
158
+ amountIn,
159
+ address(_coin),
160
+ minAmountOut,
161
+ _coin.getPoolKey(),
162
+ bytes("")
163
+ );
164
+
165
+ vm.startPrank(trader);
166
+ UniV4SwapHelper.approveTokenWithPermit2(permit2, address(router), currency, amountIn, uint48(block.timestamp + 1 days));
167
+
168
+ // Execute the swap
169
+ uint256 deadline = block.timestamp + 20;
170
+ router.execute(commands, inputs, deadline);
171
+
172
+ vm.stopPrank();
173
+ }
174
+
175
+ function _swapSomeCoinForCurrency(ICoinV4 _coin, address currency, uint128 amountIn, address trader) internal {
176
+ uint128 minAmountOut = uint128(0);
177
+
178
+ (bytes memory commands, bytes[] memory inputs) = UniV4SwapHelper.buildExactInputSingleSwapCommand(
179
+ address(_coin),
180
+ amountIn,
181
+ currency,
182
+ minAmountOut,
183
+ _coin.getPoolKey(),
184
+ bytes("")
185
+ );
186
+
187
+ vm.startPrank(trader);
188
+ UniV4SwapHelper.approveTokenWithPermit2(permit2, address(router), address(_coin), amountIn, uint48(block.timestamp + 1 days));
189
+
190
+ // Execute the swap
191
+ uint256 deadline = block.timestamp + 20;
192
+ router.execute(commands, inputs, deadline);
193
+
194
+ vm.stopPrank();
195
+ }
66
196
 
67
197
  function setUp() public virtual {
68
198
  setUpWithBlockNumber(28415528);
@@ -78,7 +208,8 @@ contract BaseTest is Test {
78
208
  swapRouter = ISwapRouter(SWAP_ROUTER);
79
209
  airlock = IAirlock(DOPPLER_AIRLOCK);
80
210
  protocolRewards = new ProtocolRewards();
81
-
211
+ permit2 = IPermit2(V4_PERMIT2);
212
+ router = IUniversalRouter(UNIVERSAL_ROUTER);
82
213
  users = Users({
83
214
  factoryOwner: makeAddr("factoryOwner"),
84
215
  feeRecipient: makeAddr("feeRecipient"),
@@ -90,11 +221,21 @@ contract BaseTest is Test {
90
221
  tradeReferrer: makeAddr("tradeReferrer")
91
222
  });
92
223
 
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))));
224
+ address[] memory trustedMessageSenders = new address[](2);
225
+ trustedMessageSenders[0] = UNIVERSAL_ROUTER;
226
+ trustedMessageSenders[1] = V4_POSITION_MANAGER;
96
227
 
97
- ZoraFactoryImpl(factory).initialize(users.factoryOwner);
228
+ ProxyShim mockUpgradeableImpl = new ProxyShim();
229
+ factory = IZoraFactory(address(new ZoraFactory(address(mockUpgradeableImpl))));
230
+ zoraV4CoinHook = ZoraV4CoinHook(address(HooksDeployment.deployZoraV4CoinHookFromContract(V4_POOL_MANAGER, address(factory), trustedMessageSenders)));
231
+ coinV3Impl = new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, V3_FACTORY, SWAP_ROUTER, DOPPLER_AIRLOCK);
232
+ coinV4Impl = new CoinV4(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK, zoraV4CoinHook);
233
+ factoryImpl = new ZoraFactoryImpl(address(coinV3Impl), address(coinV4Impl));
234
+ UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
235
+ factory = IZoraFactory(address(factory));
236
+ // factory = ZoraFactoryImpl(address(new ZoraFactory(address(factoryImpl))));
237
+
238
+ ZoraFactoryImpl(address(factory)).initialize(users.factoryOwner);
98
239
 
99
240
  vm.label(address(factory), "ZORA_FACTORY");
100
241
  vm.label(address(protocolRewards), "PROTOCOL_REWARDS");
@@ -120,54 +261,6 @@ contract BaseTest is Test {
120
261
  uint256 protocol;
121
262
  }
122
263
 
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
264
  function _calculateTradeRewards(uint256 ethAmount) internal pure returns (TradeRewards memory) {
172
265
  return
173
266
  TradeRewards({
@@ -207,6 +300,18 @@ contract BaseTest is Test {
207
300
  return airlock.owner();
208
301
  }
209
302
 
303
+ function _generatePoolConfig(address currency_) internal pure returns (bytes memory) {
304
+ return
305
+ _generatePoolConfig(
306
+ CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
307
+ currency_,
308
+ DEFAULT_DISCOVERY_TICK_LOWER,
309
+ DEFAULT_DISCOVERY_TICK_UPPER,
310
+ DEFAULT_NUM_DISCOVERY_POSITIONS,
311
+ DEFAULT_DISCOVERY_SUPPLY_SHARE
312
+ );
313
+ }
314
+
210
315
  function _generatePoolConfig(
211
316
  uint8 version_,
212
317
  address currency_,
@@ -0,0 +1,127 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {BaseTest} from "../utils/BaseTest.sol";
5
+ import {DeployedCoinVersionLookup} from "../../src/utils/DeployedCoinVersionLookup.sol";
6
+
7
+ contract TestDeployedCoinVersionLookupImplementation is DeployedCoinVersionLookup {
8
+ function setVersionForTesting(address coin, uint8 version) external {
9
+ _setVersionForDeployedCoin(coin, version);
10
+ }
11
+ }
12
+
13
+ /**
14
+ * @title Mock implementation with different namespace
15
+ * @dev Used to verify that different namespaces don't collide
16
+ */
17
+ contract DifferentNamespaceVersionLookup {
18
+ /// @custom:storage-location erc7201:different.namespace
19
+ struct DeployedCoinVersionStorage {
20
+ mapping(address => uint8) deployedCoinWithVersion;
21
+ }
22
+
23
+ // keccak256(abi.encode(uint256(keccak256("different.namespace")) - 1)) & ~bytes32(uint256(0xff))
24
+ bytes32 private constant DEPLOYED_COIN_VERSION_STORAGE_LOCATION = 0xf0ec9c7ea8b861b539967dd0659fb8887a9724eca55e932839a2a8e01f50c400;
25
+
26
+ function _getDeployedCoinVersionStorage() private pure returns (DeployedCoinVersionStorage storage $) {
27
+ assembly {
28
+ $.slot := DEPLOYED_COIN_VERSION_STORAGE_LOCATION
29
+ }
30
+ }
31
+
32
+ function getVersionForDeployedCoin(address coin) public view returns (uint8) {
33
+ return _getDeployedCoinVersionStorage().deployedCoinWithVersion[coin];
34
+ }
35
+
36
+ function setVersionForTesting(address coin, uint8 version) external {
37
+ _getDeployedCoinVersionStorage().deployedCoinWithVersion[coin] = version;
38
+ }
39
+ }
40
+
41
+ contract DeployedCoinVersionLookupTest is BaseTest {
42
+ TestDeployedCoinVersionLookupImplementation public versionLookup;
43
+ DifferentNamespaceVersionLookup public differentNamespaceLookup;
44
+ address public testCoin1;
45
+ address public testCoin2;
46
+ address public testContractAddress;
47
+
48
+ function setUp() public override {
49
+ super.setUp();
50
+ versionLookup = new TestDeployedCoinVersionLookupImplementation();
51
+ differentNamespaceLookup = new DifferentNamespaceVersionLookup();
52
+ testCoin1 = makeAddr("testCoin1");
53
+ testCoin2 = makeAddr("testCoin2");
54
+ testContractAddress = makeAddr("testContractAddress");
55
+ }
56
+
57
+ function test_getAndSetVersionForDeployedCoin() public {
58
+ // Default version should be 0
59
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 0);
60
+
61
+ // Set version and verify
62
+ versionLookup.setVersionForTesting(testCoin1, 1);
63
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 1);
64
+
65
+ // Set version for a different coin
66
+ versionLookup.setVersionForTesting(testCoin2, 2);
67
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin2), 2);
68
+
69
+ // First coin's version should remain unchanged
70
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 1);
71
+
72
+ // Update version and verify
73
+ versionLookup.setVersionForTesting(testCoin1, 3);
74
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 3);
75
+ }
76
+
77
+ function test_differentNamespaceIndependence() public {
78
+ // First deploy the original implementation at a fixed address
79
+ TestDeployedCoinVersionLookupImplementation originalImpl = new TestDeployedCoinVersionLookupImplementation();
80
+ bytes memory originalBytecode = address(originalImpl).code;
81
+
82
+ // Deploy a different implementation
83
+ DifferentNamespaceVersionLookup differentImpl = new DifferentNamespaceVersionLookup();
84
+ bytes memory differentBytecode = address(differentImpl).code;
85
+
86
+ // Etch the original implementation to the test address
87
+ vm.etch(testContractAddress, originalBytecode);
88
+
89
+ // Test setting values with the first implementation
90
+ TestDeployedCoinVersionLookupImplementation(testContractAddress).setVersionForTesting(testCoin1, 42);
91
+ assertEq(TestDeployedCoinVersionLookupImplementation(testContractAddress).getVersionForDeployedCoin(testCoin1), 42);
92
+
93
+ // Save the bytecode location for the first implementation
94
+ bytes32 firstSlot = vm.load(
95
+ testContractAddress,
96
+ bytes32(uint256(keccak256(abi.encode(testCoin1, 0x9a79df0b86f39d0543c14aee714123562f798115071e932933bcc3e29cc86f00))))
97
+ );
98
+ assertEq(uint256(firstSlot), 42);
99
+
100
+ // Now replace the code with the different namespace implementation
101
+ vm.etch(testContractAddress, differentBytecode);
102
+
103
+ // Set a value with the different implementation
104
+ DifferentNamespaceVersionLookup(testContractAddress).setVersionForTesting(testCoin1, 99);
105
+
106
+ // This should use a different storage slot, so it shouldn't affect the original value
107
+ assertEq(DifferentNamespaceVersionLookup(testContractAddress).getVersionForDeployedCoin(testCoin1), 99);
108
+
109
+ // Verify the original storage slot still has the original value
110
+ bytes32 secondSlot = vm.load(
111
+ testContractAddress,
112
+ bytes32(uint256(keccak256(abi.encode(testCoin1, 0xf0ec9c7ea8b861b539967dd0659fb8887a9724eca55e932839a2a8e01f50c400))))
113
+ );
114
+ assertEq(uint256(secondSlot), 99);
115
+
116
+ // Switch back to the original implementation to verify its storage is unchanged
117
+ vm.etch(testContractAddress, originalBytecode);
118
+ assertEq(TestDeployedCoinVersionLookupImplementation(testContractAddress).getVersionForDeployedCoin(testCoin1), 42);
119
+
120
+ // Change the value in the original implementation
121
+ TestDeployedCoinVersionLookupImplementation(testContractAddress).setVersionForTesting(testCoin1, 123);
122
+
123
+ // Switch back to the different namespace implementation to verify its storage is unchanged
124
+ vm.etch(testContractAddress, differentBytecode);
125
+ assertEq(DifferentNamespaceVersionLookup(testContractAddress).getVersionForDeployedCoin(testCoin1), 99);
126
+ }
127
+ }
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
5
+ import {IPoolManager, PoolKey} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
6
+ import {IDeployedCoinVersionLookup} from "../../src/interfaces/IDeployedCoinVersionLookup.sol";
7
+ import {IHasRewardsRecipients} from "../../src/interfaces/ICoin.sol";
8
+ import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
9
+ import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
10
+ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
11
+ import {CoinCommon} from "../../src/libs/CoinCommon.sol";
12
+ import {V4Liquidity} from "../../src/libs/V4Liquidity.sol";
13
+ import {BaseHook} from "@uniswap/v4-periphery/src/utils/BaseHook.sol";
14
+ import {ICoinV4, IHasSwapPath} from "../../src/interfaces/ICoinV4.sol";
15
+ import {UniV4SwapToCurrency} from "../../src/libs/UniV4SwapToCurrency.sol";
16
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
17
+ import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
18
+
19
+ /// @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
20
+ contract FeeEstimatorHook is ZoraV4CoinHook {
21
+ struct FeeEstimatorState {
22
+ uint128 fees0;
23
+ uint128 fees1;
24
+ Currency afterSwapCurrency;
25
+ uint128 afterSwapCurrencyAmount;
26
+ BalanceDelta lastDelta;
27
+ SwapParams lastSwapParams;
28
+ uint256 currencyBalanceChange;
29
+ uint256 coinBalanceChange;
30
+ }
31
+
32
+ constructor(IPoolManager _poolManager, IDeployedCoinVersionLookup _coinVersionLookup) ZoraV4CoinHook(_poolManager, _coinVersionLookup, new address[](0)) {}
33
+
34
+ FeeEstimatorState public feeState;
35
+
36
+ function getFeeState() public view returns (FeeEstimatorState memory) {
37
+ return feeState;
38
+ }
39
+
40
+ function _afterSwap(
41
+ address,
42
+ PoolKey calldata key,
43
+ SwapParams calldata params,
44
+ BalanceDelta _delta,
45
+ bytes calldata
46
+ ) internal override returns (bytes4, int128) {
47
+ bytes32 poolKeyHash = CoinCommon.hashPoolKey(key);
48
+
49
+ // get the coin address and positions for the pool key; they must have been set in the afterInitialize callback
50
+ address coin = poolCoins[poolKeyHash].coin;
51
+ require(coin != address(0), NoCoinForHook(key));
52
+
53
+ {
54
+ uint256 coinBalanceBefore = IERC20(coin).balanceOf(address(this));
55
+ uint256 currencyBalanceBefore = IERC20(ICoinV4(coin).currency()).balanceOf(address(this));
56
+
57
+ IHasSwapPath.PayoutSwapPath memory payoutSwapPath = IHasSwapPath(coin).getPayoutSwapPath(coinVersionLookup);
58
+
59
+ int128 fee0;
60
+ int128 fee1;
61
+
62
+ (fee0, fee1, feeState.afterSwapCurrency, feeState.afterSwapCurrencyAmount) = CoinRewardsV4.collectFeesAndConvertToPayout(
63
+ poolManager,
64
+ key,
65
+ poolCoins[poolKeyHash].positions,
66
+ payoutSwapPath
67
+ );
68
+
69
+ feeState.fees0 += uint128(fee0);
70
+ feeState.fees1 += uint128(fee1);
71
+
72
+ uint256 coinBalanceAfter = IERC20(coin).balanceOf(address(this));
73
+ uint256 currencyBalanceAfter = IERC20(ICoinV4(coin).currency()).balanceOf(address(this));
74
+
75
+ feeState.coinBalanceChange = coinBalanceAfter - coinBalanceBefore;
76
+ feeState.currencyBalanceChange = currencyBalanceAfter - currencyBalanceBefore;
77
+ }
78
+
79
+ feeState.lastDelta = _delta;
80
+ feeState.lastSwapParams = params;
81
+
82
+ return (BaseHook.afterSwap.selector, 0);
83
+ }
84
+ }
@@ -0,0 +1,17 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.18;
3
+
4
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5
+
6
+ /// Used to deploy the factory before we know the impl address
7
+ contract ProxyShim is UUPSUpgradeable {
8
+ address immutable canUpgrade;
9
+
10
+ constructor() {
11
+ canUpgrade = msg.sender;
12
+ }
13
+
14
+ function _authorizeUpgrade(address) internal view override {
15
+ require(msg.sender == canUpgrade, "not authorized");
16
+ }
17
+ }
package/wagmi.config.ts CHANGED
@@ -10,9 +10,13 @@ export default defineConfig({
10
10
  },
11
11
  include: [
12
12
  "Coin",
13
+ "CoinV4",
13
14
  "ZoraFactoryImpl",
14
15
  "IUniswapV3Pool",
15
16
  "BuySupplyWithSwapRouterHook",
17
+ "IPoolConfigEncoding",
18
+ "IUniversalRouter",
19
+ "IPermit2",
16
20
  ].map((contractName) => `${contractName}.json`),
17
21
  }),
18
22
  ],
package/.env DELETED
@@ -1 +0,0 @@
1
- ALCHEMY_KEY=Ebx4-biYR4T-p-1BOId9DozVRrr3nHu4
@@ -1,22 +0,0 @@
1
-
2
- 
3
- > @zoralabs/coins@0.8.0 update-contract-version /Users/danovedzora/source/zora-protocol/packages/coins
4
- > pnpm exec update-contract-version
5
-
6
- updating contract version to 0.8.0
7
- generated contract version code: // This file is automatically generated by code; do not manually update
8
- // SPDX-License-Identifier: MIT
9
- pragma solidity ^0.8.23;
10
-
11
- import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
12
-
13
- /// @title ContractVersionBase
14
- /// @notice Base contract for versioning contracts
15
- contract ContractVersionBase is IVersionedContract {
16
- /// @notice The version of the contract
17
- function contractVersion() external pure override returns (string memory) {
18
- return "0.8.0";
19
- }
20
- }
21
-
22
- writing file to /Users/danovedzora/source/zora-protocol/packages/coins/src/version/ContractVersionBase.sol
@@ -1,7 +0,0 @@
1
- [
2
- {
3
- "type": "error",
4
- "name": "InvalidPoolVersion",
5
- "inputs": []
6
- }
7
- ]
@@ -1,68 +0,0 @@
1
- [
2
- {
3
- "type": "function",
4
- "name": "deployZoraV4CoinHookFromContract",
5
- "inputs": [
6
- {
7
- "name": "poolManager",
8
- "type": "address",
9
- "internalType": "address"
10
- }
11
- ],
12
- "outputs": [
13
- {
14
- "name": "hook",
15
- "type": "address",
16
- "internalType": "contract IHooks"
17
- }
18
- ],
19
- "stateMutability": "nonpayable"
20
- },
21
- {
22
- "type": "error",
23
- "name": "Create2EmptyBytecode",
24
- "inputs": []
25
- },
26
- {
27
- "type": "error",
28
- "name": "Create2FailedDeployment",
29
- "inputs": []
30
- },
31
- {
32
- "type": "error",
33
- "name": "Create2InsufficientBalance",
34
- "inputs": [
35
- {
36
- "name": "balance",
37
- "type": "uint256",
38
- "internalType": "uint256"
39
- },
40
- {
41
- "name": "needed",
42
- "type": "uint256",
43
- "internalType": "uint256"
44
- }
45
- ]
46
- },
47
- {
48
- "type": "error",
49
- "name": "HookNotDeployed",
50
- "inputs": []
51
- },
52
- {
53
- "type": "error",
54
- "name": "InvalidHookAddress",
55
- "inputs": [
56
- {
57
- "name": "expected",
58
- "type": "address",
59
- "internalType": "address"
60
- },
61
- {
62
- "name": "actual",
63
- "type": "address",
64
- "internalType": "address"
65
- }
66
- ]
67
- }
68
- ]
@@ -1,42 +0,0 @@
1
- [
2
- {
3
- "type": "function",
4
- "name": "deployZoraV4CoinHookFromContract",
5
- "inputs": [
6
- {
7
- "name": "poolManager",
8
- "type": "address",
9
- "internalType": "address"
10
- }
11
- ],
12
- "outputs": [
13
- {
14
- "name": "hook",
15
- "type": "address",
16
- "internalType": "contract IHooks"
17
- }
18
- ],
19
- "stateMutability": "nonpayable"
20
- },
21
- {
22
- "type": "error",
23
- "name": "HookNotDeployed",
24
- "inputs": []
25
- },
26
- {
27
- "type": "error",
28
- "name": "InvalidHookAddress",
29
- "inputs": [
30
- {
31
- "name": "expected",
32
- "type": "address",
33
- "internalType": "address"
34
- },
35
- {
36
- "name": "actual",
37
- "type": "address",
38
- "internalType": "address"
39
- }
40
- ]
41
- }
42
- ]