@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.
Files changed (69) hide show
  1. package/.turbo/turbo-build.log +106 -110
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +30 -109
  4. package/abis/BaseCoin.json +442 -0
  5. package/abis/CoinTest.json +3 -246
  6. package/abis/FactoryTest.json +5 -137
  7. package/abis/HooksTest.json +0 -26
  8. package/abis/ICoin.json +378 -0
  9. package/abis/ICoinV3.json +378 -0
  10. package/abis/IZoraFactory.json +0 -18
  11. package/abis/LiquidityMigrationTest.json +101 -0
  12. package/abis/MockBadFactory.json +15 -0
  13. package/abis/ZoraFactoryImpl.json +1 -67
  14. package/dist/index.cjs +236 -265
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +235 -264
  17. package/dist/index.js.map +1 -1
  18. package/dist/wagmiGenerated.d.ts +389 -493
  19. package/dist/wagmiGenerated.d.ts.map +1 -1
  20. package/package/wagmiGenerated.ts +240 -269
  21. package/package.json +3 -3
  22. package/script/DeployPostDeploymentHooks.s.sol +2 -2
  23. package/script/TestBackingCoinSwap.s.sol +8 -8
  24. package/script/TestV4Swap.s.sol +8 -8
  25. package/script/UpgradeFactoryImpl.s.sol +0 -1
  26. package/src/BaseCoin.sol +109 -6
  27. package/src/ContentCoin.sol +4 -4
  28. package/src/CreatorCoin.sol +5 -5
  29. package/src/ZoraFactoryImpl.sol +10 -93
  30. package/src/deployment/CoinsDeployerBase.sol +10 -27
  31. package/src/hooks/BaseZoraV4CoinHook.sol +5 -5
  32. package/src/hooks/deployment/BuySupplyWithSwapRouterHook.sol +4 -5
  33. package/src/interfaces/ICoin.sol +67 -1
  34. package/src/interfaces/ICreatorCoin.sol +2 -2
  35. package/src/interfaces/IZoraFactory.sol +0 -5
  36. package/src/libs/CoinConfigurationVersions.sol +1 -39
  37. package/src/libs/CoinRewardsV4.sol +2 -2
  38. package/src/libs/CoinSetup.sol +1 -4
  39. package/src/libs/UniV4SwapHelper.sol +1 -1
  40. package/src/libs/UniV4SwapToCurrency.sol +2 -2
  41. package/src/libs/V4Liquidity.sol +1 -1
  42. package/src/version/ContractVersionBase.sol +1 -1
  43. package/test/Coin.t.sol +112 -535
  44. package/test/CoinUniV4.t.sol +5 -5
  45. package/test/DeploymentHooks.t.sol +5 -102
  46. package/test/Factory.t.sol +23 -306
  47. package/test/LiquidityMigration.t.sol +160 -2
  48. package/test/MultiOwnable.t.sol +36 -36
  49. package/test/Upgrades.t.sol +16 -35
  50. package/test/utils/BaseTest.sol +16 -69
  51. package/test/utils/FeeEstimatorHook.sol +3 -3
  52. package/wagmi.config.ts +1 -1
  53. package/abis/BaseCoinV4.json +0 -1840
  54. package/abis/Coin.json +0 -1912
  55. package/abis/DopplerUniswapV3Test.json +0 -800
  56. package/abis/ICoinV4.json +0 -1048
  57. package/abis/Simulate.json +0 -29
  58. package/abis/UniV3BuySell.json +0 -12
  59. package/abis/UniV3Errors.json +0 -32
  60. package/script/Simulate.s.sol +0 -59
  61. package/src/BaseCoinV4.sol +0 -143
  62. package/src/Coin.sol +0 -236
  63. package/src/interfaces/ICoinV4.sol +0 -74
  64. package/src/libs/CoinDopplerUniV3.sol +0 -50
  65. package/src/libs/CoinRewards.sol +0 -201
  66. package/src/libs/CoinSetupV3.sol +0 -50
  67. package/src/libs/UniV3BuySell.sol +0 -231
  68. package/src/libs/UniV3Errors.sol +0 -11
  69. package/test/CoinDopplerUniV3.t.sol +0 -310
