@zoralabs/coins 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +131 -114
- package/CHANGELOG.md +40 -0
- package/abis/BaseCoin.json +26 -118
- package/abis/BaseTest.json +47 -0
- package/abis/Coin.json +171 -63
- package/abis/CoinDopplerMultiCurve.json +38 -0
- package/abis/CoinRewardsV4.json +54 -0
- package/abis/CoinTest.json +53 -20
- package/abis/CoinUniV4Test.json +1053 -0
- package/abis/CoinV4.json +234 -211
- package/abis/DeployScript.json +47 -0
- package/abis/DeployedCoinVersionLookup.json +21 -0
- package/abis/DeployedCoinVersionLookupTest.json +716 -0
- package/abis/DifferentNamespaceVersionLookup.json +39 -0
- package/abis/DopplerUniswapV3Test.json +49 -93
- package/abis/ERC20.json +310 -0
- package/abis/FactoryTest.json +85 -7
- package/abis/FeeEstimatorHook.json +1528 -0
- package/abis/HooksDeployment.json +23 -0
- package/abis/HooksTest.json +47 -0
- package/abis/ICoin.json +40 -71
- package/abis/ICoinV3.json +879 -0
- package/abis/ICoinV4.json +915 -0
- package/abis/IDeployedCoinVersionLookup.json +21 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasPoolKey.json +42 -0
- package/abis/IHasRewardsRecipients.json +54 -0
- package/abis/IHasSwapPath.json +60 -0
- package/abis/IMsgSender.json +15 -0
- package/abis/IPoolConfigEncoding.json +46 -0
- package/abis/ISwapPathRouter.json +92 -0
- package/abis/IUniversalRouter.json +61 -0
- package/abis/IUnlockCallback.json +21 -0
- package/abis/IV4Quoter.json +310 -0
- package/abis/IZoraFactory.json +191 -11
- package/abis/IZoraV4CoinHook.json +348 -4
- package/abis/MockERC20.json +21 -0
- package/abis/MultiOwnableTest.json +47 -0
- package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
- package/abis/PrintUpgradeCommand.json +9 -0
- package/abis/ProxyShim.json +24 -0
- package/abis/StateLibrary.json +80 -0
- package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
- package/abis/TestV4Swap.json +9 -0
- package/abis/UpgradeCoinImpl.json +47 -0
- package/abis/UpgradesTest.json +67 -0
- package/abis/Vm.json +1482 -111
- package/abis/VmSafe.json +856 -32
- package/abis/ZoraFactoryImpl.json +339 -1
- package/abis/ZoraV4CoinHook.json +455 -5
- package/addresses/8453.json +8 -4
- package/addresses/84532.json +8 -4
- package/dist/index.cjs +1920 -169
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1916 -169
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +2599 -183
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +1928 -165
- package/package.json +8 -3
- package/remappings.txt +6 -1
- package/script/CoinsDeployerBase.sol +74 -11
- package/script/DeployDevFactory.s.sol +21 -0
- package/script/PrintUpgradeCommand.s.sol +13 -0
- package/script/Simulate.s.sol +1 -10
- package/script/TestBackingCoinSwap.s.sol +146 -0
- package/script/TestV4Swap.s.sol +136 -0
- package/script/UpgradeFactoryImpl.s.sol +1 -1
- package/src/BaseCoin.sol +176 -0
- package/src/Coin.sol +87 -202
- package/src/CoinV4.sol +121 -0
- package/src/ZoraFactoryImpl.sol +208 -36
- package/src/hooks/ZoraV4CoinHook.sol +195 -0
- package/src/hooks/{BaseCoinDeployHook.sol → deployment/BaseCoinDeployHook.sol} +3 -3
- package/src/hooks/{BuySupplyWithSwapRouterHook.sol → deployment/BuySupplyWithSwapRouterHook.sol} +7 -5
- package/src/interfaces/ICoin.sol +31 -39
- package/src/interfaces/ICoinV3.sol +71 -0
- package/src/interfaces/ICoinV4.sol +69 -0
- package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
- package/src/interfaces/IMsgSender.sol +9 -0
- package/src/interfaces/IPoolConfigEncoding.sol +14 -0
- package/src/interfaces/ISwapPathRouter.sol +14 -0
- package/src/interfaces/IZoraFactory.sol +65 -27
- package/src/interfaces/IZoraV4CoinHook.sol +116 -0
- package/src/libs/CoinCommon.sol +15 -0
- package/src/libs/CoinConfigurationVersions.sol +116 -1
- package/src/libs/CoinConstants.sol +5 -0
- package/src/libs/CoinDopplerMultiCurve.sol +134 -0
- package/src/libs/CoinDopplerUniV3.sol +19 -171
- package/src/libs/CoinRewards.sol +195 -0
- package/src/libs/CoinRewardsV4.sol +180 -0
- package/src/libs/CoinSetup.sol +57 -0
- package/src/libs/CoinSetupV3.sol +6 -67
- package/src/libs/DopplerMath.sol +156 -0
- package/src/libs/HooksDeployment.sol +84 -0
- package/src/libs/MarketConstants.sol +4 -0
- package/src/libs/PoolStateReader.sol +22 -0
- package/src/libs/UniV3BuySell.sol +74 -292
- package/src/libs/UniV4SwapHelper.sol +65 -0
- package/src/libs/UniV4SwapToCurrency.sol +109 -0
- package/src/libs/V4Liquidity.sol +129 -0
- package/src/types/PoolConfiguration.sol +15 -0
- package/src/utils/DeployedCoinVersionLookup.sol +52 -0
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +78 -88
- package/test/CoinDopplerUniV3.t.sol +32 -171
- package/test/CoinUniV4.t.sol +752 -0
- package/test/{Hooks.t.sol → DeploymentHooks.t.sol} +2 -6
- package/test/Factory.t.sol +80 -47
- package/test/MultiOwnable.t.sol +6 -3
- package/test/Upgrades.t.sol +6 -5
- package/test/mocks/MockERC20.sol +12 -0
- package/test/utils/BaseTest.sol +106 -56
- package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
- package/test/utils/FeeEstimatorHook.sol +84 -0
- package/test/utils/ProxyShim.sol +17 -0
- package/wagmi.config.ts +4 -0
- package/.env +0 -1
- package/.turbo/turbo-update-contract-version.log +0 -22
- package/abis/CoinSetupV3.json +0 -7
- package/abis/HookDeployer.json +0 -68
- package/abis/IHookDeployer.json +0 -42
- package/src/libs/CoinLegacy.sol +0 -48
- package/src/libs/CoinLegacyMarket.sol +0 -182
package/src/CoinV4.sol
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {IPoolManager, PoolKey, Currency, IHooks} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
5
|
+
|
|
6
|
+
import {BaseCoin} from "./BaseCoin.sol";
|
|
7
|
+
import {ICoinV4, IHasPoolKey, IHasSwapPath} from "./interfaces/ICoinV4.sol";
|
|
8
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
9
|
+
import {PoolConfiguration} from "./types/PoolConfiguration.sol";
|
|
10
|
+
import {UniV4SwapToCurrency} from "./libs/UniV4SwapToCurrency.sol";
|
|
11
|
+
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
|
12
|
+
import {IDeployedCoinVersionLookup} from "./interfaces/IDeployedCoinVersionLookup.sol";
|
|
13
|
+
|
|
14
|
+
contract CoinV4 is BaseCoin, ICoinV4 {
|
|
15
|
+
/// @notice The Uniswap v4 pool manager singleton contract reference.
|
|
16
|
+
IPoolManager public immutable poolManager;
|
|
17
|
+
|
|
18
|
+
/// @notice The hooks contract used by this coin.
|
|
19
|
+
IHooks public immutable hooks;
|
|
20
|
+
|
|
21
|
+
/// @notice The pool key for the coin. Type from Uniswap V4 core.
|
|
22
|
+
PoolKey private poolKey;
|
|
23
|
+
|
|
24
|
+
/// @notice The configuration for the pool.
|
|
25
|
+
PoolConfiguration private poolConfiguration;
|
|
26
|
+
|
|
27
|
+
/// @notice The constructor for the static CoinV4 contract deployment shared across all Coins.
|
|
28
|
+
/// @dev All arguments are required and cannot be set to teh 0 address.
|
|
29
|
+
/// @param protocolRewardRecipient_ The address of the protocol reward recipient
|
|
30
|
+
/// @param protocolRewards_ The address of the protocol rewards contract
|
|
31
|
+
/// @param poolManager_ The address of the pool manager
|
|
32
|
+
/// @param airlock_ The address of the Airlock contract, ownership is used for a protocol fee split.
|
|
33
|
+
/// @param hooks_ The address of the hooks contract
|
|
34
|
+
/// @notice Returns the pool key for the coin
|
|
35
|
+
constructor(
|
|
36
|
+
address protocolRewardRecipient_,
|
|
37
|
+
address protocolRewards_,
|
|
38
|
+
IPoolManager poolManager_,
|
|
39
|
+
address airlock_,
|
|
40
|
+
IHooks hooks_
|
|
41
|
+
) BaseCoin(protocolRewardRecipient_, protocolRewards_, airlock_) {
|
|
42
|
+
if (address(poolManager_) == address(0)) {
|
|
43
|
+
revert AddressZero();
|
|
44
|
+
}
|
|
45
|
+
if (address(hooks_) == address(0)) {
|
|
46
|
+
revert AddressZero();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
poolManager = poolManager_;
|
|
50
|
+
hooks = hooks_;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// @inheritdoc IHasPoolKey
|
|
54
|
+
function getPoolKey() public view returns (PoolKey memory) {
|
|
55
|
+
return poolKey;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @inheritdoc ICoinV4
|
|
59
|
+
function getPoolConfiguration() public view returns (PoolConfiguration memory) {
|
|
60
|
+
return poolConfiguration;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// @inheritdoc ICoinV4
|
|
64
|
+
function initialize(
|
|
65
|
+
address payoutRecipient_,
|
|
66
|
+
address[] memory owners_,
|
|
67
|
+
string memory tokenURI_,
|
|
68
|
+
string memory name_,
|
|
69
|
+
string memory symbol_,
|
|
70
|
+
address platformReferrer_,
|
|
71
|
+
address currency_,
|
|
72
|
+
PoolKey memory poolKey_,
|
|
73
|
+
uint160 sqrtPriceX96,
|
|
74
|
+
PoolConfiguration memory poolConfiguration_
|
|
75
|
+
) public initializer {
|
|
76
|
+
super._initialize(payoutRecipient_, owners_, tokenURI_, name_, symbol_, platformReferrer_);
|
|
77
|
+
|
|
78
|
+
currency = currency_;
|
|
79
|
+
poolKey = poolKey_;
|
|
80
|
+
poolConfiguration = poolConfiguration_;
|
|
81
|
+
|
|
82
|
+
// transfer the supply to the hook
|
|
83
|
+
_transfer(address(this), address(hooks), balanceOf(address(this)));
|
|
84
|
+
// initialize the pool - the hook will mint its positions in the afterInitialize callback
|
|
85
|
+
poolManager.initialize(poolKey, sqrtPriceX96);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function supportsInterface(bytes4 interfaceId) public pure virtual override(BaseCoin, IERC165) returns (bool) {
|
|
89
|
+
return interfaceId == type(IHasPoolKey).interfaceId || type(IHasSwapPath).interfaceId == interfaceId || super.supportsInterface(interfaceId);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// @inheritdoc IHasSwapPath
|
|
93
|
+
function getPayoutSwapPath(IDeployedCoinVersionLookup coinVersionLookup) external view returns (IHasSwapPath.PayoutSwapPath memory payoutSwapPath) {
|
|
94
|
+
// if to swap in is this currency,
|
|
95
|
+
// if backing currency is a coin, then recursively get the path from the coin
|
|
96
|
+
payoutSwapPath.currencyIn = Currency.wrap(address(this));
|
|
97
|
+
|
|
98
|
+
// swap to backing currency
|
|
99
|
+
PathKey memory thisPathKey = PathKey({
|
|
100
|
+
intermediateCurrency: Currency.wrap(currency),
|
|
101
|
+
fee: poolKey.fee,
|
|
102
|
+
tickSpacing: poolKey.tickSpacing,
|
|
103
|
+
hooks: poolKey.hooks,
|
|
104
|
+
hookData: ""
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// get backing currency swap path - if the backing currency is a v4 coin and has a swap path.
|
|
108
|
+
PathKey[] memory subPath = UniV4SwapToCurrency.getSubSwapPath(currency, coinVersionLookup);
|
|
109
|
+
|
|
110
|
+
if (subPath.length > 0) {
|
|
111
|
+
payoutSwapPath.path = new PathKey[](1 + subPath.length);
|
|
112
|
+
payoutSwapPath.path[0] = thisPathKey;
|
|
113
|
+
for (uint256 i = 0; i < subPath.length; i++) {
|
|
114
|
+
payoutSwapPath.path[i + 1] = subPath[i];
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
payoutSwapPath.path = new PathKey[](1);
|
|
118
|
+
payoutSwapPath.path[0] = thisPathKey;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
package/src/ZoraFactoryImpl.sol
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
+
bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
|
|
88
125
|
|
|
89
|
-
|
|
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
|
-
|
|
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
|
|
105
|
-
|
|
198
|
+
) internal {
|
|
199
|
+
address v3Factory = coin.v3Factory();
|
|
106
200
|
|
|
107
|
-
|
|
201
|
+
address poolAddress = CoinSetupV3.createV3Pool(address(coin), currency, isCoinToken0, sqrtPriceX96, v3Factory);
|
|
108
202
|
|
|
109
|
-
|
|
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
|
-
|
|
115
|
-
|
|
211
|
+
platformReferrer,
|
|
212
|
+
currency,
|
|
116
213
|
uri,
|
|
117
214
|
name,
|
|
118
215
|
symbol,
|
|
119
216
|
address(coin),
|
|
120
|
-
|
|
121
|
-
coin.contractVersion()
|
|
217
|
+
poolAddress,
|
|
218
|
+
IVersionedContract(address(coin)).contractVersion()
|
|
122
219
|
);
|
|
220
|
+
}
|
|
123
221
|
|
|
124
|
-
|
|
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
|
|
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(
|
|
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
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {BaseHook} from "@uniswap/v4-periphery/src/utils/BaseHook.sol";
|
|
5
|
+
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
|
|
6
|
+
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
7
|
+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
8
|
+
import {BalanceDelta, BalanceDeltaLibrary} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
|
|
9
|
+
import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
|
|
10
|
+
import {IZoraV4CoinHook} from "../interfaces/IZoraV4CoinHook.sol";
|
|
11
|
+
import {IMsgSender} from "../interfaces/IMsgSender.sol";
|
|
12
|
+
import {IHasSwapPath} from "../interfaces/ICoinV4.sol";
|
|
13
|
+
import {LpPosition} from "../types/LpPosition.sol";
|
|
14
|
+
import {V4Liquidity} from "../libs/V4Liquidity.sol";
|
|
15
|
+
import {CoinRewardsV4} from "../libs/CoinRewardsV4.sol";
|
|
16
|
+
import {ICoinV4} from "../interfaces/ICoinV4.sol";
|
|
17
|
+
import {IDeployedCoinVersionLookup} from "../interfaces/IDeployedCoinVersionLookup.sol";
|
|
18
|
+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
19
|
+
import {CoinCommon} from "../libs/CoinCommon.sol";
|
|
20
|
+
import {PoolConfiguration} from "../types/PoolConfiguration.sol";
|
|
21
|
+
import {CoinDopplerMultiCurve} from "../libs/CoinDopplerMultiCurve.sol";
|
|
22
|
+
import {PoolStateReader} from "../libs/PoolStateReader.sol";
|
|
23
|
+
import {IHasSwapPath} from "../interfaces/ICoinV4.sol";
|
|
24
|
+
import {ContractVersionBase} from "../version/ContractVersionBase.sol";
|
|
25
|
+
import {CoinConfigurationVersions} from "../libs/CoinConfigurationVersions.sol";
|
|
26
|
+
|
|
27
|
+
contract ZoraV4CoinHook is BaseHook, ContractVersionBase, IZoraV4CoinHook {
|
|
28
|
+
using BalanceDeltaLibrary for BalanceDelta;
|
|
29
|
+
|
|
30
|
+
mapping(address => bool) internal trustedMessageSender;
|
|
31
|
+
|
|
32
|
+
IDeployedCoinVersionLookup internal immutable coinVersionLookup;
|
|
33
|
+
|
|
34
|
+
/// @inheritdoc IZoraV4CoinHook
|
|
35
|
+
function isTrustedMessageSender(address sender) external view returns (bool) {
|
|
36
|
+
return trustedMessageSender[sender];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// @notice The constructor for the ZoraV4CoinHook.
|
|
40
|
+
/// @param poolManager_ The pool manager for the coin.
|
|
41
|
+
/// @param trustedMessageSenders_ The addresses of the trusted message senders.
|
|
42
|
+
constructor(IPoolManager poolManager_, IDeployedCoinVersionLookup coinVersionLookup_, address[] memory trustedMessageSenders_) BaseHook(poolManager_) {
|
|
43
|
+
if (address(coinVersionLookup_) == address(0)) {
|
|
44
|
+
revert CoinVersionLookupCannotBeZeroAddress();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
coinVersionLookup = coinVersionLookup_;
|
|
48
|
+
|
|
49
|
+
for (uint256 i = 0; i < trustedMessageSenders_.length; i++) {
|
|
50
|
+
trustedMessageSender[trustedMessageSenders_[i]] = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// @notice Returns the uniswap v4 hook settings / permissions.
|
|
55
|
+
/// @dev The permissions currently requested are: afterInitialize and afterSwap.
|
|
56
|
+
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
|
|
57
|
+
return
|
|
58
|
+
Hooks.Permissions({
|
|
59
|
+
beforeInitialize: false,
|
|
60
|
+
afterInitialize: true,
|
|
61
|
+
beforeAddLiquidity: false,
|
|
62
|
+
afterAddLiquidity: false,
|
|
63
|
+
beforeRemoveLiquidity: false,
|
|
64
|
+
afterRemoveLiquidity: false,
|
|
65
|
+
beforeSwap: false,
|
|
66
|
+
afterSwap: true,
|
|
67
|
+
beforeDonate: false,
|
|
68
|
+
afterDonate: false,
|
|
69
|
+
beforeSwapReturnDelta: false,
|
|
70
|
+
afterSwapReturnDelta: false,
|
|
71
|
+
afterAddLiquidityReturnDelta: false,
|
|
72
|
+
afterRemoveLiquidityReturnDelta: false
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
mapping(bytes32 => IZoraV4CoinHook.PoolCoin) internal poolCoins;
|
|
77
|
+
|
|
78
|
+
function getPoolCoinByHash(bytes23 poolKeyHash) external view returns (IZoraV4CoinHook.PoolCoin memory) {
|
|
79
|
+
return poolCoins[poolKeyHash];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getPoolCoin(PoolKey memory key) external view returns (IZoraV4CoinHook.PoolCoin memory) {
|
|
83
|
+
return poolCoins[CoinCommon.hashPoolKey(key)];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// @notice Internal fn generating the positions for a given pool key.
|
|
87
|
+
/// @param coin The coin address.
|
|
88
|
+
/// @param key The pool key for the coin.
|
|
89
|
+
/// @return positions The contract-created liquidity positions the positions for the coin's pool.
|
|
90
|
+
function _generatePositions(ICoinV4 coin, PoolKey memory key) internal view returns (LpPosition[] memory positions) {
|
|
91
|
+
bool isCoinToken0 = Currency.unwrap(key.currency0) == address(coin);
|
|
92
|
+
|
|
93
|
+
positions = CoinDopplerMultiCurve.calculatePositions(isCoinToken0, coin.getPoolConfiguration());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// @notice Internal fn called when a pool is initialized.
|
|
97
|
+
/// @dev This hook is called from BaseHook library from uniswap v4.
|
|
98
|
+
/// @param sender The address of the sender.
|
|
99
|
+
/// @param key The pool key.
|
|
100
|
+
/// @return selector The selector of the afterInitialize hook to confirm the action.
|
|
101
|
+
function _afterInitialize(address sender, PoolKey calldata key, uint160, int24) internal override returns (bytes4) {
|
|
102
|
+
address coin = sender;
|
|
103
|
+
if (!CoinConfigurationVersions.isV4(coinVersionLookup.getVersionForDeployedCoin(coin))) {
|
|
104
|
+
revert NotACoin(coin);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
LpPosition[] memory positions = _generatePositions(ICoinV4(coin), key);
|
|
108
|
+
|
|
109
|
+
poolCoins[CoinCommon.hashPoolKey(key)] = PoolCoin({coin: coin, positions: positions});
|
|
110
|
+
|
|
111
|
+
V4Liquidity.lockAndMint(poolManager, key, positions);
|
|
112
|
+
|
|
113
|
+
return BaseHook.afterInitialize.selector;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// @notice Internal fn called when a swap is executed.
|
|
117
|
+
/// @dev This hook is called from BaseHook library from uniswap v4.
|
|
118
|
+
/// @param sender The address of the sender.
|
|
119
|
+
/// @param key The pool key.
|
|
120
|
+
/// @param params The swap parameters.
|
|
121
|
+
/// @param delta The balance delta.
|
|
122
|
+
/// @param hookData The hook data.
|
|
123
|
+
/// @return selector The selector of the afterSwap hook to confirm the action.
|
|
124
|
+
function _afterSwap(
|
|
125
|
+
address sender,
|
|
126
|
+
PoolKey calldata key,
|
|
127
|
+
SwapParams calldata params,
|
|
128
|
+
BalanceDelta delta,
|
|
129
|
+
bytes calldata hookData
|
|
130
|
+
) internal virtual override returns (bytes4, int128) {
|
|
131
|
+
bytes32 poolKeyHash = CoinCommon.hashPoolKey(key);
|
|
132
|
+
|
|
133
|
+
// get the coin address and positions for the pool key; they must have been set in the afterInitialize callback
|
|
134
|
+
address coin = poolCoins[poolKeyHash].coin;
|
|
135
|
+
require(coin != address(0), NoCoinForHook(key));
|
|
136
|
+
|
|
137
|
+
// get path for swapping the payout to a single currency
|
|
138
|
+
IHasSwapPath.PayoutSwapPath memory payoutSwapPath = IHasSwapPath(coin).getPayoutSwapPath(coinVersionLookup);
|
|
139
|
+
|
|
140
|
+
// Collect accrued LP fees from all positions, swap them to the target payout currency,
|
|
141
|
+
// and transfer the converted amount to this hook contract for distribution
|
|
142
|
+
(, , Currency receivedCurrency, uint128 receivedAmount) = CoinRewardsV4.collectFeesAndConvertToPayout(
|
|
143
|
+
poolManager,
|
|
144
|
+
key,
|
|
145
|
+
poolCoins[poolKeyHash].positions,
|
|
146
|
+
payoutSwapPath
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Distribute the collected and converted fees to all reward recipients (creator, referrers, protocol, etc.)
|
|
150
|
+
CoinRewardsV4.distributeMarketRewards(receivedCurrency, receivedAmount, ICoinV4(coin), CoinRewardsV4.getTradeReferral(hookData));
|
|
151
|
+
|
|
152
|
+
{
|
|
153
|
+
(address swapper, bool isTrustedSwapSenderAddress) = _getOriginalMsgSender(sender);
|
|
154
|
+
bool isCoinBuy = params.zeroForOne ? Currency.unwrap(key.currency1) == address(coin) : Currency.unwrap(key.currency0) == address(coin);
|
|
155
|
+
emit Swapped(
|
|
156
|
+
sender,
|
|
157
|
+
swapper,
|
|
158
|
+
isTrustedSwapSenderAddress,
|
|
159
|
+
key,
|
|
160
|
+
poolKeyHash,
|
|
161
|
+
params,
|
|
162
|
+
delta.amount0(),
|
|
163
|
+
delta.amount1(),
|
|
164
|
+
isCoinBuy,
|
|
165
|
+
hookData,
|
|
166
|
+
PoolStateReader.getSqrtPriceX96(key, poolManager)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return (BaseHook.afterSwap.selector, 0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// @notice Internal fn called when a liquidity is unlocked. Only callable from the PoolManager.
|
|
174
|
+
/// @dev This hook is called from BaseHook library from uniswap v4.
|
|
175
|
+
/// @param data The data.
|
|
176
|
+
/// @return selector The selector of the unlockCallback hook to confirm the action.
|
|
177
|
+
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
|
|
178
|
+
return abi.encode(V4Liquidity.handleMintPositionsCallback(poolManager, data));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// @notice Internal fn to get the original message sender.
|
|
182
|
+
/// @param sender The address of the sender.
|
|
183
|
+
/// @return swapper The original message sender.
|
|
184
|
+
/// @return senderIsTrusted Whether the sender is a trusted message sender.
|
|
185
|
+
function _getOriginalMsgSender(address sender) internal view returns (address swapper, bool senderIsTrusted) {
|
|
186
|
+
senderIsTrusted = trustedMessageSender[sender];
|
|
187
|
+
|
|
188
|
+
// If getter function reverts, we return a 0 address by default and continue execution.
|
|
189
|
+
try IMsgSender(sender).msgSender() returns (address _swapper) {
|
|
190
|
+
swapper = _swapper;
|
|
191
|
+
} catch {
|
|
192
|
+
swapper = address(0);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.13;
|
|
3
3
|
|
|
4
|
-
import {ICoin} from "
|
|
5
|
-
import {IZoraFactory} from "
|
|
6
|
-
import {ICoinDeployHook} from "
|
|
4
|
+
import {ICoin} from "../../interfaces/ICoin.sol";
|
|
5
|
+
import {IZoraFactory} from "../../interfaces/IZoraFactory.sol";
|
|
6
|
+
import {ICoinDeployHook} from "../../interfaces/ICoinDeployHook.sol";
|
|
7
7
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
8
8
|
|
|
9
9
|
/// @title Immutable State
|