@zoralabs/coins 2.0.0 → 2.1.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 +106 -110
- package/CHANGELOG.md +28 -0
- package/README.md +30 -109
- package/abis/BaseCoin.json +442 -0
- package/abis/CoinTest.json +3 -246
- package/abis/FactoryTest.json +5 -137
- package/abis/HooksTest.json +0 -26
- package/abis/ICoin.json +378 -0
- package/abis/ICoinV3.json +378 -0
- package/abis/IZoraFactory.json +0 -18
- package/abis/LiquidityMigrationTest.json +101 -0
- package/abis/MockBadFactory.json +15 -0
- package/abis/ZoraFactoryImpl.json +1 -67
- package/dist/index.cjs +236 -265
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +235 -264
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +389 -493
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +240 -269
- package/package.json +3 -3
- package/script/DeployPostDeploymentHooks.s.sol +2 -2
- package/script/TestBackingCoinSwap.s.sol +8 -8
- package/script/TestV4Swap.s.sol +8 -8
- package/script/UpgradeFactoryImpl.s.sol +0 -1
- package/src/BaseCoin.sol +109 -6
- package/src/ContentCoin.sol +4 -4
- package/src/CreatorCoin.sol +5 -5
- package/src/ZoraFactoryImpl.sol +10 -93
- package/src/deployment/CoinsDeployerBase.sol +10 -27
- package/src/hooks/BaseZoraV4CoinHook.sol +5 -5
- package/src/hooks/deployment/BuySupplyWithSwapRouterHook.sol +4 -5
- package/src/interfaces/ICoin.sol +67 -1
- package/src/interfaces/ICreatorCoin.sol +2 -2
- package/src/interfaces/IZoraFactory.sol +0 -5
- package/src/libs/CoinConfigurationVersions.sol +1 -39
- package/src/libs/CoinRewardsV4.sol +2 -2
- package/src/libs/CoinSetup.sol +1 -4
- package/src/libs/UniV4SwapHelper.sol +1 -1
- package/src/libs/UniV4SwapToCurrency.sol +2 -2
- package/src/libs/V4Liquidity.sol +1 -1
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +112 -535
- package/test/CoinUniV4.t.sol +5 -5
- package/test/DeploymentHooks.t.sol +5 -102
- package/test/Factory.t.sol +23 -306
- package/test/LiquidityMigration.t.sol +160 -2
- package/test/MultiOwnable.t.sol +36 -36
- package/test/Upgrades.t.sol +16 -35
- package/test/utils/BaseTest.sol +16 -69
- package/test/utils/FeeEstimatorHook.sol +3 -3
- package/wagmi.config.ts +1 -1
- package/abis/BaseCoinV4.json +0 -1840
- package/abis/Coin.json +0 -1912
- package/abis/DopplerUniswapV3Test.json +0 -800
- package/abis/ICoinV4.json +0 -1048
- package/abis/Simulate.json +0 -29
- package/abis/UniV3BuySell.json +0 -12
- package/abis/UniV3Errors.json +0 -32
- package/script/Simulate.s.sol +0 -59
- package/src/BaseCoinV4.sol +0 -143
- package/src/Coin.sol +0 -236
- package/src/interfaces/ICoinV4.sol +0 -74
- package/src/libs/CoinDopplerUniV3.sol +0 -50
- package/src/libs/CoinRewards.sol +0 -201
- package/src/libs/CoinSetupV3.sol +0 -50
- package/src/libs/UniV3BuySell.sol +0 -231
- package/src/libs/UniV3Errors.sol +0 -11
- package/test/CoinDopplerUniV3.t.sol +0 -310
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoralabs/coins",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"tsx": "^3.13.0",
|
|
36
36
|
"typescript": "^5.2.2",
|
|
37
37
|
"viem": "^2.21.18",
|
|
38
|
-
"@zoralabs/
|
|
38
|
+
"@zoralabs/tsconfig": "^0.0.1",
|
|
39
39
|
"@zoralabs/shared-scripts": "^0.0.0",
|
|
40
|
-
"@zoralabs/
|
|
40
|
+
"@zoralabs/shared-contracts": "^0.0.5"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "pnpm run wagmi:generate && pnpm run copy-abis && pnpm run prettier:write && tsup",
|
|
@@ -10,9 +10,9 @@ contract DeployHooks is CoinsDeployerBase {
|
|
|
10
10
|
|
|
11
11
|
vm.startBroadcast();
|
|
12
12
|
|
|
13
|
-
address buySupplyWithSwapRouterHook = address(deployBuySupplyWithSwapRouterHook(deployment));
|
|
13
|
+
// address buySupplyWithSwapRouterHook = address(deployBuySupplyWithSwapRouterHook(deployment));
|
|
14
14
|
|
|
15
|
-
deployment.buySupplyWithSwapRouterHook = buySupplyWithSwapRouterHook;
|
|
15
|
+
// deployment.buySupplyWithSwapRouterHook = buySupplyWithSwapRouterHook;
|
|
16
16
|
|
|
17
17
|
vm.stopBroadcast();
|
|
18
18
|
|
|
@@ -16,7 +16,7 @@ import {MockERC20} from "../test/mocks/MockERC20.sol";
|
|
|
16
16
|
import {MarketConstants} from "../src/libs/MarketConstants.sol";
|
|
17
17
|
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
18
18
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
19
|
-
import {
|
|
19
|
+
import {ICoin} from "../src/interfaces/ICoin.sol";
|
|
20
20
|
|
|
21
21
|
import {console} from "forge-std/console.sol";
|
|
22
22
|
|
|
@@ -34,7 +34,7 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
34
34
|
string memory uri,
|
|
35
35
|
address createReferral,
|
|
36
36
|
bytes32 salt
|
|
37
|
-
) internal returns (
|
|
37
|
+
) internal returns (ICoin coin) {
|
|
38
38
|
CoinsDeployment memory deployment = readDeployment();
|
|
39
39
|
address[] memory owners = new address[](1);
|
|
40
40
|
owners[0] = creator;
|
|
@@ -54,10 +54,10 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
54
54
|
salt
|
|
55
55
|
);
|
|
56
56
|
|
|
57
|
-
coin =
|
|
57
|
+
coin = ICoin(coinAddress);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
function _swap(address currencyIn, uint128 amountIn,
|
|
60
|
+
function _swap(address currencyIn, uint128 amountIn, ICoin coin, address trader, address tradeReferral) internal returns (uint256 amountOut) {
|
|
61
61
|
uint128 minAmountOut = 0;
|
|
62
62
|
|
|
63
63
|
PoolKey memory poolKey = coin.getPoolKey();
|
|
@@ -106,8 +106,8 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
106
106
|
address createReferral = 0xC077e4cC02fa01A5b7fAca1acE9BBe9f5ac5Af9F;
|
|
107
107
|
address tradeReferral = 0xC077e4cC02fa01A5b7fAca1acE9BBe9f5ac5Af9F;
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
ICoin backingCoin = _deployCoin(zora, trader, "Backing Coin", "BACK", "https://testc.com", createReferral, bytes32("creator"));
|
|
110
|
+
ICoin contentCoin = _deployCoin(
|
|
111
111
|
address(backingCoin),
|
|
112
112
|
trader,
|
|
113
113
|
"Content Coin",
|
|
@@ -116,8 +116,8 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
116
116
|
createReferral,
|
|
117
117
|
bytes32("content coin")
|
|
118
118
|
);
|
|
119
|
-
//
|
|
120
|
-
//
|
|
119
|
+
// ICoin backingCoin = ICoin(0xeA734b5997F35cD469921cCa7BB9A03C104f2f64);
|
|
120
|
+
// ICoin contentCoin = ICoin(0x72218BFEEc7D556BD3Dd8eFf2a317CEd49533769);
|
|
121
121
|
|
|
122
122
|
console.log("backingCoin", address(backingCoin));
|
|
123
123
|
console.log("contentCoin", address(contentCoin));
|
package/script/TestV4Swap.s.sol
CHANGED
|
@@ -16,7 +16,7 @@ import {MockERC20} from "../test/mocks/MockERC20.sol";
|
|
|
16
16
|
import {MarketConstants} from "../src/libs/MarketConstants.sol";
|
|
17
17
|
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
18
18
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
19
|
-
import {
|
|
19
|
+
import {ICoin} from "../src/interfaces/ICoin.sol";
|
|
20
20
|
|
|
21
21
|
import {console} from "forge-std/console.sol";
|
|
22
22
|
|
|
@@ -31,7 +31,7 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
31
31
|
currency.mint(getUniswapV4PoolManager(), 1000000 ether);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function _deployMockCoin(address currency, address creator, address createReferral, bytes32 salt) internal returns (
|
|
34
|
+
function _deployMockCoin(address currency, address creator, address createReferral, bytes32 salt) internal returns (ICoin coin) {
|
|
35
35
|
CoinsDeployment memory deployment = readDeployment();
|
|
36
36
|
address[] memory owners = new address[](1);
|
|
37
37
|
owners[0] = creator;
|
|
@@ -51,10 +51,10 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
51
51
|
salt
|
|
52
52
|
);
|
|
53
53
|
|
|
54
|
-
coin =
|
|
54
|
+
coin = ICoin(coinAddress);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function _swap(address currencyIn, uint128 amountIn,
|
|
57
|
+
function _swap(address currencyIn, uint128 amountIn, ICoin coin, address trader, address tradeReferral) internal returns (uint256 amountOut) {
|
|
58
58
|
uint128 minAmountOut = 0;
|
|
59
59
|
|
|
60
60
|
PoolKey memory poolKey = coin.getPoolKey();
|
|
@@ -101,12 +101,12 @@ contract TestV4Swap is CoinsDeployerBase {
|
|
|
101
101
|
|
|
102
102
|
// MockERC20 currency = _deployMockCurrency();
|
|
103
103
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
104
|
+
// ICoin backingCoin = _deployMockCoin(address(currency), trader, createReferral, bytes32("backing coin"));
|
|
105
|
+
// ICoin contentCoin = _deployMockCoin(address(backingCoin), trader, createReferral, bytes32("content coin"));
|
|
106
106
|
|
|
107
107
|
MockERC20 currency = MockERC20(0x1b183Bd0E2c03Fc830F4d813bA37E82F9F97cA21);
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
ICoin backingCoin = ICoin(0x7D74416C4c295A592Fc6F9232911C945354b253C);
|
|
109
|
+
ICoin contentCoin = ICoin(0xf6d6660bcdA588F7f99e2961f279f500fB501730);
|
|
110
110
|
|
|
111
111
|
console.log("currency", address(currency));
|
|
112
112
|
console.log("backingCoin", address(backingCoin));
|
package/src/BaseCoin.sol
CHANGED
|
@@ -19,6 +19,15 @@ import {IAirlock} from "./interfaces/IAirlock.sol";
|
|
|
19
19
|
import {IProtocolRewards} from "./interfaces/IProtocolRewards.sol";
|
|
20
20
|
import {IWETH} from "./interfaces/IWETH.sol";
|
|
21
21
|
|
|
22
|
+
import {IPoolManager, PoolKey, Currency, IHooks} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
23
|
+
import {IHasPoolKey, IHasSwapPath} from "./interfaces/ICoin.sol";
|
|
24
|
+
import {PoolConfiguration} from "./types/PoolConfiguration.sol";
|
|
25
|
+
import {UniV4SwapToCurrency} from "./libs/UniV4SwapToCurrency.sol";
|
|
26
|
+
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
|
27
|
+
import {IDeployedCoinVersionLookup} from "./interfaces/IDeployedCoinVersionLookup.sol";
|
|
28
|
+
import {IUpgradeableV4Hook} from "./interfaces/IUpgradeableV4Hook.sol";
|
|
29
|
+
import {CoinCommon} from "./libs/CoinCommon.sol";
|
|
30
|
+
|
|
22
31
|
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
23
32
|
import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
|
|
24
33
|
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
|
|
@@ -33,8 +42,6 @@ import {CoinConstants} from "./libs/CoinConstants.sol";
|
|
|
33
42
|
import {MarketConstants} from "./libs/MarketConstants.sol";
|
|
34
43
|
import {LpPosition} from "./types/LpPosition.sol";
|
|
35
44
|
import {PoolState} from "./types/PoolState.sol";
|
|
36
|
-
import {CoinSetupV3, UniV3Config, CoinV3Config} from "./libs/CoinSetupV3.sol";
|
|
37
|
-
import {UniV3BuySell, CoinConfig} from "./libs/UniV3BuySell.sol";
|
|
38
45
|
|
|
39
46
|
/*
|
|
40
47
|
$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
|
@@ -56,6 +63,15 @@ abstract contract BaseCoin is ICoin, ContractVersionBase, ERC20PermitUpgradeable
|
|
|
56
63
|
/// @notice The address of the Airlock contract, ownership is used for a protocol fee split.
|
|
57
64
|
address public immutable airlock;
|
|
58
65
|
|
|
66
|
+
/// @notice The Uniswap v4 pool manager singleton contract reference.
|
|
67
|
+
IPoolManager public immutable poolManager;
|
|
68
|
+
|
|
69
|
+
/// @notice The pool key for the coin. Type from Uniswap V4 core.
|
|
70
|
+
PoolKey internal poolKey;
|
|
71
|
+
|
|
72
|
+
/// @notice The configuration for the pool.
|
|
73
|
+
PoolConfiguration internal poolConfiguration;
|
|
74
|
+
|
|
59
75
|
/// @notice The metadata URI
|
|
60
76
|
string public tokenURI;
|
|
61
77
|
/// @notice The address of the coin creator
|
|
@@ -76,24 +92,53 @@ abstract contract BaseCoin is ICoin, ContractVersionBase, ERC20PermitUpgradeable
|
|
|
76
92
|
* @param _protocolRewards The address of the protocol rewards contract
|
|
77
93
|
* @param _airlock The address of the Airlock contract
|
|
78
94
|
*/
|
|
79
|
-
constructor(address _protocolRewardRecipient, address _protocolRewards, address _airlock) initializer {
|
|
95
|
+
constructor(address _protocolRewardRecipient, address _protocolRewards, IPoolManager poolManager_, address _airlock) initializer {
|
|
80
96
|
if (_protocolRewardRecipient == address(0)) {
|
|
81
97
|
revert AddressZero();
|
|
82
98
|
}
|
|
83
99
|
if (_protocolRewards == address(0)) {
|
|
84
100
|
revert AddressZero();
|
|
85
101
|
}
|
|
86
|
-
|
|
102
|
+
if (address(poolManager_) == address(0)) {
|
|
103
|
+
revert AddressZero();
|
|
104
|
+
}
|
|
87
105
|
if (_airlock == address(0)) {
|
|
88
106
|
revert AddressZero();
|
|
89
107
|
}
|
|
90
108
|
|
|
91
109
|
protocolRewardRecipient = _protocolRewardRecipient;
|
|
92
110
|
protocolRewards = _protocolRewards;
|
|
111
|
+
poolManager = poolManager_;
|
|
93
112
|
airlock = _airlock;
|
|
94
113
|
}
|
|
95
114
|
|
|
96
|
-
/// @
|
|
115
|
+
/// @inheritdoc ICoin
|
|
116
|
+
function initialize(
|
|
117
|
+
address payoutRecipient_,
|
|
118
|
+
address[] memory owners_,
|
|
119
|
+
string memory tokenURI_,
|
|
120
|
+
string memory name_,
|
|
121
|
+
string memory symbol_,
|
|
122
|
+
address platformReferrer_,
|
|
123
|
+
address currency_,
|
|
124
|
+
PoolKey memory poolKey_,
|
|
125
|
+
uint160 sqrtPriceX96,
|
|
126
|
+
PoolConfiguration memory poolConfiguration_
|
|
127
|
+
) public virtual initializer {
|
|
128
|
+
currency = currency_;
|
|
129
|
+
// we need to set this before initialization, because
|
|
130
|
+
// distributing currency relies on the poolkey being set since the hooks
|
|
131
|
+
// are retrieved from there
|
|
132
|
+
poolKey = poolKey_;
|
|
133
|
+
poolConfiguration = poolConfiguration_;
|
|
134
|
+
|
|
135
|
+
_initialize(payoutRecipient_, owners_, tokenURI_, name_, symbol_, platformReferrer_);
|
|
136
|
+
|
|
137
|
+
// initialize the pool - the hook will mint its positions in the afterInitialize callback
|
|
138
|
+
poolManager.initialize(poolKey, sqrtPriceX96);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// @notice Initializes a new coin (internal version)
|
|
97
142
|
/// @param payoutRecipient_ The address of the coin creator
|
|
98
143
|
/// @param tokenURI_ The metadata URI
|
|
99
144
|
/// @param name_ The coin name
|
|
@@ -210,7 +255,9 @@ abstract contract BaseCoin is ICoin, ContractVersionBase, ERC20PermitUpgradeable
|
|
|
210
255
|
interfaceId == type(ICoin).interfaceId ||
|
|
211
256
|
interfaceId == type(ICoinComments).interfaceId ||
|
|
212
257
|
interfaceId == type(IERC7572).interfaceId ||
|
|
213
|
-
interfaceId == type(IHasRewardsRecipients).interfaceId
|
|
258
|
+
interfaceId == type(IHasRewardsRecipients).interfaceId ||
|
|
259
|
+
interfaceId == type(IHasPoolKey).interfaceId ||
|
|
260
|
+
type(IHasSwapPath).interfaceId == interfaceId;
|
|
214
261
|
}
|
|
215
262
|
|
|
216
263
|
/// @dev Overrides ERC20's _update function to emit a superset `CoinTransfer` event
|
|
@@ -245,4 +292,60 @@ abstract contract BaseCoin is ICoin, ContractVersionBase, ERC20PermitUpgradeable
|
|
|
245
292
|
function dopplerFeeRecipient() public view returns (address) {
|
|
246
293
|
return IAirlock(airlock).owner();
|
|
247
294
|
}
|
|
295
|
+
|
|
296
|
+
/// @inheritdoc IHasPoolKey
|
|
297
|
+
function getPoolKey() public view returns (PoolKey memory) {
|
|
298
|
+
return poolKey;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/// @inheritdoc ICoin
|
|
302
|
+
function getPoolConfiguration() public view returns (PoolConfiguration memory) {
|
|
303
|
+
return poolConfiguration;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/// @inheritdoc ICoin
|
|
307
|
+
function hooks() external view returns (IHooks) {
|
|
308
|
+
return poolKey.hooks;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/// @notice Migrate liquidity from current hook to a new hook implementation
|
|
312
|
+
/// @param newHook Address of the new hook implementation
|
|
313
|
+
/// @param additionalData Additional data to pass to the new hook during initialization
|
|
314
|
+
function migrateLiquidity(address newHook, bytes calldata additionalData) external onlyOwner returns (PoolKey memory newPoolKey) {
|
|
315
|
+
newPoolKey = IUpgradeableV4Hook(address(poolKey.hooks)).migrateLiquidity(newHook, poolKey, additionalData);
|
|
316
|
+
|
|
317
|
+
emit LiquidityMigrated(poolKey, CoinCommon.hashPoolKey(poolKey), newPoolKey, CoinCommon.hashPoolKey(newPoolKey));
|
|
318
|
+
|
|
319
|
+
poolKey = newPoolKey;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// @inheritdoc IHasSwapPath
|
|
323
|
+
function getPayoutSwapPath(IDeployedCoinVersionLookup coinVersionLookup) external view returns (IHasSwapPath.PayoutSwapPath memory payoutSwapPath) {
|
|
324
|
+
// if to swap in is this currency,
|
|
325
|
+
// if backing currency is a coin, then recursively get the path from the coin
|
|
326
|
+
payoutSwapPath.currencyIn = Currency.wrap(address(this));
|
|
327
|
+
|
|
328
|
+
// swap to backing currency
|
|
329
|
+
PathKey memory thisPathKey = PathKey({
|
|
330
|
+
intermediateCurrency: Currency.wrap(currency),
|
|
331
|
+
fee: poolKey.fee,
|
|
332
|
+
tickSpacing: poolKey.tickSpacing,
|
|
333
|
+
hooks: poolKey.hooks,
|
|
334
|
+
hookData: ""
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// get backing currency swap path - if the backing currency is a v4 coin and has a swap path.
|
|
338
|
+
PathKey[] memory subPath = UniV4SwapToCurrency.getSubSwapPath(currency, coinVersionLookup);
|
|
339
|
+
|
|
340
|
+
if (subPath.length > 0) {
|
|
341
|
+
payoutSwapPath.path = new PathKey[](1 + subPath.length);
|
|
342
|
+
payoutSwapPath.path[0] = thisPathKey;
|
|
343
|
+
for (uint256 i = 0; i < subPath.length; i++) {
|
|
344
|
+
payoutSwapPath.path[i + 1] = subPath[i];
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
payoutSwapPath.path = new PathKey[](1);
|
|
348
|
+
payoutSwapPath.path[0] = thisPathKey;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
248
351
|
}
|
package/src/ContentCoin.sol
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
pragma solidity ^0.8.23;
|
|
9
9
|
|
|
10
10
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
11
|
-
import {
|
|
11
|
+
import {BaseCoin} from "./BaseCoin.sol";
|
|
12
12
|
import {CoinConstants} from "./libs/CoinConstants.sol";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @title ContentCoin
|
|
16
16
|
* @notice Content coin implementation that uses creator coins as backing currency
|
|
17
|
-
* @dev Inherits from
|
|
17
|
+
* @dev Inherits from BaseCoin and implements content-specific distribution logic
|
|
18
18
|
*/
|
|
19
|
-
contract ContentCoin is
|
|
19
|
+
contract ContentCoin is BaseCoin {
|
|
20
20
|
/// @notice The constructor for the static ContentCoin contract deployment shared across all content coins.
|
|
21
21
|
/// @dev All arguments are required and cannot be set to the 0 address.
|
|
22
22
|
/// @param protocolRewardRecipient_ The address of the protocol reward recipient
|
|
@@ -28,7 +28,7 @@ contract ContentCoin is BaseCoinV4 {
|
|
|
28
28
|
address protocolRewards_,
|
|
29
29
|
IPoolManager poolManager_,
|
|
30
30
|
address airlock_
|
|
31
|
-
)
|
|
31
|
+
) BaseCoin(protocolRewardRecipient_, protocolRewards_, poolManager_, airlock_) {}
|
|
32
32
|
|
|
33
33
|
/// @dev The initial mint and distribution of the coin supply.
|
|
34
34
|
/// Implements content coin specific distribution: 990M to liquidity pool, 10M to creator.
|
package/src/CreatorCoin.sol
CHANGED
|
@@ -9,11 +9,11 @@ pragma solidity ^0.8.28;
|
|
|
9
9
|
|
|
10
10
|
import {ICreatorCoin} from "./interfaces/ICreatorCoin.sol";
|
|
11
11
|
import {CreatorCoinConstants} from "./libs/CreatorCoinConstants.sol";
|
|
12
|
-
import {IHooks, PoolConfiguration, PoolKey,
|
|
12
|
+
import {IHooks, PoolConfiguration, PoolKey, ICoin} from "./interfaces/ICoin.sol";
|
|
13
13
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
14
|
-
import {
|
|
14
|
+
import {BaseCoin} from "./BaseCoin.sol";
|
|
15
15
|
|
|
16
|
-
contract CreatorCoin is ICreatorCoin,
|
|
16
|
+
contract CreatorCoin is ICreatorCoin, BaseCoin {
|
|
17
17
|
uint256 public vestingStartTime;
|
|
18
18
|
uint256 public vestingEndTime;
|
|
19
19
|
uint256 public totalClaimed;
|
|
@@ -23,7 +23,7 @@ contract CreatorCoin is ICreatorCoin, BaseCoinV4 {
|
|
|
23
23
|
address _protocolRewards,
|
|
24
24
|
IPoolManager _poolManager,
|
|
25
25
|
address _airlock
|
|
26
|
-
)
|
|
26
|
+
) BaseCoin(_protocolRewardRecipient, _protocolRewards, _poolManager, _airlock) initializer {}
|
|
27
27
|
|
|
28
28
|
function initialize(
|
|
29
29
|
address payoutRecipient_,
|
|
@@ -36,7 +36,7 @@ contract CreatorCoin is ICreatorCoin, BaseCoinV4 {
|
|
|
36
36
|
PoolKey memory poolKey_,
|
|
37
37
|
uint160 sqrtPriceX96,
|
|
38
38
|
PoolConfiguration memory poolConfiguration_
|
|
39
|
-
) public override(
|
|
39
|
+
) public override(BaseCoin, ICoin) {
|
|
40
40
|
require(currency_ == CreatorCoinConstants.CURRENCY, InvalidCurrency());
|
|
41
41
|
|
|
42
42
|
super.initialize(payoutRecipient_, owners_, tokenURI_, name_, symbol_, platformReferrer_, currency_, poolKey_, sqrtPriceX96, poolConfiguration_);
|
package/src/ZoraFactoryImpl.sol
CHANGED
|
@@ -20,18 +20,13 @@ import {IWETH} from "./interfaces/IWETH.sol";
|
|
|
20
20
|
import {IZoraFactory} from "./interfaces/IZoraFactory.sol";
|
|
21
21
|
import {IHasAfterCoinDeploy} from "./hooks/deployment/BaseCoinDeployHook.sol";
|
|
22
22
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
23
|
-
import {Coin} from "./Coin.sol";
|
|
24
|
-
import {ContentCoin} from "./ContentCoin.sol";
|
|
25
23
|
import {ICoin, PoolKeyStruct} from "./interfaces/ICoin.sol";
|
|
26
|
-
import {
|
|
27
|
-
import {ICoinV4} from "./interfaces/ICoinV4.sol";
|
|
24
|
+
import {ICoin} from "./interfaces/ICoin.sol";
|
|
28
25
|
import {IHasContractName} from "@zoralabs/shared-contracts/interfaces/IContractMetadata.sol";
|
|
29
26
|
import {ContractVersionBase} from "./version/ContractVersionBase.sol";
|
|
30
27
|
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
31
28
|
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
|
|
32
29
|
import {CoinCommon} from "./libs/CoinCommon.sol";
|
|
33
|
-
import {UniV3Config} from "./libs/CoinSetupV3.sol";
|
|
34
|
-
import {CoinSetupV3} from "./libs/CoinSetupV3.sol";
|
|
35
30
|
import {PoolConfiguration} from "./types/PoolConfiguration.sol";
|
|
36
31
|
import {LpPosition} from "./types/LpPosition.sol";
|
|
37
32
|
import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
|
|
@@ -53,16 +48,14 @@ contract ZoraFactoryImpl is
|
|
|
53
48
|
using SafeERC20 for IERC20;
|
|
54
49
|
|
|
55
50
|
/// @notice The coin contract implementation address
|
|
56
|
-
address public immutable coinImpl;
|
|
57
51
|
address public immutable coinV4Impl;
|
|
58
52
|
address public immutable creatorCoinImpl;
|
|
59
53
|
address public immutable contentCoinHook;
|
|
60
54
|
address public immutable creatorCoinHook;
|
|
61
55
|
|
|
62
|
-
constructor(address
|
|
56
|
+
constructor(address _coinV4Impl, address _creatorCoinImpl, address _contentCoinHook, address _creatorCoinHook) {
|
|
63
57
|
_disableInitializers();
|
|
64
58
|
|
|
65
|
-
coinImpl = _coinImpl;
|
|
66
59
|
coinV4Impl = _coinV4Impl;
|
|
67
60
|
creatorCoinImpl = _creatorCoinImpl;
|
|
68
61
|
contentCoinHook = _contentCoinHook;
|
|
@@ -180,13 +173,13 @@ contract ZoraFactoryImpl is
|
|
|
180
173
|
string memory symbol,
|
|
181
174
|
bytes memory poolConfig,
|
|
182
175
|
address platformReferrer,
|
|
183
|
-
uint256 orderSize
|
|
176
|
+
uint256 /*orderSize*/
|
|
184
177
|
) public payable nonReentrant returns (address, uint256) {
|
|
185
178
|
bytes32 salt = _randomSalt(payoutRecipient, uri, bytes32(0));
|
|
186
179
|
|
|
187
180
|
ICoin coin = _createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt);
|
|
188
181
|
|
|
189
|
-
uint256 coinsPurchased =
|
|
182
|
+
uint256 coinsPurchased = 0;
|
|
190
183
|
|
|
191
184
|
return (address(coin), coinsPurchased);
|
|
192
185
|
}
|
|
@@ -225,15 +218,11 @@ contract ZoraFactoryImpl is
|
|
|
225
218
|
|
|
226
219
|
ICoin coin = _createAndInitializeCoin(payoutRecipient, owners, uri, name, symbol, poolConfig, platformReferrer, salt);
|
|
227
220
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return (address(coin), coinsPurchased);
|
|
221
|
+
return (address(coin), 0);
|
|
231
222
|
}
|
|
232
223
|
|
|
233
224
|
function getCoinImpl(uint8 version) internal view returns (address) {
|
|
234
|
-
if (CoinConfigurationVersions.
|
|
235
|
-
return coinImpl;
|
|
236
|
-
} else if (CoinConfigurationVersions.isV4(version)) {
|
|
225
|
+
if (CoinConfigurationVersions.isV4(version)) {
|
|
237
226
|
return coinV4Impl;
|
|
238
227
|
}
|
|
239
228
|
|
|
@@ -244,44 +233,8 @@ contract ZoraFactoryImpl is
|
|
|
244
233
|
return payable(Clones.cloneDeterministic(getCoinImpl(version), salt));
|
|
245
234
|
}
|
|
246
235
|
|
|
247
|
-
function _setupV3Coin(
|
|
248
|
-
ICoinV3 coin,
|
|
249
|
-
address currency,
|
|
250
|
-
bool isCoinToken0,
|
|
251
|
-
uint160 sqrtPriceX96,
|
|
252
|
-
PoolConfiguration memory poolConfiguration,
|
|
253
|
-
address payoutRecipient,
|
|
254
|
-
address[] memory owners,
|
|
255
|
-
string memory uri,
|
|
256
|
-
string memory name,
|
|
257
|
-
string memory symbol,
|
|
258
|
-
address platformReferrer
|
|
259
|
-
) internal {
|
|
260
|
-
address v3Factory = coin.v3Factory();
|
|
261
|
-
|
|
262
|
-
address poolAddress = CoinSetupV3.createV3Pool(address(coin), currency, isCoinToken0, sqrtPriceX96, v3Factory);
|
|
263
|
-
|
|
264
|
-
LpPosition[] memory positions = CoinDopplerMultiCurve.calculatePositions(isCoinToken0, poolConfiguration, MarketConstants.POOL_LAUNCH_SUPPLY);
|
|
265
|
-
|
|
266
|
-
// Initialize coin with pre-configured pool
|
|
267
|
-
coin.initialize(payoutRecipient, owners, uri, name, symbol, platformReferrer, currency, poolAddress, poolConfiguration, positions);
|
|
268
|
-
|
|
269
|
-
emit CoinCreated(
|
|
270
|
-
msg.sender,
|
|
271
|
-
payoutRecipient,
|
|
272
|
-
platformReferrer,
|
|
273
|
-
currency,
|
|
274
|
-
uri,
|
|
275
|
-
name,
|
|
276
|
-
symbol,
|
|
277
|
-
address(coin),
|
|
278
|
-
poolAddress,
|
|
279
|
-
IVersionedContract(address(coin)).contractVersion()
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
236
|
function _setupV4Coin(
|
|
284
|
-
|
|
237
|
+
ICoin coin,
|
|
285
238
|
address currency,
|
|
286
239
|
bool isCoinToken0,
|
|
287
240
|
uint160 sqrtPriceX96,
|
|
@@ -335,9 +288,10 @@ contract ZoraFactoryImpl is
|
|
|
335
288
|
);
|
|
336
289
|
|
|
337
290
|
if (CoinConfigurationVersions.isV3(version)) {
|
|
338
|
-
|
|
291
|
+
// V3 is no longer supported
|
|
292
|
+
revert ICoin.InvalidPoolVersion();
|
|
339
293
|
} else if (CoinConfigurationVersions.isV4(version)) {
|
|
340
|
-
_setupV4Coin(
|
|
294
|
+
_setupV4Coin(ICoin(coin), currency, isCoinToken0, sqrtPriceX96, poolConfiguration, payoutRecipient, owners, uri, name, symbol, platformReferrer);
|
|
341
295
|
} else {
|
|
342
296
|
revert ICoin.InvalidPoolVersion();
|
|
343
297
|
}
|
|
@@ -375,43 +329,6 @@ contract ZoraFactoryImpl is
|
|
|
375
329
|
);
|
|
376
330
|
}
|
|
377
331
|
|
|
378
|
-
/// @dev Handles the first buy of a newly created coin
|
|
379
|
-
/// @param coin The newly created coin contract
|
|
380
|
-
/// @param orderSize The size of the first buy order; must match msg.value for ETH/WETH pairs
|
|
381
|
-
function _handleFirstOrder(ICoin coin, uint256 orderSize) internal returns (uint256 coinsPurchased) {
|
|
382
|
-
if (msg.value > 0 || orderSize > 0) {
|
|
383
|
-
address currency = coin.currency();
|
|
384
|
-
address payoutRecipient = coin.payoutRecipient();
|
|
385
|
-
|
|
386
|
-
if (currency != Coin(payable(address(coin))).WETH()) {
|
|
387
|
-
if (msg.value != 0) {
|
|
388
|
-
revert EthTransferInvalid();
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
_handleIncomingCurrency(currency, orderSize);
|
|
392
|
-
|
|
393
|
-
IERC20(currency).approve(address(coin), orderSize);
|
|
394
|
-
|
|
395
|
-
(, coinsPurchased) = Coin(payable(address(coin))).buy(payoutRecipient, orderSize, 0, 0, address(0));
|
|
396
|
-
} else {
|
|
397
|
-
(, coinsPurchased) = Coin(payable(address(coin))).buy{value: msg.value}(payoutRecipient, orderSize, 0, 0, address(0));
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/// @dev Safely transfers ERC20 tokens from the caller to this contract to be sent to the newly created coin
|
|
403
|
-
/// @param currency The ERC20 token address to transfer
|
|
404
|
-
/// @param orderSize The amount of tokens to transfer for the order
|
|
405
|
-
function _handleIncomingCurrency(address currency, uint256 orderSize) internal {
|
|
406
|
-
uint256 beforeBalance = IERC20(currency).balanceOf(address(this));
|
|
407
|
-
IERC20(currency).safeTransferFrom(msg.sender, address(this), orderSize);
|
|
408
|
-
uint256 afterBalance = IERC20(currency).balanceOf(address(this));
|
|
409
|
-
|
|
410
|
-
if ((afterBalance - beforeBalance) != orderSize) {
|
|
411
|
-
revert ERC20TransferAmountMismatch();
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
332
|
/// @notice Initializes the factory proxy contract
|
|
416
333
|
/// @param initialOwner Address of the contract owner
|
|
417
334
|
/// @dev Can only be called once due to initializer modifier
|
|
@@ -6,9 +6,8 @@ import "forge-std/Script.sol";
|
|
|
6
6
|
import {ProxyDeployerScript, DeterministicContractConfig, DeterministicDeployerAndCaller} from "@zoralabs/shared-contracts/deployment/ProxyDeployerScript.sol";
|
|
7
7
|
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
8
8
|
import {ZoraFactoryImpl} from "../ZoraFactoryImpl.sol";
|
|
9
|
-
import {Coin} from "../Coin.sol";
|
|
10
9
|
import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
|
|
11
|
-
import {BuySupplyWithSwapRouterHook} from "../hooks/deployment/BuySupplyWithSwapRouterHook.sol";
|
|
10
|
+
// import {BuySupplyWithSwapRouterHook} from "../hooks/deployment/BuySupplyWithSwapRouterHook.sol";
|
|
12
11
|
import {IZoraFactory} from "../interfaces/IZoraFactory.sol";
|
|
13
12
|
import {ContentCoin} from "../ContentCoin.sol";
|
|
14
13
|
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
|
|
@@ -102,18 +101,6 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
102
101
|
deployment.hookUpgradeGate = readAddressOrDefaultToZero(json, "HOOK_UPGRADE_GATE");
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
function deployCoinV3Impl() internal returns (Coin) {
|
|
106
|
-
return
|
|
107
|
-
new Coin({
|
|
108
|
-
protocolRewardRecipient_: getZoraRecipient(),
|
|
109
|
-
protocolRewards_: PROTOCOL_REWARDS,
|
|
110
|
-
weth_: getWeth(),
|
|
111
|
-
v3Factory_: getUniswapV3Factory(),
|
|
112
|
-
swapRouter_: getUniswapSwapRouter(),
|
|
113
|
-
airlock_: getDopplerAirlock()
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
104
|
function deployCoinV4Impl(address zoraV4CoinHook) internal returns (ContentCoin) {
|
|
118
105
|
return
|
|
119
106
|
new ContentCoin({
|
|
@@ -135,7 +122,6 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
135
122
|
}
|
|
136
123
|
|
|
137
124
|
function deployZoraFactoryImpl(
|
|
138
|
-
address _coinV3Impl,
|
|
139
125
|
address _coinV4Impl,
|
|
140
126
|
address _creatorCoinImpl,
|
|
141
127
|
address _contentCoinHook,
|
|
@@ -143,7 +129,6 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
143
129
|
) internal returns (ZoraFactoryImpl) {
|
|
144
130
|
return
|
|
145
131
|
new ZoraFactoryImpl({
|
|
146
|
-
_coinImpl: _coinV3Impl,
|
|
147
132
|
_coinV4Impl: _coinV4Impl,
|
|
148
133
|
_creatorCoinImpl: _creatorCoinImpl,
|
|
149
134
|
_contentCoinHook: _contentCoinHook,
|
|
@@ -151,14 +136,14 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
151
136
|
});
|
|
152
137
|
}
|
|
153
138
|
|
|
154
|
-
function deployBuySupplyWithSwapRouterHook(CoinsDeployment memory deployment) internal returns (BuySupplyWithSwapRouterHook) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
139
|
+
// function deployBuySupplyWithSwapRouterHook(CoinsDeployment memory deployment) internal returns (BuySupplyWithSwapRouterHook) {
|
|
140
|
+
// return
|
|
141
|
+
// new BuySupplyWithSwapRouterHook({
|
|
142
|
+
// _factory: IZoraFactory(deployment.zoraFactory),
|
|
143
|
+
// _swapRouter: getUniswapSwapRouter(),
|
|
144
|
+
// _poolManager: getUniswapV4PoolManager()
|
|
145
|
+
// });
|
|
146
|
+
// }
|
|
162
147
|
|
|
163
148
|
function deployUpgradeGate(CoinsDeployment memory deployment) internal returns (CoinsDeployment memory) {
|
|
164
149
|
deployment.hookUpgradeGate = address(new HookUpgradeGate(getProxyAdmin()));
|
|
@@ -205,7 +190,6 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
205
190
|
return
|
|
206
191
|
address(
|
|
207
192
|
deployZoraFactoryImpl({
|
|
208
|
-
_coinV3Impl: deployment.coinV3Impl,
|
|
209
193
|
_coinV4Impl: deployment.coinV4Impl,
|
|
210
194
|
_creatorCoinImpl: deployment.creatorCoinImpl,
|
|
211
195
|
_contentCoinHook: deployment.zoraV4CoinHook,
|
|
@@ -216,7 +200,6 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
216
200
|
|
|
217
201
|
function deployImpls(CoinsDeployment memory deployment) internal returns (CoinsDeployment memory) {
|
|
218
202
|
// Deploy implementation contracts
|
|
219
|
-
deployment.coinV3Impl = address(deployCoinV3Impl());
|
|
220
203
|
|
|
221
204
|
// Deploy hook first, then use its address for coin v4 impl
|
|
222
205
|
console.log("deploying content coin hook");
|
|
@@ -233,7 +216,7 @@ contract CoinsDeployerBase is ProxyDeployerScript {
|
|
|
233
216
|
deployment.creatorCoinImpl = address(deployCreatorCoinImpl(deployment.creatorCoinHook));
|
|
234
217
|
deployment.zoraFactoryImpl = deployFactoryImpl(deployment);
|
|
235
218
|
deployment.coinVersion = IVersionedContract(deployment.coinV4Impl).contractVersion();
|
|
236
|
-
deployment.buySupplyWithSwapRouterHook = address(deployBuySupplyWithSwapRouterHook(deployment));
|
|
219
|
+
// deployment.buySupplyWithSwapRouterHook = address(deployBuySupplyWithSwapRouterHook(deployment));
|
|
237
220
|
|
|
238
221
|
return deployment;
|
|
239
222
|
}
|