@zoralabs/coins 1.1.2 → 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 +107 -110
- package/CHANGELOG.md +50 -0
- package/README.md +48 -1
- package/abis/BaseCoin.json +442 -0
- package/abis/BaseZoraV4CoinHook.json +6 -2
- package/abis/CoinTest.json +3 -246
- package/abis/CoinUniV4Test.json +20 -0
- package/abis/ContentCoinHook.json +6 -2
- package/abis/CreatorCoinHook.json +6 -2
- package/abis/FactoryTest.json +8 -133
- package/abis/FeeEstimatorHook.json +6 -2
- 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/IZoraV4CoinHook.json +2 -2
- package/abis/LiquidityMigrationTest.json +101 -0
- package/abis/MockBadFactory.json +15 -0
- package/abis/Ownable2StepUpgradeable.json +138 -0
- package/abis/ZoraFactoryImpl.json +38 -65
- package/addresses/8453.json +5 -5
- package/dist/index.cjs +272 -268
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +270 -266
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +397 -470
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +275 -271
- package/package.json +3 -3
- package/script/DeployPostDeploymentHooks.s.sol +2 -2
- package/script/TestBackingCoinSwap.s.sol +9 -9
- package/script/TestV4Swap.s.sol +9 -9
- package/script/UpgradeFactoryImpl.s.sol +0 -1
- package/src/BaseCoin.sol +109 -6
- package/src/ContentCoin.sol +45 -0
- package/src/CreatorCoin.sol +7 -5
- package/src/ZoraFactoryImpl.sol +12 -95
- package/src/deployment/CoinsDeployerBase.sol +13 -30
- package/src/hooks/BaseZoraV4CoinHook.sol +8 -6
- 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/interfaces/IZoraV4CoinHook.sol +1 -1
- 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 +66 -10
- package/test/DeploymentHooks.t.sol +5 -102
- package/test/Factory.t.sol +49 -291
- package/test/LiquidityMigration.t.sol +160 -2
- package/test/MultiOwnable.t.sol +36 -36
- package/test/Upgrades.t.sol +23 -42
- package/test/utils/BaseTest.sol +39 -84
- package/test/utils/FeeEstimatorHook.sol +3 -3
- package/wagmi.config.ts +2 -2
- 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/Coin.sol +0 -236
- package/src/CoinV4.sol +0 -151
- 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/abis/{CoinV4.json → ContentCoin.json} +0 -0
package/test/CoinUniV4.t.sol
CHANGED
|
@@ -29,7 +29,7 @@ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
|
|
|
29
29
|
import {PoolStateReader} from "../src/libs/PoolStateReader.sol";
|
|
30
30
|
import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol";
|
|
31
31
|
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
|
|
32
|
-
import {
|
|
32
|
+
import {ICoin, IHasSwapPath, PathKey} from "../src/interfaces/ICoin.sol";
|
|
33
33
|
import {IDeployedCoinVersionLookup} from "../src/interfaces/IDeployedCoinVersionLookup.sol";
|
|
34
34
|
|
|
35
35
|
contract CoinUniV4Test is BaseTest {
|
|
@@ -63,7 +63,7 @@ contract CoinUniV4Test is BaseTest {
|
|
|
63
63
|
uint256 deadline = block.timestamp + 20;
|
|
64
64
|
router.execute(commands, inputs, deadline);
|
|
65
65
|
|
|
66
|
-
feeState = FeeEstimatorHook(address(contentCoinHook)).getFeeState();
|
|
66
|
+
feeState = FeeEstimatorHook(payable(address(contentCoinHook))).getFeeState();
|
|
67
67
|
|
|
68
68
|
vm.revertToState(snapshot);
|
|
69
69
|
}
|
|
@@ -80,8 +80,8 @@ contract CoinUniV4Test is BaseTest {
|
|
|
80
80
|
uint256 deadline = block.timestamp + 20;
|
|
81
81
|
router.execute(commands, inputs, deadline);
|
|
82
82
|
|
|
83
|
-
delta = FeeEstimatorHook(address(contentCoinHook)).getFeeState().lastDelta;
|
|
84
|
-
swapParams = FeeEstimatorHook(address(contentCoinHook)).getFeeState().lastSwapParams;
|
|
83
|
+
delta = FeeEstimatorHook(payable(address(contentCoinHook))).getFeeState().lastDelta;
|
|
84
|
+
swapParams = FeeEstimatorHook(payable(address(contentCoinHook))).getFeeState().lastSwapParams;
|
|
85
85
|
|
|
86
86
|
sqrtPriceX96 = PoolStateReader.getSqrtPriceX96(coinV4.getPoolKey(), poolManager);
|
|
87
87
|
|
|
@@ -90,7 +90,7 @@ contract CoinUniV4Test is BaseTest {
|
|
|
90
90
|
|
|
91
91
|
function test_setupZeroAddressForPoolManager() public {
|
|
92
92
|
vm.expectRevert(ICoin.AddressZero.selector);
|
|
93
|
-
new
|
|
93
|
+
new ContentCoin({
|
|
94
94
|
protocolRewardRecipient_: address(0x1234),
|
|
95
95
|
protocolRewards_: address(0x1234),
|
|
96
96
|
poolManager_: IPoolManager(address(0)),
|
|
@@ -100,7 +100,7 @@ contract CoinUniV4Test is BaseTest {
|
|
|
100
100
|
|
|
101
101
|
// function test_setupZeroAddressForHooks() public {
|
|
102
102
|
// vm.expectRevert(ICoin.AddressZero.selector);
|
|
103
|
-
// new
|
|
103
|
+
// new ContentCoin({
|
|
104
104
|
// protocolRewardRecipient_: address(0x1234),
|
|
105
105
|
// protocolRewards_: address(0x1234),
|
|
106
106
|
// poolManager_: IPoolManager(address(0x1234)),
|
|
@@ -293,6 +293,62 @@ contract CoinUniV4Test is BaseTest {
|
|
|
293
293
|
assertApproxEqAbs(mockERC20A.balanceOf(coinV4.protocolRewardRecipient()), totalRewards.protocol, 10, "protocol reward currency");
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
function test_distributesMarketRewardsInEth() public {
|
|
297
|
+
uint64 amountIn = 0.1 ether;
|
|
298
|
+
|
|
299
|
+
// Use address(0) as currency to price the coin in ETH
|
|
300
|
+
address currency = address(0);
|
|
301
|
+
bytes32 salt = keccak256(abi.encodePacked("eth-rewards-test"));
|
|
302
|
+
_deployV4Coin(currency, address(0), salt);
|
|
303
|
+
|
|
304
|
+
address trader = makeAddr("trader");
|
|
305
|
+
|
|
306
|
+
// Give trader ETH
|
|
307
|
+
vm.deal(trader, amountIn);
|
|
308
|
+
|
|
309
|
+
// Record initial ETH balance of payout recipient
|
|
310
|
+
uint256 initialPayoutBalance = coinV4.payoutRecipient().balance;
|
|
311
|
+
|
|
312
|
+
// Swap ETH for coin
|
|
313
|
+
_swapSomeCurrencyForCoin(coinV4, currency, amountIn, trader);
|
|
314
|
+
|
|
315
|
+
// Verify that rewards were paid out in ETH
|
|
316
|
+
assertGt(coinV4.payoutRecipient().balance, initialPayoutBalance, "backing reward should be paid in ETH");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function test_canSwapEthForCoin(uint128 amountIn) public {
|
|
320
|
+
vm.assume(amountIn > 0.00001 ether);
|
|
321
|
+
vm.assume(amountIn < 1 ether);
|
|
322
|
+
|
|
323
|
+
// Use address(0) as currency to price the coin in ETH
|
|
324
|
+
address currency = address(0);
|
|
325
|
+
bytes32 salt = keccak256(abi.encodePacked("eth-coin-test"));
|
|
326
|
+
_deployV4Coin(currency, address(0), salt);
|
|
327
|
+
|
|
328
|
+
address trader = makeAddr("trader");
|
|
329
|
+
|
|
330
|
+
// Give trader ETH
|
|
331
|
+
vm.deal(trader, amountIn);
|
|
332
|
+
|
|
333
|
+
uint256 initialEthBalance = trader.balance;
|
|
334
|
+
|
|
335
|
+
// Swap ETH for coin
|
|
336
|
+
_swapSomeCurrencyForCoin(coinV4, currency, amountIn, trader);
|
|
337
|
+
|
|
338
|
+
// Verify the swap worked
|
|
339
|
+
assertEq(trader.balance, initialEthBalance - amountIn, "trader should have spent ETH");
|
|
340
|
+
assertGt(coinV4.balanceOf(trader), 0, "trader should have received coin");
|
|
341
|
+
|
|
342
|
+
// Now swap some coin back for ETH
|
|
343
|
+
uint128 coinBalance = uint128(coinV4.balanceOf(trader));
|
|
344
|
+
|
|
345
|
+
_swapSomeCoinForCurrency(coinV4, currency, coinBalance, trader);
|
|
346
|
+
|
|
347
|
+
// Verify the reverse swap worked
|
|
348
|
+
assertEq(coinV4.balanceOf(trader), 0, "trader should have no coins left");
|
|
349
|
+
assertGt(trader.balance, 0, "trader should have received ETH back");
|
|
350
|
+
}
|
|
351
|
+
|
|
296
352
|
function test_swap_emitsCoinMarketRewardsV4(uint64 amountIn) public {
|
|
297
353
|
vm.assume(amountIn > 0.00001 ether);
|
|
298
354
|
address currency = address(mockERC20A);
|
|
@@ -649,9 +705,9 @@ contract CoinUniV4Test is BaseTest {
|
|
|
649
705
|
|
|
650
706
|
function test_getSwapPath_whenBackingCurrencyProvidesPath() public {
|
|
651
707
|
address zora = address(mockERC20A);
|
|
652
|
-
|
|
708
|
+
ICoin backingCoin = _deployV4Coin(zora);
|
|
653
709
|
// now create a final coin paired with the backing coin
|
|
654
|
-
|
|
710
|
+
ICoin contentCoin = _deployV4Coin(address(backingCoin));
|
|
655
711
|
|
|
656
712
|
PathKey[] memory path = contentCoin.getPayoutSwapPath(IDeployedCoinVersionLookup(address(factory))).path;
|
|
657
713
|
|
|
@@ -690,9 +746,9 @@ contract CoinUniV4Test is BaseTest {
|
|
|
690
746
|
mockERC20A.mint(address(poolManager), 10000000000000000 ether);
|
|
691
747
|
|
|
692
748
|
// backing coin is a mock coin that is paired with zora
|
|
693
|
-
|
|
749
|
+
ICoin backingCoin = _deployV4Coin(zora);
|
|
694
750
|
// now create a final coin paired with the backing coin
|
|
695
|
-
|
|
751
|
+
ICoin contentCoin = _deployV4Coin(address(backingCoin));
|
|
696
752
|
|
|
697
753
|
vm.assume(amountIn > 0.000000000001 ether);
|
|
698
754
|
vm.assume(amountIn < 10000000000000000 ether);
|
|
@@ -5,7 +5,6 @@ import {BaseTest} from "./utils/BaseTest.sol";
|
|
|
5
5
|
import {BuySupplyWithSwapRouterHook} from "../src/hooks/deployment/BuySupplyWithSwapRouterHook.sol";
|
|
6
6
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
7
7
|
import {IUniswapV3Pool} from "../src/interfaces/IUniswapV3Pool.sol";
|
|
8
|
-
import {Coin} from "../src/Coin.sol";
|
|
9
8
|
import {CoinConfigurationVersions} from "../src/libs/CoinConfigurationVersions.sol";
|
|
10
9
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
11
10
|
import {ICoin} from "../src/interfaces/ICoin.sol";
|
|
@@ -13,6 +12,7 @@ import {IHasAfterCoinDeploy} from "../src/hooks/deployment/BaseCoinDeployHook.so
|
|
|
13
12
|
import {IZoraFactory} from "../src/interfaces/IZoraFactory.sol";
|
|
14
13
|
import {ISwapRouter} from "../src/interfaces/ISwapRouter.sol";
|
|
15
14
|
import {CoinConstants} from "../src/libs/CoinConstants.sol";
|
|
15
|
+
import {ContentCoin} from "../src/ContentCoin.sol";
|
|
16
16
|
|
|
17
17
|
// Create a fake hook that doesn't support the IHasAfterCoinDeploy interface
|
|
18
18
|
contract FakeHookNoInterface {
|
|
@@ -26,15 +26,7 @@ contract HooksTest is BaseTest {
|
|
|
26
26
|
BuySupplyWithSwapRouterHook hook;
|
|
27
27
|
|
|
28
28
|
function _generateDefaultPoolConfig(address currency) internal pure returns (bytes memory) {
|
|
29
|
-
return
|
|
30
|
-
_generatePoolConfig(
|
|
31
|
-
CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
|
|
32
|
-
currency,
|
|
33
|
-
DEFAULT_DISCOVERY_TICK_LOWER,
|
|
34
|
-
DEFAULT_DISCOVERY_TICK_UPPER,
|
|
35
|
-
DEFAULT_NUM_DISCOVERY_POSITIONS,
|
|
36
|
-
DEFAULT_DISCOVERY_SUPPLY_SHARE
|
|
37
|
-
);
|
|
29
|
+
return _generatePoolConfig(currency);
|
|
38
30
|
}
|
|
39
31
|
|
|
40
32
|
function setUp() public override {
|
|
@@ -71,50 +63,6 @@ contract HooksTest is BaseTest {
|
|
|
71
63
|
return _encodeAfterCoinDeploy(buyRecipient, abi.encodeWithSelector(ISwapRouter.exactInput.selector, params));
|
|
72
64
|
}
|
|
73
65
|
|
|
74
|
-
function test_buySupplyWithEthUsingV3Hook_withExactInputSingle(uint256 initialOrderSize) public {
|
|
75
|
-
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
76
|
-
vm.assume(initialOrderSize < 1 ether);
|
|
77
|
-
|
|
78
|
-
vm.deal(users.creator, initialOrderSize);
|
|
79
|
-
|
|
80
|
-
bytes memory hookData = _encodeExactInputSingle(
|
|
81
|
-
users.creator,
|
|
82
|
-
ISwapRouter.ExactInputSingleParams({
|
|
83
|
-
tokenIn: address(weth),
|
|
84
|
-
tokenOut: zora,
|
|
85
|
-
fee: 3000,
|
|
86
|
-
recipient: address(hook),
|
|
87
|
-
amountIn: initialOrderSize,
|
|
88
|
-
amountOutMinimum: 0,
|
|
89
|
-
sqrtPriceLimitX96: 0
|
|
90
|
-
})
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
vm.prank(users.creator);
|
|
94
|
-
(address coinAddress, bytes memory hookDataOut) = factory.deployWithHook{value: initialOrderSize}(
|
|
95
|
-
users.creator,
|
|
96
|
-
_getDefaultOwners(),
|
|
97
|
-
"https://test.com",
|
|
98
|
-
"Testcoin",
|
|
99
|
-
"TEST",
|
|
100
|
-
_generateDefaultPoolConfig(zora),
|
|
101
|
-
users.platformReferrer,
|
|
102
|
-
address(hook),
|
|
103
|
-
hookData
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
coin = Coin(payable(coinAddress));
|
|
107
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
108
|
-
|
|
109
|
-
(uint256 amountCurrency, uint256 coinsPurchased) = abi.decode(hookDataOut, (uint256, uint256));
|
|
110
|
-
|
|
111
|
-
assertEq(coin.currency(), zora, "currency");
|
|
112
|
-
assertGt(amountCurrency, 0, "amountCurrency > 0");
|
|
113
|
-
assertGt(coinsPurchased, 0, "coinsPurchased > 0");
|
|
114
|
-
assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased, "balanceOf creator");
|
|
115
|
-
assertGt(IERC20(zora).balanceOf(address(pool)), 0, "Pool ZORA balance");
|
|
116
|
-
}
|
|
117
|
-
|
|
118
66
|
function test_buySupplyWithEthUsingV4Hook_withExactInputMultiHop(uint256 initialOrderSize) public {
|
|
119
67
|
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
120
68
|
vm.assume(initialOrderSize < 1 ether);
|
|
@@ -150,62 +98,17 @@ contract HooksTest is BaseTest {
|
|
|
150
98
|
hookData
|
|
151
99
|
);
|
|
152
100
|
|
|
153
|
-
|
|
101
|
+
coinV4 = ContentCoin(payable(coinAddress));
|
|
154
102
|
|
|
155
103
|
(uint256 amountCurrency, uint256 coinsPurchased) = abi.decode(hookDataOut, (uint256, uint256));
|
|
156
104
|
|
|
157
|
-
assertEq(
|
|
105
|
+
assertEq(coinV4.currency(), zora, "currency");
|
|
158
106
|
assertGt(amountCurrency, 0, "amountCurrency > 0");
|
|
159
107
|
assertGt(coinsPurchased, 0, "coinsPurchased > 0");
|
|
160
|
-
assertEq(
|
|
108
|
+
assertEq(coinV4.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased, "balanceOf creator");
|
|
161
109
|
// assertGt(IERC20(zora).balanceOf(address(pool)), 0, "Pool ZORA balance");
|
|
162
110
|
}
|
|
163
111
|
|
|
164
|
-
function test_buySupplyWithEthUsingV3Hook_withExactInputMultiHop(uint256 initialOrderSize) public {
|
|
165
|
-
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
166
|
-
vm.assume(initialOrderSize < 1 ether);
|
|
167
|
-
|
|
168
|
-
vm.deal(users.creator, initialOrderSize);
|
|
169
|
-
|
|
170
|
-
// lets try weth to usdc to zora
|
|
171
|
-
|
|
172
|
-
uint24 poolFee = 3000;
|
|
173
|
-
|
|
174
|
-
bytes memory hookData = _encodeExactInput(
|
|
175
|
-
users.creator,
|
|
176
|
-
ISwapRouter.ExactInputParams({
|
|
177
|
-
path: abi.encodePacked(address(weth), poolFee, USDC_ADDRESS, poolFee, zora),
|
|
178
|
-
recipient: address(hook),
|
|
179
|
-
amountIn: initialOrderSize,
|
|
180
|
-
amountOutMinimum: 0
|
|
181
|
-
})
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
vm.prank(users.creator);
|
|
185
|
-
(address coinAddress, bytes memory hookDataOut) = factory.deployWithHook{value: initialOrderSize}(
|
|
186
|
-
users.creator,
|
|
187
|
-
_getDefaultOwners(),
|
|
188
|
-
"https://test.com",
|
|
189
|
-
"Testcoin",
|
|
190
|
-
"TEST",
|
|
191
|
-
_generateDefaultPoolConfig(zora),
|
|
192
|
-
users.platformReferrer,
|
|
193
|
-
address(hook),
|
|
194
|
-
hookData
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
coin = Coin(payable(coinAddress));
|
|
198
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
199
|
-
|
|
200
|
-
(uint256 amountCurrency, uint256 coinsPurchased) = abi.decode(hookDataOut, (uint256, uint256));
|
|
201
|
-
|
|
202
|
-
assertEq(coin.currency(), zora, "currency");
|
|
203
|
-
assertGt(amountCurrency, 0, "amountCurrency > 0");
|
|
204
|
-
assertGt(coinsPurchased, 0, "coinsPurchased > 0");
|
|
205
|
-
assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased, "balanceOf creator");
|
|
206
|
-
assertGt(IERC20(zora).balanceOf(address(pool)), 0, "Pool ZORA balance");
|
|
207
|
-
}
|
|
208
|
-
|
|
209
112
|
function test_buySupplyWithEthUsingV3Hook_revertsWhenBadCall() public {
|
|
210
113
|
vm.deal(users.creator, 0.0001 ether);
|
|
211
114
|
|
package/test/Factory.t.sol
CHANGED
|
@@ -3,6 +3,8 @@ pragma solidity ^0.8.13;
|
|
|
3
3
|
|
|
4
4
|
import "./utils/BaseTest.sol";
|
|
5
5
|
import {CoinConstants} from "../src/libs/CoinConstants.sol";
|
|
6
|
+
import {IHasContractName} from "@zoralabs/shared-contracts/interfaces/IContractMetadata.sol";
|
|
7
|
+
import {IZoraFactory} from "../src/interfaces/IZoraFactory.sol";
|
|
6
8
|
|
|
7
9
|
contract FactoryTest is BaseTest {
|
|
8
10
|
function setUp() public override {
|
|
@@ -11,14 +13,7 @@ contract FactoryTest is BaseTest {
|
|
|
11
13
|
|
|
12
14
|
function test_factory_constructor_and_proxy_setup() public {
|
|
13
15
|
// Impl constructor test
|
|
14
|
-
ZoraFactoryImpl impl = new ZoraFactoryImpl(
|
|
15
|
-
address(coinV3Impl),
|
|
16
|
-
address(coinV4Impl),
|
|
17
|
-
address(creatorCoinImpl),
|
|
18
|
-
address(contentCoinHook),
|
|
19
|
-
address(creatorCoinHook)
|
|
20
|
-
);
|
|
21
|
-
assertEq(ZoraFactoryImpl(address(factory)).coinImpl(), address(coinV3Impl));
|
|
16
|
+
ZoraFactoryImpl impl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook));
|
|
22
17
|
assertEq(ZoraFactoryImpl(address(factory)).owner(), users.factoryOwner);
|
|
23
18
|
assertEq(ZoraFactoryImpl(address(factory)).coinV4Impl(), address(coinV4Impl));
|
|
24
19
|
|
|
@@ -29,296 +24,49 @@ contract FactoryTest is BaseTest {
|
|
|
29
24
|
assertEq(ZoraFactoryImpl(address(factory)).owner(), initialOwner);
|
|
30
25
|
}
|
|
31
26
|
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
(address coinAddress, ) = factory.deploy(
|
|
37
|
-
users.creator,
|
|
38
|
-
owners,
|
|
39
|
-
"https://test2.com",
|
|
40
|
-
"Test2 Token",
|
|
41
|
-
"TEST2",
|
|
42
|
-
_generatePoolConfig(address(weth)),
|
|
43
|
-
users.platformReferrer,
|
|
44
|
-
0
|
|
45
|
-
);
|
|
46
|
-
coin = Coin(payable(coinAddress));
|
|
47
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
48
|
-
vm.label(address(coin), "COIN");
|
|
49
|
-
vm.label(address(pool), "POOL");
|
|
50
|
-
|
|
51
|
-
uint160 sqrtPriceX96 = pool.slot0().sqrtPriceX96;
|
|
52
|
-
uint256 poolCoinBalance = coin.balanceOf(address(pool));
|
|
53
|
-
uint256 poolEthBalance = weth.balanceOf(address(pool));
|
|
54
|
-
|
|
55
|
-
console.log("POOL_TOKEN_0: ", pool.token0());
|
|
56
|
-
console.log("POOL_TOKEN_1: ", pool.token1());
|
|
57
|
-
console.log("POOL_SQRT_PRICE_X96: ", sqrtPriceX96);
|
|
58
|
-
console.log("");
|
|
59
|
-
console.log("POOL_COIN_BALANCE: ", poolCoinBalance);
|
|
60
|
-
console.log("POOL_ETH_BALANCE: ", poolEthBalance);
|
|
61
|
-
console.log("");
|
|
62
|
-
|
|
63
|
-
assertEq(coin.payoutRecipient(), users.creator, "payoutRecipient");
|
|
64
|
-
assertEq(coin.protocolRewardRecipient(), users.feeRecipient, "protocolRewardRecipient");
|
|
65
|
-
assertEq(coin.platformReferrer(), users.platformReferrer, "platformReferrer");
|
|
66
|
-
assertEq(coin.tokenURI(), "https://test2.com", "tokenURI");
|
|
67
|
-
assertEq(coin.name(), "Test2 Token", "name");
|
|
68
|
-
assertEq(coin.symbol(), "TEST2", "symbol");
|
|
69
|
-
assertEq(coin.currency(), address(weth), "currency");
|
|
70
|
-
assertEq(coin.totalSupply(), 1_000_000_000e18, "totalSupply");
|
|
71
|
-
assertEq(coin.balanceOf(users.creator), 10_000_000e18, "balanceOf creator");
|
|
72
|
-
assertGt(coin.balanceOf(coin.poolAddress()), 989_999_999e18, "balanceOf pool");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function test_deploy_with_eth(uint256 initialOrderSize) public {
|
|
76
|
-
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
77
|
-
vm.assume(initialOrderSize < 10 ether);
|
|
78
|
-
|
|
79
|
-
address[] memory owners = new address[](1);
|
|
80
|
-
owners[0] = users.creator;
|
|
81
|
-
|
|
82
|
-
vm.deal(users.creator, initialOrderSize);
|
|
83
|
-
vm.prank(users.creator);
|
|
84
|
-
(address coinAddress, ) = factory.deploy{value: initialOrderSize}(
|
|
85
|
-
users.creator,
|
|
86
|
-
owners,
|
|
87
|
-
"https://test2.com",
|
|
88
|
-
"Test2 Token",
|
|
89
|
-
"TEST2",
|
|
90
|
-
_generatePoolConfig(address(weth)),
|
|
91
|
-
users.platformReferrer,
|
|
92
|
-
initialOrderSize
|
|
93
|
-
);
|
|
94
|
-
coin = Coin(payable(coinAddress));
|
|
95
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
96
|
-
vm.label(address(coin), "COIN");
|
|
97
|
-
vm.label(address(pool), "POOL");
|
|
98
|
-
|
|
99
|
-
uint160 sqrtPriceX96 = pool.slot0().sqrtPriceX96;
|
|
100
|
-
uint256 poolCoinBalance = coin.balanceOf(address(pool));
|
|
101
|
-
uint256 poolEthBalance = weth.balanceOf(address(pool));
|
|
102
|
-
|
|
103
|
-
console.log("POOL_TOKEN_0: ", pool.token0());
|
|
104
|
-
console.log("POOL_TOKEN_1: ", pool.token1());
|
|
105
|
-
console.log("POOL_SQRT_PRICE_X96: ", sqrtPriceX96);
|
|
106
|
-
console.log("");
|
|
107
|
-
console.log("POOL_COIN_BALANCE: ", poolCoinBalance);
|
|
108
|
-
console.log("POOL_ETH_BALANCE: ", poolEthBalance);
|
|
109
|
-
console.log("");
|
|
110
|
-
console.log("BUYER_COIN_BALANCE ", coin.balanceOf(users.creator) - 10_000_000e18);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function test_deploy_with_weth(uint256 initialOrderSize) public {
|
|
114
|
-
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
115
|
-
vm.assume(initialOrderSize < 10 ether);
|
|
116
|
-
|
|
117
|
-
address[] memory owners = new address[](1);
|
|
118
|
-
owners[0] = users.creator;
|
|
119
|
-
|
|
120
|
-
vm.deal(users.creator, initialOrderSize);
|
|
121
|
-
|
|
122
|
-
vm.startPrank(users.creator);
|
|
123
|
-
weth.deposit{value: initialOrderSize}();
|
|
124
|
-
|
|
125
|
-
weth.approve(address(factory), type(uint256).max);
|
|
126
|
-
|
|
127
|
-
// Expect this to revert because WETH needs to be sent with msg.value.
|
|
128
|
-
vm.expectRevert();
|
|
129
|
-
factory.deploy(
|
|
130
|
-
users.creator,
|
|
131
|
-
owners,
|
|
132
|
-
"https://test2.com",
|
|
133
|
-
"Test2 Token",
|
|
134
|
-
"TEST2",
|
|
135
|
-
_generatePoolConfig(address(weth)),
|
|
136
|
-
users.platformReferrer,
|
|
137
|
-
initialOrderSize
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function test_deploy_with_one_eth() public {
|
|
142
|
-
address[] memory owners = new address[](1);
|
|
143
|
-
owners[0] = users.creator;
|
|
144
|
-
|
|
145
|
-
uint256 orderSize = 1 ether;
|
|
146
|
-
vm.deal(users.creator, orderSize);
|
|
147
|
-
|
|
148
|
-
(address coinAddress, ) = factory.deploy{value: orderSize}(
|
|
149
|
-
users.creator,
|
|
150
|
-
owners,
|
|
151
|
-
"https://test2.com",
|
|
152
|
-
"Test2 Token",
|
|
153
|
-
"TEST2",
|
|
154
|
-
_generatePoolConfig(address(weth)),
|
|
155
|
-
users.platformReferrer,
|
|
156
|
-
orderSize
|
|
157
|
-
);
|
|
158
|
-
coin = Coin(payable(coinAddress));
|
|
159
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
160
|
-
vm.label(address(coin), "COIN");
|
|
161
|
-
vm.label(address(pool), "POOL");
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function test_deploy_with_usdc() public {
|
|
165
|
-
address[] memory owners = new address[](1);
|
|
166
|
-
owners[0] = users.creator;
|
|
167
|
-
|
|
168
|
-
(address coinAddress, ) = factory.deploy(
|
|
169
|
-
users.creator,
|
|
170
|
-
owners,
|
|
171
|
-
"https://testcoinusdcpair.com",
|
|
172
|
-
"Testcoinusdcpair",
|
|
173
|
-
"TESTCOINUSDCPAIR",
|
|
174
|
-
_generatePoolConfig(USDC_ADDRESS),
|
|
175
|
-
users.platformReferrer,
|
|
176
|
-
0
|
|
177
|
-
);
|
|
178
|
-
coin = Coin(payable(coinAddress));
|
|
179
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
180
|
-
vm.label(address(coin), "COIN");
|
|
181
|
-
vm.label(address(pool), "POOL");
|
|
182
|
-
|
|
183
|
-
assertEq(coin.currency(), USDC_ADDRESS, "currency");
|
|
184
|
-
assertEq(coin.payoutRecipient(), users.creator, "payoutRecipient");
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function test_deploy_with_usdc_order() public {
|
|
188
|
-
address[] memory owners = new address[](1);
|
|
189
|
-
owners[0] = users.creator;
|
|
190
|
-
|
|
191
|
-
uint256 orderSize = dealUSDC(users.creator, 100);
|
|
27
|
+
function test_ownable2Step() public {
|
|
28
|
+
// old current impl
|
|
29
|
+
assertEq(ZoraFactoryImpl(address(factory)).owner(), users.factoryOwner);
|
|
192
30
|
|
|
193
|
-
|
|
194
|
-
usdc.approve(address(factory), orderSize);
|
|
31
|
+
// 1st ensure owner slot is set at expected address
|
|
195
32
|
|
|
196
|
-
|
|
197
|
-
|
|
33
|
+
bytes32 ownableSlot = hex"9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300";
|
|
34
|
+
bytes32 ownable2StepSlot = hex"237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00";
|
|
198
35
|
|
|
199
|
-
vm.
|
|
200
|
-
(
|
|
201
|
-
users.creator,
|
|
202
|
-
owners,
|
|
203
|
-
"https://testcoinusdcpair.com",
|
|
204
|
-
"Testcoinusdcpair",
|
|
205
|
-
"TESTCOINUSDCPAIR",
|
|
206
|
-
_generatePoolConfig(USDC_ADDRESS),
|
|
207
|
-
users.platformReferrer,
|
|
208
|
-
orderSize
|
|
209
|
-
);
|
|
210
|
-
coin = Coin(payable(coinAddress));
|
|
211
|
-
pool = IUniswapV3Pool(coin.poolAddress());
|
|
212
|
-
vm.label(address(coin), "COIN");
|
|
213
|
-
vm.label(address(pool), "POOL");
|
|
36
|
+
address ownerAddress = address(uint160(uint256(vm.load(address(factory), ownableSlot))));
|
|
37
|
+
assertEq(ownerAddress, users.factoryOwner);
|
|
214
38
|
|
|
215
|
-
assertEq(
|
|
216
|
-
assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased);
|
|
217
|
-
}
|
|
39
|
+
assertEq(ZoraFactoryImpl(address(factory)).pendingOwner(), address(0));
|
|
218
40
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
owners[0] = users.creator;
|
|
222
|
-
|
|
223
|
-
vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
|
|
224
|
-
factory.deploy(
|
|
225
|
-
address(0),
|
|
226
|
-
owners,
|
|
227
|
-
"https://testcoinusdcpair.com",
|
|
228
|
-
"Testcoinusdcpair",
|
|
229
|
-
"TESTCOINUSDCPAIR",
|
|
230
|
-
_generatePoolConfig(USDC_ADDRESS),
|
|
231
|
-
users.platformReferrer,
|
|
232
|
-
0
|
|
41
|
+
address newFactoryImpl = address(
|
|
42
|
+
new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook))
|
|
233
43
|
);
|
|
234
|
-
}
|
|
235
44
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
vm.expectRevert(abi.encodeWithSelector(MultiOwnable.OneOwnerRequired.selector));
|
|
240
|
-
factory.deploy(
|
|
241
|
-
users.creator,
|
|
242
|
-
owners,
|
|
243
|
-
"https://testcoinusdcpair.com",
|
|
244
|
-
"Testcoinusdcpair",
|
|
245
|
-
"TESTCOINUSDCPAIR",
|
|
246
|
-
_generatePoolConfig(USDC_ADDRESS),
|
|
247
|
-
users.platformReferrer,
|
|
248
|
-
0
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function test_deploy_with_usdc_platform_referrer_zero() public {
|
|
253
|
-
address[] memory owners = new address[](1);
|
|
254
|
-
owners[0] = users.creator;
|
|
255
|
-
|
|
256
|
-
(address coinAddress, ) = factory.deploy(
|
|
257
|
-
users.creator,
|
|
258
|
-
owners,
|
|
259
|
-
"https://testcoinusdcpair.com",
|
|
260
|
-
"Testcoinusdcpair",
|
|
261
|
-
"TESTCOINUSDCPAIR",
|
|
262
|
-
_generatePoolConfig(USDC_ADDRESS),
|
|
263
|
-
address(0),
|
|
264
|
-
0
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
coin = Coin(payable(coinAddress));
|
|
268
|
-
|
|
269
|
-
assertEq(coin.platformReferrer(), coin.protocolRewardRecipient(), "platformReferrer");
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function test_deploy_with_usdc_revert_invalid_eth_transfer() public {
|
|
273
|
-
address[] memory owners = new address[](1);
|
|
274
|
-
owners[0] = users.creator;
|
|
275
|
-
|
|
276
|
-
dealUSDC(users.creator, 1);
|
|
45
|
+
// Upgrade to current / new impl
|
|
46
|
+
vm.prank(users.factoryOwner);
|
|
47
|
+
ZoraFactoryImpl(address(factory)).upgradeToAndCall(newFactoryImpl, "");
|
|
277
48
|
|
|
278
|
-
|
|
49
|
+
// 2nd ensure owner is read from correct slot
|
|
50
|
+
assertEq(ZoraFactoryImpl(address(factory)).owner(), users.factoryOwner);
|
|
279
51
|
|
|
280
|
-
|
|
281
|
-
vm.expectRevert(abi.encodeWithSelector(ICoin.EthTransferInvalid.selector));
|
|
282
|
-
|
|
283
|
-
factory.deploy{value: 1e6}(
|
|
284
|
-
users.creator,
|
|
285
|
-
owners,
|
|
286
|
-
"https://testcoinusdcpair.com",
|
|
287
|
-
"Testcoinusdcpair",
|
|
288
|
-
"TESTCOINUSDCPAIR",
|
|
289
|
-
_generatePoolConfig(USDC_ADDRESS),
|
|
290
|
-
users.platformReferrer,
|
|
291
|
-
0
|
|
292
|
-
);
|
|
293
|
-
}
|
|
52
|
+
address newOwner = makeAddr("newOwner");
|
|
294
53
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
54
|
+
// 3rd ensure pending owner is set correctly
|
|
55
|
+
vm.prank(users.factoryOwner);
|
|
56
|
+
ZoraFactoryImpl(address(factory)).transferOwnership(newOwner);
|
|
57
|
+
assertEq(ZoraFactoryImpl(address(factory)).pendingOwner(), newOwner);
|
|
298
58
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
owners,
|
|
302
|
-
"https://test.com",
|
|
303
|
-
"Test Token",
|
|
304
|
-
"TEST",
|
|
305
|
-
_generatePoolConfig(address(weth)),
|
|
306
|
-
users.platformReferrer,
|
|
307
|
-
0
|
|
308
|
-
);
|
|
309
|
-
coin = Coin(payable(coinAddress));
|
|
59
|
+
address ownerAddress2Step = address(uint160(uint256(vm.load(address(factory), ownable2StepSlot))));
|
|
60
|
+
assertEq(ownerAddress2Step, newOwner);
|
|
310
61
|
|
|
311
|
-
|
|
62
|
+
// 4th ensure owner is set correctly
|
|
63
|
+
vm.prank(newOwner);
|
|
64
|
+
ZoraFactoryImpl(address(factory)).acceptOwnership();
|
|
65
|
+
assertEq(ZoraFactoryImpl(address(factory)).owner(), newOwner);
|
|
312
66
|
}
|
|
313
67
|
|
|
314
68
|
function test_upgrade() public {
|
|
315
|
-
ZoraFactoryImpl newImpl = new ZoraFactoryImpl(
|
|
316
|
-
address(coinV3Impl),
|
|
317
|
-
address(coinV4Impl),
|
|
318
|
-
address(creatorCoinImpl),
|
|
319
|
-
address(contentCoinHook),
|
|
320
|
-
address(creatorCoinHook)
|
|
321
|
-
);
|
|
69
|
+
ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook));
|
|
322
70
|
|
|
323
71
|
vm.prank(users.factoryOwner);
|
|
324
72
|
ZoraFactoryImpl(address(factory)).upgradeToAndCall(address(newImpl), "");
|
|
@@ -339,13 +87,7 @@ contract FactoryTest is BaseTest {
|
|
|
339
87
|
}
|
|
340
88
|
|
|
341
89
|
function test_revert_invalid_owner() public {
|
|
342
|
-
ZoraFactoryImpl newImpl = new ZoraFactoryImpl(
|
|
343
|
-
address(coinV3Impl),
|
|
344
|
-
address(coinV4Impl),
|
|
345
|
-
address(creatorCoinImpl),
|
|
346
|
-
address(contentCoinHook),
|
|
347
|
-
address(creatorCoinHook)
|
|
348
|
-
);
|
|
90
|
+
ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook));
|
|
349
91
|
|
|
350
92
|
vm.prank(users.creator);
|
|
351
93
|
vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, users.creator));
|
|
@@ -376,7 +118,7 @@ contract FactoryTest is BaseTest {
|
|
|
376
118
|
address platformReferrer = users.platformReferrer;
|
|
377
119
|
|
|
378
120
|
bytes memory poolConfig = CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(address(weth));
|
|
379
|
-
bytes memory poolConfigForGettingAddress = poolConfigChanged ? CoinConfigurationVersions.
|
|
121
|
+
bytes memory poolConfigForGettingAddress = poolConfigChanged ? CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(address(0)) : poolConfig;
|
|
380
122
|
|
|
381
123
|
address expectedCoinAddress = factory.coinAddress(msgSender, name, symbol, poolConfigForGettingAddress, platformReferrer, salt);
|
|
382
124
|
|
|
@@ -412,4 +154,20 @@ contract FactoryTest is BaseTest {
|
|
|
412
154
|
assertEq(coinAddress, expectedCoinAddress, "coinAddress should match");
|
|
413
155
|
}
|
|
414
156
|
}
|
|
157
|
+
|
|
158
|
+
function test_upgrade_with_mismatched_contract_name() public {
|
|
159
|
+
// Create a mock implementation with different contract name
|
|
160
|
+
MockBadFactory badImpl = new MockBadFactory();
|
|
161
|
+
|
|
162
|
+
vm.prank(users.factoryOwner);
|
|
163
|
+
vm.expectRevert(abi.encodeWithSelector(IZoraFactory.UpgradeToMismatchedContractName.selector, "ZoraCoinFactory", "BadFactory"));
|
|
164
|
+
ZoraFactoryImpl(address(factory)).upgradeToAndCall(address(badImpl), "");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Mock contracts for testing
|
|
169
|
+
contract MockBadFactory is IHasContractName {
|
|
170
|
+
function contractName() external pure returns (string memory) {
|
|
171
|
+
return "BadFactory";
|
|
172
|
+
}
|
|
415
173
|
}
|