@zoralabs/coins 0.6.1 → 0.7.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 (66) hide show
  1. package/.turbo/turbo-build.log +69 -55
  2. package/CHANGELOG.md +12 -0
  3. package/abis/BaseTest.json +0 -23
  4. package/abis/Coin.json +186 -77
  5. package/abis/CoinConfigurationVersions.json +7 -0
  6. package/abis/CoinSetup.json +7 -0
  7. package/abis/CoinTest.json +5 -49
  8. package/abis/CustomRevert.json +28 -0
  9. package/abis/DopplerUniswapV3Test.json +891 -0
  10. package/abis/FactoryTest.json +7 -23
  11. package/abis/IAirlock.json +15 -0
  12. package/abis/ICoin.json +52 -34
  13. package/abis/IDopplerErrors.json +44 -0
  14. package/abis/INonfungiblePositionManager.json +13 -0
  15. package/abis/IUniswapV3Factory.json +198 -0
  16. package/abis/IUniswapV3Pool.json +135 -0
  17. package/abis/MultiOwnableTest.json +0 -23
  18. package/abis/SafeCast.json +7 -0
  19. package/abis/Simulate.json +120 -0
  20. package/abis/SqrtPriceMath.json +22 -0
  21. package/abis/TickMath.json +24 -0
  22. package/abis/ZoraFactoryImpl.json +59 -0
  23. package/addresses/8453.json +3 -3
  24. package/dist/index.cjs +160 -39
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +160 -39
  27. package/dist/index.js.map +1 -1
  28. package/dist/wagmiGenerated.d.ts +349 -67
  29. package/dist/wagmiGenerated.d.ts.map +1 -1
  30. package/package/wagmiGenerated.ts +161 -40
  31. package/package.json +3 -3
  32. package/script/CoinsDeployerBase.sol +1 -1
  33. package/script/Simulate.s.sol +67 -0
  34. package/src/Coin.sol +159 -90
  35. package/src/ZoraFactoryImpl.sol +47 -1
  36. package/src/interfaces/IAirlock.sol +6 -0
  37. package/src/interfaces/ICoin.sol +18 -2
  38. package/src/interfaces/IDopplerErrors.sol +14 -0
  39. package/src/interfaces/INonfungiblePositionManager.sol +2 -0
  40. package/src/interfaces/IUniswapV3Factory.sol +64 -0
  41. package/src/interfaces/IUniswapV3Pool.sol +48 -0
  42. package/src/libs/CoinConfigurationVersions.sol +9 -0
  43. package/src/libs/CoinDopplerUniV3.sol +202 -0
  44. package/src/libs/CoinLegacy.sol +48 -0
  45. package/src/libs/CoinSetup.sol +37 -0
  46. package/src/libs/MarketConstants.sol +25 -0
  47. package/src/types/LpPosition.sol +8 -0
  48. package/src/types/PoolState.sol +24 -0
  49. package/src/utils/CoinConstants.sol +5 -12
  50. package/src/utils/uniswap/BitMath.sol +55 -0
  51. package/src/utils/uniswap/CustomRevert.sol +111 -0
  52. package/src/utils/uniswap/FixedPoint96.sol +11 -0
  53. package/src/utils/uniswap/FullMath.sol +118 -0
  54. package/src/utils/uniswap/LiquidityAmounts.sol +117 -0
  55. package/src/utils/uniswap/SafeCast.sol +61 -0
  56. package/src/utils/uniswap/SqrtPriceMath.sol +249 -0
  57. package/src/utils/uniswap/TickMath.sol +244 -0
  58. package/src/utils/uniswap/UnsafeMath.sol +30 -0
  59. package/src/version/ContractVersionBase.sol +1 -1
  60. package/test/Coin.t.sol +65 -65
  61. package/test/CoinDopplerUniV3.t.sol +452 -0
  62. package/test/Factory.t.sol +49 -7
  63. package/test/utils/BaseTest.sol +26 -7
  64. package/wagmi.config.ts +1 -1
  65. package/abis/IERC721Receiver.json +0 -36
  66. package/src/utils/TickMath.sol +0 -210