@@ -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 {ICoinV4, IHasSwapPath, PathKey} from "../src/interfaces/ICoinV4.sol";
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 {
@@ -705,9 +705,9 @@ contract CoinUniV4Test is BaseTest {
705
705
 
706
706
  function test_getSwapPath_whenBackingCurrencyProvidesPath() public {
707
707
  address zora = address(mockERC20A);
708
- ICoinV4 backingCoin = _deployV4Coin(zora);
708
+ ICoin backingCoin = _deployV4Coin(zora);
709
709
  // now create a final coin paired with the backing coin
710
- ICoinV4 contentCoin = _deployV4Coin(address(backingCoin));
710
+ ICoin contentCoin = _deployV4Coin(address(backingCoin));
711
711
 
712
712
  PathKey[] memory path = contentCoin.getPayoutSwapPath(IDeployedCoinVersionLookup(address(factory))).path;
713
713
 
@@ -746,9 +746,9 @@ contract CoinUniV4Test is BaseTest {
746
746
  mockERC20A.mint(address(poolManager), 10000000000000000 ether);
747
747
 
748
748
  // backing coin is a mock coin that is paired with zora
749
- ICoinV4 backingCoin = _deployV4Coin(zora);
749
+ ICoin backingCoin = _deployV4Coin(zora);
750
750
  // now create a final coin paired with the backing coin
751
- ICoinV4 contentCoin = _deployV4Coin(address(backingCoin));
751
+ ICoin contentCoin = _deployV4Coin(address(backingCoin));
752
752
 
753
753
  vm.assume(amountIn > 0.000000000001 ether);
754
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
- coin = Coin(payable(coinAddress));
101
+ coinV4 = ContentCoin(payable(coinAddress));
154
102
 
155
103
  (uint256 amountCurrency, uint256 coinsPurchased) = abi.decode(hookDataOut, (uint256, uint256));
156
104
 
157
- assertEq(coin.currency(), zora, "currency");
105
+ assertEq(coinV4.currency(), zora, "currency");
158
106
  assertGt(amountCurrency, 0, "amountCurrency > 0");
159
107
  assertGt(coinsPurchased, 0, "coinsPurchased > 0");
160
- assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased, "balanceOf creator");
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
 
@@ -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
 
@@ -44,7 +39,7 @@ contract FactoryTest is BaseTest {
44
39
  assertEq(ZoraFactoryImpl(address(factory)).pendingOwner(), address(0));
45
40
 
46
41
  address newFactoryImpl = address(
47
- new ZoraFactoryImpl(address(coinV3Impl), address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook))
42
+ new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook))
48
43
  );
49
44
 
50
45
  // Upgrade to current / new impl
@@ -70,296 +65,8 @@ contract FactoryTest is BaseTest {
70
65
  assertEq(ZoraFactoryImpl(address(factory)).owner(), newOwner);
71
66
  }
72
67
 
73
- function test_deploy_no_eth() public {
74
- address[] memory owners = new address[](1);
75
- owners[0] = users.creator;
76
-
77
- (address coinAddress, ) = factory.deploy(
78
- users.creator,
79
- owners,
80
- "https://test2.com",
81
- "Test2 Token",
82
- "TEST2",
83
- _generatePoolConfig(address(weth)),
84
- users.platformReferrer,
85
- 0
86
- );
87
- coin = Coin(payable(coinAddress));
88
- pool = IUniswapV3Pool(coin.poolAddress());
89
- vm.label(address(coin), "COIN");
90
- vm.label(address(pool), "POOL");
91
-
92
- uint160 sqrtPriceX96 = pool.slot0().sqrtPriceX96;
93
- uint256 poolCoinBalance = coin.balanceOf(address(pool));
94
- uint256 poolEthBalance = weth.balanceOf(address(pool));
95
-
96
- console.log("POOL_TOKEN_0: ", pool.token0());
97
- console.log("POOL_TOKEN_1: ", pool.token1());
98
- console.log("POOL_SQRT_PRICE_X96: ", sqrtPriceX96);
99
- console.log("");
100
- console.log("POOL_COIN_BALANCE: ", poolCoinBalance);
101
- console.log("POOL_ETH_BALANCE: ", poolEthBalance);
102
- console.log("");
103
-
104
- assertEq(coin.payoutRecipient(), users.creator, "payoutRecipient");
105
- assertEq(coin.protocolRewardRecipient(), users.feeRecipient, "protocolRewardRecipient");
106
- assertEq(coin.platformReferrer(), users.platformReferrer, "platformReferrer");
107
- assertEq(coin.tokenURI(), "https://test2.com", "tokenURI");
108
- assertEq(coin.name(), "Test2 Token", "name");
109
- assertEq(coin.symbol(), "TEST2", "symbol");
110
- assertEq(coin.currency(), address(weth), "currency");
111
- assertEq(coin.totalSupply(), 1_000_000_000e18, "totalSupply");
112
- assertEq(coin.balanceOf(users.creator), 10_000_000e18, "balanceOf creator");
113
- assertGt(coin.balanceOf(coin.poolAddress()), 989_999_999e18, "balanceOf pool");
114
- }
115
-
116
- function test_deploy_with_eth(uint256 initialOrderSize) public {
117
- vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
118
- vm.assume(initialOrderSize < 10 ether);
119
-
120
- address[] memory owners = new address[](1);
121
- owners[0] = users.creator;
122
-
123
- vm.deal(users.creator, initialOrderSize);
124
- vm.prank(users.creator);
125
- (address coinAddress, ) = factory.deploy{value: initialOrderSize}(
126
- users.creator,
127
- owners,
128
- "https://test2.com",
129
- "Test2 Token",
130
- "TEST2",
131
- _generatePoolConfig(address(weth)),
132
- users.platformReferrer,
133
- initialOrderSize
134
- );
135
- coin = Coin(payable(coinAddress));
136
- pool = IUniswapV3Pool(coin.poolAddress());
137
- vm.label(address(coin), "COIN");
138
- vm.label(address(pool), "POOL");
139
-
140
- uint160 sqrtPriceX96 = pool.slot0().sqrtPriceX96;
141
- uint256 poolCoinBalance = coin.balanceOf(address(pool));
142
- uint256 poolEthBalance = weth.balanceOf(address(pool));
143
-
144
- console.log("POOL_TOKEN_0: ", pool.token0());
145
- console.log("POOL_TOKEN_1: ", pool.token1());
146
- console.log("POOL_SQRT_PRICE_X96: ", sqrtPriceX96);
147
- console.log("");
148
- console.log("POOL_COIN_BALANCE: ", poolCoinBalance);
149
- console.log("POOL_ETH_BALANCE: ", poolEthBalance);
150
- console.log("");
151
- console.log("BUYER_COIN_BALANCE ", coin.balanceOf(users.creator) - 10_000_000e18);
152
- }
153
-
154
- function test_deploy_with_weth(uint256 initialOrderSize) public {
155
- vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
156
- vm.assume(initialOrderSize < 10 ether);
157
-
158
- address[] memory owners = new address[](1);
159
- owners[0] = users.creator;
160
-
161
- vm.deal(users.creator, initialOrderSize);
162
-
163
- vm.startPrank(users.creator);
164
- weth.deposit{value: initialOrderSize}();
165
-
166
- weth.approve(address(factory), type(uint256).max);
167
-
168
- // Expect this to revert because WETH needs to be sent with msg.value.
169
- vm.expectRevert();
170
- factory.deploy(
171
- users.creator,
172
- owners,
173
- "https://test2.com",
174
- "Test2 Token",
175
- "TEST2",
176
- _generatePoolConfig(address(weth)),
177
- users.platformReferrer,
178
- initialOrderSize
179
- );
180
- }
181
-
182
- function test_deploy_with_one_eth() public {
183
- address[] memory owners = new address[](1);
184
- owners[0] = users.creator;
185
-
186
- uint256 orderSize = 1 ether;
187
- vm.deal(users.creator, orderSize);
188
-
189
- (address coinAddress, ) = factory.deploy{value: orderSize}(
190
- users.creator,
191
- owners,
192
- "https://test2.com",
193
- "Test2 Token",
194
- "TEST2",
195
- _generatePoolConfig(address(weth)),
196
- users.platformReferrer,
197
- orderSize
198
- );
199
- coin = Coin(payable(coinAddress));
200
- pool = IUniswapV3Pool(coin.poolAddress());
201
- vm.label(address(coin), "COIN");
202
- vm.label(address(pool), "POOL");
203
- }
204
-
205
- function test_deploy_with_usdc() public {
206
- address[] memory owners = new address[](1);
207
- owners[0] = users.creator;
208
-
209
- (address coinAddress, ) = factory.deploy(
210
- users.creator,
211
- owners,
212
- "https://testcoinusdcpair.com",
213
- "Testcoinusdcpair",
214
- "TESTCOINUSDCPAIR",
215
- _generatePoolConfig(USDC_ADDRESS),
216
- users.platformReferrer,
217
- 0
218
- );
219
- coin = Coin(payable(coinAddress));
220
- pool = IUniswapV3Pool(coin.poolAddress());
221
- vm.label(address(coin), "COIN");
222
- vm.label(address(pool), "POOL");
223
-
224
- assertEq(coin.currency(), USDC_ADDRESS, "currency");
225
- assertEq(coin.payoutRecipient(), users.creator, "payoutRecipient");
226
- }
227
-
228
- function test_deploy_with_usdc_order() public {
229
- address[] memory owners = new address[](1);
230
- owners[0] = users.creator;
231
-
232
- uint256 orderSize = dealUSDC(users.creator, 100);
233
-
234
- vm.prank(users.creator);
235
- usdc.approve(address(factory), orderSize);
236
-
237
- assertEq(usdc.balanceOf(users.creator), orderSize);
238
- assertEq(usdc.allowance(users.creator, address(factory)), orderSize);
239
-
240
- vm.prank(users.creator);
241
- (address coinAddress, uint256 coinsPurchased) = factory.deploy(
242
- users.creator,
243
- owners,
244
- "https://testcoinusdcpair.com",
245
- "Testcoinusdcpair",
246
- "TESTCOINUSDCPAIR",
247
- _generatePoolConfig(USDC_ADDRESS),
248
- users.platformReferrer,
249
- orderSize
250
- );
251
- coin = Coin(payable(coinAddress));
252
- pool = IUniswapV3Pool(coin.poolAddress());
253
- vm.label(address(coin), "COIN");
254
- vm.label(address(pool), "POOL");
255
-
256
- assertEq(coin.currency(), USDC_ADDRESS, "currency");
257
- assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased);
258
- }
259
-
260
- function test_deploy_with_usdc_revert_payout_recipient_zero() public {
261
- address[] memory owners = new address[](1);
262
- owners[0] = users.creator;
263
-
264
- vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
265
- factory.deploy(
266
- address(0),
267
- owners,
268
- "https://testcoinusdcpair.com",
269
- "Testcoinusdcpair",
270
- "TESTCOINUSDCPAIR",
271
- _generatePoolConfig(USDC_ADDRESS),
272
- users.platformReferrer,
273
- 0
274
- );
275
- }
276
-
277
- function test_deploy_with_usdc_revert_one_owner_required() public {
278
- address[] memory owners = new address[](0);
279
-
280
- vm.expectRevert(abi.encodeWithSelector(MultiOwnable.OneOwnerRequired.selector));
281
- factory.deploy(
282
- users.creator,
283
- owners,
284
- "https://testcoinusdcpair.com",
285
- "Testcoinusdcpair",
286
- "TESTCOINUSDCPAIR",
287
- _generatePoolConfig(USDC_ADDRESS),
288
- users.platformReferrer,
289
- 0
290
- );
291
- }
292
-
293
- function test_deploy_with_usdc_platform_referrer_zero() public {
294
- address[] memory owners = new address[](1);
295
- owners[0] = users.creator;
296
-
297
- (address coinAddress, ) = factory.deploy(
298
- users.creator,
299
- owners,
300
- "https://testcoinusdcpair.com",
301
- "Testcoinusdcpair",
302
- "TESTCOINUSDCPAIR",
303
- _generatePoolConfig(USDC_ADDRESS),
304
- address(0),
305
- 0
306
- );
307
-
308
- coin = Coin(payable(coinAddress));
309
-
310
- assertEq(coin.platformReferrer(), coin.protocolRewardRecipient(), "platformReferrer");
311
- }
312
-
313
- function test_deploy_with_usdc_revert_invalid_eth_transfer() public {
314
- address[] memory owners = new address[](1);
315
- owners[0] = users.creator;
316
-
317
- dealUSDC(users.creator, 1);
318
-
319
- vm.deal(users.creator, 1e6);
320
-
321
- vm.prank(users.creator);
322
- vm.expectRevert(abi.encodeWithSelector(ICoin.EthTransferInvalid.selector));
323
-
324
- factory.deploy{value: 1e6}(
325
- users.creator,
326
- owners,
327
- "https://testcoinusdcpair.com",
328
- "Testcoinusdcpair",
329
- "TESTCOINUSDCPAIR",
330
- _generatePoolConfig(USDC_ADDRESS),
331
- users.platformReferrer,
332
- 0
333
- );
334
- }
335
-
336
- function test_deploy_without_initial_order() public {
337
- address[] memory owners = new address[](1);
338
- owners[0] = users.creator;
339
-
340
- (address coinAddress, ) = factory.deploy(
341
- users.creator,
342
- owners,
343
- "https://test.com",
344
- "Test Token",
345
- "TEST",
346
- _generatePoolConfig(address(weth)),
347
- users.platformReferrer,
348
- 0
349
- );
350
- coin = Coin(payable(coinAddress));
351
-
352
- assertEq(coin.balanceOf(users.creator), 10_000_000e18, "Should only have initial creator allocation");
353
- }
354
-
355
68
  function test_upgrade() public {
356
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(
357
- address(coinV3Impl),
358
- address(coinV4Impl),
359
- address(creatorCoinImpl),
360
- address(contentCoinHook),
361
- address(creatorCoinHook)
362
- );
69
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook));
363
70
 
