@zoralabs/coins 0.7.0 → 0.9.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/.env +1 -0
- package/.turbo/turbo-build.log +114 -109
- package/.turbo/turbo-update-contract-version.log +22 -0
- package/CHANGELOG.md +34 -0
- package/abis/BadImpl.json +15 -0
- package/abis/BalanceDeltaLibrary.json +15 -0
- package/abis/BaseCoin.json +1442 -0
- package/abis/BaseCoinDeployHook.json +78 -0
- package/abis/BaseHook.json +897 -0
- package/abis/BaseTest.json +13 -91
- package/abis/BeforeSwapDeltaLibrary.json +15 -0
- package/abis/BuySupplyWithSwapRouterHook.json +126 -0
- package/abis/Coin.json +48 -92
- package/abis/CoinConstants.json +65 -0
- package/abis/CoinTest.json +27 -91
- package/abis/CoinV4.json +1664 -0
- package/abis/CurrencyLibrary.json +25 -0
- package/abis/DeployHooks.json +9 -0
- package/abis/DopplerUniswapV3Test.json +20 -91
- package/abis/FactoryTest.json +13 -91
- package/abis/FakeHookNoInterface.json +21 -0
- package/abis/HookDeployer.json +68 -0
- package/abis/Hooks.json +28 -0
- package/abis/HooksTest.json +651 -0
- package/abis/IAllowanceTransfer.json +486 -0
- package/abis/ICoin.json +25 -1
- package/abis/ICoinDeployHook.json +31 -0
- package/abis/IContractMetadata.json +28 -0
- package/abis/IEIP712.json +15 -0
- package/abis/IEIP712_v4.json +15 -0
- package/abis/IERC20Minimal.json +172 -0
- package/abis/IERC6909Claims.json +288 -0
- package/abis/IERC721Permit_v4.json +88 -0
- package/abis/IExtsload.json +64 -0
- package/abis/IExttload.json +40 -0
- package/abis/IHasAfterCoinDeploy.json +31 -0
- package/abis/IHasContractName.json +15 -0
- package/abis/IHookDeployer.json +42 -0
- package/abis/IHooks.json +789 -0
- package/abis/IImmutableState.json +15 -0
- package/abis/IMulticall_v4.json +21 -0
- package/abis/INotifier.json +187 -0
- package/abis/IPermit2.json +865 -0
- package/abis/IPermit2Forwarder.json +138 -0
- package/abis/IPoolInitializer_v4.json +53 -0
- package/abis/IPoolManager.json +1286 -0
- package/abis/IPositionManager.json +712 -0
- package/abis/IProtocolFees.json +174 -0
- package/abis/ISignatureTransfer.json +394 -0
- package/abis/ISubscriber.json +89 -0
- package/abis/ISwapRouter.json +82 -0
- package/abis/IUnorderedNonce.json +44 -0
- package/abis/IV4Router.json +47 -0
- package/abis/IZoraFactory.json +144 -0
- package/abis/IZoraV4CoinHook.json +83 -0
- package/abis/ImmutableState.json +36 -0
- package/abis/LPFeeLibrary.json +65 -0
- package/abis/MultiOwnableTest.json +13 -91
- package/abis/Simulate.json +0 -91
- package/abis/UniV3BuySell.json +12 -0
- package/abis/UniV3Errors.json +32 -0
- package/abis/UpgradeFactoryImpl.json +9 -0
- package/abis/UpgradesTest.json +604 -0
- package/abis/ZoraFactoryImpl.json +111 -0
- package/abis/ZoraV4CoinHook.json +989 -0
- package/addresses/8453.json +2 -1
- package/addresses/84532.json +4 -3
- package/dist/index.cjs +125 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +120 -56
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +212 -464
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +122 -66
- package/package.json +4 -4
- package/script/CoinsDeployerBase.sol +32 -0
- package/script/DeployHooks.s.sol +22 -0
- package/script/Simulate.s.sol +3 -2
- package/script/UpgradeCoinImpl.sol +2 -2
- package/script/UpgradeFactoryImpl.s.sol +23 -0
- package/src/Coin.sol +35 -342
- package/src/ZoraFactoryImpl.sol +73 -45
- package/src/hooks/BaseCoinDeployHook.sol +62 -0
- package/src/hooks/BuySupplyWithSwapRouterHook.sol +78 -0
- package/src/interfaces/ICoin.sol +5 -1
- package/src/interfaces/ICoinDeployHook.sol +8 -0
- package/src/interfaces/ISwapRouter.sol +1 -35
- package/src/interfaces/IZoraFactory.sol +52 -0
- package/src/{utils → libs}/CoinConstants.sol +6 -6
- package/src/libs/CoinLegacy.sol +4 -4
- package/src/libs/CoinLegacyMarket.sol +182 -0
- package/src/libs/CoinSetupV3.sol +111 -0
- package/src/libs/UniV3BuySell.sol +449 -0
- package/src/libs/UniV3Errors.sol +11 -0
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +35 -17
- package/test/CoinDopplerUniV3.t.sol +14 -17
- package/test/Factory.t.sol +4 -3
- package/test/Hooks.t.sol +274 -0
- package/test/Upgrades.t.sol +67 -0
- package/test/utils/BaseTest.sol +18 -3
- package/wagmi.config.ts +6 -9
- package/src/libs/CoinSetup.sol +0 -37
- /package/abis/{CoinSetup.json → CoinSetupV3.json} +0 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
5
|
+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import {ISwapRouter} from "../interfaces/ISwapRouter.sol";
|
|
7
|
+
import {IWETH} from "../interfaces/IWETH.sol";
|
|
8
|
+
import {MarketConstants} from "./MarketConstants.sol";
|
|
9
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
10
|
+
import {ICoin} from "../interfaces/ICoin.sol";
|
|
11
|
+
import {IProtocolRewards} from "../interfaces/IProtocolRewards.sol";
|
|
12
|
+
import {LpPosition} from "../types/LpPosition.sol";
|
|
13
|
+
import {PoolConfiguration} from "../interfaces/ICoin.sol";
|
|
14
|
+
import {IUniswapV3Pool} from "../interfaces/IUniswapV3Pool.sol";
|
|
15
|
+
import {IAirlock} from "../interfaces/IAirlock.sol";
|
|
16
|
+
import {UniV3Config, CoinV3Config} from "./CoinSetupV3.sol";
|
|
17
|
+
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
18
|
+
import {CoinLegacyMarket} from "./CoinLegacyMarket.sol";
|
|
19
|
+
import {CoinDopplerUniV3} from "./CoinDopplerUniV3.sol";
|
|
20
|
+
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
21
|
+
|
|
22
|
+
struct CoinConfig {
|
|
23
|
+
address protocolRewardRecipient;
|
|
24
|
+
address platformReferrer;
|
|
25
|
+
address currency;
|
|
26
|
+
address payoutRecipient;
|
|
27
|
+
address protocolRewards;
|
|
28
|
+
address poolAddress;
|
|
29
|
+
PoolConfiguration poolConfiguration;
|
|
30
|
+
UniV3Config uniswapV3Config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
library UniV3BuySell {
|
|
34
|
+
using SafeERC20 for IERC20;
|
|
35
|
+
|
|
36
|
+
error AddressZero();
|
|
37
|
+
error InvalidPoolVersion();
|
|
38
|
+
|
|
39
|
+
function _handleBuy(
|
|
40
|
+
address recipient,
|
|
41
|
+
uint256 orderSize,
|
|
42
|
+
uint256 minAmountOut,
|
|
43
|
+
uint160 sqrtPriceLimitX96,
|
|
44
|
+
address tradeReferrer,
|
|
45
|
+
address coin,
|
|
46
|
+
CoinConfig memory coinConfig
|
|
47
|
+
) internal returns (uint256 amountOut, uint256 tradeReward, uint256 trueOrderSize) {
|
|
48
|
+
if (recipient == address(0)) {
|
|
49
|
+
revert AddressZero();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Calculate the trade reward
|
|
53
|
+
tradeReward = _calculateReward(orderSize, CoinConstants.TOTAL_FEE_BPS);
|
|
54
|
+
|
|
55
|
+
// Calculate the remaining size
|
|
56
|
+
trueOrderSize = orderSize - tradeReward;
|
|
57
|
+
|
|
58
|
+
// Handle incoming currency
|
|
59
|
+
_handleIncomingCurrency(orderSize, trueOrderSize, coinConfig.currency, coinConfig.uniswapV3Config.weth, coinConfig.uniswapV3Config.swapRouter);
|
|
60
|
+
|
|
61
|
+
// Set up the swap parameters
|
|
62
|
+
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
|
|
63
|
+
tokenIn: coinConfig.currency,
|
|
64
|
+
tokenOut: coin,
|
|
65
|
+
fee: MarketConstants.LP_FEE,
|
|
66
|
+
recipient: recipient,
|
|
67
|
+
amountIn: trueOrderSize,
|
|
68
|
+
amountOutMinimum: minAmountOut,
|
|
69
|
+
sqrtPriceLimitX96: sqrtPriceLimitX96
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Execute the swap
|
|
73
|
+
amountOut = ISwapRouter(coinConfig.uniswapV3Config.swapRouter).exactInputSingle(params);
|
|
74
|
+
|
|
75
|
+
_handleTradeRewards(tradeReward, tradeReferrer, coinConfig);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// @notice Executes a buy order
|
|
79
|
+
/// @param recipient The recipient address of the coins
|
|
80
|
+
/// @param orderSize The amount of coins to buy
|
|
81
|
+
/// @param tradeReferrer The address of the trade referrer
|
|
82
|
+
/// @param sqrtPriceLimitX96 The price limit for Uniswap V3 pool swap
|
|
83
|
+
function buy(
|
|
84
|
+
address recipient,
|
|
85
|
+
uint256 orderSize,
|
|
86
|
+
uint256 minAmountOut,
|
|
87
|
+
uint160 sqrtPriceLimitX96,
|
|
88
|
+
address tradeReferrer,
|
|
89
|
+
address coin,
|
|
90
|
+
CoinConfig memory coinConfig
|
|
91
|
+
) internal returns (uint256, uint256) {
|
|
92
|
+
(uint256 amountOut, uint256 tradeReward, uint256 trueOrderSize) = _handleBuy(
|
|
93
|
+
recipient,
|
|
94
|
+
orderSize,
|
|
95
|
+
minAmountOut,
|
|
96
|
+
sqrtPriceLimitX96,
|
|
97
|
+
tradeReferrer,
|
|
98
|
+
coin,
|
|
99
|
+
coinConfig
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
handleMarketRewards(coin, coinConfig);
|
|
103
|
+
|
|
104
|
+
emit ICoin.CoinBuy(msg.sender, recipient, tradeReferrer, amountOut, coinConfig.currency, tradeReward, trueOrderSize);
|
|
105
|
+
|
|
106
|
+
return (orderSize, amountOut);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _handleSell(
|
|
110
|
+
address recipient,
|
|
111
|
+
uint256 beforeCoinBalance,
|
|
112
|
+
uint256 orderSize,
|
|
113
|
+
uint256 minAmountOut,
|
|
114
|
+
uint160 sqrtPriceLimitX96,
|
|
115
|
+
address tradeReferrer,
|
|
116
|
+
CoinConfig memory coinConfig
|
|
117
|
+
) internal returns (uint256 payoutSize, uint256 tradeReward, uint256 trueOrderSize) {
|
|
118
|
+
// Ensure the recipient is not the zero address
|
|
119
|
+
if (recipient == address(0)) {
|
|
120
|
+
revert AddressZero();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Set the swap parameters
|
|
124
|
+
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
|
|
125
|
+
tokenIn: address(this),
|
|
126
|
+
tokenOut: coinConfig.currency,
|
|
127
|
+
fee: MarketConstants.LP_FEE,
|
|
128
|
+
recipient: address(this),
|
|
129
|
+
amountIn: orderSize,
|
|
130
|
+
amountOutMinimum: minAmountOut,
|
|
131
|
+
sqrtPriceLimitX96: sqrtPriceLimitX96
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Execute the swap
|
|
135
|
+
uint256 amountOut = ISwapRouter(coinConfig.uniswapV3Config.swapRouter).exactInputSingle(params);
|
|
136
|
+
|
|
137
|
+
// Record the coin balance of this contract after the swap
|
|
138
|
+
uint256 afterCoinBalance = IERC20(address(this)).balanceOf(address(this));
|
|
139
|
+
|
|
140
|
+
trueOrderSize = orderSize;
|
|
141
|
+
|
|
142
|
+
// If the swap was partially executed:
|
|
143
|
+
if (afterCoinBalance > beforeCoinBalance) {
|
|
144
|
+
// Calculate the refund
|
|
145
|
+
uint256 coinRefund = afterCoinBalance - beforeCoinBalance;
|
|
146
|
+
|
|
147
|
+
// Update the order size
|
|
148
|
+
trueOrderSize -= coinRefund;
|
|
149
|
+
|
|
150
|
+
// Transfer the refund back to the seller
|
|
151
|
+
IERC20(address(this)).safeTransfer(recipient, coinRefund);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// If currency is WETH, convert to ETH
|
|
155
|
+
if (coinConfig.currency == coinConfig.uniswapV3Config.weth) {
|
|
156
|
+
IWETH(coinConfig.uniswapV3Config.weth).withdraw(amountOut);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Calculate the trade reward
|
|
160
|
+
tradeReward = _calculateReward(amountOut, CoinConstants.TOTAL_FEE_BPS);
|
|
161
|
+
|
|
162
|
+
// Calculate the payout after the fee
|
|
163
|
+
payoutSize = amountOut - tradeReward;
|
|
164
|
+
|
|
165
|
+
_handlePayout(payoutSize, recipient, coinConfig.currency, coinConfig.uniswapV3Config.weth);
|
|
166
|
+
|
|
167
|
+
_handleTradeRewards(tradeReward, tradeReferrer, coinConfig);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// @notice Executes a sell order
|
|
171
|
+
/// @param recipient The recipient of the currency
|
|
172
|
+
/// @param _orderSize The amount of coins to sell
|
|
173
|
+
/// @param minAmountOut The minimum amount of currency to receive
|
|
174
|
+
/// @param sqrtPriceLimitX96 The price limit for the swap
|
|
175
|
+
/// @param tradeReferrer The address of the trade referrer
|
|
176
|
+
function sell(
|
|
177
|
+
address recipient,
|
|
178
|
+
uint256 beforeCoinBalance,
|
|
179
|
+
uint256 _orderSize,
|
|
180
|
+
uint256 minAmountOut,
|
|
181
|
+
uint160 sqrtPriceLimitX96,
|
|
182
|
+
address tradeReferrer,
|
|
183
|
+
CoinConfig memory coinConfig
|
|
184
|
+
) internal returns (uint256 trueOrderSize, uint256 payoutSize) {
|
|
185
|
+
uint256 tradeReward;
|
|
186
|
+
(payoutSize, tradeReward, trueOrderSize) = _handleSell(
|
|
187
|
+
recipient,
|
|
188
|
+
beforeCoinBalance,
|
|
189
|
+
_orderSize,
|
|
190
|
+
minAmountOut,
|
|
191
|
+
sqrtPriceLimitX96,
|
|
192
|
+
tradeReferrer,
|
|
193
|
+
coinConfig
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
handleMarketRewards(address(this), coinConfig);
|
|
197
|
+
|
|
198
|
+
emit ICoin.CoinSell(msg.sender, recipient, tradeReferrer, trueOrderSize, coinConfig.currency, tradeReward, payoutSize);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// @dev Handles incoming currency transfers for buy orders; if WETH is the currency the caller has the option to send native-ETH
|
|
202
|
+
/// @param orderSize The total size of the order in the currency
|
|
203
|
+
/// @param trueOrderSize The actual amount being used for the swap after fees
|
|
204
|
+
function _handleIncomingCurrency(uint256 orderSize, uint256 trueOrderSize, address currency, address weth, address swapRouter) internal {
|
|
205
|
+
if (currency == weth && msg.value > 0) {
|
|
206
|
+
if (msg.value != orderSize) {
|
|
207
|
+
revert ICoin.EthAmountMismatch();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (msg.value < CoinConstants.MIN_ORDER_SIZE) {
|
|
211
|
+
revert ICoin.EthAmountTooSmall();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
IWETH(weth).deposit{value: trueOrderSize}();
|
|
215
|
+
IWETH(weth).approve(swapRouter, trueOrderSize);
|
|
216
|
+
} else {
|
|
217
|
+
// Ensure ETH is not sent with a non-ETH pair
|
|
218
|
+
if (msg.value != 0) {
|
|
219
|
+
revert ICoin.EthTransferInvalid();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
uint256 beforeBalance = IERC20(currency).balanceOf(address(this));
|
|
223
|
+
IERC20(currency).safeTransferFrom(msg.sender, address(this), orderSize);
|
|
224
|
+
uint256 afterBalance = IERC20(currency).balanceOf(address(this));
|
|
225
|
+
|
|
226
|
+
if ((afterBalance - beforeBalance) != orderSize) {
|
|
227
|
+
revert ICoin.ERC20TransferAmountMismatch();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
IERC20(currency).approve(swapRouter, trueOrderSize);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/// @dev Handles sending ETH and ERC20 payouts and refunds to recipients
|
|
235
|
+
/// @param orderPayout The amount of currency to pay out
|
|
236
|
+
/// @param recipient The address to receive the payout
|
|
237
|
+
function _handlePayout(uint256 orderPayout, address recipient, address currency, address weth) internal {
|
|
238
|
+
if (currency == weth) {
|
|
239
|
+
Address.sendValue(payable(recipient), orderPayout);
|
|
240
|
+
} else {
|
|
241
|
+
IERC20(currency).safeTransfer(recipient, orderPayout);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// @dev Handles calculating and depositing fees to an escrow protocol rewards contract
|
|
246
|
+
function _handleTradeRewards(uint256 totalValue, address _tradeReferrer, CoinConfig memory coinConfig) internal {
|
|
247
|
+
address protocolRewardRecipient = coinConfig.protocolRewardRecipient;
|
|
248
|
+
address platformReferrer = coinConfig.platformReferrer;
|
|
249
|
+
address currency = coinConfig.currency;
|
|
250
|
+
address weth = coinConfig.uniswapV3Config.weth;
|
|
251
|
+
address payoutRecipient = coinConfig.payoutRecipient;
|
|
252
|
+
IProtocolRewards protocolRewards = IProtocolRewards(coinConfig.protocolRewards);
|
|
253
|
+
|
|
254
|
+
if (_tradeReferrer == address(0)) {
|
|
255
|
+
_tradeReferrer = protocolRewardRecipient;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
uint256 tokenCreatorFee = _calculateReward(totalValue, CoinConstants.TOKEN_CREATOR_FEE_BPS);
|
|
259
|
+
uint256 platformReferrerFee = _calculateReward(totalValue, CoinConstants.PLATFORM_REFERRER_FEE_BPS);
|
|
260
|
+
uint256 tradeReferrerFee = _calculateReward(totalValue, CoinConstants.TRADE_REFERRER_FEE_BPS);
|
|
261
|
+
uint256 protocolFee = totalValue - tokenCreatorFee - platformReferrerFee - tradeReferrerFee;
|
|
262
|
+
|
|
263
|
+
if (currency == weth) {
|
|
264
|
+
address[] memory recipients = new address[](4);
|
|
265
|
+
uint256[] memory amounts = new uint256[](4);
|
|
266
|
+
bytes4[] memory reasons = new bytes4[](4);
|
|
267
|
+
|
|
268
|
+
recipients[0] = payoutRecipient;
|
|
269
|
+
amounts[0] = tokenCreatorFee;
|
|
270
|
+
reasons[0] = bytes4(keccak256("COIN_CREATOR_REWARD"));
|
|
271
|
+
|
|
272
|
+
recipients[1] = platformReferrer;
|
|
273
|
+
amounts[1] = platformReferrerFee;
|
|
274
|
+
reasons[1] = bytes4(keccak256("COIN_PLATFORM_REFERRER_REWARD"));
|
|
275
|
+
|
|
276
|
+
recipients[2] = _tradeReferrer;
|
|
277
|
+
amounts[2] = tradeReferrerFee;
|
|
278
|
+
reasons[2] = bytes4(keccak256("COIN_TRADE_REFERRER_REWARD"));
|
|
279
|
+
|
|
280
|
+
recipients[3] = protocolRewardRecipient;
|
|
281
|
+
amounts[3] = protocolFee;
|
|
282
|
+
reasons[3] = bytes4(keccak256("COIN_PROTOCOL_REWARD"));
|
|
283
|
+
|
|
284
|
+
IProtocolRewards(protocolRewards).depositBatch{value: totalValue}(recipients, amounts, reasons, "");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (currency != weth) {
|
|
288
|
+
IERC20(currency).safeTransfer(payoutRecipient, tokenCreatorFee);
|
|
289
|
+
IERC20(currency).safeTransfer(platformReferrer, platformReferrerFee);
|
|
290
|
+
IERC20(currency).safeTransfer(_tradeReferrer, tradeReferrerFee);
|
|
291
|
+
IERC20(currency).safeTransfer(protocolRewardRecipient, protocolFee);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
emit ICoin.CoinTradeRewards(
|
|
295
|
+
payoutRecipient,
|
|
296
|
+
platformReferrer,
|
|
297
|
+
_tradeReferrer,
|
|
298
|
+
protocolRewardRecipient,
|
|
299
|
+
tokenCreatorFee,
|
|
300
|
+
platformReferrerFee,
|
|
301
|
+
tradeReferrerFee,
|
|
302
|
+
protocolFee,
|
|
303
|
+
currency
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function _distributeMarketRewards(
|
|
308
|
+
LpPosition[] memory positions,
|
|
309
|
+
address poolAddress,
|
|
310
|
+
address currency,
|
|
311
|
+
address coin,
|
|
312
|
+
CoinConfig memory coinConfig
|
|
313
|
+
) internal returns (ICoin.MarketRewards memory) {
|
|
314
|
+
uint256 totalAmountToken0;
|
|
315
|
+
uint256 totalAmountToken1;
|
|
316
|
+
uint256 amount0;
|
|
317
|
+
uint256 amount1;
|
|
318
|
+
|
|
319
|
+
for (uint256 i; i < positions.length; i++) {
|
|
320
|
+
// Must burn to update the collect mapping on the pool
|
|
321
|
+
IUniswapV3Pool(poolAddress).burn(positions[i].tickLower, positions[i].tickUpper, 0);
|
|
322
|
+
|
|
323
|
+
(amount0, amount1) = IUniswapV3Pool(poolAddress).collect(
|
|
324
|
+
address(this),
|
|
325
|
+
positions[i].tickLower,
|
|
326
|
+
positions[i].tickUpper,
|
|
327
|
+
type(uint128).max,
|
|
328
|
+
type(uint128).max
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
totalAmountToken0 += amount0;
|
|
332
|
+
totalAmountToken1 += amount1;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
address token0 = currency < address(this) ? currency : address(this);
|
|
336
|
+
address token1 = currency < address(this) ? address(this) : currency;
|
|
337
|
+
|
|
338
|
+
ICoin.MarketRewards memory rewards;
|
|
339
|
+
|
|
340
|
+
rewards = _transferMarketRewards(token0, totalAmountToken0, rewards, coin, coinConfig);
|
|
341
|
+
rewards = _transferMarketRewards(token1, totalAmountToken1, rewards, coin, coinConfig);
|
|
342
|
+
|
|
343
|
+
emit ICoin.CoinMarketRewards(coinConfig.payoutRecipient, coinConfig.platformReferrer, coinConfig.protocolRewardRecipient, coinConfig.currency, rewards);
|
|
344
|
+
|
|
345
|
+
return rewards;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/// @dev Collects and distributes accrued fees from all LP positions
|
|
349
|
+
function handleMarketRewards(address coin, CoinConfig memory coinConfig) internal returns (ICoin.MarketRewards memory) {
|
|
350
|
+
address poolAddress = coinConfig.poolAddress;
|
|
351
|
+
address currency = coinConfig.currency;
|
|
352
|
+
|
|
353
|
+
bool isCoinToken0 = coin < currency;
|
|
354
|
+
LpPosition[] memory positions = calculatePositions(isCoinToken0, coinConfig.poolConfiguration);
|
|
355
|
+
|
|
356
|
+
return _distributeMarketRewards(positions, poolAddress, currency, coin, coinConfig);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function _transferMarketRewards(
|
|
360
|
+
address token,
|
|
361
|
+
uint256 totalAmount,
|
|
362
|
+
ICoin.MarketRewards memory rewards,
|
|
363
|
+
address coin,
|
|
364
|
+
CoinConfig memory coinConfig
|
|
365
|
+
) internal returns (ICoin.MarketRewards memory) {
|
|
366
|
+
address payoutRecipient = coinConfig.payoutRecipient;
|
|
367
|
+
address platformReferrer = coinConfig.platformReferrer;
|
|
368
|
+
address protocolRewardRecipient = coinConfig.protocolRewardRecipient;
|
|
369
|
+
address currency = coinConfig.currency;
|
|
370
|
+
address weth = coinConfig.uniswapV3Config.weth;
|
|
371
|
+
address airlock = coinConfig.uniswapV3Config.airlock;
|
|
372
|
+
address protocolRewards = coinConfig.protocolRewards;
|
|
373
|
+
|
|
374
|
+
if (totalAmount > 0) {
|
|
375
|
+
address dopplerRecipient = IAirlock(airlock).owner();
|
|
376
|
+
uint256 dopplerPayout = _calculateReward(totalAmount, CoinConstants.DOPPLER_MARKET_REWARD_BPS);
|
|
377
|
+
uint256 creatorPayout = _calculateReward(totalAmount, CoinConstants.CREATOR_MARKET_REWARD_BPS);
|
|
378
|
+
uint256 platformReferrerPayout = _calculateReward(totalAmount, CoinConstants.PLATFORM_REFERRER_MARKET_REWARD_BPS);
|
|
379
|
+
uint256 protocolPayout = totalAmount - creatorPayout - platformReferrerPayout - dopplerPayout;
|
|
380
|
+
|
|
381
|
+
if (token == weth) {
|
|
382
|
+
IWETH(weth).withdraw(totalAmount);
|
|
383
|
+
|
|
384
|
+
rewards.totalAmountCurrency = totalAmount;
|
|
385
|
+
rewards.creatorPayoutAmountCurrency = creatorPayout;
|
|
386
|
+
rewards.platformReferrerAmountCurrency = platformReferrerPayout;
|
|
387
|
+
rewards.protocolAmountCurrency = protocolPayout;
|
|
388
|
+
|
|
389
|
+
address[] memory recipients = new address[](4);
|
|
390
|
+
recipients[0] = payoutRecipient;
|
|
391
|
+
recipients[1] = platformReferrer;
|
|
392
|
+
recipients[2] = protocolRewardRecipient;
|
|
393
|
+
recipients[3] = dopplerRecipient;
|
|
394
|
+
|
|
395
|
+
uint256[] memory amounts = new uint256[](4);
|
|
396
|
+
amounts[0] = rewards.creatorPayoutAmountCurrency;
|
|
397
|
+
amounts[1] = rewards.platformReferrerAmountCurrency;
|
|
398
|
+
amounts[2] = rewards.protocolAmountCurrency;
|
|
399
|
+
amounts[3] = dopplerPayout;
|
|
400
|
+
|
|
401
|
+
bytes4[] memory reasons = new bytes4[](4);
|
|
402
|
+
reasons[0] = bytes4(keccak256("COIN_CREATOR_MARKET_REWARD"));
|
|
403
|
+
reasons[1] = bytes4(keccak256("COIN_PLATFORM_REFERRER_MARKET_REWARD"));
|
|
404
|
+
reasons[2] = bytes4(keccak256("COIN_PROTOCOL_MARKET_REWARD"));
|
|
405
|
+
reasons[3] = bytes4(keccak256("COIN_DOPPLER_MARKET_REWARD"));
|
|
406
|
+
|
|
407
|
+
IProtocolRewards(protocolRewards).depositBatch{value: totalAmount}(recipients, amounts, reasons, "");
|
|
408
|
+
IProtocolRewards(protocolRewards).withdrawFor(dopplerRecipient, dopplerPayout);
|
|
409
|
+
} else if (token == coin) {
|
|
410
|
+
rewards.totalAmountCoin = totalAmount;
|
|
411
|
+
rewards.creatorPayoutAmountCoin = creatorPayout;
|
|
412
|
+
rewards.platformReferrerAmountCoin = platformReferrerPayout;
|
|
413
|
+
rewards.protocolAmountCoin = protocolPayout;
|
|
414
|
+
|
|
415
|
+
IERC20(coin).safeTransfer(payoutRecipient, rewards.creatorPayoutAmountCoin);
|
|
416
|
+
IERC20(coin).safeTransfer(platformReferrer, rewards.platformReferrerAmountCoin);
|
|
417
|
+
IERC20(coin).safeTransfer(protocolRewardRecipient, rewards.protocolAmountCoin);
|
|
418
|
+
IERC20(coin).safeTransfer(dopplerRecipient, dopplerPayout);
|
|
419
|
+
} else {
|
|
420
|
+
rewards.totalAmountCurrency = totalAmount;
|
|
421
|
+
rewards.creatorPayoutAmountCurrency = creatorPayout;
|
|
422
|
+
rewards.platformReferrerAmountCurrency = platformReferrerPayout;
|
|
423
|
+
rewards.protocolAmountCurrency = protocolPayout;
|
|
424
|
+
|
|
425
|
+
IERC20(currency).safeTransfer(payoutRecipient, creatorPayout);
|
|
426
|
+
IERC20(currency).safeTransfer(platformReferrer, platformReferrerPayout);
|
|
427
|
+
IERC20(currency).safeTransfer(protocolRewardRecipient, protocolPayout);
|
|
428
|
+
IERC20(currency).safeTransfer(dopplerRecipient, dopplerPayout);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return rewards;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/// @dev Utility for computing amounts in basis points.
|
|
436
|
+
function _calculateReward(uint256 amount, uint256 bps) internal pure returns (uint256) {
|
|
437
|
+
return (amount * bps) / 10_000;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
|
|
441
|
+
if (poolConfiguration.version == CoinConfigurationVersions.LEGACY_POOL_VERSION) {
|
|
442
|
+
positions = CoinLegacyMarket.calculatePositions(isCoinToken0, poolConfiguration);
|
|
443
|
+
} else if (poolConfiguration.version == CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION) {
|
|
444
|
+
positions = CoinDopplerUniV3.calculatePositions(isCoinToken0, poolConfiguration);
|
|
445
|
+
} else {
|
|
446
|
+
revert InvalidPoolVersion();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
library UniV3Errors {
|
|
5
|
+
error InvalidPoolAddress();
|
|
6
|
+
error InvalidCurrency();
|
|
7
|
+
error InvalidWeth();
|
|
8
|
+
error InvalidTickLower();
|
|
9
|
+
error InvalidTickUpper();
|
|
10
|
+
error InvalidUniswapV3Factory();
|
|
11
|
+
}
|
|
@@ -9,6 +9,6 @@ import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersion
|
|
|
9
9
|
contract ContractVersionBase is IVersionedContract {
|
|
10
10
|
/// @notice The version of the contract
|
|
11
11
|
function contractVersion() external pure override returns (string memory) {
|
|
12
|
-
return "0.
|
|
12
|
+
return "0.9.0";
|
|
13
13
|
}
|
|
14
14
|
}
|
package/test/Coin.t.sol
CHANGED
|
@@ -3,6 +3,8 @@ pragma solidity ^0.8.13;
|
|
|
3
3
|
|
|
4
4
|
import "./utils/BaseTest.sol";
|
|
5
5
|
import {ISwapRouter} from "../src/interfaces/ISwapRouter.sol";
|
|
6
|
+
import {CoinConfigurationVersions} from "../src/libs/CoinConfigurationVersions.sol";
|
|
7
|
+
import {CoinConstants} from "../src/libs/CoinConstants.sol";
|
|
6
8
|
|
|
7
9
|
contract CoinTest is BaseTest {
|
|
8
10
|
function setUp() public override {
|
|
@@ -12,19 +14,19 @@ contract CoinTest is BaseTest {
|
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
function test_contract_version() public view {
|
|
15
|
-
assertEq(coin.contractVersion(), "0.
|
|
17
|
+
assertEq(coin.contractVersion(), "0.8.0");
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
function test_supply_constants() public view {
|
|
19
|
-
assertEq(MAX_TOTAL_SUPPLY, POOL_LAUNCH_SUPPLY + CREATOR_LAUNCH_REWARD);
|
|
21
|
+
assertEq(CoinConstants.MAX_TOTAL_SUPPLY, CoinConstants.POOL_LAUNCH_SUPPLY + CoinConstants.CREATOR_LAUNCH_REWARD);
|
|
20
22
|
|
|
21
|
-
assertEq(MAX_TOTAL_SUPPLY, 1_000_000_000e18);
|
|
22
|
-
assertEq(POOL_LAUNCH_SUPPLY, 990_000_000e18);
|
|
23
|
-
assertEq(CREATOR_LAUNCH_REWARD, 10_000_000e18);
|
|
23
|
+
assertEq(CoinConstants.MAX_TOTAL_SUPPLY, 1_000_000_000e18);
|
|
24
|
+
assertEq(CoinConstants.POOL_LAUNCH_SUPPLY, 990_000_000e18);
|
|
25
|
+
assertEq(CoinConstants.CREATOR_LAUNCH_REWARD, 10_000_000e18);
|
|
24
26
|
|
|
25
|
-
assertEq(coin.totalSupply(), MAX_TOTAL_SUPPLY);
|
|
26
|
-
assertEq(coin.balanceOf(coin.payoutRecipient()), CREATOR_LAUNCH_REWARD);
|
|
27
|
-
assertApproxEqAbs(coin.balanceOf(address(pool)), POOL_LAUNCH_SUPPLY, 1e18);
|
|
27
|
+
assertEq(coin.totalSupply(), CoinConstants.MAX_TOTAL_SUPPLY);
|
|
28
|
+
assertEq(coin.balanceOf(coin.payoutRecipient()), CoinConstants.CREATOR_LAUNCH_REWARD);
|
|
29
|
+
assertApproxEqAbs(coin.balanceOf(address(pool)), CoinConstants.POOL_LAUNCH_SUPPLY, 1e18);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
function test_constructor_validation() public {
|
|
@@ -91,6 +93,20 @@ contract CoinTest is BaseTest {
|
|
|
91
93
|
assertEq(coin.symbol(), "INIT");
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
function test_invalid_pool_config_version() public {
|
|
97
|
+
bytes memory poolConfig = abi.encode(0, address(weth));
|
|
98
|
+
|
|
99
|
+
vm.expectRevert(abi.encodeWithSignature("InvalidPoolVersion()"));
|
|
100
|
+
factory.deploy(users.creator, _getDefaultOwners(), "https://test.com", "Testcoin", "TEST", poolConfig, users.platformReferrer, 0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function test_invalid_pool_config_currency() public {
|
|
104
|
+
bytes memory poolConfig = abi.encode(CoinConfigurationVersions.LEGACY_POOL_VERSION);
|
|
105
|
+
|
|
106
|
+
vm.expectRevert();
|
|
107
|
+
factory.deploy(users.creator, _getDefaultOwners(), "https://test.com", "Testcoin", "TEST", poolConfig, users.platformReferrer, 0);
|
|
108
|
+
}
|
|
109
|
+
|
|
94
110
|
function test_revert_already_initialized() public {
|
|
95
111
|
address[] memory owners = new address[](1);
|
|
96
112
|
owners[0] = users.creator;
|
|
@@ -147,7 +163,7 @@ contract CoinTest is BaseTest {
|
|
|
147
163
|
}
|
|
148
164
|
|
|
149
165
|
function test_buy_with_eth_fuzz(uint256 ethOrderSize) public {
|
|
150
|
-
vm.assume(ethOrderSize >= MIN_ORDER_SIZE);
|
|
166
|
+
vm.assume(ethOrderSize >= CoinConstants.MIN_ORDER_SIZE);
|
|
151
167
|
vm.assume(ethOrderSize < 10 ether);
|
|
152
168
|
|
|
153
169
|
uint256 platformReferrerBalanceBeforeSale = users.platformReferrer.balance;
|
|
@@ -168,11 +184,11 @@ contract CoinTest is BaseTest {
|
|
|
168
184
|
|
|
169
185
|
function test_buy_with_eth_too_small() public {
|
|
170
186
|
vm.expectRevert(abi.encodeWithSelector(ICoin.EthAmountTooSmall.selector));
|
|
171
|
-
coin.buy{value: MIN_ORDER_SIZE - 1}(users.coinRecipient, MIN_ORDER_SIZE - 1, 0, 0, users.tradeReferrer);
|
|
187
|
+
coin.buy{value: CoinConstants.MIN_ORDER_SIZE - 1}(users.coinRecipient, CoinConstants.MIN_ORDER_SIZE - 1, 0, 0, users.tradeReferrer);
|
|
172
188
|
}
|
|
173
189
|
|
|
174
190
|
function test_buy_with_minimum_eth() public {
|
|
175
|
-
uint256 minEth = MIN_ORDER_SIZE;
|
|
191
|
+
uint256 minEth = CoinConstants.MIN_ORDER_SIZE;
|
|
176
192
|
vm.deal(users.buyer, minEth);
|
|
177
193
|
vm.prank(users.buyer);
|
|
178
194
|
coin.buy{value: minEth}(users.coinRecipient, minEth, 0, 0, users.tradeReferrer);
|
|
@@ -221,7 +237,7 @@ contract CoinTest is BaseTest {
|
|
|
221
237
|
}
|
|
222
238
|
|
|
223
239
|
function test_buy_validate_return_amounts(uint256 orderSize) public {
|
|
224
|
-
vm.assume(orderSize >= MIN_ORDER_SIZE);
|
|
240
|
+
vm.assume(orderSize >= CoinConstants.MIN_ORDER_SIZE);
|
|
225
241
|
vm.assume(orderSize < 10 ether);
|
|
226
242
|
|
|
227
243
|
vm.deal(users.buyer, orderSize);
|
|
@@ -326,7 +342,7 @@ contract CoinTest is BaseTest {
|
|
|
326
342
|
|
|
327
343
|
function test_sell_for_eth_fuzz(uint256 ethOrderSize) public {
|
|
328
344
|
vm.assume(ethOrderSize < 10 ether);
|
|
329
|
-
vm.assume(ethOrderSize >= MIN_ORDER_SIZE);
|
|
345
|
+
vm.assume(ethOrderSize >= CoinConstants.MIN_ORDER_SIZE);
|
|
330
346
|
|
|
331
347
|
vm.deal(users.buyer, ethOrderSize);
|
|
332
348
|
vm.prank(users.buyer);
|
|
@@ -396,14 +412,14 @@ contract CoinTest is BaseTest {
|
|
|
396
412
|
coin.buy{value: 0.001 ether}(users.creator, 0.001 ether, 0, 0, users.tradeReferrer);
|
|
397
413
|
|
|
398
414
|
uint256 beforeBalance = coin.balanceOf(users.creator);
|
|
399
|
-
assertEq(beforeBalance, 11077349369032224007213331); // 11,077,349 coins
|
|
415
|
+
assertEq(beforeBalance, 11077349369032224007213331, "before balance"); // 11,077,349 coins
|
|
400
416
|
|
|
401
417
|
vm.prank(users.creator);
|
|
402
418
|
(uint256 amountSold, ) = coin.sell(users.creator, beforeBalance, 0, 0, users.tradeReferrer);
|
|
403
|
-
assertEq(amountSold, 1088231685891135360821548); // 1,088,232 coins (max that could be sold)
|
|
419
|
+
assertEq(amountSold, 1088231685891135360821548, "amountSold"); // 1,088,232 coins (max that could be sold)
|
|
404
420
|
|
|
405
421
|
uint256 afterBalance = coin.balanceOf(users.creator);
|
|
406
|
-
assertEq(afterBalance, 9994558841570544323195890); // 9,994,559 coins
|
|
422
|
+
assertEq(afterBalance, 9994558841570544323195890, "after balance"); // 9,994,559 coins
|
|
407
423
|
|
|
408
424
|
uint256 expectedMarketReward = 5441158429455676804107; // 5,441 coins
|
|
409
425
|
|
|
@@ -497,7 +513,9 @@ contract CoinTest is BaseTest {
|
|
|
497
513
|
function test_eth_transfer_fail() public {
|
|
498
514
|
vm.deal(users.buyer, 1 ether);
|
|
499
515
|
vm.prank(users.buyer);
|
|
500
|
-
coin.buy{value: 1 ether}(users.coinRecipient, 1 ether, 0, 0, users.tradeReferrer);
|
|
516
|
+
(, uint256 amountOut) = coin.buy{value: 1 ether}(users.coinRecipient, 1 ether, 0, 0, users.tradeReferrer);
|
|
517
|
+
|
|
518
|
+
assertEq(coin.balanceOf(users.coinRecipient), amountOut);
|
|
501
519
|
|
|
502
520
|
// Recipient reverts on ETH receive
|
|
503
521
|
address payable badRecipient = payable(makeAddr("badRecipient"));
|
|
@@ -5,6 +5,7 @@ import {CoinConfigurationVersions} from "../src/libs/CoinConfigurationVersions.s
|
|
|
5
5
|
import {MarketConstants} from "../src/libs/MarketConstants.sol";
|
|
6
6
|
import {BaseTest} from "./utils/BaseTest.sol";
|
|
7
7
|
import {Coin} from "../src/Coin.sol";
|
|
8
|
+
import {CoinConstants} from "../src/libs/CoinConstants.sol";
|
|
8
9
|
import {IUniswapV3Pool} from "../src/interfaces/IUniswapV3Pool.sol";
|
|
9
10
|
import {LpPosition} from "../src/types/LpPosition.sol";
|
|
10
11
|
import {IDopplerErrors} from "../src/interfaces/IDopplerErrors.sol";
|
|
@@ -17,17 +18,6 @@ contract DopplerUniswapV3Test is BaseTest {
|
|
|
17
18
|
uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = 10; // will be 11 total with tail position
|
|
18
19
|
uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = 0.495e18; // half of the 990m total pool supply
|
|
19
20
|
|
|
20
|
-
function _generatePoolConfig(
|
|
21
|
-
uint8 version_,
|
|
22
|
-
address currency_,
|
|
23
|
-
int24 tickLower_,
|
|
24
|
-
int24 tickUpper_,
|
|
25
|
-
uint16 numDiscoveryPositions_,
|
|
26
|
-
uint256 maxDiscoverySupplyShare_
|
|
27
|
-
) internal pure returns (bytes memory) {
|
|
28
|
-
return abi.encode(version_, currency_, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
21
|
function _deployCoin(bytes memory poolConfig_) internal {
|
|
32
22
|
vm.prank(users.creator);
|
|
33
23
|
(address coinAddress, ) = factory.deploy(
|
|
@@ -87,7 +77,7 @@ contract DopplerUniswapV3Test is BaseTest {
|
|
|
87
77
|
assertTrue(isInitialized);
|
|
88
78
|
assertFalse(isExited);
|
|
89
79
|
assertEq(maxShareToBeSold, 0);
|
|
90
|
-
assertEq(totalTokensOnBondingCurve, POOL_LAUNCH_SUPPLY);
|
|
80
|
+
assertEq(totalTokensOnBondingCurve, CoinConstants.POOL_LAUNCH_SUPPLY);
|
|
91
81
|
|
|
92
82
|
bool isCoinToken0 = address(coin) < address(weth);
|
|
93
83
|
|
|
@@ -116,7 +106,7 @@ contract DopplerUniswapV3Test is BaseTest {
|
|
|
116
106
|
}
|
|
117
107
|
|
|
118
108
|
function test_deploy_legacy_eth_config_with_prebuy(uint256 initialOrderSize) public {
|
|
119
|
-
vm.assume(initialOrderSize > MIN_ORDER_SIZE);
|
|
109
|
+
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
120
110
|
vm.assume(initialOrderSize < 10 ether);
|
|
121
111
|
|
|
122
112
|
vm.deal(users.creator, initialOrderSize);
|
|
@@ -176,7 +166,7 @@ contract DopplerUniswapV3Test is BaseTest {
|
|
|
176
166
|
vm.label(address(pool), "POOL");
|
|
177
167
|
|
|
178
168
|
assertEq(coin.currency(), address(usdc), "currency");
|
|
179
|
-
assertEq(coin.balanceOf(users.creator), CREATOR_LAUNCH_REWARD + coinsPurchased);
|
|
169
|
+
assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased);
|
|
180
170
|
}
|
|
181
171
|
|
|
182
172
|
function test_deploy_doppler_eth() public {
|
|
@@ -209,11 +199,11 @@ contract DopplerUniswapV3Test is BaseTest {
|
|
|
209
199
|
assertTrue(isInitialized, "poolState.isInitialized");
|
|
210
200
|
assertFalse(isExited, "poolState.isExited");
|
|
211
201
|
assertEq(maxShareToBeSold, DEFAULT_DISCOVERY_SUPPLY_SHARE, "poolState.maxShareToBeSold");
|
|
212
|
-
assertEq(totalTokensOnBondingCurve, POOL_LAUNCH_SUPPLY, "poolState.totalTokensOnBondingCurve");
|
|
202
|
+
assertEq(totalTokensOnBondingCurve, CoinConstants.POOL_LAUNCH_SUPPLY, "poolState.totalTokensOnBondingCurve");
|
|
213
203
|
}
|
|
214
204
|
|
|
215
205
|
function test_deploy_doppler_eth_with_prebuy(uint256 initialOrderSize) public {
|
|
216
|
-
vm.assume(initialOrderSize > MIN_ORDER_SIZE);
|
|
206
|
+
vm.assume(initialOrderSize > CoinConstants.MIN_ORDER_SIZE);
|
|
217
207
|
vm.assume(initialOrderSize < 1 ether);
|
|
218
208
|
|
|
219
209
|
vm.deal(users.creator, initialOrderSize);
|
|
@@ -244,10 +234,17 @@ contract DopplerUniswapV3Test is BaseTest {
|
|
|
244
234
|
|
|
245
235
|
assertEq(coin.currency(), address(weth), "currency");
|
|
246
236
|
assertGt(coinsPurchased, 0, "coinsPurchased > 0");
|
|
247
|
-
assertEq(coin.balanceOf(users.creator), CREATOR_LAUNCH_REWARD + coinsPurchased, "balanceOf creator");
|
|
237
|
+
assertEq(coin.balanceOf(users.creator), CoinConstants.CREATOR_LAUNCH_REWARD + coinsPurchased, "balanceOf creator");
|
|
248
238
|
assertGt(weth.balanceOf(address(pool)), 0, "Pool WETH balance");
|
|
249
239
|
}
|
|
250
240
|
|
|
241
|
+
function test_invalid_pool_config() public {
|
|
242
|
+
bytes memory poolConfig = _generatePoolConfig(CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION, address(weth), -100, 100, 0, 10);
|
|
243
|
+
|
|
244
|
+
vm.expectRevert(abi.encodeWithSignature("NumDiscoveryPositionsOutOfRange()"));
|
|
245
|
+
factory.deploy(users.creator, _getDefaultOwners(), "https://test.com", "Testcoin", "TEST", poolConfig, users.platformReferrer, 0);
|
|
246
|
+
}
|
|
247
|
+
|
|
251
248
|
function test_revert_deploy_invalid_discovery_supply_share() public {
|
|
252
249
|
bytes memory poolConfig = _generatePoolConfig(
|
|
253
250
|
CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
|