@zoralabs/coins 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +76 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/abis/Address.json +29 -0
- package/abis/BaseTest.json +691 -0
- package/abis/Clones.json +7 -0
- package/abis/Coin.json +1693 -0
- package/abis/CoinConstants.json +93 -0
- package/abis/CoinTest.json +998 -0
- package/abis/ContextUpgradeable.json +25 -0
- package/abis/ContractVersionBase.json +15 -0
- package/abis/Deploy.json +29 -0
- package/abis/ECDSA.json +29 -0
- package/abis/EIP712.json +67 -0
- package/abis/EIP712Upgradeable.json +74 -0
- package/abis/ERC1967Proxy.json +67 -0
- package/abis/ERC1967Utils.json +85 -0
- package/abis/ERC20PermitUpgradeable.json +527 -0
- package/abis/ERC20Upgradeable.json +333 -0
- package/abis/FactoryTest.json +845 -0
- package/abis/IBeacon.json +15 -0
- package/abis/ICoin.json +541 -0
- package/abis/ICoinComments.json +53 -0
- package/abis/IERC1155Errors.json +104 -0
- package/abis/IERC165.json +21 -0
- package/abis/IERC1822Proxiable.json +15 -0
- package/abis/IERC20.json +221 -0
- package/abis/IERC20Errors.json +88 -0
- package/abis/IERC20Metadata.json +224 -0
- package/abis/IERC20Permit.json +77 -0
- package/abis/IERC5267.json +51 -0
- package/abis/IERC721.json +287 -0
- package/abis/IERC721Enumerable.json +343 -0
- package/abis/IERC721Errors.json +105 -0
- package/abis/IERC721Metadata.json +332 -0
- package/abis/IERC721Receiver.json +36 -0
- package/abis/IERC721TokenReceiver.json +36 -0
- package/abis/IERC7572.json +21 -0
- package/abis/IMulticall3.json +440 -0
- package/abis/INonfungiblePositionManager.json +366 -0
- package/abis/IProtocolRewards.json +348 -0
- package/abis/ISwapRouter.json +137 -0
- package/abis/IUniswapV3Pool.json +148 -0
- package/abis/IUniswapV3SwapCallback.json +25 -0
- package/abis/IVersionedContract.json +15 -0
- package/abis/IWETH.json +118 -0
- package/abis/IZoraFactory.json +138 -0
- package/abis/Initializable.json +25 -0
- package/abis/Math.json +7 -0
- package/abis/MockERC20.json +322 -0
- package/abis/MockERC721.json +350 -0
- package/abis/MultiOwnable.json +171 -0
- package/abis/MultiOwnableTest.json +796 -0
- package/abis/NoncesUpgradeable.json +60 -0
- package/abis/OwnableUpgradeable.json +99 -0
- package/abis/ProtocolRewards.json +494 -0
- package/abis/Proxy.json +6 -0
- package/abis/ReentrancyGuardUpgradeable.json +30 -0
- package/abis/SafeERC20.json +34 -0
- package/abis/Script.json +15 -0
- package/abis/ShortStrings.json +18 -0
- package/abis/StdAssertions.json +379 -0
- package/abis/StdInvariant.json +180 -0
- package/abis/Strings.json +18 -0
- package/abis/Test.json +570 -0
- package/abis/UUPSUpgradeable.json +130 -0
- package/abis/Vm.json +8627 -0
- package/abis/VmSafe.json +7297 -0
- package/abis/ZoraFactory.json +67 -0
- package/abis/ZoraFactoryImpl.json +422 -0
- package/abis/stdError.json +119 -0
- package/abis/stdStorageSafe.json +52 -0
- package/addresses/1.json +4 -0
- package/addresses/8453.json +9 -0
- package/addresses/84532.json +9 -0
- package/dist/index.cjs +1236 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1208 -0
- package/dist/index.js.map +1 -0
- package/dist/wagmiGenerated.d.ts +1645 -0
- package/dist/wagmiGenerated.d.ts.map +1 -0
- package/foundry.toml +9 -0
- package/package/index.ts +1 -0
- package/package/wagmiGenerated.ts +1211 -0
- package/package.json +48 -0
- package/remappings.txt +4 -0
- package/script/Deploy.s.sol +14 -0
- package/slither.config.json +6 -0
- package/src/Coin.sol +579 -0
- package/src/ZoraFactoryImpl.sol +142 -0
- package/src/interfaces/ICoin.sol +194 -0
- package/src/interfaces/ICoinComments.sol +8 -0
- package/src/interfaces/IERC7572.sol +12 -0
- package/src/interfaces/INonfungiblePositionManager.sol +104 -0
- package/src/interfaces/IProtocolRewards.sol +12 -0
- package/src/interfaces/ISwapRouter.sol +38 -0
- package/src/interfaces/IUniswapV3Pool.sol +43 -0
- package/src/interfaces/IUniswapV3SwapCallback.sol +17 -0
- package/src/interfaces/IWETH.sol +16 -0
- package/src/interfaces/IZoraFactory.sol +56 -0
- package/src/proxy/ZoraFactory.sol +26 -0
- package/src/utils/CoinConstants.sol +67 -0
- package/src/utils/MultiOwnable.sol +126 -0
- package/src/utils/TickMath.sol +210 -0
- package/src/version/ContractVersionBase.sol +14 -0
- package/test/Coin.t.sol +443 -0
- package/test/Factory.t.sol +298 -0
- package/test/MultiOwnable.t.sol +156 -0
- package/test/utils/BaseTest.sol +178 -0
- package/test/utils/ProtocolRewards.sol +1499 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +11 -0
- package/wagmi.config.ts +16 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import "./utils/BaseTest.sol";
|
|
5
|
+
|
|
6
|
+
contract FactoryTest is BaseTest {
|
|
7
|
+
function setUp() public override {
|
|
8
|
+
super.setUp();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function test_constructor() public view {
|
|
12
|
+
assertEq(factory.coinImpl(), address(coinImpl));
|
|
13
|
+
assertEq(factory.owner(), users.factoryOwner);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function test_deploy_no_eth() public {
|
|
17
|
+
address[] memory owners = new address[](1);
|
|
18
|
+
owners[0] = users.creator;
|
|
19
|
+
|
|
20
|
+
vm.prank(users.creator);
|
|
21
|
+
coin = Coin(
|
|
22
|
+
payable(
|
|
23
|
+
factory.deploy(users.creator, owners, "https://test2.com", "Test2 Token", "TEST2", users.platformReferrer, address(weth), LP_TICK_LOWER_WETH, 0)
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
pool = IUniswapV3Pool(coin.poolAddress());
|
|
27
|
+
vm.label(address(coin), "COIN");
|
|
28
|
+
vm.label(address(pool), "POOL");
|
|
29
|
+
|
|
30
|
+
uint160 sqrtPriceX96 = pool.slot0().sqrtPriceX96;
|
|
31
|
+
uint256 poolCoinBalance = coin.balanceOf(address(pool));
|
|
32
|
+
uint256 poolEthBalance = weth.balanceOf(address(pool));
|
|
33
|
+
|
|
34
|
+
console.log("POOL_TOKEN_0: ", pool.token0());
|
|
35
|
+
console.log("POOL_TOKEN_1: ", pool.token1());
|
|
36
|
+
console.log("POOL_SQRT_PRICE_X96: ", sqrtPriceX96);
|
|
37
|
+
console.log("");
|
|
38
|
+
console.log("POOL_COIN_BALANCE: ", poolCoinBalance);
|
|
39
|
+
console.log("POOL_ETH_BALANCE: ", poolEthBalance);
|
|
40
|
+
console.log("");
|
|
41
|
+
|
|
42
|
+
assertEq(coin.payoutRecipient(), users.creator, "payoutRecipient");
|
|
43
|
+
assertEq(coin.protocolRewardRecipient(), users.feeRecipient, "protocolRewardRecipient");
|
|
44
|
+
assertEq(coin.platformReferrer(), users.platformReferrer, "platformReferrer");
|
|
45
|
+
assertEq(coin.tokenURI(), "https://test2.com", "tokenURI");
|
|
46
|
+
assertEq(coin.name(), "Test2 Token", "name");
|
|
47
|
+
assertEq(coin.symbol(), "TEST2", "symbol");
|
|
48
|
+
assertEq(coin.currency(), address(weth), "currency");
|
|
49
|
+
assertEq(coin.totalSupply(), 1_000_000_000e18, "totalSupply");
|
|
50
|
+
assertEq(coin.balanceOf(users.creator), 10_000_000e18, "balanceOf creator");
|
|
51
|
+
assertGe(coin.balanceOf(users.platformReferrer), 5_000_000e18, "balanceOf platformReferrer");
|
|
52
|
+
assertGe(coin.balanceOf(users.feeRecipient), 5_000_000e18, "balanceOf protocolRewardRecipient");
|
|
53
|
+
assertGt(coin.balanceOf(coin.poolAddress()), 979_999_983e18, "balanceOf pool");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function test_deploy_with_eth(uint256 initialOrderSize) public {
|
|
57
|
+
vm.assume(initialOrderSize > MIN_ORDER_SIZE);
|
|
58
|
+
vm.assume(initialOrderSize < 10 ether);
|
|
59
|
+
|
|
60
|
+
address[] memory owners = new address[](1);
|
|
61
|
+
owners[0] = users.creator;
|
|
62
|
+
|
|
63
|
+
vm.deal(users.creator, initialOrderSize);
|
|
64
|
+
vm.prank(users.creator);
|
|
65
|
+
coin = Coin(
|
|
66
|
+
payable(
|
|
67
|
+
factory.deploy{value: initialOrderSize}(
|
|
68
|
+
users.creator,
|
|
69
|
+
owners,
|
|
70
|
+
"https://test2.com",
|
|
71
|
+
"Test2 Token",
|
|
72
|
+
"TEST2",
|
|
73
|
+
users.platformReferrer,
|
|
74
|
+
address(weth),
|
|
75
|
+
LP_TICK_LOWER_WETH,
|
|
76
|
+
initialOrderSize
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
pool = IUniswapV3Pool(coin.poolAddress());
|
|
81
|
+
vm.label(address(coin), "COIN");
|
|
82
|
+
vm.label(address(pool), "POOL");
|
|
83
|
+
|
|
84
|
+
uint160 sqrtPriceX96 = pool.slot0().sqrtPriceX96;
|
|
85
|
+
uint256 poolCoinBalance = coin.balanceOf(address(pool));
|
|
86
|
+
uint256 poolEthBalance = weth.balanceOf(address(pool));
|
|
87
|
+
|
|
88
|
+
console.log("POOL_TOKEN_0: ", pool.token0());
|
|
89
|
+
console.log("POOL_TOKEN_1: ", pool.token1());
|
|
90
|
+
console.log("POOL_SQRT_PRICE_X96: ", sqrtPriceX96);
|
|
91
|
+
console.log("");
|
|
92
|
+
console.log("POOL_COIN_BALANCE: ", poolCoinBalance);
|
|
93
|
+
console.log("POOL_ETH_BALANCE: ", poolEthBalance);
|
|
94
|
+
console.log("");
|
|
95
|
+
console.log("BUYER_COIN_BALANCE ", coin.balanceOf(users.creator) - 10_000_000e18);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function test_deploy_with_one_eth() public {
|
|
99
|
+
address[] memory owners = new address[](1);
|
|
100
|
+
owners[0] = users.creator;
|
|
101
|
+
|
|
102
|
+
uint256 orderSize = 1 ether;
|
|
103
|
+
vm.deal(users.creator, orderSize);
|
|
104
|
+
|
|
105
|
+
vm.prank(users.creator);
|
|
106
|
+
coin = Coin(
|
|
107
|
+
payable(
|
|
108
|
+
factory.deploy{value: orderSize}(
|
|
109
|
+
users.creator,
|
|
110
|
+
owners,
|
|
111
|
+
"https://test2.com",
|
|
112
|
+
"Test2 Token",
|
|
113
|
+
"TEST2",
|
|
114
|
+
users.platformReferrer,
|
|
115
|
+
address(weth),
|
|
116
|
+
LP_TICK_LOWER_WETH,
|
|
117
|
+
orderSize
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
pool = IUniswapV3Pool(coin.poolAddress());
|
|
122
|
+
vm.label(address(coin), "COIN");
|
|
123
|
+
vm.label(address(pool), "POOL");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function test_deploy_with_usdc() public {
|
|
127
|
+
address[] memory owners = new address[](1);
|
|
128
|
+
owners[0] = users.creator;
|
|
129
|
+
|
|
130
|
+
vm.prank(users.creator);
|
|
131
|
+
coin = Coin(
|
|
132
|
+
payable(
|
|
133
|
+
factory.deploy(
|
|
134
|
+
users.creator,
|
|
135
|
+
owners,
|
|
136
|
+
"https://testcoinusdcpair.com",
|
|
137
|
+
"Testcoinusdcpair",
|
|
138
|
+
"TESTCOINUSDCPAIR",
|
|
139
|
+
users.platformReferrer,
|
|
140
|
+
USDC_ADDRESS,
|
|
141
|
+
USDC_TICK_LOWER,
|
|
142
|
+
0
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
pool = IUniswapV3Pool(coin.poolAddress());
|
|
147
|
+
vm.label(address(coin), "COIN");
|
|
148
|
+
vm.label(address(pool), "POOL");
|
|
149
|
+
|
|
150
|
+
assertEq(coin.currency(), USDC_ADDRESS, "currency");
|
|
151
|
+
assertEq(coin.payoutRecipient(), users.creator, "payoutRecipient");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function test_deploy_with_usdc_revert_payout_recipient_zero() public {
|
|
155
|
+
address[] memory owners = new address[](1);
|
|
156
|
+
owners[0] = users.creator;
|
|
157
|
+
|
|
158
|
+
vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
|
|
159
|
+
factory.deploy(
|
|
160
|
+
address(0),
|
|
161
|
+
owners,
|
|
162
|
+
"https://testcoinusdcpair.com",
|
|
163
|
+
"Testcoinusdcpair",
|
|
164
|
+
"TESTCOINUSDCPAIR",
|
|
165
|
+
users.platformReferrer,
|
|
166
|
+
USDC_ADDRESS,
|
|
167
|
+
USDC_TICK_LOWER,
|
|
168
|
+
0
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function test_deploy_with_usdc_revert_one_owner_required() public {
|
|
173
|
+
address[] memory owners = new address[](0);
|
|
174
|
+
|
|
175
|
+
vm.expectRevert(abi.encodeWithSelector(MultiOwnable.OneOwnerRequired.selector));
|
|
176
|
+
factory.deploy(
|
|
177
|
+
users.creator,
|
|
178
|
+
owners,
|
|
179
|
+
"https://testcoinusdcpair.com",
|
|
180
|
+
"Testcoinusdcpair",
|
|
181
|
+
"TESTCOINUSDCPAIR",
|
|
182
|
+
users.platformReferrer,
|
|
183
|
+
USDC_ADDRESS,
|
|
184
|
+
USDC_TICK_LOWER,
|
|
185
|
+
0
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function test_deploy_with_usdc_platform_referrer_zero() public {
|
|
190
|
+
address[] memory owners = new address[](1);
|
|
191
|
+
owners[0] = users.creator;
|
|
192
|
+
|
|
193
|
+
coin = Coin(
|
|
194
|
+
payable(
|
|
195
|
+
factory.deploy(
|
|
196
|
+
users.creator,
|
|
197
|
+
owners,
|
|
198
|
+
"https://testcoinusdcpair.com",
|
|
199
|
+
"Testcoinusdcpair",
|
|
200
|
+
"TESTCOINUSDCPAIR",
|
|
201
|
+
address(0),
|
|
202
|
+
USDC_ADDRESS,
|
|
203
|
+
USDC_TICK_LOWER,
|
|
204
|
+
0
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
assertEq(coin.platformReferrer(), coin.protocolRewardRecipient(), "platformReferrer");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function test_revert_deploy_with_invalid_currency_tick() public {
|
|
213
|
+
address[] memory owners = new address[](1);
|
|
214
|
+
owners[0] = users.creator;
|
|
215
|
+
|
|
216
|
+
vm.expectRevert(abi.encodeWithSelector(ICoin.InvalidWethLowerTick.selector));
|
|
217
|
+
coin = Coin(
|
|
218
|
+
payable(
|
|
219
|
+
factory.deploy(
|
|
220
|
+
users.creator,
|
|
221
|
+
owners,
|
|
222
|
+
"https://testcoinusdcpair.com",
|
|
223
|
+
"Testcoinusdcpair",
|
|
224
|
+
"TESTCOINUSDCPAIR",
|
|
225
|
+
users.platformReferrer,
|
|
226
|
+
address(0),
|
|
227
|
+
USDC_TICK_LOWER,
|
|
228
|
+
0
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function test_deploy_with_usdc_revert_invalid_eth_transfer() public {
|
|
235
|
+
address[] memory owners = new address[](1);
|
|
236
|
+
owners[0] = users.creator;
|
|
237
|
+
|
|
238
|
+
dealUSDC(users.creator, 1);
|
|
239
|
+
|
|
240
|
+
vm.deal(users.creator, 1e6);
|
|
241
|
+
|
|
242
|
+
vm.prank(users.creator);
|
|
243
|
+
vm.expectRevert(abi.encodeWithSelector(ICoin.EthTransferInvalid.selector));
|
|
244
|
+
coin = Coin(
|
|
245
|
+
payable(
|
|
246
|
+
factory.deploy{value: 1e6}(
|
|
247
|
+
users.creator,
|
|
248
|
+
owners,
|
|
249
|
+
"https://testcoinusdcpair.com",
|
|
250
|
+
"Testcoinusdcpair",
|
|
251
|
+
"TESTCOINUSDCPAIR",
|
|
252
|
+
users.platformReferrer,
|
|
253
|
+
USDC_ADDRESS,
|
|
254
|
+
USDC_TICK_LOWER,
|
|
255
|
+
0
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function test_deploy_without_initial_order() public {
|
|
262
|
+
address[] memory owners = new address[](1);
|
|
263
|
+
owners[0] = users.creator;
|
|
264
|
+
|
|
265
|
+
coin = Coin(
|
|
266
|
+
payable(
|
|
267
|
+
factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(weth), LP_TICK_LOWER_WETH, 0)
|
|
268
|
+
)
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
assertEq(coin.balanceOf(users.creator), 10_000_000e18, "Should only have initial creator allocation");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function test_upgrade() public {
|
|
275
|
+
ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinImpl));
|
|
276
|
+
|
|
277
|
+
vm.prank(users.factoryOwner);
|
|
278
|
+
factory.upgradeToAndCall(address(newImpl), "");
|
|
279
|
+
|
|
280
|
+
assertEq(factory.implementation(), address(newImpl), "implementation");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function test_revert_invalid_upgrade_impl() public {
|
|
284
|
+
address newImpl = address(this);
|
|
285
|
+
|
|
286
|
+
vm.prank(users.factoryOwner);
|
|
287
|
+
vm.expectRevert(abi.encodeWithSelector(ERC1967Utils.ERC1967InvalidImplementation.selector, address(newImpl)));
|
|
288
|
+
factory.upgradeToAndCall(address(newImpl), "");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function test_revert_invalid_owner() public {
|
|
292
|
+
ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinImpl));
|
|
293
|
+
|
|
294
|
+
vm.prank(users.creator);
|
|
295
|
+
vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, users.creator));
|
|
296
|
+
factory.upgradeToAndCall(address(newImpl), "");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import "./utils/BaseTest.sol";
|
|
5
|
+
|
|
6
|
+
contract MultiOwnableTest is BaseTest {
|
|
7
|
+
function setUp() public override {
|
|
8
|
+
super.setUp();
|
|
9
|
+
|
|
10
|
+
_deployCoin();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function test_initial_owners() public view {
|
|
14
|
+
assertEq(coin.owners().length, 1);
|
|
15
|
+
assertEq(coin.owners()[0], users.creator);
|
|
16
|
+
assertEq(coin.isOwner(users.creator), true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function test_add_owners() public {
|
|
20
|
+
address[] memory newOwners = new address[](2);
|
|
21
|
+
newOwners[0] = makeAddr("NewOwner1");
|
|
22
|
+
newOwners[1] = makeAddr("NewOwner2");
|
|
23
|
+
|
|
24
|
+
vm.prank(users.creator);
|
|
25
|
+
coin.addOwners(newOwners);
|
|
26
|
+
|
|
27
|
+
assertEq(coin.owners().length, 3);
|
|
28
|
+
assertEq(coin.isOwner(users.creator), true);
|
|
29
|
+
assertEq(coin.isOwner(newOwners[0]), true);
|
|
30
|
+
assertEq(coin.isOwner(newOwners[1]), true);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function test_add_owner() public {
|
|
34
|
+
vm.prank(users.creator);
|
|
35
|
+
coin.addOwner(address(this));
|
|
36
|
+
|
|
37
|
+
assertEq(coin.owners().length, 2);
|
|
38
|
+
assertEq(coin.isOwner(users.creator), true);
|
|
39
|
+
assertEq(coin.isOwner(address(this)), true);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function test_remove_owners() public {
|
|
43
|
+
address[] memory newOwners = new address[](2);
|
|
44
|
+
newOwners[0] = makeAddr("NewOwner1");
|
|
45
|
+
newOwners[1] = makeAddr("NewOwner2");
|
|
46
|
+
|
|
47
|
+
vm.prank(users.creator);
|
|
48
|
+
coin.addOwners(newOwners);
|
|
49
|
+
|
|
50
|
+
vm.prank(users.creator);
|
|
51
|
+
coin.removeOwners(newOwners);
|
|
52
|
+
|
|
53
|
+
assertEq(coin.owners().length, 1);
|
|
54
|
+
assertEq(coin.isOwner(users.creator), true);
|
|
55
|
+
assertEq(coin.isOwner(newOwners[0]), false);
|
|
56
|
+
assertEq(coin.isOwner(newOwners[1]), false);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function test_remove_owner() public {
|
|
60
|
+
vm.prank(users.creator);
|
|
61
|
+
coin.addOwner(address(this));
|
|
62
|
+
|
|
63
|
+
vm.prank(address(this));
|
|
64
|
+
coin.removeOwner(users.creator);
|
|
65
|
+
|
|
66
|
+
assertEq(coin.owners().length, 1);
|
|
67
|
+
assertEq(coin.isOwner(users.creator), false);
|
|
68
|
+
assertEq(coin.isOwner(address(this)), true);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function test_revert_only_owner_can_add() public {
|
|
72
|
+
vm.expectRevert(abi.encodeWithSelector(MultiOwnable.OnlyOwner.selector));
|
|
73
|
+
coin.addOwner(address(this));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function test_revert_only_owner_can_remove() public {
|
|
77
|
+
vm.expectRevert(abi.encodeWithSelector(MultiOwnable.OnlyOwner.selector));
|
|
78
|
+
|
|
79
|
+
vm.prank(address(this));
|
|
80
|
+
coin.removeOwner(users.creator);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function test_revert_cannot_remove_not_owner() public {
|
|
84
|
+
vm.expectRevert(abi.encodeWithSelector(MultiOwnable.NotOwner.selector));
|
|
85
|
+
vm.prank(users.creator);
|
|
86
|
+
coin.removeOwner(address(this));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function test_revert_last_owner_must_revoke() public {
|
|
90
|
+
address newOwner = makeAddr("NewOwner1");
|
|
91
|
+
|
|
92
|
+
vm.prank(users.creator);
|
|
93
|
+
coin.addOwner(newOwner);
|
|
94
|
+
|
|
95
|
+
vm.prank(newOwner);
|
|
96
|
+
coin.removeOwner(users.creator);
|
|
97
|
+
|
|
98
|
+
vm.prank(newOwner);
|
|
99
|
+
vm.expectRevert(abi.encodeWithSelector(MultiOwnable.UseRevokeOwnershipToRemoveSelf.selector));
|
|
100
|
+
coin.removeOwner(newOwner);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function test_revoke_ownership() public {
|
|
104
|
+
vm.prank(users.creator);
|
|
105
|
+
coin.revokeOwnership();
|
|
106
|
+
|
|
107
|
+
assertEq(coin.owners().length, 0);
|
|
108
|
+
assertEq(coin.isOwner(users.creator), false);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function test_revert_add_owners_with_zero_address() public {
|
|
112
|
+
address newOwner = makeAddr("NewOwner1");
|
|
113
|
+
address[] memory newOwners = new address[](2);
|
|
114
|
+
newOwners[0] = newOwner;
|
|
115
|
+
newOwners[1] = address(0);
|
|
116
|
+
|
|
117
|
+
vm.prank(users.creator);
|
|
118
|
+
vm.expectRevert(MultiOwnable.OwnerCannotBeAddressZero.selector);
|
|
119
|
+
coin.addOwners(newOwners);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function test_revert_add_owners_with_duplicate() public {
|
|
123
|
+
address newOwner = makeAddr("NewOwner1");
|
|
124
|
+
address[] memory newOwners = new address[](2);
|
|
125
|
+
newOwners[0] = newOwner;
|
|
126
|
+
newOwners[1] = newOwner;
|
|
127
|
+
|
|
128
|
+
vm.prank(users.creator);
|
|
129
|
+
coin.addOwner(newOwner);
|
|
130
|
+
|
|
131
|
+
vm.expectRevert(MultiOwnable.AlreadyOwner.selector);
|
|
132
|
+
vm.prank(users.creator);
|
|
133
|
+
coin.addOwners(newOwners);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function test_revert_init_with_zero_owners() public {
|
|
137
|
+
address[] memory emptyOwners = new address[](0);
|
|
138
|
+
vm.expectRevert(MultiOwnable.OneOwnerRequired.selector);
|
|
139
|
+
factory.deploy(users.creator, emptyOwners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(0), 0, 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function test_revert_init_with_zero_address() public {
|
|
143
|
+
address[] memory owners = new address[](1);
|
|
144
|
+
owners[0] = address(0);
|
|
145
|
+
vm.expectRevert(MultiOwnable.OwnerCannotBeAddressZero.selector);
|
|
146
|
+
factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(0), 0, 0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function test_revert_init_with_duplicate_owner() public {
|
|
150
|
+
address[] memory owners = new address[](2);
|
|
151
|
+
owners[0] = users.creator;
|
|
152
|
+
owners[1] = users.creator;
|
|
153
|
+
vm.expectRevert(MultiOwnable.AlreadyOwner.selector);
|
|
154
|
+
factory.deploy(users.creator, owners, "https://test.com", "Test Token", "TEST", users.platformReferrer, address(0), 0, 0);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import "forge-std/Test.sol";
|
|
5
|
+
|
|
6
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
7
|
+
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
8
|
+
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
|
|
9
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
10
|
+
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
11
|
+
|
|
12
|
+
import {ZoraFactoryImpl} from "../../src/ZoraFactoryImpl.sol";
|
|
13
|
+
import {ZoraFactory} from "../../src/proxy/ZoraFactory.sol";
|
|
14
|
+
import {Coin} from "../../src/Coin.sol";
|
|
15
|
+
import {CoinConstants} from "../../src/utils/CoinConstants.sol";
|
|
16
|
+
import {MultiOwnable} from "../../src/utils/MultiOwnable.sol";
|
|
17
|
+
import {ICoin} from "../../src/interfaces/ICoin.sol";
|
|
18
|
+
import {IERC7572} from "../../src/interfaces/IERC7572.sol";
|
|
19
|
+
import {IWETH} from "../../src/interfaces/IWETH.sol";
|
|
20
|
+
import {INonfungiblePositionManager} from "../../src/interfaces/INonfungiblePositionManager.sol";
|
|
21
|
+
import {ISwapRouter} from "../../src/interfaces/ISwapRouter.sol";
|
|
22
|
+
import {IUniswapV3Pool} from "../../src/interfaces/IUniswapV3Pool.sol";
|
|
23
|
+
import {IProtocolRewards} from "../../src/interfaces/IProtocolRewards.sol";
|
|
24
|
+
import {ProtocolRewards} from "../utils/ProtocolRewards.sol";
|
|
25
|
+
|
|
26
|
+
contract BaseTest is Test, CoinConstants {
|
|
27
|
+
using stdStorage for StdStorage;
|
|
28
|
+
|
|
29
|
+
address internal constant PROTOCOL_REWARDS = 0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B;
|
|
30
|
+
address internal constant WETH_ADDRESS = 0x4200000000000000000000000000000000000006;
|
|
31
|
+
address internal constant NONFUNGIBLE_POSITION_MANAGER = 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1;
|
|
32
|
+
address internal constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
|
|
33
|
+
address internal constant USDC_ADDRESS = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
|
|
34
|
+
int24 internal constant USDC_TICK_LOWER = 57200;
|
|
35
|
+
|
|
36
|
+
struct Users {
|
|
37
|
+
address factoryOwner;
|
|
38
|
+
address feeRecipient;
|
|
39
|
+
address creator;
|
|
40
|
+
address platformReferrer;
|
|
41
|
+
address buyer;
|
|
42
|
+
address seller;
|
|
43
|
+
address coinRecipient;
|
|
44
|
+
address tradeReferrer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
uint256 internal forkId;
|
|
48
|
+
IERC20Metadata internal usdc;
|
|
49
|
+
IWETH internal weth;
|
|
50
|
+
ProtocolRewards internal protocolRewards;
|
|
51
|
+
INonfungiblePositionManager internal nonfungiblePositionManager;
|
|
52
|
+
ISwapRouter internal swapRouter;
|
|
53
|
+
Users internal users;
|
|
54
|
+
|
|
55
|
+
Coin internal coinImpl;
|
|
56
|
+
ZoraFactoryImpl internal factoryImpl;
|
|
57
|
+
ZoraFactoryImpl internal factory;
|
|
58
|
+
Coin internal coin;
|
|
59
|
+
IUniswapV3Pool internal pool;
|
|
60
|
+
|
|
61
|
+
function setUp() public virtual {
|
|
62
|
+
forkId = vm.createSelectFork("https://mainnet.base.org", 21179722);
|
|
63
|
+
|
|
64
|
+
weth = IWETH(WETH_ADDRESS);
|
|
65
|
+
usdc = IERC20Metadata(USDC_ADDRESS);
|
|
66
|
+
nonfungiblePositionManager = INonfungiblePositionManager(NONFUNGIBLE_POSITION_MANAGER);
|
|
67
|
+
swapRouter = ISwapRouter(SWAP_ROUTER);
|
|
68
|
+
protocolRewards = new ProtocolRewards();
|
|
69
|
+
|
|
70
|
+
users = Users({
|
|
71
|
+
factoryOwner: makeAddr("factoryOwner"),
|
|
72
|
+
feeRecipient: makeAddr("feeRecipient"),
|
|
73
|
+
creator: makeAddr("creator"),
|
|
74
|
+
platformReferrer: makeAddr("platformReferrer"),
|
|
75
|
+
buyer: makeAddr("buyer"),
|
|
76
|
+
seller: makeAddr("seller"),
|
|
77
|
+
coinRecipient: makeAddr("coinRecipient"),
|
|
78
|
+
tradeReferrer: makeAddr("tradeReferrer")
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
coinImpl = new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER);
|
|
82
|
+
factoryImpl = new ZoraFactoryImpl(address(coinImpl));
|
|
83
|
+
factory = ZoraFactoryImpl(
|
|
84
|
+
address(new ZoraFactory(address(factoryImpl), abi.encodeWithSelector(ZoraFactoryImpl.initialize.selector, users.factoryOwner)))
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
vm.label(address(factory), "ZORA_FACTORY");
|
|
88
|
+
vm.label(address(protocolRewards), "PROTOCOL_REWARDS");
|
|
89
|
+
vm.label(address(nonfungiblePositionManager), "NONFUNGIBLE_POSITION_MANAGER");
|
|
90
|
+
vm.label(address(swapRouter), "SWAP_ROUTER");
|
|
91
|
+
vm.label(address(weth), "WETH");
|
|
92
|
+
vm.label(address(usdc), "USDC");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
struct TradeRewards {
|
|
96
|
+
uint256 creator;
|
|
97
|
+
uint256 platformReferrer;
|
|
98
|
+
uint256 tradeReferrer;
|
|
99
|
+
uint256 protocol;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
struct MarketRewards {
|
|
103
|
+
uint256 creator;
|
|
104
|
+
uint256 platformReferrer;
|
|
105
|
+
uint256 protocol;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function _deployCoin() internal {
|
|
109
|
+
address[] memory owners = new address[](1);
|
|
110
|
+
owners[0] = users.creator;
|
|
111
|
+
|
|
112
|
+
vm.prank(users.creator);
|
|
113
|
+
coin = Coin(
|
|
114
|
+
payable(factory.deploy(users.creator, owners, "https://test.com", "Testcoin", "TEST", users.platformReferrer, address(weth), LP_TICK_LOWER_WETH, 0))
|
|
115
|
+
);
|
|
116
|
+
pool = IUniswapV3Pool(coin.poolAddress());
|
|
117
|
+
|
|
118
|
+
vm.label(address(coin), "COIN");
|
|
119
|
+
vm.label(address(pool), "POOL");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function _deployCoinUSDCPair() internal {
|
|
123
|
+
address[] memory owners = new address[](1);
|
|
124
|
+
owners[0] = users.creator;
|
|
125
|
+
|
|
126
|
+
vm.prank(users.creator);
|
|
127
|
+
coin = Coin(
|
|
128
|
+
payable(
|
|
129
|
+
factory.deploy(
|
|
130
|
+
users.creator,
|
|
131
|
+
owners,
|
|
132
|
+
"https://testusdccoin.com",
|
|
133
|
+
"Testusdccoin",
|
|
134
|
+
"TESTUSDCCOIN",
|
|
135
|
+
users.platformReferrer,
|
|
136
|
+
USDC_ADDRESS,
|
|
137
|
+
USDC_TICK_LOWER,
|
|
138
|
+
0
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
pool = IUniswapV3Pool(coin.poolAddress());
|
|
143
|
+
|
|
144
|
+
vm.label(address(coin), "COIN");
|
|
145
|
+
vm.label(address(pool), "POOL");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function _calculateTradeRewards(uint256 ethAmount) internal pure returns (TradeRewards memory) {
|
|
149
|
+
return
|
|
150
|
+
TradeRewards({
|
|
151
|
+
creator: (ethAmount * 5000) / 10_000,
|
|
152
|
+
platformReferrer: (ethAmount * 1500) / 10_000,
|
|
153
|
+
tradeReferrer: (ethAmount * 1500) / 10_000,
|
|
154
|
+
protocol: (ethAmount * 2000) / 10_000
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function _calculateExpectedFee(uint256 ethAmount) internal pure returns (uint256) {
|
|
159
|
+
uint256 feeBps = 100; // 1%
|
|
160
|
+
return (ethAmount * feeBps) / 10_000;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function _calculateMarketRewards(uint256 ethAmount) internal pure returns (MarketRewards memory) {
|
|
164
|
+
uint256 creator = (ethAmount * 5000) / 10_000;
|
|
165
|
+
uint256 platformReferrer = (ethAmount * 2500) / 10_000;
|
|
166
|
+
uint256 protocol = ethAmount - creator - platformReferrer;
|
|
167
|
+
|
|
168
|
+
return MarketRewards({creator: creator, platformReferrer: platformReferrer, protocol: protocol});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function dealUSDC(address to, uint256 numUSDC) internal returns (uint256) {
|
|
172
|
+
uint256 amount = numUSDC * 1e6;
|
|
173
|
+
deal(address(usdc), to, amount);
|
|
174
|
+
return amount;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {}
|
|
178
|
+
}
|