@@ -0,0 +1,244 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {BitMath} from "./BitMath.sol";
5
+ import {CustomRevert} from "./CustomRevert.sol";
6
+
7
+ /// @dev https://github.com/Uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/TickMath.sol
8
+ /// @title Math library for computing sqrt prices from ticks and vice versa
9
+ /// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
10
+ /// prices between 2**-128 and 2**128
11
+ library TickMath {
12
+ using CustomRevert for bytes4;
13
+
14
+ /// @notice Thrown when the tick passed to #getSqrtPriceAtTick is not between MIN_TICK and MAX_TICK
15
+ error InvalidTick(int24 tick);
16
+ /// @notice Thrown when the price passed to #getTickAtSqrtPrice does not correspond to a price between MIN_TICK and MAX_TICK
17
+ error InvalidSqrtPrice(uint160 sqrtPriceX96);
18
+
19
+ /// @dev The minimum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**-128
20
+ /// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
21
+ int24 internal constant MIN_TICK = -887272;
22
+ /// @dev The maximum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**128
23
+ /// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
24
+ int24 internal constant MAX_TICK = 887272;
25
+
26
+ /// @dev The minimum tick spacing value drawn from the range of type int16 that is greater than 0, i.e. min from the range [1, 32767]
27
+ int24 internal constant MIN_TICK_SPACING = 1;
28
+ /// @dev The maximum tick spacing value drawn from the range of type int16, i.e. max from the range [1, 32767]
29
+ int24 internal constant MAX_TICK_SPACING = type(int16).max;
30
+
31
+ /// @dev The minimum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MIN_TICK)
32
+ uint160 internal constant MIN_SQRT_PRICE = 4295128739;
33
+ /// @dev The maximum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MAX_TICK)
34
+ uint160 internal constant MAX_SQRT_PRICE = 1461446703485210103287273052203988822378723970342;
35
+ /// @dev A threshold used for optimized bounds check, equals `MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1`
36
+ uint160 internal constant MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE = 1461446703485210103287273052203988822378723970342 - 4295128739 - 1;
37
+
38
+ /// @notice Given a tickSpacing, compute the maximum usable tick
39
+ function maxUsableTick(int24 tickSpacing) internal pure returns (int24) {
40
+ unchecked {
41
+ return (MAX_TICK / tickSpacing) * tickSpacing;
42
+ }
43
+ }
44
+
45
+ /// @notice Given a tickSpacing, compute the minimum usable tick
46
+ function minUsableTick(int24 tickSpacing) internal pure returns (int24) {
47
+ unchecked {
48
+ return (MIN_TICK / tickSpacing) * tickSpacing;
49
+ }
50
+ }
51
+
52
+ /// @notice Calculates sqrt(1.0001^tick) * 2^96
53
+ /// @dev Throws if |tick| > max tick
54
+ /// @param tick The input tick for the above formula
55
+ /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the price of the two assets (currency1/currency0)
56
+ /// at the given tick
57
+ function getSqrtPriceAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
58
+ unchecked {
59
+ uint256 absTick;
60
+ assembly ("memory-safe") {
61
+ tick := signextend(2, tick)
62
+ // mask = 0 if tick >= 0 else -1 (all 1s)
63
+ let mask := sar(255, tick)
64
+ // if tick >= 0, |tick| = tick = 0 ^ tick
65
+ // if tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = (-1) ^ (tick - 1)
66
+ // either way, |tick| = mask ^ (tick + mask)
67
+ absTick := xor(mask, add(mask, tick))
68
+ }
69
+
70
+ if (absTick > uint256(int256(MAX_TICK))) InvalidTick.selector.revertWith(tick);
71
+
72
+ // The tick is decomposed into bits, and for each bit with index i that is set, the product of 1/sqrt(1.0001^(2^i))
73
+ // is calculated (using Q128.128). The constants used for this calculation are rounded to the nearest integer
74
+
75
+ // Equivalent to:
76
+ // price = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
77
+ // or price = int(2**128 / sqrt(1.0001)) if (absTick & 0x1) else 1 << 128
78
+ uint256 price;
79
+ assembly ("memory-safe") {
80
+ price := xor(shl(128, 1), mul(xor(shl(128, 1), 0xfffcb933bd6fad37aa2d162d1a594001), and(absTick, 0x1)))
81
+ }
82
+ if (absTick & 0x2 != 0) price = (price * 0xfff97272373d413259a46990580e213a) >> 128;
83
+ if (absTick & 0x4 != 0) price = (price * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
84
+ if (absTick & 0x8 != 0) price = (price * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
85
+ if (absTick & 0x10 != 0) price = (price * 0xffcb9843d60f6159c9db58835c926644) >> 128;
86
+ if (absTick & 0x20 != 0) price = (price * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
87
+ if (absTick & 0x40 != 0) price = (price * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
88
+ if (absTick & 0x80 != 0) price = (price * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
89
+ if (absTick & 0x100 != 0) price = (price * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
90
+ if (absTick & 0x200 != 0) price = (price * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
91
+ if (absTick & 0x400 != 0) price = (price * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
92
+ if (absTick & 0x800 != 0) price = (price * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
93
+ if (absTick & 0x1000 != 0) price = (price * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
94
+ if (absTick & 0x2000 != 0) price = (price * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
95
+ if (absTick & 0x4000 != 0) price = (price * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
96
+ if (absTick & 0x8000 != 0) price = (price * 0x31be135f97d08fd981231505542fcfa6) >> 128;
97
+ if (absTick & 0x10000 != 0) price = (price * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
98
+ if (absTick & 0x20000 != 0) price = (price * 0x5d6af8dedb81196699c329225ee604) >> 128;
99
+ if (absTick & 0x40000 != 0) price = (price * 0x2216e584f5fa1ea926041bedfe98) >> 128;
100
+ if (absTick & 0x80000 != 0) price = (price * 0x48a170391f7dc42444e8fa2) >> 128;
101
+
102
+ assembly ("memory-safe") {
103
+ // if (tick > 0) price = type(uint256).max / price;
104
+ if sgt(tick, 0) {
105
+ price := div(not(0), price)
106
+ }
107
+
108
+ // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
109
+ // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
110
+ // we round up in the division so getTickAtSqrtPrice of the output price is always consistent
111
+ // `sub(shl(32, 1), 1)` is `type(uint32).max`
112
+ // `price + type(uint32).max` will not overflow because `price` fits in 192 bits
113
+ sqrtPriceX96 := shr(32, add(price, sub(shl(32, 1), 1)))
114
+ }
115
+ }
116
+ }
117
+
118
+ /// @notice Calculates the greatest tick value such that getSqrtPriceAtTick(tick) <= sqrtPriceX96
119
+ /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_PRICE, as MIN_SQRT_PRICE is the lowest value getSqrtPriceAtTick may
120
+ /// ever return.
121
+ /// @param sqrtPriceX96 The sqrt price for which to compute the tick as a Q64.96
122
+ /// @return tick The greatest tick for which the getSqrtPriceAtTick(tick) is less than or equal to the input sqrtPriceX96
123
+ function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
124
+ unchecked {
125
+ // Equivalent: if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 >= MAX_SQRT_PRICE) revert InvalidSqrtPrice();
126
+ // second inequality must be >= because the price can never reach the price at the max tick
127
+ // if sqrtPriceX96 < MIN_SQRT_PRICE, the `sub` underflows and `gt` is true
128
+ // if sqrtPriceX96 >= MAX_SQRT_PRICE, sqrtPriceX96 - MIN_SQRT_PRICE > MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1
129
+ if ((sqrtPriceX96 - MIN_SQRT_PRICE) > MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE) {
130
+ InvalidSqrtPrice.selector.revertWith(sqrtPriceX96);
131
+ }
132
+
133
+ uint256 price = uint256(sqrtPriceX96) << 32;
134
+
135
+ uint256 r = price;
136
+ uint256 msb = BitMath.mostSignificantBit(r);
137
+
138
+ if (msb >= 128) r = price >> (msb - 127);
139
+ else r = price << (127 - msb);
140
+
141
+ int256 log_2 = (int256(msb) - 128) << 64;
142
+
143
+ assembly ("memory-safe") {
144
+ r := shr(127, mul(r, r))
145
+ let f := shr(128, r)
146
+ log_2 := or(log_2, shl(63, f))
147
+ r := shr(f, r)
148
+ }
149
+ assembly ("memory-safe") {
150
+ r := shr(127, mul(r, r))
151
+ let f := shr(128, r)
152
+ log_2 := or(log_2, shl(62, f))
153
+ r := shr(f, r)
154
+ }
155
+ assembly ("memory-safe") {
156
+ r := shr(127, mul(r, r))
157
+ let f := shr(128, r)
158
+ log_2 := or(log_2, shl(61, f))
159
+ r := shr(f, r)
160
+ }
161
+ assembly ("memory-safe") {
162
+ r := shr(127, mul(r, r))
163
+ let f := shr(128, r)
164
+ log_2 := or(log_2, shl(60, f))
165
+ r := shr(f, r)
166
+ }
167
+ assembly ("memory-safe") {
168
+ r := shr(127, mul(r, r))
169
+ let f := shr(128, r)
170
+ log_2 := or(log_2, shl(59, f))
171
+ r := shr(f, r)
172
+ }
173
+ assembly ("memory-safe") {
174
+ r := shr(127, mul(r, r))
175
+ let f := shr(128, r)
176
+ log_2 := or(log_2, shl(58, f))
177
+ r := shr(f, r)
178
+ }
179
+ assembly ("memory-safe") {
180
+ r := shr(127, mul(r, r))
181
+ let f := shr(128, r)
182
+ log_2 := or(log_2, shl(57, f))
183
+ r := shr(f, r)
184
+ }
185
+ assembly ("memory-safe") {
186
+ r := shr(127, mul(r, r))
187
+ let f := shr(128, r)
188
+ log_2 := or(log_2, shl(56, f))
189
+ r := shr(f, r)
190
+ }
191
+ assembly ("memory-safe") {
192
+ r := shr(127, mul(r, r))
193
+ let f := shr(128, r)
194
+ log_2 := or(log_2, shl(55, f))
195
+ r := shr(f, r)
196
+ }
197
+ assembly ("memory-safe") {
198
+ r := shr(127, mul(r, r))
199
+ let f := shr(128, r)
200
+ log_2 := or(log_2, shl(54, f))
201
+ r := shr(f, r)
202
+ }
203
+ assembly ("memory-safe") {
204
+ r := shr(127, mul(r, r))
205
+ let f := shr(128, r)
206
+ log_2 := or(log_2, shl(53, f))
207
+ r := shr(f, r)
208
+ }
209
+ assembly ("memory-safe") {
210
+ r := shr(127, mul(r, r))
211
+ let f := shr(128, r)
212
+ log_2 := or(log_2, shl(52, f))
213
+ r := shr(f, r)
214
+ }
215
+ assembly ("memory-safe") {
216
+ r := shr(127, mul(r, r))
217
+ let f := shr(128, r)
218
+ log_2 := or(log_2, shl(51, f))
219
+ r := shr(f, r)
220
+ }
221
+ assembly ("memory-safe") {
222
+ r := shr(127, mul(r, r))
223
+ let f := shr(128, r)
224
+ log_2 := or(log_2, shl(50, f))
225
+ }
226
+
227
+ int256 log_sqrt10001 = log_2 * 255738958999603826347141; // Q22.128 number
228
+
229
+ // Magic number represents the ceiling of the maximum value of the error when approximating log_sqrt10001(x)
230
+ int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
231
+
232
+ // Magic number represents the minimum value of the error when approximating log_sqrt10001(x), when
233
+ // sqrtPrice is from the range (2^-64, 2^64). This is safe as MIN_SQRT_PRICE is more than 2^-64. If MIN_SQRT_PRICE
234
+ // is changed, this may need to be changed too
235
+ int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
236
+
237
+ tick = tickLow == tickHi
238
+ ? tickLow
239
+ : getSqrtPriceAtTick(tickHi) <= sqrtPriceX96
240
+ ? tickHi
241
+ : tickLow;
242
+ }
243
+ }
244
+ }
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /// @dev https://github.com/Uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/UnsafeMath.sol
5
+ /// @title Math functions that do not check inputs or outputs
6
+ /// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
7
+ library UnsafeMath {
8
+ /// @notice Returns ceil(x / y)
9
+ /// @dev division by 0 will return 0, and should be checked externally
10
+ /// @param x The dividend
11
+ /// @param y The divisor
12
+ /// @return z The quotient, ceil(x / y)
13
+ function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
14
+ assembly ("memory-safe") {
15
+ z := add(div(x, y), gt(mod(x, y), 0))
16
+ }
17
+ }
18
+
19
+ /// @notice Calculates floor(a×b÷denominator)
20
+ /// @dev division by 0 will return 0, and should be checked externally
21
+ /// @param a The multiplicand
22
+ /// @param b The multiplier
23
+ /// @param denominator The divisor
24
+ /// @return result The 256-bit result, floor(a×b÷denominator)
25
+ function simpleMulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
26
+ assembly ("memory-safe") {
27
+ result := div(mul(a, b), denominator)
28
+ }
29
+ }
30
+ }
@@ -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.6.1";
12
+ return "0.7.0";
13
13
  }
14
14
  }
package/test/Coin.t.sol CHANGED
@@ -12,7 +12,7 @@ contract CoinTest is BaseTest {
12
12
  }
13
13
 
14
14
  function test_contract_version() public view {
15
- assertEq(coin.contractVersion(), "0.6.0");
15
+ assertEq(coin.contractVersion(), "0.7.0");
16
16
  }
17
17
 
18
18
  function test_supply_constants() public view {
@@ -29,25 +29,27 @@ contract CoinTest is BaseTest {
29
29
 
30
30
  function test_constructor_validation() public {
31
31
  vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
32
- new Coin(address(0), address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER);
32
+ new Coin(address(0), address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER, DOPPLER_AIRLOCK);
33
33
 
34
34
  vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
35
- new Coin(users.feeRecipient, address(0), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER);
35
+ new Coin(users.feeRecipient, address(0), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER, DOPPLER_AIRLOCK);
36
36
 
37
37
  vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
38
- new Coin(users.feeRecipient, address(protocolRewards), address(0), NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER);
38
+ new Coin(users.feeRecipient, address(protocolRewards), address(0), NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER, DOPPLER_AIRLOCK);
39
39
 
40
40
  vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
41
- new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, address(0), SWAP_ROUTER);
41
+ new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, address(0), SWAP_ROUTER, DOPPLER_AIRLOCK);
42
42
 
43
43
  vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
44
- new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, address(0));
44
+ new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, address(0), DOPPLER_AIRLOCK);
45
45
 
46
- Coin newToken = new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER);
46
+ vm.expectRevert(abi.encodeWithSelector(ICoin.AddressZero.selector));
47
+ new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER, address(0));
48
+
49
+ Coin newToken = new Coin(users.feeRecipient, address(protocolRewards), WETH_ADDRESS, NONFUNGIBLE_POSITION_MANAGER, SWAP_ROUTER, DOPPLER_AIRLOCK);
47
50
  assertEq(address(newToken.protocolRewardRecipient()), users.feeRecipient);
48
51
  assertEq(address(newToken.protocolRewards()), address(protocolRewards));
49
52
  assertEq(address(newToken.WETH()), WETH_ADDRESS);
50
- assertEq(address(newToken.nonfungiblePositionManager()), NONFUNGIBLE_POSITION_MANAGER);
51
53
  assertEq(address(newToken.swapRouter()), SWAP_ROUTER);
52
54
  }
53
55
 
@@ -64,12 +66,22 @@ contract CoinTest is BaseTest {
64
66
  "INIT",
65
67
  users.platformReferrer,
66
68
  address(weth),
67
- LP_TICK_LOWER_WETH,
69
+ MarketConstants.LP_TICK_LOWER_WETH,
68
70
  0
69
71
  );
70
72
  coin = Coin(payable(coinAddress));
71
73
 
72
- (coinAddress, ) = factory.deploy(users.creator, owners, "https://init.com", "Init Token", "INIT", address(0), address(weth), LP_TICK_LOWER_WETH, 0);
74
+ (coinAddress, ) = factory.deploy(
75
+ users.creator,
76
+ owners,
77
+ "https://init.com",
78
+ "Init Token",
79
+ "INIT",
80
+ address(0),
81
+ address(weth),
82
+ MarketConstants.LP_TICK_LOWER_WETH,
83
+ 0
84
+ );
73
85
  coin = Coin(payable(coinAddress));
74
86
 
75
87
  assertEq(coin.payoutRecipient(), users.creator);
@@ -91,13 +103,33 @@ contract CoinTest is BaseTest {
91
103
  "INIT",
92
104
  users.platformReferrer,
93
105
  address(weth),
94
- LP_TICK_LOWER_WETH,
106
+ MarketConstants.LP_TICK_LOWER_WETH,
95
107
  0
96
108
  );
97
109
  coin = Coin(payable(coinAddress));
98
110
 
99
111
  vm.expectRevert(abi.encodeWithSignature("InvalidInitialization()"));
100
- coin.initialize(users.creator, new address[](0), "https://init.com", "Init Token", "INIT", users.platformReferrer, address(weth), LP_TICK_LOWER_WETH);
112
+ coin.initialize(users.creator, owners, "https://init.com", "Init Token", "INIT", abi.encode(""), users.platformReferrer);
113
+ }
114
+
115
+ function test_revert_pool_exists() public {
116
+ address[] memory owners = new address[](1);
117
+ owners[0] = users.creator;
118
+
119
+ vm.etch(address(0x1C61DAa59b45525d4fb139106EFEC97c2D8De9be), abi.encode(bytes32(uint256(1))));
120
+
121
+ vm.expectRevert();
122
+ factory.deploy(
123
+ users.creator,
124
+ owners,
125
+ "https://init.com",
126
+ "Init Token",
127
+ "INIT",
128
+ users.platformReferrer,
129
+ address(weth),
130
+ MarketConstants.LP_TICK_LOWER_WETH,
131
+ 0
132
+ );
101
133
  }
102
134
 
103
135
  function test_erc165_interface_support() public view {
@@ -209,11 +241,13 @@ contract CoinTest is BaseTest {
209
241
  vm.prank(users.buyer);
210
242
  weth.approve(address(swapRouter), 100_000);
211
243
 
244
+ assertEq(coin.balanceOf(users.buyer), 0, "buyer coin balance initial");
245
+
212
246
  // Set up the swap parameters
213
247
  ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
214
248
  tokenIn: WETH_ADDRESS,
215
249
  tokenOut: address(coin),
216
- fee: LP_FEE,
250
+ fee: MarketConstants.LP_FEE,
217
251
  recipient: address(users.buyer),
218
252
  amountIn: 100_000,
219
253
  amountOutMinimum: 0,
@@ -235,7 +269,8 @@ contract CoinTest is BaseTest {
235
269
  coin.claimSecondaryRewards(false);
236
270
  assertEq(protocolRewards.balanceOf(users.creator), 499);
237
271
  assertEq(protocolRewards.balanceOf(users.platformReferrer), 249);
238
- assertEq(protocolRewards.balanceOf(users.feeRecipient), 251);
272
+ assertEq(protocolRewards.balanceOf(users.feeRecipient), 202);
273
+ assertEq(dopplerFeeRecipient().balance, 49);
239
274
  }
240
275
 
241
276
  function test_sell_for_eth_direct_and_claim_secondary_push_eth() public {
@@ -251,7 +286,7 @@ contract CoinTest is BaseTest {
251
286
  ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
252
287
  tokenIn: WETH_ADDRESS,
253
288
  tokenOut: address(coin),
254
- fee: LP_FEE,
289
+ fee: MarketConstants.LP_FEE,
255
290
  recipient: address(users.buyer),
256
291
  amountIn: 100_000,
257
292
  amountOutMinimum: 0,
@@ -406,11 +441,6 @@ contract CoinTest is BaseTest {
406
441
  assertTrue(success);
407
442
  }
408
443
 
409
- function test_only_pool_callback() public {
410
- vm.expectRevert(abi.encodeWithSelector(ICoin.OnlyPool.selector));
411
- coin.onERC721Received(address(0), address(0), 0, "");
412
- }
413
-
414
444
  function test_default_platform_referrer() public {
415
445
  address[] memory owners = new address[](1);
416
446
  owners[0] = users.creator;
@@ -423,7 +453,7 @@ contract CoinTest is BaseTest {
423
453
  "TEST",
424
454
  users.platformReferrer,
425
455
  address(weth),
426
- LP_TICK_LOWER_WETH,
456
+ MarketConstants.LP_TICK_LOWER_WETH,
427
457
  0
428
458
  );
429
459
  Coin newCoin = Coin(payable(newCoinAddr));
@@ -438,42 +468,6 @@ contract CoinTest is BaseTest {
438
468
  assertGt(protocolRewards.balanceOf(users.feeRecipient), expectedFees.platformReferrer, "feeRecipient eth balance");
439
469
  }
440
470
 
441
- function test_invalid_weth_tick() public {
442
- address[] memory owners = new address[](1);
443
- owners[0] = users.creator;
444
-
445
- vm.expectRevert(ICoin.InvalidWethLowerTick.selector);
446
- (address newCoinAddr, ) = factory.deploy(
447
- users.creator,
448
- owners,
449
- "https://test.com",
450
- "Test Token",
451
- "TEST",
452
- users.platformReferrer,
453
- address(weth),
454
- LP_TICK_LOWER_WETH - 1,
455
- 0
456
- );
457
- }
458
-
459
- function test_invalid_currency_tick() public {
460
- address[] memory owners = new address[](1);
461
- owners[0] = users.creator;
462
-
463
- vm.expectRevert(ICoin.InvalidCurrencyLowerTick.selector);
464
- (address newCoinAddr, ) = factory.deploy(
465
- users.creator,
466
- owners,
467
- "https://test.com",
468
- "Test Token",
469
- "TEST",
470
- users.platformReferrer,
471
- address(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913),
472
- 20,
473
- 0
474
- );
475
- }
476
-
477
471
  function test_default_order_referrer() public {
478
472
  vm.deal(users.buyer, 1 ether);
479
473
  vm.prank(users.buyer);
@@ -500,12 +494,6 @@ contract CoinTest is BaseTest {
500
494
  coin.sell(users.coinRecipient, 1e18, type(uint256).max, 0, users.tradeReferrer);
501
495
  }
502
496
 
503
- function test_uniswap_swap_callback() public {
504
- // Test swap callback
505
- vm.prank(address(pool));
506
- coin.uniswapV3SwapCallback(100, -100, "");
507
- }
508
-
509
497
  function test_eth_transfer_fail() public {
510
498
  vm.deal(users.buyer, 1 ether);
511
499
  vm.prank(users.buyer);
@@ -524,7 +512,8 @@ contract CoinTest is BaseTest {
524
512
  vm.deal(users.buyer, 1 ether);
525
513
  vm.prank(users.buyer);
526
514
  vm.expectRevert(abi.encodeWithSelector(ICoin.OnlyWeth.selector));
527
- address(coin).call{value: 1 ether}("");
515
+ (bool ignoredSuccess, ) = address(coin).call{value: 1 ether}("");
516
+ (ignoredSuccess);
528
517
 
529
518
  assertEq(address(coin).balance, 0, "coin balance");
530
519
  }
@@ -534,6 +523,7 @@ contract CoinTest is BaseTest {
534
523
  uint256 initialTokenCreatorBalance = protocolRewards.balanceOf(users.creator);
535
524
  uint256 initialOrderReferrerBalance = protocolRewards.balanceOf(users.tradeReferrer);
536
525
  uint256 initialFeeRecipientBalance = protocolRewards.balanceOf(users.feeRecipient);
526
+ uint256 initialDopplerRecipientBalance = airlock.owner().balance;
537
527
 
538
528
  uint256 buyAmount = 1 ether;
539
529
  vm.deal(users.buyer, buyAmount);
@@ -546,7 +536,11 @@ contract CoinTest is BaseTest {
546
536
  uint256 expectedLpFee = 9900000000000000; // 0.99 ETH * 1% --> ~0.00989 ETH
547
537
  MarketRewards memory marketRewards = _calculateMarketRewards(expectedLpFee);
548
538
 
549
- assertEq(marketRewards.creator + marketRewards.platformReferrer + marketRewards.protocol, expectedLpFee, "Secondary rewards incorrect");
539
+ assertEq(
540
+ marketRewards.creator + marketRewards.platformReferrer + marketRewards.protocol + marketRewards.doppler,
541
+ expectedLpFee,
542
+ "Secondary rewards incorrect"
543
+ );
550
544
  assertApproxEqAbs(
551
545
  protocolRewards.balanceOf(users.creator),
552
546
  initialTokenCreatorBalance + orderFees.creator + marketRewards.creator,
@@ -559,6 +553,12 @@ contract CoinTest is BaseTest {
559
553
  0.0000000000000001 ether,
560
554
  "Platform referrer rewards incorrect"
561
555
  );
556
+ assertApproxEqAbs(
557
+ airlock.owner().balance,
558
+ initialDopplerRecipientBalance + marketRewards.doppler,
559
+ 0.0000000000000001 ether,
560
+ "Doppler rewards incorrect"
561
+ );
562
562
  assertApproxEqAbs(
563
563
  protocolRewards.balanceOf(users.feeRecipient),
564
564
  initialFeeRecipientBalance + orderFees.protocol + marketRewards.protocol,