@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
@@ -12,21 +12,46 @@ import {CoinConfigurationVersions} from "./libs/CoinConfigurationVersions.sol";
12
12
  import {ISwapRouter} from "./interfaces/ISwapRouter.sol";
13
13
  import {IWETH} from "./interfaces/IWETH.sol";
14
14
  import {IZoraFactory} from "./interfaces/IZoraFactory.sol";
15
- import {IHasAfterCoinDeploy} from "./hooks/BaseCoinDeployHook.sol";
15
+ import {IHasAfterCoinDeploy} from "./hooks/deployment/BaseCoinDeployHook.sol";
16
16
  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
17
17
  import {Coin} from "./Coin.sol";
18
- import {ICoin} from "./interfaces/ICoin.sol";
18
+ import {CoinV4} from "./CoinV4.sol";
19
+ import {ICoin, PoolKeyStruct} from "./interfaces/ICoin.sol";
20
+ import {ICoinV3} from "./interfaces/ICoinV3.sol";
21
+ import {ICoinV4} from "./interfaces/ICoinV4.sol";
19
22
  import {IHasContractName} from "@zoralabs/shared-contracts/interfaces/IContractMetadata.sol";
20
23
  import {ContractVersionBase} from "./version/ContractVersionBase.sol";
24
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
25
+ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
26
+ import {CoinCommon} from "./libs/CoinCommon.sol";
27
+ import {UniV3Config} from "./libs/CoinSetupV3.sol";
28
+ import {CoinSetupV3} from "./libs/CoinSetupV3.sol";
29
+ import {PoolConfiguration} from "./types/PoolConfiguration.sol";
30
+ import {LpPosition} from "./types/LpPosition.sol";
31
+ import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
32
+ import {CoinSetup} from "./libs/CoinSetup.sol";
33
+ import {CoinDopplerMultiCurve} from "./libs/CoinDopplerMultiCurve.sol";
21
34
 
