@zoralabs/coins 2.1.1 → 2.2.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 +152 -0
- package/CHANGELOG.md +60 -0
- package/abis/BaseCoin.json +26 -0
- package/abis/BaseTest.json +2 -7
- package/abis/CoinConstants.json +0 -104
- package/abis/ContentCoin.json +26 -0
- package/abis/CreatorCoin.json +30 -4
- package/abis/FeeEstimatorHook.json +0 -5
- package/abis/ICoin.json +26 -0
- package/abis/ICoinV3.json +26 -0
- package/abis/ICreatorCoin.json +39 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasCoinType.json +15 -0
- package/abis/IHasTotalSupplyForPositions.json +15 -0
- package/abis/IZoraFactory.json +52 -0
- package/abis/IZoraHookRegistry.json +188 -0
- package/abis/VmContractHelper227.json +233 -0
- package/abis/ZoraFactoryImpl.json +32 -6
- package/abis/ZoraHookRegistry.json +375 -0
- package/abis/{CreatorCoinHook.json → ZoraV4CoinHook.json} +1 -1
- package/addresses/8453.json +2 -1
- package/dist/index.cjs +72 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +72 -10
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +90 -10
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/foundry.toml +4 -1
- package/package/wagmiGenerated.ts +72 -10
- package/package.json +7 -5
- package/script/PrintRegisterUpgradePath.s.sol +0 -7
- package/script/TestBackingCoinSwap.s.sol +0 -1
- package/script/TestV4Swap.s.sol +0 -1
- package/script/UpgradeFactoryImpl.s.sol +1 -1
- package/src/BaseCoin.sol +15 -12
- package/src/ContentCoin.sol +10 -0
- package/src/CreatorCoin.sol +28 -7
- package/src/ZoraFactoryImpl.sol +62 -23
- package/src/deployment/CoinsDeployerBase.sol +24 -58
- package/src/hook-registry/ZoraHookRegistry.sol +93 -0
- package/src/hooks/{BaseZoraV4CoinHook.sol → ZoraV4CoinHook.sol} +13 -8
- package/src/interfaces/ICoin.sol +19 -1
- package/src/interfaces/ICreatorCoin.sol +4 -0
- package/src/interfaces/IZoraFactory.sol +32 -10
- package/src/interfaces/IZoraHookRegistry.sol +47 -0
- package/src/libs/CoinConstants.sol +0 -32
- package/src/libs/CoinRewardsV4.sol +53 -15
- package/src/libs/CreatorCoinConstants.sol +0 -1
- package/src/libs/HooksDeployment.sol +13 -65
- package/src/libs/MarketConstants.sol +10 -12
- package/src/libs/V4Liquidity.sol +30 -0
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/CoinUniV4.t.sol +33 -30
- package/test/ContentCoinRewards.t.sol +320 -0
- package/test/CreatorCoin.t.sol +1 -1
- package/test/CreatorCoinRewards.t.sol +375 -0
- package/test/DeploymentHooks.t.sol +10 -10
- package/test/Factory.t.sol +24 -7
- package/test/HooksDeployment.t.sol +4 -4
- package/test/LiquidityMigration.t.sol +4 -9
- package/test/Upgrades.t.sol +44 -48
- package/test/ZoraHookRegistry.t.sol +266 -0
- package/test/utils/BaseTest.sol +25 -42
- package/test/utils/FeeEstimatorHook.sol +4 -6
- package/test/utils/RewardTestHelpers.sol +106 -0
- package/.turbo/turbo-build.log +0 -199
- package/abis/AutoSwapperTest.json +0 -618
- package/abis/BadImpl.json +0 -15
- package/abis/BaseZoraV4CoinHook.json +0 -1664
- package/abis/CoinTest.json +0 -819
- package/abis/CoinUniV4Test.json +0 -1128
- package/abis/ContentCoinHook.json +0 -1733
- package/abis/CreatorCoinTest.json +0 -887
- package/abis/Deploy.json +0 -9
- package/abis/DeployHooks.json +0 -9
- package/abis/DeployScript.json +0 -35
- package/abis/DeployedCoinVersionLookupTest.json +0 -740
- package/abis/DifferentNamespaceVersionLookup.json +0 -39
- package/abis/FactoryTest.json +0 -748
- package/abis/FakeHookNoInterface.json +0 -21
- package/abis/GenerateDeterministicParams.json +0 -9
- package/abis/HooksDeploymentTest.json +0 -645
- package/abis/HooksTest.json +0 -709
- package/abis/InvalidLiquidityMigrationReceiver.json +0 -21
- package/abis/LiquidityMigrationReceiver.json +0 -103
- package/abis/LiquidityMigrationTest.json +0 -889
- package/abis/MockBadFactory.json +0 -15
- package/abis/MultiOwnableTest.json +0 -766
- package/abis/PrintUpgradeCommand.json +0 -9
- package/abis/TestDeployedCoinVersionLookupImplementation.json +0 -39
- package/abis/TestV4Swap.json +0 -9
- package/abis/UpgradeFactoryImpl.json +0 -9
- package/abis/UpgradeHooks.json +0 -35
- package/abis/UpgradesTest.json +0 -723
- package/src/hooks/ContentCoinHook.sol +0 -27
- package/src/hooks/CreatorCoinHook.sol +0 -27
- package/src/libs/CreatorCoinRewards.sol +0 -34
package/src/ZoraFactoryImpl.sol
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
pragma solidity ^0.8.23;
|
|
9
9
|
|
|
10
10
|
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
11
|
+
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
|
11
12
|
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
|
12
13
|
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
|
13
14
|
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
@@ -35,6 +36,7 @@ import {CoinDopplerMultiCurve} from "./libs/CoinDopplerMultiCurve.sol";
|
|
|
35
36
|
import {ICreatorCoin} from "./interfaces/ICreatorCoin.sol";
|
|
36
37
|
import {MarketConstants} from "./libs/MarketConstants.sol";
|
|
37
38
|
import {DeployedCoinVersionLookup} from "./utils/DeployedCoinVersionLookup.sol";
|
|
39
|
+
import {IZoraHookRegistry} from "./interfaces/IZoraHookRegistry.sol";
|
|
38
40
|
|
|
39
41
|
contract ZoraFactoryImpl is
|
|
40
42
|
IZoraFactory,
|
|
@@ -47,21 +49,33 @@ contract ZoraFactoryImpl is
|
|
|
47
49
|
{
|
|
48
50
|
using SafeERC20 for IERC20;
|
|
49
51
|
|
|
50
|
-
/// @notice The coin contract implementation address
|
|
52
|
+
/// @notice The ZORA coin contract implementation address
|
|
51
53
|
address public immutable coinV4Impl;
|
|
54
|
+
/// @notice The creator coin contract implementation address
|
|
52
55
|
address public immutable creatorCoinImpl;
|
|
53
|
-
|
|
54
|
-
address public immutable
|
|
56
|
+
/// @notice The uniswap v4 coin hook address
|
|
57
|
+
address public immutable hook;
|
|
58
|
+
/// @notice The zora hook registry address
|
|
59
|
+
address public immutable zoraHookRegistry;
|
|
55
60
|
|
|
56
|
-
constructor(address
|
|
61
|
+
constructor(address coinV4Impl_, address creatorCoinImpl_, address hook_, address zoraHookRegistry_) {
|
|
57
62
|
_disableInitializers();
|
|
58
63
|
|
|
59
|
-
coinV4Impl =
|
|
60
|
-
creatorCoinImpl =
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
coinV4Impl = coinV4Impl_;
|
|
65
|
+
creatorCoinImpl = creatorCoinImpl_;
|
|
66
|
+
hook = hook_;
|
|
67
|
+
zoraHookRegistry = zoraHookRegistry_;
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
/// @notice Creates a new creator coin contract
|
|
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
|
|
65
79
|
function deployCreatorCoin(
|
|
66
80
|
address payoutRecipient,
|
|
67
81
|
address[] memory owners,
|
|
@@ -87,7 +101,7 @@ contract ZoraFactoryImpl is
|
|
|
87
101
|
poolConfig
|
|
88
102
|
);
|
|
89
103
|
|
|
90
|
-
PoolKey memory poolKey = CoinSetup.buildPoolKey(address(creatorCoin), currency, isCoinToken0, IHooks(
|
|
104
|
+
PoolKey memory poolKey = CoinSetup.buildPoolKey(address(creatorCoin), currency, isCoinToken0, IHooks(hook));
|
|
91
105
|
|
|
92
106
|
ICreatorCoin(creatorCoin).initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolKey, sqrtPriceX96, poolConfiguration);
|
|
93
107
|
|
|
@@ -125,6 +139,7 @@ contract ZoraFactoryImpl is
|
|
|
125
139
|
return _deployWithHook(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, postDeployHook, postDeployHookData, salt);
|
|
126
140
|
}
|
|
127
141
|
|
|
142
|
+
/// @inheritdoc IZoraFactory
|
|
128
143
|
function coinAddress(
|
|
129
144
|
address msgSender,
|
|
130
145
|
string memory name,
|
|
@@ -137,6 +152,7 @@ contract ZoraFactoryImpl is
|
|
|
137
152
|
return Clones.predictDeterministicAddress(getCoinImpl(CoinConfigurationVersions.getVersion(poolConfig)), salt, address(this));
|
|
138
153
|
}
|
|
139
154
|
|
|
155
|
+
/// @dev Internal function to deploy a coin with a hook
|
|
140
156
|
function _deployWithHook(
|
|
141
157
|
address payoutRecipient,
|
|
142
158
|
address[] memory owners,
|
|
@@ -145,24 +161,26 @@ contract ZoraFactoryImpl is
|
|
|
145
161
|
string memory symbol,
|
|
146
162
|
bytes memory poolConfig,
|
|
147
163
|
address platformReferrer,
|
|
148
|
-
address
|
|
164
|
+
address deployHook,
|
|
149
165
|
bytes calldata hookData,
|
|
150
166
|
bytes32 salt
|
|
151
167
|
) internal returns (address coin, bytes memory hookDataOut) {
|
|
152
168
|
coin = address(_createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt));
|
|
153
169
|
|
|
154
|
-
if (
|
|
155
|
-
if (!IERC165(
|
|
170
|
+
if (deployHook != address(0)) {
|
|
171
|
+
if (!IERC165(deployHook).supportsInterface(type(IHasAfterCoinDeploy).interfaceId)) {
|
|
156
172
|
revert InvalidHook();
|
|
157
173
|
}
|
|
158
|
-
hookDataOut = IHasAfterCoinDeploy(
|
|
174
|
+
hookDataOut = IHasAfterCoinDeploy(deployHook).afterCoinDeploy{value: msg.value}(msg.sender, ICoin(coin), hookData);
|
|
159
175
|
} else if (msg.value > 0) {
|
|
160
176
|
// cannot send eth without a hook
|
|
161
177
|
revert EthTransferInvalid();
|
|
162
178
|
}
|
|
163
179
|
}
|
|
164
180
|
|
|
165
|
-
/**
|
|
181
|
+
/**
|
|
182
|
+
* Deprecated deploy functions
|
|
183
|
+
*/
|
|
166
184
|
|
|
167
185
|
/// @dev Deprecated: use `deploy` instead that has a salt and hook specified
|
|
168
186
|
function deploy(
|
|
@@ -193,11 +211,11 @@ contract ZoraFactoryImpl is
|
|
|
193
211
|
string memory symbol,
|
|
194
212
|
bytes memory poolConfig,
|
|
195
213
|
address platformReferrer,
|
|
196
|
-
address
|
|
214
|
+
address deployHook,
|
|
197
215
|
bytes calldata hookData
|
|
198
216
|
) public payable nonReentrant returns (address coin, bytes memory hookDataOut) {
|
|
199
217
|
bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
|
|
200
|
-
return _deployWithHook(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer,
|
|
218
|
+
return _deployWithHook(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, deployHook, hookData, salt);
|
|
201
219
|
}
|
|
202
220
|
|
|
203
221
|
/// @dev deprecated Use deploy() with poolConfig instead
|
|
@@ -210,8 +228,9 @@ contract ZoraFactoryImpl is
|
|
|
210
228
|
address platformReferrer,
|
|
211
229
|
address currency,
|
|
212
230
|
// tickLower is no longer used
|
|
213
|
-
int24 /*tickLower*/,
|
|
214
|
-
|
|
231
|
+
int24 /* tickLower */,
|
|
232
|
+
// orderSize is no longer used
|
|
233
|
+
uint256 /* orderSize */
|
|
215
234
|
) public payable nonReentrant returns (address, uint256) {
|
|
216
235
|
bytes memory poolConfig = CoinConfigurationVersions.defaultConfig(currency);
|
|
217
236
|
bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
|
|
@@ -221,6 +240,10 @@ contract ZoraFactoryImpl is
|
|
|
221
240
|
return (address(coin), 0);
|
|
222
241
|
}
|
|
223
242
|
|
|
243
|
+
/**
|
|
244
|
+
* End Deprecated deploy functions
|
|
245
|
+
*/
|
|
246
|
+
|
|
224
247
|
function getCoinImpl(uint8 version) internal view returns (address) {
|
|
225
248
|
if (CoinConfigurationVersions.isV4(version)) {
|
|
226
249
|
return coinV4Impl;
|
|
@@ -246,7 +269,7 @@ contract ZoraFactoryImpl is
|
|
|
246
269
|
string memory symbol,
|
|
247
270
|
address platformReferrer
|
|
248
271
|
) internal {
|
|
249
|
-
PoolKey memory poolKey = CoinSetup.buildPoolKey(address(coin), currency, isCoinToken0, IHooks(
|
|
272
|
+
PoolKey memory poolKey = CoinSetup.buildPoolKey(address(coin), currency, isCoinToken0, IHooks(hook));
|
|
250
273
|
|
|
251
274
|
// Initialize coin with pre-configured pool
|
|
252
275
|
coin.initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolKey, sqrtPriceX96, poolConfiguration);
|
|
@@ -310,7 +333,6 @@ contract ZoraFactoryImpl is
|
|
|
310
333
|
return keccak256(abi.encodePacked(msgSender, name, symbol, poolConfig, platformReferrer, coinSalt));
|
|
311
334
|
}
|
|
312
335
|
|
|
313
|
-
/// @dev Generates a unique salt for deterministic deployment
|
|
314
336
|
function _randomSalt(address payoutRecipient, string memory uri, bytes32 coinSalt) internal view returns (bytes32) {
|
|
315
337
|
return
|
|
316
338
|
keccak256(
|
|
@@ -354,13 +376,30 @@ contract ZoraFactoryImpl is
|
|
|
354
376
|
// try to get the existing contract name - if it reverts, the existing contract was an older version that didn't have the contract name
|
|
355
377
|
// unfortunately we cannot use supportsInterface here because the existing implementation did not have that function
|
|
356
378
|
try IHasContractName(newImpl).contractName() returns (string memory name) {
|
|
357
|
-
if (!
|
|
379
|
+
if (!Strings.equal(name, contractName())) {
|
|
358
380
|
revert UpgradeToMismatchedContractName(contractName(), name);
|
|
359
381
|
}
|
|
360
382
|
} catch {}
|
|
383
|
+
|
|
384
|
+
// Auto-register the new hooks in the Zora hook registry
|
|
385
|
+
address[] memory hooks = new address[](1);
|
|
386
|
+
string[] memory tags = new string[](1);
|
|
387
|
+
|
|
388
|
+
hooks[0] = IZoraFactory(newImpl).hook();
|
|
389
|
+
tags[0] = "CoinHook";
|
|
390
|
+
|
|
391
|
+
IZoraHookRegistry(zoraHookRegistry).registerHooks(hooks, tags);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/// @notice The address of the latest creator coin hook
|
|
395
|
+
/// @dev Deprecated: use `hook` instead
|
|
396
|
+
function creatorCoinHook() external view returns (address) {
|
|
397
|
+
return hook;
|
|
361
398
|
}
|
|
362
399
|
|
|
363
|
-
|
|
364
|
-
|
|
400
|
+
/// @notice The address of the latest coin hook
|
|
401
|
+
/// @dev Deprecated: use `hook` instead
|
|
402
|
+
function contentCoinHook() external view returns (address) {
|
|
403
|
+
return hook;
|
|
365
404
|
}
|
|
366
405
|
}
|
|
@@ -37,12 +37,12 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
37
37
|
// hooks
|
|
38
38
|
address buySupplyWithSwapRouterHook;
|
|
39
39
|
address zoraV4CoinHook;
|
|
40
|
-
address creatorCoinHook;
|
|
41
40
|
address hookUpgradeGate;
|
|
42
41
|
// Hook deployment salt (for deterministic deployment)
|
|
43
42
|
bytes32 zoraV4CoinHookSalt;
|
|
44
|
-
bytes32 creatorCoinHookSalt;
|
|
45
43
|
bool isDev;
|
|
44
|
+
// Hook registry
|
|
45
|
+
address zoraHookRegistry;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function addressesFile() internal view returns (string memory) {
|
|
@@ -67,9 +67,8 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
67
67
|
vm.serializeAddress(objectKey, "ZORA_V4_COIN_HOOK", deployment.zoraV4CoinHook);
|
|
68
68
|
vm.serializeBytes32(objectKey, "ZORA_V4_COIN_HOOK_SALT", deployment.zoraV4CoinHookSalt);
|
|
69
69
|
vm.serializeAddress(objectKey, "CREATOR_COIN_IMPL", deployment.creatorCoinImpl);
|
|
70
|
-
vm.serializeAddress(objectKey, "CREATOR_COIN_HOOK", deployment.creatorCoinHook);
|
|
71
|
-
vm.serializeBytes32(objectKey, "CREATOR_COIN_HOOK_SALT", deployment.creatorCoinHookSalt);
|
|
72
70
|
vm.serializeAddress(objectKey, "HOOK_UPGRADE_GATE", deployment.hookUpgradeGate);
|
|
71
|
+
vm.serializeAddress(objectKey, "ZORA_HOOK_REGISTRY", deployment.zoraHookRegistry);
|
|
73
72
|
string memory result = vm.serializeAddress(objectKey, "COIN_V4_IMPL", deployment.coinV4Impl);
|
|
74
73
|
|
|
75
74
|
vm.writeJson(result, addressesFile(deployment.isDev));
|
|
@@ -96,12 +95,11 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
96
95
|
deployment.zoraV4CoinHook = readAddressOrDefaultToZero(json, "ZORA_V4_COIN_HOOK");
|
|
97
96
|
deployment.zoraV4CoinHookSalt = readBytes32OrDefaultToZero(json, "ZORA_V4_COIN_HOOK_SALT");
|
|
98
97
|
deployment.creatorCoinImpl = readAddressOrDefaultToZero(json, "CREATOR_COIN_IMPL");
|
|
99
|
-
deployment.creatorCoinHook = readAddressOrDefaultToZero(json, "CREATOR_COIN_HOOK");
|
|
100
|
-
deployment.creatorCoinHookSalt = readBytes32OrDefaultToZero(json, "CREATOR_COIN_HOOK_SALT");
|
|
101
98
|
deployment.hookUpgradeGate = readAddressOrDefaultToZero(json, "HOOK_UPGRADE_GATE");
|
|
99
|
+
deployment.zoraHookRegistry = readAddressOrDefaultToZero(json, "ZORA_HOOK_REGISTRY");
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
function deployCoinV4Impl(
|
|
102
|
+
function deployCoinV4Impl() internal returns (ContentCoin) {
|
|
105
103
|
return
|
|
106
104
|
new ContentCoin({
|
|
107
105
|
protocolRewardRecipient_: getZoraRecipient(),
|
|
@@ -111,29 +109,18 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
111
109
|
});
|
|
112
110
|
}
|
|
113
111
|
|
|
114
|
-
function deployCreatorCoinImpl(
|
|
112
|
+
function deployCreatorCoinImpl() internal returns (CreatorCoin) {
|
|
115
113
|
return
|
|
116
114
|
new CreatorCoin({
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
protocolRewardRecipient_: getZoraRecipient(),
|
|
116
|
+
protocolRewards_: PROTOCOL_REWARDS,
|
|
117
|
+
poolManager_: IPoolManager(getUniswapV4PoolManager()),
|
|
118
|
+
airlock_: getDopplerAirlock()
|
|
121
119
|
});
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
function deployZoraFactoryImpl(
|
|
125
|
-
|
|
126
|
-
address _creatorCoinImpl,
|
|
127
|
-
address _contentCoinHook,
|
|
128
|
-
address _creatorCoinHook
|
|
129
|
-
) internal returns (ZoraFactoryImpl) {
|
|
130
|
-
return
|
|
131
|
-
new ZoraFactoryImpl({
|
|
132
|
-
_coinV4Impl: _coinV4Impl,
|
|
133
|
-
_creatorCoinImpl: _creatorCoinImpl,
|
|
134
|
-
_contentCoinHook: _contentCoinHook,
|
|
135
|
-
_creatorCoinHook: _creatorCoinHook
|
|
136
|
-
});
|
|
122
|
+
function deployZoraFactoryImpl(address coinV4Impl_, address creatorCoinImpl_, address hook_, address zoraHookRegistry_) internal returns (ZoraFactoryImpl) {
|
|
123
|
+
return new ZoraFactoryImpl({coinV4Impl_: coinV4Impl_, creatorCoinImpl_: creatorCoinImpl_, hook_: hook_, zoraHookRegistry_: zoraHookRegistry_});
|
|
137
124
|
}
|
|
138
125
|
|
|
139
126
|
// function deployBuySupplyWithSwapRouterHook(CoinsDeployment memory deployment) internal returns (BuySupplyWithSwapRouterHook) {
|
|
@@ -151,11 +138,11 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
151
138
|
return deployment;
|
|
152
139
|
}
|
|
153
140
|
|
|
154
|
-
function
|
|
141
|
+
function deployZoraV4CoinHook(CoinsDeployment memory deployment) internal returns (IHooks hook, bytes32 salt) {
|
|
155
142
|
return
|
|
156
143
|
HooksDeployment.deployHookWithExistingOrNewSalt(
|
|
157
144
|
HooksDeployment.FOUNDRY_SCRIPT_ADDRESS,
|
|
158
|
-
HooksDeployment.
|
|
145
|
+
HooksDeployment.makeHookCreationCode(
|
|
159
146
|
getUniswapV4PoolManager(),
|
|
160
147
|
deployment.zoraFactory,
|
|
161
148
|
getDefaultTrustedMessageSenders(),
|
|
@@ -165,20 +152,6 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
165
152
|
);
|
|
166
153
|
}
|
|
167
154
|
|
|
168
|
-
function deployCreatorCoinHook(CoinsDeployment memory deployment) internal returns (IHooks hook, bytes32 salt) {
|
|
169
|
-
return
|
|
170
|
-
HooksDeployment.deployHookWithExistingOrNewSalt(
|
|
171
|
-
HooksDeployment.FOUNDRY_SCRIPT_ADDRESS,
|
|
172
|
-
HooksDeployment.creatorCoinHookCreationCode(
|
|
173
|
-
getUniswapV4PoolManager(),
|
|
174
|
-
deployment.zoraFactory,
|
|
175
|
-
getDefaultTrustedMessageSenders(),
|
|
176
|
-
deployment.hookUpgradeGate
|
|
177
|
-
),
|
|
178
|
-
deployment.creatorCoinHookSalt
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
155
|
function getDefaultTrustedMessageSenders() internal view returns (address[] memory) {
|
|
183
156
|
address[] memory trustedMessageSenders = new address[](2);
|
|
184
157
|
trustedMessageSenders[0] = getUniswapUniversalRouter();
|
|
@@ -190,10 +163,10 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
190
163
|
return
|
|
191
164
|
address(
|
|
192
165
|
deployZoraFactoryImpl({
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
166
|
+
coinV4Impl_: deployment.coinV4Impl,
|
|
167
|
+
creatorCoinImpl_: deployment.creatorCoinImpl,
|
|
168
|
+
hook_: deployment.zoraV4CoinHook,
|
|
169
|
+
zoraHookRegistry_: deployment.zoraHookRegistry
|
|
197
170
|
})
|
|
198
171
|
);
|
|
199
172
|
}
|
|
@@ -203,17 +176,12 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
203
176
|
|
|
204
177
|
// Deploy hook first, then use its address for coin v4 impl
|
|
205
178
|
console.log("deploying content coin hook");
|
|
206
|
-
(IHooks zoraV4CoinHook, bytes32 usedSalt) =
|
|
179
|
+
(IHooks zoraV4CoinHook, bytes32 usedSalt) = deployZoraV4CoinHook(deployment);
|
|
207
180
|
deployment.zoraV4CoinHook = address(zoraV4CoinHook);
|
|
208
181
|
deployment.zoraV4CoinHookSalt = usedSalt;
|
|
209
182
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
deployment.creatorCoinHook = address(creatorCoinHook);
|
|
213
|
-
deployment.creatorCoinHookSalt = usedCreatorCoinHookSalt;
|
|
214
|
-
|
|
215
|
-
deployment.coinV4Impl = address(deployCoinV4Impl(deployment.zoraV4CoinHook));
|
|
216
|
-
deployment.creatorCoinImpl = address(deployCreatorCoinImpl(deployment.creatorCoinHook));
|
|
183
|
+
deployment.coinV4Impl = address(deployCoinV4Impl());
|
|
184
|
+
deployment.creatorCoinImpl = address(deployCreatorCoinImpl());
|
|
217
185
|
deployment.zoraFactoryImpl = deployFactoryImpl(deployment);
|
|
218
186
|
deployment.coinVersion = IVersionedContract(deployment.coinV4Impl).contractVersion();
|
|
219
187
|
// deployment.buySupplyWithSwapRouterHook = address(deployBuySupplyWithSwapRouterHook(deployment));
|
|
@@ -223,14 +191,10 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
223
191
|
|
|
224
192
|
function deployHooks(CoinsDeployment memory deployment) internal returns (CoinsDeployment memory) {
|
|
225
193
|
// Deploy hook first, then use its address for coin v4 impl
|
|
226
|
-
(IHooks zoraV4CoinHook, bytes32 usedSalt) =
|
|
194
|
+
(IHooks zoraV4CoinHook, bytes32 usedSalt) = deployZoraV4CoinHook(deployment);
|
|
227
195
|
deployment.zoraV4CoinHook = address(zoraV4CoinHook);
|
|
228
196
|
deployment.zoraV4CoinHookSalt = usedSalt;
|
|
229
197
|
|
|
230
|
-
(IHooks creatorCoinHook, bytes32 usedCreatorCoinHookSalt) = deployCreatorCoinHook(deployment);
|
|
231
|
-
deployment.creatorCoinHook = address(creatorCoinHook);
|
|
232
|
-
deployment.creatorCoinHookSalt = usedCreatorCoinHookSalt;
|
|
233
|
-
|
|
234
198
|
deployment.zoraFactoryImpl = deployFactoryImpl(deployment);
|
|
235
199
|
|
|
236
200
|
return deployment;
|
|
@@ -284,6 +248,8 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
284
248
|
ZoraFactoryImpl(deployment.zoraFactory).initialize(owner);
|
|
285
249
|
|
|
286
250
|
saveDeployment(deployment);
|
|
251
|
+
|
|
252
|
+
return ZoraFactory(payable(deployment.zoraFactory));
|
|
287
253
|
}
|
|
288
254
|
|
|
289
255
|
function printUpgradeFactoryCommand(CoinsDeployment memory deployment) internal view {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// SPDX-License-Identifier: ZORA-DELAYED-OSL-v1
|
|
2
|
+
// This software is licensed under the Zora Delayed Open Source License.
|
|
3
|
+
// Under this license, you may use, copy, modify, and distribute this software for
|
|
4
|
+
// non-commercial purposes only. Commercial use and competitive products are prohibited
|
|
5
|
+
// until the "Open Date" (3 years from first public distribution or earlier at Zora's discretion),
|
|
6
|
+
// at which point this software automatically becomes available under the MIT License.
|
|
7
|
+
// Full license terms available at: https://docs.zora.co/coins/license
|
|
8
|
+
pragma solidity ^0.8.23;
|
|
9
|
+
|
|
10
|
+
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
11
|
+
import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
|
|
12
|
+
import {MultiOwnable} from "../utils/MultiOwnable.sol";
|
|
13
|
+
import {IZoraHookRegistry} from "../interfaces/IZoraHookRegistry.sol";
|
|
14
|
+
|
|
15
|
+
/// @title Zora Hook Registry
|
|
16
|
+
/// @notice A registry of Zora hook contracts for Uniswap V4
|
|
17
|
+
contract ZoraHookRegistry is IZoraHookRegistry, MultiOwnable {
|
|
18
|
+
using EnumerableSet for EnumerableSet.AddressSet;
|
|
19
|
+
|
|
20
|
+
/// @dev The set of registered hook addresses
|
|
21
|
+
EnumerableSet.AddressSet internal registeredHooks;
|
|
22
|
+
|
|
23
|
+
/// @dev The tag for each hook
|
|
24
|
+
mapping(address hook => string tag) internal hookTags;
|
|
25
|
+
|
|
26
|
+
constructor() {}
|
|
27
|
+
|
|
28
|
+
/// @notice Initializes the registry with initial owners
|
|
29
|
+
function initialize(address[] memory initialOwners) external initializer {
|
|
30
|
+
__MultiOwnable_init(initialOwners);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// @notice Returns whether a hook is currently registered
|
|
34
|
+
function isRegisteredHook(address hook) external view returns (bool) {
|
|
35
|
+
return registeredHooks.contains(hook);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// @notice Returns all registered hooks
|
|
39
|
+
function getHooks() external view returns (ZoraHook[] memory) {
|
|
40
|
+
uint256 numHooks = registeredHooks.length();
|
|
41
|
+
|
|
42
|
+
ZoraHook[] memory hooks = new ZoraHook[](numHooks);
|
|
43
|
+
|
|
44
|
+
for (uint256 i; i < numHooks; i++) {
|
|
45
|
+
address hook = registeredHooks.at(i);
|
|
46
|
+
|
|
47
|
+
hooks[i] = ZoraHook({hook: hook, tag: getHookTag(hook), version: getHookVersion(hook)});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return hooks;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// @notice Returns all registered hook addresses
|
|
54
|
+
function getHookAddresses() external view returns (address[] memory) {
|
|
55
|
+
return registeredHooks.values();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @notice Returns the tag for a hook
|
|
59
|
+
function getHookTag(address hook) public view returns (string memory) {
|
|
60
|
+
return hookTags[hook];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// @notice Returns the contract version for a hook if it exists
|
|
64
|
+
function getHookVersion(address hook) public pure returns (string memory version) {
|
|
65
|
+
try IVersionedContract(hook).contractVersion() returns (string memory _version) {
|
|
66
|
+
version = _version;
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// @notice Adds hooks to the registry
|
|
71
|
+
function registerHooks(address[] calldata hooks, string[] calldata tags) external onlyOwner {
|
|
72
|
+
require(hooks.length == tags.length, ArrayLengthMismatch());
|
|
73
|
+
|
|
74
|
+
for (uint256 i; i < hooks.length; i++) {
|
|
75
|
+
if (registeredHooks.add(hooks[i])) {
|
|
76
|
+
hookTags[hooks[i]] = tags[i];
|
|
77
|
+
|
|
78
|
+
emit ZoraHookRegistered(hooks[i], tags[i], getHookVersion(hooks[i]));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// @notice Removes hooks from the registry
|
|
84
|
+
function removeHooks(address[] calldata hooks) external onlyOwner {
|
|
85
|
+
for (uint256 i; i < hooks.length; i++) {
|
|
86
|
+
if (registeredHooks.remove(hooks[i])) {
|
|
87
|
+
emit ZoraHookRemoved(hooks[i], hookTags[hooks[i]], getHookVersion(hooks[i]));
|
|
88
|
+
|
|
89
|
+
delete hookTags[hooks[i]];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -37,6 +37,7 @@ 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
|
+
import {IHasCoinType} from "../interfaces/ICoin.sol";
|
|
40
41
|
|
|
41
42
|
/// @title ZoraV4CoinHook
|
|
42
43
|
/// @notice Uniswap V4 hook that automatically handles fee collection and reward distributions on every swap,
|
|
@@ -48,7 +49,7 @@ import {ContractVersionBase, IVersionedContract} from "../version/ContractVersio
|
|
|
48
49
|
/// 2. Swaps collected fees to the backing currency through multi-hop paths
|
|
49
50
|
/// 3. Distributes converted fees as rewards
|
|
50
51
|
/// @author oveddan
|
|
51
|
-
|
|
52
|
+
contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook, ERC165, IUpgradeableDestinationV4Hook {
|
|
52
53
|
using BalanceDeltaLibrary for BalanceDelta;
|
|
53
54
|
|
|
54
55
|
/// @notice Mapping of trusted message senders - these are addresses that are trusted to provide a
|
|
@@ -64,8 +65,6 @@ abstract contract BaseZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4Co
|
|
|
64
65
|
/// @notice The upgrade gate contract - used to verify allowed upgrade paths
|
|
65
66
|
IHooksUpgradeGate internal immutable upgradeGate;
|
|
66
67
|
|
|
67
|
-
uint256 internal immutable totalSupplyForPositions;
|
|
68
|
-
|
|
69
68
|
/// @notice The constructor for the ZoraV4CoinHook.
|
|
70
69
|
/// @param poolManager_ The Uniswap V4 pool manager
|
|
71
70
|
/// @param coinVersionLookup_ The coin version lookup contract - used to determine if an address is a coin and what version it is.
|
|
@@ -75,8 +74,7 @@ abstract contract BaseZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4Co
|
|
|
75
74
|
IPoolManager poolManager_,
|
|
76
75
|
IDeployedCoinVersionLookup coinVersionLookup_,
|
|
77
76
|
address[] memory trustedMessageSenders_,
|
|
78
|
-
IHooksUpgradeGate upgradeGate_
|
|
79
|
-
uint256 totalSupplyForPositions_
|
|
77
|
+
IHooksUpgradeGate upgradeGate_
|
|
80
78
|
) BaseHook(poolManager_) {
|
|
81
79
|
require(address(coinVersionLookup_) != address(0), CoinVersionLookupCannotBeZeroAddress());
|
|
82
80
|
|
|
@@ -84,7 +82,6 @@ abstract contract BaseZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4Co
|
|
|
84
82
|
|
|
85
83
|
coinVersionLookup = coinVersionLookup_;
|
|
86
84
|
upgradeGate = upgradeGate_;
|
|
87
|
-
totalSupplyForPositions = totalSupplyForPositions_;
|
|
88
85
|
|
|
89
86
|
for (uint256 i = 0; i < trustedMessageSenders_.length; i++) {
|
|
90
87
|
trustedMessageSender[trustedMessageSenders_[i]] = true;
|
|
@@ -143,7 +140,7 @@ abstract contract BaseZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4Co
|
|
|
143
140
|
function _generatePositions(ICoin coin, PoolKey memory key) internal view returns (LpPosition[] memory positions) {
|
|
144
141
|
bool isCoinToken0 = Currency.unwrap(key.currency0) == address(coin);
|
|
145
142
|
|
|
146
|
-
positions = CoinDopplerMultiCurve.calculatePositions(isCoinToken0, coin.getPoolConfiguration(), totalSupplyForPositions);
|
|
143
|
+
positions = CoinDopplerMultiCurve.calculatePositions(isCoinToken0, coin.getPoolConfiguration(), coin.totalSupplyForPositions());
|
|
147
144
|
}
|
|
148
145
|
|
|
149
146
|
/// @notice Internal fn called when a pool is initialized.
|
|
@@ -336,7 +333,15 @@ abstract contract BaseZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4Co
|
|
|
336
333
|
}
|
|
337
334
|
|
|
338
335
|
/// @dev Internal fn to allow for overriding market reward distribution logic
|
|
339
|
-
function _distributeMarketRewards(Currency currency, uint128 fees, IHasRewardsRecipients coin, address tradeReferrer) internal virtual
|
|
336
|
+
function _distributeMarketRewards(Currency currency, uint128 fees, IHasRewardsRecipients coin, address tradeReferrer) internal virtual {
|
|
337
|
+
// get rewards distribution methodology from the coin
|
|
338
|
+
IHasCoinType.CoinType coinType = _getCoinType(coin);
|
|
339
|
+
CoinRewardsV4.distributeMarketRewards(currency, fees, coin, tradeReferrer, coinType);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function _getCoinType(IHasRewardsRecipients coin) internal view returns (IHasCoinType.CoinType) {
|
|
343
|
+
return CoinRewardsV4.getCoinType(coin);
|
|
344
|
+
}
|
|
340
345
|
|
|
341
346
|
/// @notice Internal fn called when the PoolManager is unlocked. Used to mint initial liquidity positions.
|
|
342
347
|
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
|
package/src/interfaces/ICoin.sol
CHANGED
|
@@ -50,7 +50,25 @@ interface IHasSwapPath {
|
|
|
50
50
|
function getPayoutSwapPath(IDeployedCoinVersionLookup coinVersionLookup) external view returns (PayoutSwapPath memory);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
interface
|
|
53
|
+
interface IHasTotalSupplyForPositions {
|
|
54
|
+
/// @notice Returns the total supply of all positions for this coin
|
|
55
|
+
/// @return The total supply of all positions
|
|
56
|
+
function totalSupplyForPositions() external view returns (uint256);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface IHasCoinType {
|
|
60
|
+
/// @notice The type of coin
|
|
61
|
+
enum CoinType {
|
|
62
|
+
Creator,
|
|
63
|
+
Content
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// @notice Returns the type of coin
|
|
67
|
+
/// @return The type of coin
|
|
68
|
+
function coinType() external view returns (CoinType);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface ICoin is IERC165, IERC7572, IDopplerErrors, IHasRewardsRecipients, IHasPoolKey, IHasSwapPath, IHasTotalSupplyForPositions, IHasCoinType {
|
|
54
72
|
/// @notice Thrown when the name is required for the coin
|
|
55
73
|
error NameIsRequired();
|
|
56
74
|
|
|
@@ -18,4 +18,8 @@ interface ICreatorCoin is ICoin {
|
|
|
18
18
|
/// @notice Allows the creator payout recipient to claim vested tokens
|
|
19
19
|
/// @return claimAmount The amount of tokens claimed
|
|
20
20
|
function claimVesting() external returns (uint256);
|
|
21
|
+
|
|
22
|
+
/// @notice Get the amount of vested tokens that can be claimed
|
|
23
|
+
/// @return claimAmount The amount of tokens that can be claimed
|
|
24
|
+
function getClaimableAmount() external view returns (uint256);
|
|
21
25
|
}
|
|
@@ -83,6 +83,29 @@ interface IZoraFactory is IDeployedCoinVersionLookup {
|
|
|
83
83
|
/// @notice Thrown when ETH is sent with a transaction but the currency is not WETH
|
|
84
84
|
error EthTransferInvalid();
|
|
85
85
|
|
|
86
|
+
/// @notice Thrown when the hook is invalid
|
|
87
|
+
error InvalidHook();
|
|
88
|
+
|
|
89
|
+
/// @notice Occurs when attempting to upgrade to a contract with a name that doesn't match the current contract's name
|
|
90
|
+
/// @param currentName The name of the current contract
|
|
91
|
+
/// @param newName The name of the contract being upgraded to
|
|
92
|
+
error UpgradeToMismatchedContractName(string currentName, string newName);
|
|
93
|
+
|
|
94
|
+
/// @notice Thrown when a method is deprecated
|
|
95
|
+
error Deprecated();
|
|
96
|
+
|
|
97
|
+
/// @notice Thrwon when an invalid config version is provided
|
|
98
|
+
error InvalidConfig();
|
|
99
|
+
|
|
100
|
+
/// @notice Creates a new creator coin contract
|
|
101
|
+
/// @param payoutRecipient The recipient of creator reward payouts; this can be updated by an owner
|
|
102
|
+
/// @param owners The list of addresses that will be able to manage the coin's payout address and metadata uri
|
|
103
|
+
/// @param uri The coin metadata uri
|
|
104
|
+
/// @param name The name of the coin
|
|
105
|
+
/// @param symbol The symbol of the coin
|
|
106
|
+
/// @param poolConfig The config parameters for the coin's pool
|
|
107
|
+
/// @param platformReferrer The address of the platform referrer
|
|
108
|
+
/// @param coinSalt The salt used to deploy the coin
|
|
86
109
|
function deployCreatorCoin(
|
|
87
110
|
address payoutRecipient,
|
|
88
111
|
address[] memory owners,
|
|
@@ -163,19 +186,18 @@ interface IZoraFactory is IDeployedCoinVersionLookup {
|
|
|
163
186
|
bytes calldata hookData
|
|
164
187
|
) external payable returns (address coin, bytes memory hookDataOut);
|
|
165
188
|
|
|
189
|
+
/// @notice The implementation address of the factory contract
|
|
166
190
|
function implementation() external view returns (address);
|
|
167
191
|
|
|
168
|
-
/// @notice
|
|
169
|
-
|
|
192
|
+
/// @notice The address of the latest coin hook
|
|
193
|
+
function hook() external view returns (address);
|
|
170
194
|
|
|
171
|
-
/// @notice
|
|
172
|
-
|
|
173
|
-
/// @param newName The name of the contract being upgraded to
|
|
174
|
-
error UpgradeToMismatchedContractName(string currentName, string newName);
|
|
195
|
+
/// @notice The address of the latest content coin hook
|
|
196
|
+
function contentCoinHook() external view returns (address);
|
|
175
197
|
|
|
176
|
-
/// @notice
|
|
177
|
-
|
|
198
|
+
/// @notice The address of the latestcreator coin hook
|
|
199
|
+
function creatorCoinHook() external view returns (address);
|
|
178
200
|
|
|
179
|
-
/// @notice
|
|
180
|
-
|
|
201
|
+
/// @notice The address of the Zora hook registry
|
|
202
|
+
function zoraHookRegistry() external view returns (address);
|
|
181
203
|
}
|