364
71
  vm.prank(users.factoryOwner);
365
72
  ZoraFactoryImpl(address(factory)).upgradeToAndCall(address(newImpl), "");
@@ -380,13 +87,7 @@ contract FactoryTest is BaseTest {
380
87
  }
381
88
 
382
89
  function test_revert_invalid_owner() public {
383
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(
384
- address(coinV3Impl),
385
- address(coinV4Impl),
386
- address(creatorCoinImpl),
387
- address(contentCoinHook),
388
- address(creatorCoinHook)
389
- );
90
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(contentCoinHook), address(creatorCoinHook));
390
91
 
391
92
  vm.prank(users.creator);
392
93
  vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, users.creator));
@@ -417,7 +118,7 @@ contract FactoryTest is BaseTest {
417
118
  address platformReferrer = users.platformReferrer;
418
119
 
419
120
  bytes memory poolConfig = CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(address(weth));
420
- bytes memory poolConfigForGettingAddress = poolConfigChanged ? CoinConfigurationVersions.defaultDopplerUniV3(address(weth)) : poolConfig;
121
+ bytes memory poolConfigForGettingAddress = poolConfigChanged ? CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(address(0)) : poolConfig;
421
122
 
422
123
  address expectedCoinAddress = factory.coinAddress(msgSender, name, symbol, poolConfigForGettingAddress, platformReferrer, salt);
423
124
 
@@ -453,4 +154,20 @@ contract FactoryTest is BaseTest {
453
154
  assertEq(coinAddress, expectedCoinAddress, "coinAddress should match");
454
155
  }
455
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
+ }
456
173
  }