22
- contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgradeable, OwnableUpgradeable, IHasContractName, ContractVersionBase {
35
+ import {DeployedCoinVersionLookup} from "./utils/DeployedCoinVersionLookup.sol";
36
+
37
+ contract ZoraFactoryImpl is
38
+ IZoraFactory,
39
+ UUPSUpgradeable,
40
+ ReentrancyGuardUpgradeable,
41
+ OwnableUpgradeable,
42
+ IHasContractName,
43
+ ContractVersionBase,
44
+ DeployedCoinVersionLookup
45
+ {
23
46
  using SafeERC20 for IERC20;
24
47
 
25
48
  /// @notice The coin contract implementation address
26
49
  address public immutable coinImpl;
50
+ address public immutable coinV4Impl;
27
51
 
28
- constructor(address _coinImpl) initializer {
52
+ constructor(address _coinImpl, address _coinV4Impl) initializer {
29
53
  coinImpl = _coinImpl;
54
+ coinV4Impl = _coinV4Impl;
30
55
  }
31
56
 
32
57
  /// @inheritdoc IZoraFactory
@@ -38,17 +63,27 @@ contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgrad
38
63
  string memory symbol,
39
64
  bytes memory poolConfig,
40
65
  address platformReferrer,
41
- uint256 orderSize
42
- ) public payable nonReentrant returns (address, uint256) {
43
- Coin coin = _createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer);
44
-
45
- uint256 coinsPurchased = _handleFirstOrder(coin, orderSize);
66
+ address postDeployHook,
67
+ bytes calldata postDeployHookData,
68
+ bytes32 coinSalt
69
+ ) external payable returns (address coin, bytes memory postDeployHookDataOut) {
70
+ bytes32 salt = _buildSalt(msg.sender, name, symbol, poolConfig, platformReferrer, coinSalt);
71
+ return _deployWithHook(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, postDeployHook, postDeployHookData, salt);
72
+ }
46
73
 
47
- return (address(coin), coinsPurchased);
74
+ function coinAddress(
75
+ address msgSender,
76
+ string memory name,
77
+ string memory symbol,
78
+ bytes memory poolConfig,
79
+ address platformReferrer,
80
+ bytes32 coinSalt
81
+ ) external view returns (address) {
82
+ bytes32 salt = _buildSalt(msgSender, name, symbol, poolConfig, platformReferrer, coinSalt);
83
+ return Clones.predictDeterministicAddress(getCoinImpl(CoinConfigurationVersions.getVersion(poolConfig)), salt, address(this));
48
84
  }
49
85
 
50
- /// @inheritdoc IZoraFactory
51
- function deployWithHook(
86
+ function _deployWithHook(
52
87
  address payoutRecipient,
53
88
  address[] memory owners,
54
89
  string memory uri,
@@ -57,9 +92,10 @@ contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgrad
57
92
  bytes memory poolConfig,
58
93
  address platformReferrer,
59
94
  address hook,
60
- bytes calldata hookData
61
- ) public payable nonReentrant returns (address coin, bytes memory hookDataOut) {
62
- coin = address(_createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer));
95
+ bytes calldata hookData,
96
+ bytes32 salt
97
+ ) internal returns (address coin, bytes memory hookDataOut) {
98
+ coin = address(_createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt));
63
99
 
64
100
  if (hook != address(0)) {
65
101
  if (!IERC165(hook).supportsInterface(type(IHasAfterCoinDeploy).interfaceId)) {
@@ -72,60 +108,195 @@ contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgrad
72
108
  }
73
109
  }
74
110
 
75
- /// @inheritdoc IZoraFactory
111
+ /** Deprecated deploy functions */
112
+
113
+ /// @dev Deprecated: use `deploy` instead that has a salt and hook specified
76
114
  function deploy(
77
115
  address payoutRecipient,
78
116
  address[] memory owners,
79
117
  string memory uri,
80
118
  string memory name,
81
119
  string memory symbol,
120
+ bytes memory poolConfig,
82
121
  address platformReferrer,
83
- address currency,
84
- int24 tickLower,
85
122
  uint256 orderSize
86
123
  ) public payable nonReentrant returns (address, uint256) {
87
- bytes memory poolConfig = abi.encode(CoinConfigurationVersions.LEGACY_POOL_VERSION, currency, tickLower);
124
+ bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
88
125
 
89
- Coin coin = _createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer);
126
+ ICoin coin = _createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt);
90
127
 
91
128
  uint256 coinsPurchased = _handleFirstOrder(coin, orderSize);
92
129
 
93
130
  return (address(coin), coinsPurchased);
94
131
  }
95
132
 
