@zoralabs/coins 2.2.1 → 2.3.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.
- package/.turbo/turbo-build$colon$js.log +125 -106
- package/CHANGELOG.md +50 -5
- package/README.md +5 -0
- package/abis/AddressConstants.json +7 -0
- package/abis/BaseCoin.json +0 -5
- package/abis/BaseTest.json +62 -0
- package/abis/BuySupplyWithV4SwapHook.json +429 -0
- package/abis/ContentCoin.json +0 -5
- package/abis/CreatorCoin.json +0 -5
- package/abis/FeeEstimatorHook.json +94 -1
- package/abis/IUniswapV4Router04.json +484 -0
- package/abis/IUpgradeableDestinationV4HookWithUpdateableFee.json +95 -0
- package/abis/IZoraFactory.json +69 -0
- package/abis/MockAirlock.json +39 -0
- package/abis/SimpleERC20.json +326 -0
- package/abis/ZoraFactoryImpl.json +69 -0
- package/abis/ZoraV4CoinHook.json +94 -1
- package/addresses/8453.json +8 -10
- package/audits/report-cantinacode-zora-0827.pdf +3498 -4
- package/audits/report-cantinacode-zora-1021.pdf +0 -0
- package/dist/index.cjs +161 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +160 -21
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +259 -40
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/foundry.toml +3 -3
- package/package/wagmiGenerated.ts +160 -21
- package/package.json +1 -1
- package/script/DeployPostDeploymentHooks.s.sol +1 -3
- package/script/TestBackingCoinSwap.s.sol +0 -2
- package/script/TestV4Swap.s.sol +0 -2
- package/src/BaseCoin.sol +4 -12
- package/src/ContentCoin.sol +3 -4
- package/src/CreatorCoin.sol +8 -10
- package/src/ZoraFactoryImpl.sol +115 -83
- package/src/deployment/CoinsDeployerBase.sol +9 -8
- package/src/hook-registry/ZoraHookRegistry.sol +4 -0
- package/src/hooks/ZoraV4CoinHook.sol +66 -9
- package/src/hooks/deployment/BuySupplyWithV4SwapHook.sol +310 -0
- package/src/interfaces/IUpgradeableV4Hook.sol +18 -0
- package/src/interfaces/IZoraFactory.sol +21 -2
- package/src/libs/CoinConstants.sol +51 -8
- package/src/libs/CoinDopplerMultiCurve.sol +11 -11
- package/src/libs/CoinRewardsV4.sol +26 -33
- package/src/libs/CoinSetup.sol +2 -9
- package/src/libs/DopplerMath.sol +2 -2
- package/src/libs/V4Liquidity.sol +79 -15
- package/src/utils/AutoSwapper.sol +1 -1
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/BuySupplyWithV4SwapHook.t.sol +509 -0
- package/test/Coin.t.sol +26 -14
- package/test/CoinRewardsV4.t.sol +33 -0
- package/test/CoinUniV4.t.sol +3 -5
- package/test/ContentCoinRewards.t.sol +44 -3
- package/test/CreatorCoin.t.sol +54 -33
- package/test/CreatorCoinRewards.t.sol +1 -3
- package/test/DeploymentHooks.t.sol +54 -2
- package/test/Factory.t.sol +3 -3
- package/test/LiquidityMigration.t.sol +145 -7
- package/test/MultiOwnable.t.sol +4 -4
- package/test/Upgrades.t.sol +26 -17
- package/test/V4Liquidity.t.sol +178 -0
- package/test/ZoraHookRegistry.t.sol +19 -9
- package/test/mocks/MockAirlock.sol +22 -0
- package/test/mocks/SimpleERC20.sol +8 -0
- package/test/utils/BaseTest.sol +155 -3
- package/test/utils/RewardTestHelpers.sol +4 -4
- package/test/utils/hookmate/README.md +50 -0
- package/test/utils/hookmate/artifacts/DeployHelper.sol +20 -0
- package/test/utils/hookmate/artifacts/Permit2.sol +16 -0
- package/test/utils/hookmate/artifacts/UniversalRouter.sol +29 -0
- package/test/utils/hookmate/artifacts/V4PoolManager.sol +17 -0
- package/test/utils/hookmate/artifacts/V4PositionManager.sol +23 -0
- package/test/utils/hookmate/artifacts/V4Quoter.sol +17 -0
- package/test/utils/hookmate/artifacts/V4Router.sol +18 -0
- package/test/utils/hookmate/constants/AddressConstants.sol +193 -0
- package/test/utils/hookmate/interfaces/router/IUniswapV4Router04.sol +173 -0
- package/test/utils/hookmate/interfaces/router/PathKey.sol +34 -0
- package/test/utils/hookmate/test/utils/SwapFeeEventAsserter.sol +24 -0
- package/wagmi.config.ts +1 -1
- package/abis/CoinConstants.json +0 -54
- package/abis/CoinRewardsV4.json +0 -67
- package/src/libs/CreatorCoinConstants.sol +0 -15
- package/src/libs/MarketConstants.sol +0 -23
- package/src/utils/uniswap/BytesLib.sol +0 -35
- package/src/utils/uniswap/Path.sol +0 -31
- /package/abis/{VmContractHelper227.json → VmContractHelper239.json} +0 -0
package/src/ZoraFactoryImpl.sol
CHANGED
|
@@ -34,7 +34,6 @@ import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersion
|
|
|
34
34
|
import {CoinSetup} from "./libs/CoinSetup.sol";
|
|
35
35
|
import {CoinDopplerMultiCurve} from "./libs/CoinDopplerMultiCurve.sol";
|
|
36
36
|
import {ICreatorCoin} from "./interfaces/ICreatorCoin.sol";
|
|
37
|
-
import {MarketConstants} from "./libs/MarketConstants.sol";
|
|
38
37
|
import {DeployedCoinVersionLookup} from "./utils/DeployedCoinVersionLookup.sol";
|
|
39
38
|
import {IZoraHookRegistry} from "./interfaces/IZoraHookRegistry.sol";
|
|
40
39
|
|
|
@@ -67,15 +66,7 @@ contract ZoraFactoryImpl is
|
|
|
67
66
|
zoraHookRegistry = zoraHookRegistry_;
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
/// @
|
|
71
|
-
/// @param payoutRecipient The recipient of creator reward payouts; this can be updated by an owner
|
|
72
|
-
/// @param owners The list of addresses that will be able to manage the coin's payout address and metadata uri
|
|
73
|
-
/// @param uri The coin metadata uri
|
|
74
|
-
/// @param name The name of the coin
|
|
75
|
-
/// @param symbol The symbol of the coin
|
|
76
|
-
/// @param poolConfig The config parameters for the coin's pool
|
|
77
|
-
/// @param platformReferrer The address of the platform referrer
|
|
78
|
-
/// @param coinSalt The salt used to deploy the coin
|
|
69
|
+
/// @inheritdoc IZoraFactory
|
|
79
70
|
function deployCreatorCoin(
|
|
80
71
|
address payoutRecipient,
|
|
81
72
|
address[] memory owners,
|
|
@@ -87,39 +78,24 @@ contract ZoraFactoryImpl is
|
|
|
87
78
|
bytes32 coinSalt
|
|
88
79
|
) public nonReentrant returns (address) {
|
|
89
80
|
bytes32 salt = _buildSalt(msg.sender, name, symbol, poolConfig, platformReferrer, coinSalt);
|
|
81
|
+
return address(_createAndInitializeCreatorCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt));
|
|
82
|
+
}
|
|
90
83
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
ICreatorCoin(creatorCoin).initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolKey, sqrtPriceX96, poolConfiguration);
|
|
107
|
-
|
|
108
|
-
emit CreatorCoinCreated(
|
|
109
|
-
msg.sender,
|
|
110
|
-
payoutRecipient,
|
|
111
|
-
platformReferrer,
|
|
112
|
-
currency,
|
|
113
|
-
uri,
|
|
114
|
-
name,
|
|
115
|
-
symbol,
|
|
116
|
-
address(creatorCoin),
|
|
117
|
-
poolKey,
|
|
118
|
-
CoinCommon.hashPoolKey(poolKey),
|
|
119
|
-
IVersionedContract(address(creatorCoin)).contractVersion()
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
return creatorCoin;
|
|
84
|
+
/// @inheritdoc IZoraFactory
|
|
85
|
+
function deployCreatorCoin(
|
|
86
|
+
address payoutRecipient,
|
|
87
|
+
address[] memory owners,
|
|
88
|
+
string memory uri,
|
|
89
|
+
string memory name,
|
|
90
|
+
string memory symbol,
|
|
91
|
+
bytes memory poolConfig,
|
|
92
|
+
address platformReferrer,
|
|
93
|
+
address postDeployHook,
|
|
94
|
+
bytes calldata postDeployHookData,
|
|
95
|
+
bytes32 coinSalt
|
|
96
|
+
) external payable nonReentrant returns (address coin, bytes memory postDeployHookDataOut) {
|
|
97
|
+
bytes32 salt = _buildSalt(msg.sender, name, symbol, poolConfig, platformReferrer, coinSalt);
|
|
98
|
+
return _deployCreatorCoinWithHook(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, postDeployHook, postDeployHookData, salt);
|
|
123
99
|
}
|
|
124
100
|
|
|
125
101
|
/// @inheritdoc IZoraFactory
|
|
@@ -152,6 +128,18 @@ contract ZoraFactoryImpl is
|
|
|
152
128
|
return Clones.predictDeterministicAddress(getCoinImpl(CoinConfigurationVersions.getVersion(poolConfig)), salt, address(this));
|
|
153
129
|
}
|
|
154
130
|
|
|
131
|
+
function _executePostDeployHook(address coin, address deployHook, bytes calldata hookData) internal returns (bytes memory hookDataOut) {
|
|
132
|
+
if (deployHook != address(0)) {
|
|
133
|
+
if (!IERC165(deployHook).supportsInterface(type(IHasAfterCoinDeploy).interfaceId)) {
|
|
134
|
+
revert InvalidHook();
|
|
135
|
+
}
|
|
136
|
+
hookDataOut = IHasAfterCoinDeploy(deployHook).afterCoinDeploy{value: msg.value}(msg.sender, ICoin(coin), hookData);
|
|
137
|
+
} else if (msg.value > 0) {
|
|
138
|
+
// cannot send eth without a hook
|
|
139
|
+
revert EthTransferInvalid();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
155
143
|
/// @dev Internal function to deploy a coin with a hook
|
|
156
144
|
function _deployWithHook(
|
|
157
145
|
address payoutRecipient,
|
|
@@ -166,16 +154,24 @@ contract ZoraFactoryImpl is
|
|
|
166
154
|
bytes32 salt
|
|
167
155
|
) internal returns (address coin, bytes memory hookDataOut) {
|
|
168
156
|
coin = address(_createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt));
|
|
157
|
+
hookDataOut = _executePostDeployHook(coin, deployHook, hookData);
|
|
158
|
+
}
|
|
169
159
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
160
|
+
/// @dev Internal function to deploy a creator coin with a hook
|
|
161
|
+
function _deployCreatorCoinWithHook(
|
|
162
|
+
address payoutRecipient,
|
|
163
|
+
address[] memory owners,
|
|
164
|
+
string memory uri,
|
|
165
|
+
string memory name,
|
|
166
|
+
string memory symbol,
|
|
167
|
+
bytes memory poolConfig,
|
|
168
|
+
address platformReferrer,
|
|
169
|
+
address deployHook,
|
|
170
|
+
bytes calldata hookData,
|
|
171
|
+
bytes32 salt
|
|
172
|
+
) internal returns (address coin, bytes memory hookDataOut) {
|
|
173
|
+
coin = address(_createAndInitializeCreatorCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt));
|
|
174
|
+
hookDataOut = _executePostDeployHook(coin, deployHook, hookData);
|
|
179
175
|
}
|
|
180
176
|
|
|
181
177
|
/**
|
|
@@ -252,29 +248,55 @@ contract ZoraFactoryImpl is
|
|
|
252
248
|
revert ICoin.InvalidPoolVersion();
|
|
253
249
|
}
|
|
254
250
|
|
|
255
|
-
function
|
|
256
|
-
|
|
251
|
+
function _createCoinWithPoolConfig(
|
|
252
|
+
address _implementation,
|
|
253
|
+
bytes memory poolConfig,
|
|
254
|
+
bytes32 coinSalt,
|
|
255
|
+
address payoutRecipient,
|
|
256
|
+
address[] memory owners,
|
|
257
|
+
string memory uri,
|
|
258
|
+
string memory name,
|
|
259
|
+
string memory symbol,
|
|
260
|
+
address platformReferrer
|
|
261
|
+
) internal returns (address coin, uint8 version, PoolKey memory poolKey, address currency) {
|
|
262
|
+
version = CoinConfigurationVersions.getVersion(poolConfig);
|
|
263
|
+
coin = Clones.cloneDeterministic(_implementation, coinSalt);
|
|
264
|
+
_setVersionForDeployedCoin(coin, version);
|
|
265
|
+
|
|
266
|
+
uint160 sqrtPriceX96;
|
|
267
|
+
bool isCoinToken0;
|
|
268
|
+
PoolConfiguration memory poolConfiguration;
|
|
269
|
+
(, currency, sqrtPriceX96, isCoinToken0, poolConfiguration) = CoinSetup.generatePoolConfig(coin, poolConfig);
|
|
270
|
+
|
|
271
|
+
poolKey = CoinSetup.buildPoolKey(coin, currency, isCoinToken0, IHooks(hook));
|
|
272
|
+
ICoin(coin).initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolKey, sqrtPriceX96, poolConfiguration);
|
|
257
273
|
}
|
|
258
274
|
|
|
259
|
-
function
|
|
260
|
-
ICoin coin,
|
|
261
|
-
address currency,
|
|
262
|
-
bool isCoinToken0,
|
|
263
|
-
uint160 sqrtPriceX96,
|
|
264
|
-
PoolConfiguration memory poolConfiguration,
|
|
275
|
+
function _createAndInitializeCreatorCoin(
|
|
265
276
|
address payoutRecipient,
|
|
266
277
|
address[] memory owners,
|
|
267
278
|
string memory uri,
|
|
268
279
|
string memory name,
|
|
269
280
|
string memory symbol,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
281
|
+
bytes memory poolConfig,
|
|
282
|
+
address platformReferrer,
|
|
283
|
+
bytes32 coinSalt
|
|
284
|
+
) internal returns (ICreatorCoin) {
|
|
285
|
+
(address creatorCoin, uint8 version, PoolKey memory poolKey, address currency) = _createCoinWithPoolConfig(
|
|
286
|
+
creatorCoinImpl,
|
|
287
|
+
poolConfig,
|
|
288
|
+
coinSalt,
|
|
289
|
+
payoutRecipient,
|
|
290
|
+
owners,
|
|
291
|
+
uri,
|
|
292
|
+
name,
|
|
293
|
+
symbol,
|
|
294
|
+
platformReferrer
|
|
295
|
+
);
|
|
273
296
|
|
|
274
|
-
|
|
275
|
-
coin.initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolKey, sqrtPriceX96, poolConfiguration);
|
|
297
|
+
require(version == CoinConfigurationVersions.DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION, InvalidConfig());
|
|
276
298
|
|
|
277
|
-
emit
|
|
299
|
+
emit CreatorCoinCreated(
|
|
278
300
|
msg.sender,
|
|
279
301
|
payoutRecipient,
|
|
280
302
|
platformReferrer,
|
|
@@ -282,11 +304,13 @@ contract ZoraFactoryImpl is
|
|
|
282
304
|
uri,
|
|
283
305
|
name,
|
|
284
306
|
symbol,
|
|
285
|
-
|
|
307
|
+
creatorCoin,
|
|
286
308
|
poolKey,
|
|
287
309
|
CoinCommon.hashPoolKey(poolKey),
|
|
288
|
-
IVersionedContract(
|
|
310
|
+
IVersionedContract(creatorCoin).contractVersion()
|
|
289
311
|
);
|
|
312
|
+
|
|
313
|
+
return ICreatorCoin(creatorCoin);
|
|
290
314
|
}
|
|
291
315
|
|
|
292
316
|
function _createAndInitializeCoin(
|
|
@@ -299,26 +323,34 @@ contract ZoraFactoryImpl is
|
|
|
299
323
|
address platformReferrer,
|
|
300
324
|
bytes32 coinSalt
|
|
301
325
|
) internal returns (ICoin) {
|
|
302
|
-
uint8 version =
|
|
303
|
-
|
|
304
|
-
|
|
326
|
+
(address coin, uint8 version, PoolKey memory poolKey, address currency) = _createCoinWithPoolConfig(
|
|
327
|
+
coinV4Impl,
|
|
328
|
+
poolConfig,
|
|
329
|
+
coinSalt,
|
|
330
|
+
payoutRecipient,
|
|
331
|
+
owners,
|
|
332
|
+
uri,
|
|
333
|
+
name,
|
|
334
|
+
symbol,
|
|
335
|
+
platformReferrer
|
|
336
|
+
);
|
|
305
337
|
|
|
306
|
-
|
|
338
|
+
require(version == CoinConfigurationVersions.DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION, ICoin.InvalidPoolVersion());
|
|
307
339
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
340
|
+
emit CoinCreatedV4(
|
|
341
|
+
msg.sender,
|
|
342
|
+
payoutRecipient,
|
|
343
|
+
platformReferrer,
|
|
344
|
+
currency,
|
|
345
|
+
uri,
|
|
346
|
+
name,
|
|
347
|
+
symbol,
|
|
348
|
+
coin,
|
|
349
|
+
poolKey,
|
|
350
|
+
CoinCommon.hashPoolKey(poolKey),
|
|
351
|
+
IVersionedContract(coin).contractVersion()
|
|
311
352
|
);
|
|
312
353
|
|
|
313
|
-
if (CoinConfigurationVersions.isV3(version)) {
|
|
314
|
-
// V3 is no longer supported
|
|
315
|
-
revert ICoin.InvalidPoolVersion();
|
|
316
|
-
} else if (CoinConfigurationVersions.isV4(version)) {
|
|
317
|
-
_setupV4Coin(ICoin(coin), currency, isCoinToken0, sqrtPriceX96, poolConfiguration, payoutRecipient, owners, uri, name, symbol, platformReferrer);
|
|
318
|
-
} else {
|
|
319
|
-
revert ICoin.InvalidPoolVersion();
|
|
320
|
-
}
|
|
321
|
-
|
|
322
354
|
return ICoin(coin);
|
|
323
355
|
}
|
|
324
356
|
|
|
@@ -18,6 +18,7 @@ import {ProxyShim} from "../../test/utils/ProxyShim.sol";
|
|
|
18
18
|
import {CreatorCoin} from "../CreatorCoin.sol";
|
|
19
19
|
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
|
|
20
20
|
import {HookUpgradeGate} from "../hooks/HookUpgradeGate.sol";
|
|
21
|
+
import {BuySupplyWithV4SwapHook} from "../hooks/deployment/BuySupplyWithV4SwapHook.sol";
|
|
21
22
|
|
|
22
23
|
contract CoinsDeployerBase is ProxyDeployerScript {
|
|
23
24
|
address internal constant PROTOCOL_REWARDS = 0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B;
|
|
@@ -123,14 +124,14 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
123
124
|
return new ZoraFactoryImpl({coinV4Impl_: coinV4Impl_, creatorCoinImpl_: creatorCoinImpl_, hook_: hook_, zoraHookRegistry_: zoraHookRegistry_});
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
function deployBuySupplyWithV4SwapHook(CoinsDeployment memory deployment) internal returns (BuySupplyWithV4SwapHook) {
|
|
128
|
+
return
|
|
129
|
+
new BuySupplyWithV4SwapHook({
|
|
130
|
+
_factory: IZoraFactory(deployment.zoraFactory),
|
|
131
|
+
_swapRouter: getUniswapSwapRouter(),
|
|
132
|
+
_poolManager: getUniswapV4PoolManager()
|
|
133
|
+
});
|
|
134
|
+
}
|
|
134
135
|
|
|
135
136
|
function deployUpgradeGate(CoinsDeployment memory deployment) internal returns (CoinsDeployment memory) {
|
|
136
137
|
deployment.hookUpgradeGate = address(new HookUpgradeGate(getProxyAdmin()));
|
|
@@ -23,6 +23,10 @@ contract ZoraHookRegistry is IZoraHookRegistry, MultiOwnable {
|
|
|
23
23
|
/// @dev The tag for each hook
|
|
24
24
|
mapping(address hook => string tag) internal hookTags;
|
|
25
25
|
|
|
26
|
+
/// @notice Constructor for deployment pathway
|
|
27
|
+
/// @dev This contract needs to be atomically initialized, otherwise it is unsafe to deploy.
|
|
28
|
+
/// @dev Initial owners need to be automatically set by the deployer contract.
|
|
29
|
+
/// @dev The initial owners is not a part of the constructor() to allow for multichain deterministic addresses.
|
|
26
30
|
constructor() {}
|
|
27
31
|
|
|
28
32
|
/// @notice Initializes the registry with initial owners
|
|
@@ -31,13 +31,14 @@ import {IUpgradeableV4Hook} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
|
31
31
|
import {IHooksUpgradeGate} from "../interfaces/IHooksUpgradeGate.sol";
|
|
32
32
|
import {MultiOwnable} from "../utils/MultiOwnable.sol";
|
|
33
33
|
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
34
|
-
import {IUpgradeableDestinationV4Hook} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
34
|
+
import {IUpgradeableDestinationV4Hook, IUpgradeableDestinationV4HookWithUpdateableFee} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
35
35
|
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
|
|
36
36
|
import {BurnedPosition} from "../interfaces/IUpgradeableV4Hook.sol";
|
|
37
37
|
import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
|
|
38
38
|
import {TickMath} from "../utils/uniswap/TickMath.sol";
|
|
39
39
|
import {ContractVersionBase, IVersionedContract} from "../version/ContractVersionBase.sol";
|
|
40
40
|
import {IHasCoinType} from "../interfaces/ICoin.sol";
|
|
41
|
+
import {CoinConstants} from "../libs/CoinConstants.sol";
|
|
41
42
|
|
|
42
43
|
/// @title ZoraV4CoinHook
|
|
43
44
|
/// @notice Uniswap V4 hook that automatically handles fee collection and reward distributions on every swap,
|
|
@@ -49,7 +50,14 @@ import {IHasCoinType} from "../interfaces/ICoin.sol";
|
|
|
49
50
|
/// 2. Swaps collected fees to the backing currency through multi-hop paths
|
|
50
51
|
/// 3. Distributes converted fees as rewards
|
|
51
52
|
/// @author oveddan
|
|
52
|
-
contract ZoraV4CoinHook is
|
|
53
|
+
contract ZoraV4CoinHook is
|
|
54
|
+
BaseHook,
|
|
55
|
+
ContractVersionBase,
|
|
56
|
+
IZoraV4CoinHook,
|
|
57
|
+
ERC165,
|
|
58
|
+
IUpgradeableDestinationV4Hook,
|
|
59
|
+
IUpgradeableDestinationV4HookWithUpdateableFee
|
|
60
|
+
{
|
|
53
61
|
using BalanceDeltaLibrary for BalanceDelta;
|
|
54
62
|
|
|
55
63
|
/// @notice Mapping of trusted message senders - these are addresses that are trusted to provide a
|
|
@@ -130,6 +138,7 @@ contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC16
|
|
|
130
138
|
return
|
|
131
139
|
super.supportsInterface(interfaceId) ||
|
|
132
140
|
interfaceId == type(IUpgradeableDestinationV4Hook).interfaceId ||
|
|
141
|
+
interfaceId == type(IUpgradeableDestinationV4HookWithUpdateableFee).interfaceId ||
|
|
133
142
|
interfaceId == type(IVersionedContract).interfaceId;
|
|
134
143
|
}
|
|
135
144
|
|
|
@@ -137,10 +146,18 @@ contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC16
|
|
|
137
146
|
/// @param coin The coin address.
|
|
138
147
|
/// @param key The pool key for the coin.
|
|
139
148
|
/// @return positions The contract-created liquidity positions the positions for the coin's pool.
|
|
140
|
-
function _generatePositions(ICoin coin, PoolKey memory key) internal view returns (LpPosition[] memory
|
|
149
|
+
function _generatePositions(ICoin coin, PoolKey memory key) internal view returns (LpPosition[] memory) {
|
|
141
150
|
bool isCoinToken0 = Currency.unwrap(key.currency0) == address(coin);
|
|
142
151
|
|
|
143
|
-
|
|
152
|
+
LpPosition[] memory calculatedPositions = CoinDopplerMultiCurve.calculatePositions(
|
|
153
|
+
isCoinToken0,
|
|
154
|
+
coin.getPoolConfiguration(),
|
|
155
|
+
coin.totalSupplyForPositions()
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// sometimes the calculated positions have liquidity added in duplicated positions. So here we dedupe them
|
|
159
|
+
// to save on gas in future swaps.
|
|
160
|
+
return V4Liquidity.dedupePositions(calculatedPositions);
|
|
144
161
|
}
|
|
145
162
|
|
|
146
163
|
/// @notice Internal fn called when a pool is initialized.
|
|
@@ -168,17 +185,50 @@ contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC16
|
|
|
168
185
|
}
|
|
169
186
|
|
|
170
187
|
/// @inheritdoc IUpgradeableDestinationV4Hook
|
|
188
|
+
/// @dev left for backward compatibility for migrating from hooks that dont support
|
|
189
|
+
/// updating the fee
|
|
171
190
|
function initializeFromMigration(
|
|
172
191
|
PoolKey calldata poolKey,
|
|
173
192
|
address coin,
|
|
174
193
|
uint160 sqrtPriceX96,
|
|
175
194
|
BurnedPosition[] calldata migratedLiquidity,
|
|
176
|
-
bytes calldata
|
|
195
|
+
bytes calldata additionalData
|
|
177
196
|
) external {
|
|
197
|
+
// keep the existing fee and tick spacing.
|
|
198
|
+
uint24 fee = poolKey.fee;
|
|
199
|
+
int24 tickSpacing = poolKey.tickSpacing;
|
|
200
|
+
|
|
201
|
+
_initializeFromMigration(poolKey, coin, sqrtPriceX96, migratedLiquidity, additionalData, fee, tickSpacing);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// @inheritdoc IUpgradeableDestinationV4HookWithUpdateableFee
|
|
205
|
+
function initializeFromMigrationWithUpdateableFee(
|
|
206
|
+
PoolKey calldata poolKey,
|
|
207
|
+
address coin,
|
|
208
|
+
uint160 sqrtPriceX96,
|
|
209
|
+
BurnedPosition[] calldata migratedLiquidity,
|
|
210
|
+
bytes calldata additionalData
|
|
211
|
+
) external returns (uint24 fee, int24 tickSpacing) {
|
|
212
|
+
// update the fee to the current one.
|
|
213
|
+
fee = CoinConstants.LP_FEE_V4;
|
|
214
|
+
tickSpacing = CoinConstants.TICK_SPACING;
|
|
215
|
+
|
|
216
|
+
_initializeFromMigration(poolKey, coin, sqrtPriceX96, migratedLiquidity, additionalData, fee, tickSpacing);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function _initializeFromMigration(
|
|
220
|
+
PoolKey calldata poolKey,
|
|
221
|
+
address coin,
|
|
222
|
+
uint160 sqrtPriceX96,
|
|
223
|
+
BurnedPosition[] calldata migratedLiquidity,
|
|
224
|
+
bytes calldata,
|
|
225
|
+
uint24 fee,
|
|
226
|
+
int24 tickSpacing
|
|
227
|
+
) internal {
|
|
178
228
|
address oldHook = msg.sender;
|
|
179
229
|
address newHook = address(this);
|
|
180
230
|
|
|
181
|
-
// Verify that the caller (
|
|
231
|
+
// Verify that the caller (old hook) is authorized to perform this migration
|
|
182
232
|
// Only registered upgrade paths in the upgrade gate are allowed to migrate liquidity
|
|
183
233
|
if (!upgradeGate.isRegisteredUpgradePath(oldHook, newHook)) {
|
|
184
234
|
revert IUpgradeableV4Hook.UpgradePathNotRegistered(oldHook, newHook);
|
|
@@ -189,8 +239,8 @@ contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC16
|
|
|
189
239
|
PoolKey memory newKey = PoolKey({
|
|
190
240
|
currency0: poolKey.currency0,
|
|
191
241
|
currency1: poolKey.currency1,
|
|
192
|
-
fee:
|
|
193
|
-
tickSpacing:
|
|
242
|
+
fee: fee,
|
|
243
|
+
tickSpacing: tickSpacing,
|
|
194
244
|
hooks: IHooks(newHook)
|
|
195
245
|
});
|
|
196
246
|
|
|
@@ -343,7 +393,7 @@ contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC16
|
|
|
343
393
|
return CoinRewardsV4.getCoinType(coin);
|
|
344
394
|
}
|
|
345
395
|
|
|
346
|
-
/// @notice Internal fn called when the PoolManager is unlocked.
|
|
396
|
+
/// @notice Internal fn called when the PoolManager is unlocked. Used to mint initial liquidity positions and burn positions during migration.
|
|
347
397
|
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
|
|
348
398
|
return V4Liquidity.handleCallback(poolManager, data);
|
|
349
399
|
}
|
|
@@ -376,7 +426,14 @@ contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC16
|
|
|
376
426
|
}
|
|
377
427
|
|
|
378
428
|
newPoolKey = V4Liquidity.lockAndMigrate(poolManager, poolKey, poolCoin.positions, poolCoin.coin, newHook, additionalData);
|
|
429
|
+
|
|
430
|
+
// Delete the old pool key mapping to prevent future operations on the migrated pool
|
|
431
|
+
delete poolCoins[poolKeyHash];
|
|
379
432
|
}
|
|
380
433
|
|
|
434
|
+
/// @notice Receives ETH from the pool manager for ETH-backed coins during fee collection.
|
|
435
|
+
/// @dev Only required for coins using ETH as backing currency (currency = address(0)).
|
|
436
|
+
/// Restricted to onlyPoolManager to prevent ETH from getting stuck in the contract.
|
|
437
|
+
/// Unused for ERC20-backed coins.
|
|
381
438
|
receive() external payable onlyPoolManager {}
|
|
382
439
|
}
|