96
- function _createAndInitializeCoin(
133
+ /// @dev Deprecated: use `deploy` instead that has a salt and hook specified
134
+ function deployWithHook(
97
135
  address payoutRecipient,
98
136
  address[] memory owners,
99
137
  string memory uri,
100
138
  string memory name,
101
139
  string memory symbol,
102
140
  bytes memory poolConfig,
141
+ address platformReferrer,
142
+ address hook,
143
+ bytes calldata hookData
144
+ ) public payable nonReentrant returns (address coin, bytes memory hookDataOut) {
145
+ bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
146
+ return _deployWithHook(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, hook, hookData, salt);
147
+ }
148
+
149
+ /// @dev deprecated Use deploy() with poolConfig instead
150
+ function deploy(
151
+ address payoutRecipient,
152
+ address[] memory owners,
153
+ string memory uri,
154
+ string memory name,
155
+ string memory symbol,
156
+ address platformReferrer,
157
+ address currency,
158
+ // tickLower is no longer used
159
+ int24 /*tickLower*/,
160
+ uint256 orderSize
161
+ ) public payable nonReentrant returns (address, uint256) {
162
+ bytes memory poolConfig = CoinConfigurationVersions.defaultConfig(currency);
163
+ bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
164
+
165
+ ICoin coin = _createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt);
166
+
167
+ uint256 coinsPurchased = _handleFirstOrder(coin, orderSize);
168
+
169
+ return (address(coin), coinsPurchased);
170
+ }
171
+
172
+ function getCoinImpl(uint8 version) internal view returns (address) {
173
+ if (CoinConfigurationVersions.isV3(version)) {
174
+ return coinImpl;
175
+ } else if (CoinConfigurationVersions.isV4(version)) {
176
+ return coinV4Impl;
177
+ }
178
+
179
+ revert ICoin.InvalidPoolVersion();
180
+ }
181
+
182
+ function _createCoin(uint8 version, bytes32 salt) internal returns (address payable) {
183
+ return payable(Clones.cloneDeterministic(getCoinImpl(version), salt));
184
+ }
185
+
186
+ function _setupV3Coin(
187
+ ICoinV3 coin,
188
+ address currency,
189
+ bool isCoinToken0,
190
+ uint160 sqrtPriceX96,
191
+ PoolConfiguration memory poolConfiguration,
192
+ address payoutRecipient,
193
+ address[] memory owners,
194
+ string memory uri,
195
+ string memory name,
196
+ string memory symbol,
103
197
  address platformReferrer
104
- ) internal returns (Coin) {
105
- bytes32 salt = _generateSalt(payoutRecipient, uri);
198
+ ) internal {
199
+ address v3Factory = coin.v3Factory();
106
200
 
107
- Coin coin = Coin(payable(Clones.cloneDeterministic(coinImpl, salt)));
201
+ address poolAddress = CoinSetupV3.createV3Pool(address(coin), currency, isCoinToken0, sqrtPriceX96, v3Factory);
108
202
 
109
- coin.initialize(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer);
203
+ LpPosition[] memory positions = CoinDopplerMultiCurve.calculatePositions(isCoinToken0, poolConfiguration);
204
+
205
+ // Initialize coin with pre-configured pool
206
+ coin.initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolAddress, poolConfiguration, positions);
110
207
 
111
208
  emit CoinCreated(
112
209
  msg.sender,
113
210
  payoutRecipient,
114
- coin.platformReferrer(),
115
- coin.currency(),
211
+ platformReferrer,
212
+ currency,
116
213
  uri,
117
214
  name,
118
215
  symbol,
119
216
  address(coin),
120
- coin.poolAddress(),
121
- coin.contractVersion()
217
+ poolAddress,
218
+ IVersionedContract(address(coin)).contractVersion()
122
219
  );
220
+ }
123
221
 
124
- return coin;
222
+ function _setupV4Coin(
223
+ ICoinV4 coin,
224
+ address currency,
225
+ bool isCoinToken0,
226
+ uint160 sqrtPriceX96,
227
+ PoolConfiguration memory poolConfiguration,
228
+ address payoutRecipient,
229
+ address[] memory owners,
230
+ string memory uri,
231
+ string memory name,
232
+ string memory symbol,
233
+ address platformReferrer
234
+ ) internal {
235
+ PoolKey memory poolKey = CoinSetup.buildPoolKey(address(coin), currency, isCoinToken0, coin.hooks());
236
+
237
+ // Initialize coin with pre-configured pool
238
+ coin.initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolKey, sqrtPriceX96, poolConfiguration);
239
+
240
+ emit CoinCreatedV4(
241
+ msg.sender,
242
+ payoutRecipient,
243
+ platformReferrer,
244
+ currency,
245
+ uri,
246
+ name,
247
+ symbol,
248
+ address(coin),
249
+ poolKey,
250
+ CoinCommon.hashPoolKey(poolKey),
251
+ IVersionedContract(address(coin)).contractVersion()
252
+ );
253
+ }
254
+
255
+ function _createAndInitializeCoin(
256
+ address payoutRecipient,
257
+ address[] memory owners,
258
+ string memory uri,
259
+ string memory name,
260
+ string memory symbol,
261
+ bytes memory poolConfig,
262
+ address platformReferrer,
263
+ bytes32 coinSalt
264
+ ) internal returns (ICoin) {
265
+ uint8 version = CoinConfigurationVersions.getVersion(poolConfig);
266
+
267
+ address payable coin = _createCoin(version, coinSalt);
268
+
269
+ _setVersionForDeployedCoin(address(coin), version);
270
+
271
+ (, address currency, uint160 sqrtPriceX96, bool isCoinToken0, PoolConfiguration memory poolConfiguration) = CoinSetup.generatePoolConfig(
272
+ address(coin),
273
+ poolConfig
274
+ );
275
+
276
+ if (CoinConfigurationVersions.isV3(version)) {
277
+ _setupV3Coin(ICoinV3(coin), currency, isCoinToken0, sqrtPriceX96, poolConfiguration, payoutRecipient, owners, uri, name, symbol, platformReferrer);
278
+ } else if (CoinConfigurationVersions.isV4(version)) {
279
+ _setupV4Coin(ICoinV4(coin), currency, isCoinToken0, sqrtPriceX96, poolConfiguration, payoutRecipient, owners, uri, name, symbol, platformReferrer);
280
+ } else {
281
+ revert ICoin.InvalidPoolVersion();
282
+ }
283
+
284
+ return ICoin(coin);
285
+ }
286
+
287
+ function _buildSalt(
288
+ address msgSender,
289
+ string memory name,
290
+ string memory symbol,
291
+ bytes memory poolConfig,
292
+ address platformReferrer,
293
+ bytes32 coinSalt
294
+ ) internal pure returns (bytes32) {
295
+ return keccak256(abi.encodePacked(msgSender, name, symbol, poolConfig, platformReferrer, coinSalt));
125
296
  }
126
297
 
127
298
  /// @dev Generates a unique salt for deterministic deployment
128
- function _generateSalt(address payoutRecipient, string memory uri) internal view returns (bytes32) {
299
+ function _randomSalt(address payoutRecipient, string memory uri, bytes32 coinSalt) internal view returns (bytes32) {
129
300
  return
130
301
  keccak256(
131
302
  abi.encodePacked(
@@ -137,7 +308,8 @@ contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgrad
137
308
  block.prevrandao,
138
309
  block.timestamp,
139
310
  tx.gasprice,
140
- tx.origin
311
+ tx.origin,
312
+ coinSalt
141
313
  )
142
314
  );
143
315
  }
@@ -145,12 +317,12 @@ contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgrad
145
317
  /// @dev Handles the first buy of a newly created coin
146
318
  /// @param coin The newly created coin contract
147
319
  /// @param orderSize The size of the first buy order; must match msg.value for ETH/WETH pairs
148
- function _handleFirstOrder(Coin coin, uint256 orderSize) internal returns (uint256 coinsPurchased) {
320
+ function _handleFirstOrder(ICoin coin, uint256 orderSize) internal returns (uint256 coinsPurchased) {
149
321
  if (msg.value > 0 || orderSize > 0) {
150
322
  address currency = coin.currency();
151
323
  address payoutRecipient = coin.payoutRecipient();
152
324
 
153
- if (currency != coin.WETH()) {
325
+ if (currency != Coin(payable(address(coin))).WETH()) {
154
326
  if (msg.value != 0) {
155
327
  revert EthTransferInvalid();
156
328
  }
@@ -159,9 +331,9 @@ contract ZoraFactoryImpl is IZoraFactory, UUPSUpgradeable, ReentrancyGuardUpgrad
159
331
 
160
332
  IERC20(currency).approve(address(coin), orderSize);
161
333
 
162
- (, coinsPurchased) = coin.buy(payoutRecipient, orderSize, 0, 0, address(0));
334
+ (, coinsPurchased) = Coin(payable(address(coin))).buy(payoutRecipient, orderSize, 0, 0, address(0));
163
335
  } else {
164
- (, coinsPurchased) = coin.buy{value: msg.value}(payoutRecipient, orderSize, 0, 0, address(0));
336
+ (, coinsPurchased) = Coin(payable(address(coin))).buy{value: msg.value}(payoutRecipient, orderSize, 0, 0, address(0));
165
337
  }
166
338
  }
167
339
  }
@@ -5,14 +5,22 @@ import "forge-std/Script.sol";
5
5
 
6
6
  import {ProxyDeployerScript, DeterministicContractConfig, DeterministicDeployerAndCaller} from "@zoralabs/shared-contracts/deployment/ProxyDeployerScript.sol";
7
7
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
8
- import {ZoraFactoryImpl} from "../src/ZoraFactoryImpl.sol";
9
- import {Coin} from "../src/Coin.sol";
8
+ import {ZoraFactoryImpl} from "../ZoraFactoryImpl.sol";
9
+ import {Coin} from "../Coin.sol";
10
10
  import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
11
- import {BuySupplyWithSwapRouterHook} from "../src/hooks/BuySupplyWithSwapRouterHook.sol";
12
- import {IZoraFactory} from "../src/interfaces/IZoraFactory.sol";
11
+ import {BuySupplyWithSwapRouterHook} from "../hooks/deployment/BuySupplyWithSwapRouterHook.sol";
12
+ import {IZoraFactory} from "../interfaces/IZoraFactory.sol";
13
+ import {CoinV4} from "../CoinV4.sol";
14
+ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
15
+ import {ZoraV4CoinHook} from "../hooks/ZoraV4CoinHook.sol";
16
+ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
17
+ import {ZoraFactory} from "../proxy/ZoraFactory.sol";
18
+ import {HooksDeployment} from "../libs/HooksDeployment.sol";
19
+ import {ProxyShim} from "../../test/utils/ProxyShim.sol";
13
20
 
14
21
  contract CoinsDeployerBase is ProxyDeployerScript {
15
22
  address internal constant PROTOCOL_REWARDS = 0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B;
23
+ address internal constant ZORA = 0x1111111111166b7FE7bd91427724B487980aFc69;
16
24
 
17
25
  using stdJson for string;
18
26
 
@@ -21,13 +29,25 @@ contract CoinsDeployerBase is ProxyDeployerScript {
21
29
  address zoraFactory;
22
30
  address zoraFactoryImpl;
23
31
  // Implementation
24
- address coinImpl;
32
+ address coinV3Impl;
33
+ address coinV4Impl;
25
34
  string coinVersion;
26
35
  // hooks
27
36
  address buySupplyWithSwapRouterHook;
37
+ address zoraV4CoinHook;
38
+ // Hook deployment salt (for deterministic deployment)
39
+ bytes32 zoraV4CoinHookSalt;
40
+ bool isDev;
28
41
  }
29
42
 
30
43
  function addressesFile() internal view returns (string memory) {
44
+ return addressesFile(false);
45
+ }
46
+
47
+ function addressesFile(bool dev) internal view returns (string memory) {
48
+ if (dev) {
49
+ return string.concat("./addresses/dev/", vm.toString(block.chainid), ".json");
50
+ }
31
51
  return string.concat("./addresses/", vm.toString(block.chainid), ".json");
32
52
  }
33
53
 
@@ -38,13 +58,21 @@ contract CoinsDeployerBase is ProxyDeployerScript {
38
58
  vm.serializeAddress(objectKey, "ZORA_FACTORY_IMPL", deployment.zoraFactoryImpl);
39
59
  vm.serializeString(objectKey, "COIN_VERSION", deployment.coinVersion);
40
60
  vm.serializeAddress(objectKey, "BUY_SUPPLY_WITH_SWAP_ROUTER_HOOK", deployment.buySupplyWithSwapRouterHook);
41
- string memory result = vm.serializeAddress(objectKey, "COIN_IMPL", deployment.coinImpl);
61
+ vm.serializeAddress(objectKey, "COIN_V3_IMPL", deployment.coinV3Impl);
62
+ vm.serializeAddress(objectKey, "ZORA_V4_COIN_HOOK", deployment.zoraV4CoinHook);
63
+ vm.serializeBytes32(objectKey, "ZORA_V4_COIN_HOOK_SALT", deployment.zoraV4CoinHookSalt);
64
+ string memory result = vm.serializeAddress(objectKey, "COIN_V4_IMPL", deployment.coinV4Impl);
42
65
 
43
- vm.writeJson(result, addressesFile());
66
+ vm.writeJson(result, addressesFile(deployment.isDev));
44
67
  }
45
68
 
46
69
  function readDeployment() internal returns (CoinsDeployment memory deployment) {
47
- string memory file = addressesFile();
70
+ return readDeployment(false);
71
+ }
72
+
73
+ function readDeployment(bool dev) internal returns (CoinsDeployment memory deployment) {
74
+ string memory file = addressesFile(dev);
75
+ deployment.isDev = dev;
48
76
  if (!vm.exists(file)) {
49
77
  return deployment;
50
78
  }
@@ -52,28 +80,78 @@ contract CoinsDeployerBase is ProxyDeployerScript {
52
80
 
53
81
  deployment.zoraFactory = readAddressOrDefaultToZero(json, "ZORA_FACTORY");
54
82
  deployment.zoraFactoryImpl = readAddressOrDefaultToZero(json, "ZORA_FACTORY_IMPL");
55
- deployment.coinImpl = readAddressOrDefaultToZero(json, "COIN_IMPL");
83
+ deployment.coinV3Impl = readAddressOrDefaultToZero(json, "COIN_V3_IMPL");
84
+ deployment.coinV4Impl = readAddressOrDefaultToZero(json, "COIN_V4_IMPL");
56
85
  deployment.coinVersion = readStringOrDefaultToEmpty(json, "COIN_VERSION");
57
86
  deployment.buySupplyWithSwapRouterHook = readAddressOrDefaultToZero(json, "BUY_SUPPLY_WITH_SWAP_ROUTER_HOOK");
87
+ deployment.zoraV4CoinHook = readAddressOrDefaultToZero(json, "ZORA_V4_COIN_HOOK");
88
+ deployment.zoraV4CoinHookSalt = readBytes32OrDefaultToZero(json, "ZORA_V4_COIN_HOOK_SALT");
89
+ }
90
+
91
+ function deployCoinV3Impl() internal returns (Coin) {
92
+ return
93
+ new Coin({
94
+ protocolRewardRecipient_: getZoraRecipient(),
95
+ protocolRewards_: PROTOCOL_REWARDS,
96
+ weth_: getWeth(),
97
+ v3Factory_: getUniswapV3Factory(),
98
+ swapRouter_: getUniswapSwapRouter(),
99
+ airlock_: getDopplerAirlock()
100
+ });
58
101
  }
59
102
 
60
- function deployCoinImpl() internal returns (Coin) {
61
- return new Coin(getZoraRecipient(), PROTOCOL_REWARDS, getWeth(), getUniswapV3Factory(), getUniswapSwapRouter(), getDopplerAirlock());
103
+ function deployCoinV4Impl(address zoraV4CoinHook) internal returns (CoinV4) {
104
+ return
105
+ new CoinV4({
106
+ protocolRewardRecipient_: getZoraRecipient(),
107
+ protocolRewards_: PROTOCOL_REWARDS,
108
+ poolManager_: IPoolManager(getUniswapV4PoolManager()),
109
+ airlock_: getDopplerAirlock(),
110
+ hooks_: IHooks(zoraV4CoinHook)
111
+ });
62
112
  }
63
113
 
64
- function deployZoraFactoryImpl(address coinImpl) internal returns (ZoraFactoryImpl) {
65
- return new ZoraFactoryImpl(coinImpl);
114
+ function deployZoraFactoryImpl(address coinV3Impl, address coinV4Impl) internal returns (ZoraFactoryImpl) {
115
+ return new ZoraFactoryImpl(coinV3Impl, coinV4Impl);
66
116
  }
67
117
 
68
118
  function deployBuySupplyWithSwapRouterHook(CoinsDeployment memory deployment) internal returns (BuySupplyWithSwapRouterHook) {
69
- return new BuySupplyWithSwapRouterHook(IZoraFactory(deployment.zoraFactory), getUniswapSwapRouter());
119
+ return new BuySupplyWithSwapRouterHook(IZoraFactory(deployment.zoraFactory), getUniswapSwapRouter(), getUniswapV4PoolManager());
120
+ }
121
+
122
+ function deployZoraV4CoinHook(CoinsDeployment memory deployment) internal returns (IHooks, bytes32) {
123
+ // Read existing deployment to get stored salt
124
+
125
+ require(deployment.zoraFactory != address(0), "Zora factory is not set");
126
+
127
+ return
128
+ HooksDeployment.deployZoraV4CoinHookFromScript(
129
+ getUniswapV4PoolManager(),
130
+ deployment.zoraFactory,
131
+ getDefaultTrustedMessageSenders(),
132
+ deployment.zoraV4CoinHookSalt
133
+ );
134
+ }
135
+
136
+ function getDefaultTrustedMessageSenders() internal view returns (address[] memory) {
137
+ address[] memory trustedMessageSenders = new address[](2);
138
+ trustedMessageSenders[0] = getUniswapUniversalRouter();
139
+ trustedMessageSenders[1] = getUniswapV4PositionManager();
140
+ return trustedMessageSenders;
70
141
  }
71
142
 
72
143
  function deployImpls(CoinsDeployment memory deployment) internal returns (CoinsDeployment memory) {
73
144
  // Deploy implementation contracts
74
- deployment.coinImpl = address(deployCoinImpl());
75
- deployment.zoraFactoryImpl = address(deployZoraFactoryImpl(deployment.coinImpl));
76
- deployment.coinVersion = IVersionedContract(deployment.coinImpl).contractVersion();
145
+ deployment.coinV3Impl = address(deployCoinV3Impl());
146
+
147
+ // Deploy hook first, then use its address for coin v4 impl
148
+ (IHooks zoraV4CoinHook, bytes32 usedSalt) = deployZoraV4CoinHook(deployment);
149
+ deployment.zoraV4CoinHook = address(zoraV4CoinHook);
150
+ deployment.zoraV4CoinHookSalt = usedSalt;
151
+
152
+ deployment.coinV4Impl = address(deployCoinV4Impl(deployment.zoraV4CoinHook));
153
+ deployment.zoraFactoryImpl = address(deployZoraFactoryImpl(deployment.coinV3Impl, deployment.coinV4Impl));
154
+ deployment.coinVersion = IVersionedContract(deployment.coinV4Impl).contractVersion();
77
155
  deployment.buySupplyWithSwapRouterHook = address(deployBuySupplyWithSwapRouterHook(deployment));
78
156
 
79
157
  return deployment;
@@ -113,6 +191,22 @@ contract CoinsDeployerBase is ProxyDeployerScript {
113
191
  require(ZoraFactoryImpl(deployment.zoraFactory).owner() == getProxyAdmin(), "Zora factory owner is not the proxy admin");
114
192
  }
115
193
 
194
+ function deployDevFactory(CoinsDeployment memory deployment) internal returns (ZoraFactory devFactory) {
195
+ address owner = 0x63545B401283c993320A5b886ecF0fc6CB5668a9;
196
+
197
+ ProxyShim shim = new ProxyShim();
198
+
199
+ deployment.zoraFactory = address(new ZoraFactory(address(shim)));
200
+
201
+ deployment = deployImpls(deployment);
202
+
203
+ UUPSUpgradeable(deployment.zoraFactory).upgradeToAndCall(deployment.zoraFactoryImpl, "");
204
+
205
+ ZoraFactoryImpl(deployment.zoraFactory).initialize(owner);
206
+
207
+ saveDeployment(deployment);
208
+ }
209
+
116
210
  function printUpgradeFactoryCommand(CoinsDeployment memory deployment) internal view {
117
211
  // build upgrade to and call for factory, with init call
118
212
  bytes memory call = abi.encodeWithSelector(UUPSUpgradeable.upgradeToAndCall.selector, deployment.zoraFactoryImpl, "");