@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
@@ -40,4 +40,52 @@ interface IUniswapV3Pool {
40
40
  }
41
41
 
42
42
  function slot0() external view returns (Slot0 memory slot0);
43
+
44
+ /// @notice Sets the initial price for the pool
45
+ /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
46
+ /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
47
+ function initialize(uint160 sqrtPriceX96) external;
48
+
49
+ /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
50
+ /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
51
+ /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
52
+ /// on tickLower, tickUpper, the amount of liquidity, and the current price.
53
+ /// @param recipient The address for which the liquidity will be created
54
+ /// @param tickLower The lower tick of the position in which to add liquidity
55
+ /// @param tickUpper The upper tick of the position in which to add liquidity
56
+ /// @param amount The amount of liquidity to mint
57
+ /// @param data Any data that should be passed through to the callback
58
+ /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
59
+ /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
60
+ function mint(address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data) external returns (uint256 amount0, uint256 amount1);
61
+
62
+ /// @notice Collects tokens owed to a position
63
+ /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
64
+ /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
65
+ /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
66
+ /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
67
+ /// @param recipient The address which should receive the fees collected
68
+ /// @param tickLower The lower tick of the position for which to collect fees
69
+ /// @param tickUpper The upper tick of the position for which to collect fees
70
+ /// @param amount0Requested How much token0 should be withdrawn from the fees owed
71
+ /// @param amount1Requested How much token1 should be withdrawn from the fees owed
72
+ /// @return amount0 The amount of fees collected in token0
73
+ /// @return amount1 The amount of fees collected in token1
74
+ function collect(
75
+ address recipient,
76
+ int24 tickLower,
77
+ int24 tickUpper,
78
+ uint128 amount0Requested,
79
+ uint128 amount1Requested
80
+ ) external returns (uint128 amount0, uint128 amount1);
81
+
82
+ /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
83
+ /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
84
+ /// @dev Fees must be collected separately via a call to #collect
85
+ /// @param tickLower The lower tick of the position for which to burn liquidity
86
+ /// @param tickUpper The upper tick of the position for which to burn liquidity
87
+ /// @param amount How much liquidity to burn
88
+ /// @return amount0 The amount of token0 sent to the recipient
89
+ /// @return amount1 The amount of token1 sent to the recipient
90
+ function burn(int24 tickLower, int24 tickUpper, uint128 amount) external returns (uint256 amount0, uint256 amount1);
43
91
  }
@@ -0,0 +1,9 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ library CoinConfigurationVersions {
5
+ uint8 constant LEGACY_POOL_VERSION = 1;
6
+ uint8 constant DOPPLER_UNI_V3_POOL_VERSION = 2;
7
+
8
+ error InvalidPoolVersion();
9
+ }
@@ -0,0 +1,202 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {PoolConfiguration} from "../interfaces/ICoin.sol";
5
+ import {TickMath} from "../utils/uniswap/TickMath.sol";
6
+ import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
7
+ import {ICoin} from "../interfaces/ICoin.sol";
8
+ import {LpPosition} from "../types/LpPosition.sol";
9
+ import {MarketConstants} from "./MarketConstants.sol";
10
+ import {FullMath} from "../utils/uniswap/FullMath.sol";
11
+ import {SqrtPriceMath} from "../utils/uniswap/SqrtPriceMath.sol";
12
+ import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
13
+ import {IDopplerErrors} from "../interfaces/IDopplerErrors.sol";
14
+
15
+ library CoinDopplerUniV3 {
16
+ function setupPool(bool isCoinToken0, bytes memory poolConfig_) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
17
+ (, , int24 tickLower_, int24 tickUpper_, uint16 numDiscoveryPositions_, uint256 maxDiscoverySupplyShare_) = abi.decode(
18
+ poolConfig_,
19
+ (uint8, address, int24, int24, uint16, uint256)
20
+ );
21
+
22
+ require(numDiscoveryPositions_ > 1 && numDiscoveryPositions_ <= 200, IDopplerErrors.NumDiscoveryPositionsOutOfRange());
23
+
24
+ if (maxDiscoverySupplyShare_ > MarketConstants.WAD) {
25
+ revert IDopplerErrors.MaxShareToBeSoldExceeded(maxDiscoverySupplyShare_, MarketConstants.WAD);
26
+ }
27
+
28
+ int24 savedTickLower = isCoinToken0 ? tickLower_ : -tickUpper_;
29
+ int24 savedTickUpper = isCoinToken0 ? tickUpper_ : -tickLower_;
30
+
31
+ sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? savedTickLower : savedTickUpper);
32
+
33
+ poolConfiguration = PoolConfiguration({
34
+ version: CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
35
+ tickLower: savedTickLower,
36
+ tickUpper: savedTickUpper,
37
+ numPositions: numDiscoveryPositions_,
38
+ maxDiscoverySupplyShare: maxDiscoverySupplyShare_
39
+ });
40
+ }
41
+
42
+ function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
43
+ positions = new LpPosition[](poolConfiguration.numPositions);
44
+
45
+ uint256 discoverySupply = FullMath.mulDiv(MarketConstants.POOL_LAUNCH_SUPPLY, poolConfiguration.maxDiscoverySupplyShare, MarketConstants.WAD);
46
+
47
+ (positions, discoverySupply) = calculateLogNormalDistribution(
48
+ poolConfiguration.tickLower,
49
+ poolConfiguration.tickUpper,
50
+ MarketConstants.TICK_SPACING,
51
+ isCoinToken0,
52
+ discoverySupply,
53
+ // Populate all positions before the last position (the tail position)
54
+ poolConfiguration.numPositions - 1, // Only discovery positions
55
+ positions
56
+ );
57
+
58
+ uint256 tailSupply = MarketConstants.POOL_LAUNCH_SUPPLY - discoverySupply;
59
+
60
+ // Calculate the tail position (the last position in the array)
61
+ positions[poolConfiguration.numPositions - 1] = calculateLpTail(
62
+ poolConfiguration.tickLower,
63
+ poolConfiguration.tickUpper,
64
+ isCoinToken0,
65
+ tailSupply,
66
+ MarketConstants.TICK_SPACING
67
+ );
68
+ }
69
+
70
+ /// @notice Calculates the distribution of liquidity positions across tick ranges.
71
+ /// @dev For example, with 1000 tokens and 10 bins starting at tick 0:
72
+ /// - Creates positions: [0,10], [1,10], [2,10], ..., [9,10]
73
+ /// - Each position gets an equal share of tokens (100 tokens each)
74
+ /// This creates a linear distribution of liquidity across the tick range
75
+ /// @dev Changed in DopplerUniswapV3:
76
+ /// - Added `LpPosition[] memory newPositions` as an input parameter, removing the internal allocation (`new LpPosition[](totalPositions + 1)`).
77
+ /// - Removed the calculation and accumulation of the `reserves` variable entirely.
78
+ /// - Return value changed from `(LpPosition[] memory, uint256)` (positions, reserves) to `(LpPosition[] memory, uint256)` (positions, totalAssetsSold).
79
+ /// @param tickLower The lower tick of the LP range set
80
+ /// @param tickUpper The upper tick of the LP range set
81
+ /// @param tickSpacing The tick spacing of the LP range set
82
+ /// @param isToken0 Whether the base asset is the token0 of the pair
83
+ /// @param discoverySupply The total supply of the base asset to be sold
84
+ /// @param totalPositions The total number of positions in the LP range set
85
+ /// @param newPositions The array of new positions to be created
86
+ /// @return newPositions The array of new positions to be created
87
+ /// @return totalAssetsSold The total assets used in the LP range set
88
+ function calculateLogNormalDistribution(
89
+ int24 tickLower,
90
+ int24 tickUpper,
91
+ int24 tickSpacing,
92
+ bool isToken0,
93
+ uint256 discoverySupply,
94
+ uint16 totalPositions,
95
+ LpPosition[] memory newPositions
96
+ ) internal pure returns (LpPosition[] memory, uint256) {
97
+ int24 farTick = isToken0 ? tickUpper : tickLower;
98
+ int24 closeTick = isToken0 ? tickLower : tickUpper;
99
+
100
+ int24 spread = tickUpper - tickLower;
101
+
102
+ uint160 farSqrtPriceX96 = TickMath.getSqrtPriceAtTick(farTick);
103
+ uint256 amountPerPosition = FullMath.mulDiv(discoverySupply, MarketConstants.WAD, totalPositions * MarketConstants.WAD);
104
+ uint256 totalAssetsSold;
105
+
106
+ for (uint256 i; i < totalPositions; i++) {
107
+ // calculate the ticks position * 1/n to optimize the division
108
+ int24 startingTick = isToken0
109
+ ? closeTick + int24(uint24(FullMath.mulDiv(i, uint256(uint24(spread)), totalPositions)))
110
+ : closeTick - int24(uint24(FullMath.mulDiv(i, uint256(uint24(spread)), totalPositions)));
111
+
112
+ // round the tick to the nearest bin
113
+ startingTick = alignTickToTickSpacing(isToken0, startingTick, tickSpacing);
114
+
115
+ if (startingTick != farTick) {
116
+ uint160 startingSqrtPriceX96 = TickMath.getSqrtPriceAtTick(startingTick);
117
+
118
+ // if discoverySupply is 0, we skip the liquidity calculation as we are burning max liquidity
119
+ // in each position
120
+ uint128 liquidity;
121
+ if (discoverySupply != 0) {
122
+ liquidity = isToken0
123
+ ? LiquidityAmounts.getLiquidityForAmount0(startingSqrtPriceX96, farSqrtPriceX96, amountPerPosition)
124
+ : LiquidityAmounts.getLiquidityForAmount1(farSqrtPriceX96, startingSqrtPriceX96, amountPerPosition);
125
+
126
+ totalAssetsSold += (
127
+ isToken0
128
+ ? SqrtPriceMath.getAmount0Delta(startingSqrtPriceX96, farSqrtPriceX96, liquidity, true)
129
+ : SqrtPriceMath.getAmount1Delta(farSqrtPriceX96, startingSqrtPriceX96, liquidity, true)
130
+ );
131
+ }
132
+
133
+ newPositions[i] = LpPosition({
134
+ tickLower: farSqrtPriceX96 < startingSqrtPriceX96 ? farTick : startingTick,
135
+ tickUpper: farSqrtPriceX96 < startingSqrtPriceX96 ? startingTick : farTick,
136
+ liquidity: liquidity
137
+ });
138
+ }
139
+ }
140
+
141
+ require(totalAssetsSold <= discoverySupply, IDopplerErrors.CannotMintZeroLiquidity());
142
+
143
+ return (newPositions, totalAssetsSold);
144
+ }
145
+
146
+ /// @notice Calculates the final LP position that extends from the far tick to the pool's min/max tick
147
+ /// @dev This position ensures price equivalence between Uniswap v2 and v3 pools beyond the LBP range
148
+ /// @dev Changed in DopplerUniswapV3:
149
+ /// - Removed parameters: `id`, `reserves`
150
+ /// - Liquidity calculation is based *solely* on the provided `tailSupply` within the calculated tail tick range using `LiquidityAmounts.getLiquidityForAmount0` or `getLiquidityForAmount1`.
151
+ function calculateLpTail(
152
+ int24 tickLower,
153
+ int24 tickUpper,
154
+ bool isToken0,
155
+ uint256 tailSupply,
156
+ int24 tickSpacing
157
+ ) internal pure returns (LpPosition memory lpTail) {
158
+ int24 posTickLower = isToken0 ? tickUpper : alignTickToTickSpacing(false, TickMath.MIN_TICK, tickSpacing);
159
+ int24 posTickUpper = isToken0 ? alignTickToTickSpacing(true, TickMath.MAX_TICK, tickSpacing) : tickLower;
160
+
161
+ require(posTickLower < posTickUpper, IDopplerErrors.InvalidTickRangeMisordered(posTickLower, posTickUpper));
162
+
163
+ // Calculate the sqrtPrices for the tail range boundaries
164
+ uint160 sqrtPriceA = TickMath.getSqrtPriceAtTick(posTickLower);
165
+ uint160 sqrtPriceB = TickMath.getSqrtPriceAtTick(posTickUpper);
166
+
167
+ // Calculate liquidity only based on the tail range supply
168
+ uint128 lpTailLiquidity = isToken0
169
+ ? LiquidityAmounts.getLiquidityForAmount0(sqrtPriceA, sqrtPriceB, tailSupply)
170
+ : LiquidityAmounts.getLiquidityForAmount1(sqrtPriceA, sqrtPriceB, tailSupply);
171
+
172
+ lpTail = LpPosition({tickLower: posTickLower, tickUpper: posTickUpper, liquidity: lpTailLiquidity});
173
+ }
174
+
175
+ /// @notice Aligns a tick to the nearest tick spacing
176
+ /// @dev The tickSpacing parameter cannot be zero
177
+ /// @param isToken0 Whether the base asset is the token0 of the pair
178
+ /// @param tick The tick to align
179
+ /// @param tickSpacing The tick spacing of the pair
180
+ /// @return alignedTick The aligned tick
181
+ function alignTickToTickSpacing(bool isToken0, int24 tick, int24 tickSpacing) internal pure returns (int24) {
182
+ if (isToken0) {
183
+ // Round down if isToken0
184
+ if (tick < 0) {
185
+ // If the tick is negative, we round up (negatively) the negative result to round down
186
+ return ((tick - tickSpacing + 1) / tickSpacing) * tickSpacing;
187
+ } else {
188
+ // Else if positive, we simply round down
189
+ return (tick / tickSpacing) * tickSpacing;
190
+ }
191
+ } else {
192
+ // Round up if isToken1
193
+ if (tick < 0) {
194
+ // If the tick is negative, we round down the negative result to round up
195
+ return (tick / tickSpacing) * tickSpacing;
196
+ } else {
197
+ // Else if positive, we simply round up
198
+ return ((tick + tickSpacing - 1) / tickSpacing) * tickSpacing;
199
+ }
200
+ }
201
+ }
202
+ }
@@ -0,0 +1,48 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {PoolConfiguration, ICoin} from "../interfaces/ICoin.sol";
5
+ import {TickMath} from "../utils/uniswap/TickMath.sol";
6
+ import {MarketConstants} from "./MarketConstants.sol";
7
+ import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
8
+ import {LpPosition} from "../types/LpPosition.sol";
9
+ import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
10
+
11
+ library CoinLegacy {
12
+ function setupPool(
13
+ bool isCoinToken0,
14
+ bytes memory poolConfig_,
15
+ address weth
16
+ ) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
17
+ (, address currency, int24 tickLower_) = abi.decode(poolConfig_, (uint8, address, int24));
18
+
19
+ // If WETH is the pool's currency, validate the lower tick
20
+ if ((currency == weth || currency == address(0)) && tickLower_ > MarketConstants.LP_TICK_LOWER_WETH) {
21
+ revert ICoin.InvalidWethLowerTick();
22
+ }
23
+
24
+ int24 savedTickLower = isCoinToken0 ? tickLower_ : -MarketConstants.LP_TICK_UPPER;
25
+ int24 savedTickUpper = isCoinToken0 ? MarketConstants.LP_TICK_UPPER : -tickLower_;
26
+
27
+ sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? savedTickLower : savedTickUpper);
28
+
29
+ poolConfiguration = PoolConfiguration({
30
+ version: CoinConfigurationVersions.LEGACY_POOL_VERSION,
31
+ tickLower: savedTickLower,
32
+ tickUpper: savedTickUpper,
33
+ numPositions: 1,
34
+ maxDiscoverySupplyShare: 0
35
+ });
36
+ }
37
+
38
+ function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
39
+ positions = new LpPosition[](1);
40
+
41
+ uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? poolConfiguration.tickLower : poolConfiguration.tickUpper);
42
+ uint160 farSqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? poolConfiguration.tickUpper : poolConfiguration.tickLower);
43
+ uint128 liquidity = isCoinToken0
44
+ ? LiquidityAmounts.getLiquidityForAmount0(sqrtPriceX96, farSqrtPriceX96, MarketConstants.POOL_LAUNCH_SUPPLY)
45
+ : LiquidityAmounts.getLiquidityForAmount1(sqrtPriceX96, farSqrtPriceX96, MarketConstants.POOL_LAUNCH_SUPPLY);
46
+ positions[0] = LpPosition({tickLower: poolConfiguration.tickLower, tickUpper: poolConfiguration.tickUpper, liquidity: liquidity});
47
+ }
48
+ }
@@ -0,0 +1,37 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {PoolConfiguration} from "../interfaces/ICoin.sol";
5
+ import {CoinLegacy} from "./CoinLegacy.sol";
6
+ import {CoinDopplerUniV3} from "./CoinDopplerUniV3.sol";
7
+ import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
8
+ import {LpPosition} from "../types/LpPosition.sol";
9
+
10
+ library CoinSetup {
11
+ error InvalidPoolVersion();
12
+
13
+ function setupPoolWithVersion(
14
+ uint8 version,
15
+ bytes memory poolConfig_,
16
+ bool isCoinToken0,
17
+ address weth
18
+ ) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
19
+ if (version == CoinConfigurationVersions.LEGACY_POOL_VERSION) {
20
+ (sqrtPriceX96, poolConfiguration) = CoinLegacy.setupPool(isCoinToken0, poolConfig_, weth);
21
+ } else if (version == CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION) {
22
+ (sqrtPriceX96, poolConfiguration) = CoinDopplerUniV3.setupPool(isCoinToken0, poolConfig_);
23
+ } else {
24
+ revert InvalidPoolVersion();
25
+ }
26
+ }
27
+
28
+ function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
29
+ if (poolConfiguration.version == CoinConfigurationVersions.LEGACY_POOL_VERSION) {
30
+ positions = CoinLegacy.calculatePositions(isCoinToken0, poolConfiguration);
31
+ } else if (poolConfiguration.version == CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION) {
32
+ positions = CoinDopplerUniV3.calculatePositions(isCoinToken0, poolConfiguration);
33
+ } else {
34
+ revert InvalidPoolVersion();
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ library MarketConstants {
5
+ /// @notice The number of coins allocated to the liquidity pool
6
+ /// @dev 990 million coins
7
+ uint256 internal constant POOL_LAUNCH_SUPPLY = 990_000_000e18;
8
+
9
+ /// @dev Constant used to increase precision during calculations
10
+ uint256 constant WAD = 1e18;
11
+
12
+ /// @notice The LP fee
13
+ /// @dev 10000 basis points = 1%
14
+ uint24 internal constant LP_FEE = 10000;
15
+
16
+ /// @notice The spacing for 1% pools
17
+ /// @dev 200 ticks
18
+ int24 internal constant TICK_SPACING = 200;
19
+
20
+ /// @notice The minimum lower tick for legacy single LP WETH pools
21
+ int24 internal constant LP_TICK_LOWER_WETH = -208200;
22
+
23
+ /// @notice The upper tick for legacy single LP WETH pools
24
+ int24 internal constant LP_TICK_UPPER = 887200;
25
+ }
@@ -0,0 +1,8 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ struct LpPosition {
5
+ int24 tickLower;
6
+ int24 tickUpper;
7
+ uint128 liquidity;
8
+ }
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ /// @notice The state of the pool configuration, as a doppler configuration
5
+ struct PoolState {
6
+ /// @notice The address of the base asset
7
+ address asset;
8
+ /// @notice The address of the currency to trade the base asset for
9
+ address numeraire;
10
+ /// @notice The lower tick of the LP range set
11
+ int24 tickLower;
12
+ /// @notice The upper tick of the LP range set
13
+ int24 tickUpper;
14
+ /// @notice The number of positions in the LP range set
15
+ uint16 numPositions;
16
+ /// @notice Whether the pool is initialized (true for this implementation)
17
+ bool isInitialized;
18
+ /// @notice Whether the pool is exited to a market (false for this implementation)
19
+ bool isExited;
20
+ /// @notice The maximum share to be sold – the size of the discovery supply
21
+ uint256 maxShareToBeSold;
22
+ /// @notice The total tokens on the bonding curve
23
+ uint256 totalTokensOnBondingCurve;
24
+ }
@@ -39,21 +39,14 @@ abstract contract CoinConstants {
39
39
  uint256 public constant TRADE_REFERRER_FEE_BPS = 1500;
40
40
 
41
41
  /// @notice The percentage of the LP fee allocated to creators
42
- /// @dev 5000 basis points = 50% of LP_FEE
42
+ /// @dev 5000 basis points = 50% of the 1% LP FEE
43
43
  uint256 internal constant CREATOR_MARKET_REWARD_BPS = 5000;
44
44
 
45
45
  /// @notice The percentage of the LP fee allocated to platform referrers
46
- /// @dev 2500 basis points = 25% of LP_FEE
46
+ /// @dev 2500 basis points = 25% of the 1% LP FEE
47
47
  uint256 internal constant PLATFORM_REFERRER_MARKET_REWARD_BPS = 2500;
48
48
 
49
- /// @notice The LP fee
50
- /// @dev 10000 basis points = 1%
51
- uint24 internal constant LP_FEE = 10000;
52
-
53
- /// @notice The LP's minimum lower tick
54
- /// @dev This is only used if the currency is WETH
55
- int24 internal constant LP_TICK_LOWER_WETH = -208200;
56
-
57
- /// @notice The LP's upper tick
58
- int24 internal constant LP_TICK_UPPER = 887200;
49
+ /// @notice The percentage of the LP fee allocated to the Doppler protocol
50
+ /// @dev 500 basis points = 5% of the 1% LP FEE
51
+ uint256 internal constant DOPPLER_MARKET_REWARD_BPS = 500;
59
52
  }
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /// @dev https://github.com/Uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/BitMath.sol
5
+ /// @title BitMath
6
+ /// @dev This library provides functionality for computing bit properties of an unsigned integer
7
+ /// @author Solady (https://github.com/Vectorized/solady/blob/8200a70e8dc2a77ecb074fc2e99a2a0d36547522/src/utils/LibBit.sol)
8
+ library BitMath {
9
+ /// @notice Returns the index of the most significant bit of the number,
10
+ /// where the least significant bit is at index 0 and the most significant bit is at index 255
11
+ /// @param x the value for which to compute the most significant bit, must be greater than 0
12
+ /// @return r the index of the most significant bit
13
+ function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
14
+ require(x > 0);
15
+
16
+ assembly ("memory-safe") {
17
+ r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
18
+ r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
19
+ r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
20
+ r := or(r, shl(4, lt(0xffff, shr(r, x))))
21
+ r := or(r, shl(3, lt(0xff, shr(r, x))))
22
+ // forgefmt: disable-next-item
23
+ r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), 0x0706060506020500060203020504000106050205030304010505030400000000))
24
+ }
25
+ }
26
+
27
+ /// @notice Returns the index of the least significant bit of the number,
28
+ /// where the least significant bit is at index 0 and the most significant bit is at index 255
29
+ /// @param x the value for which to compute the least significant bit, must be greater than 0
30
+ /// @return r the index of the least significant bit
31
+ function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
32
+ require(x > 0);
33
+
34
+ assembly ("memory-safe") {
35
+ // Isolate the least significant bit.
36
+ x := and(x, sub(0, x))
37
+ // For the upper 3 bits of the result, use a De Bruijn-like lookup.
38
+ // Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
39
+ // forgefmt: disable-next-item
40
+ r := shl(
41
+ 5,
42
+ shr(
43
+ 252,
44
+ shl(
45
+ shl(2, shr(250, mul(x, 0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
46
+ 0x8040405543005266443200005020610674053026020000107506200176117077
47
+ )
48
+ )
49
+ )
50
+ // For the lower 5 bits of the result, use a De Bruijn lookup.
51
+ // forgefmt: disable-next-item
52
+ r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f), 0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,111 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /// @dev https://github.com/Uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/CustomRevert.sol
5
+ /// @title Library for reverting with custom errors efficiently
6
+ /// @notice Contains functions for reverting with custom errors with different argument types efficiently
7
+ /// @dev To use this library, declare `using CustomRevert for bytes4;` and replace `revert CustomError()` with
8
+ /// `CustomError.selector.revertWith()`
9
+ /// @dev The functions may tamper with the free memory pointer but it is fine since the call context is exited immediately
10
+ library CustomRevert {
11
+ /// @dev ERC-7751 error for wrapping bubbled up reverts
12
+ error WrappedError(address target, bytes4 selector, bytes reason, bytes details);
13
+
14
+ /// @dev Reverts with the selector of a custom error in the scratch space
15
+ function revertWith(bytes4 selector) internal pure {
16
+ assembly ("memory-safe") {
17
+ mstore(0, selector)
18
+ revert(0, 0x04)
19
+ }
20
+ }
21
+
22
+ /// @dev Reverts with a custom error with an address argument in the scratch space
23
+ function revertWith(bytes4 selector, address addr) internal pure {
24
+ assembly ("memory-safe") {
25
+ mstore(0, selector)
26
+ mstore(0x04, and(addr, 0xffffffffffffffffffffffffffffffffffffffff))
27
+ revert(0, 0x24)
28
+ }
29
+ }
30
+
31
+ /// @dev Reverts with a custom error with an int24 argument in the scratch space
32
+ function revertWith(bytes4 selector, int24 value) internal pure {
33
+ assembly ("memory-safe") {
34
+ mstore(0, selector)
35
+ mstore(0x04, signextend(2, value))
36
+ revert(0, 0x24)
37
+ }
38
+ }
39
+
40
+ /// @dev Reverts with a custom error with a uint160 argument in the scratch space
41
+ function revertWith(bytes4 selector, uint160 value) internal pure {
42
+ assembly ("memory-safe") {
43
+ mstore(0, selector)
44
+ mstore(0x04, and(value, 0xffffffffffffffffffffffffffffffffffffffff))
45
+ revert(0, 0x24)
46
+ }
47
+ }
48
+
49
+ /// @dev Reverts with a custom error with two int24 arguments
50
+ function revertWith(bytes4 selector, int24 value1, int24 value2) internal pure {
51
+ assembly ("memory-safe") {
52
+ let fmp := mload(0x40)
53
+ mstore(fmp, selector)
54
+ mstore(add(fmp, 0x04), signextend(2, value1))
55
+ mstore(add(fmp, 0x24), signextend(2, value2))
56
+ revert(fmp, 0x44)
57
+ }
58
+ }
59
+
60
+ /// @dev Reverts with a custom error with two uint160 arguments
61
+ function revertWith(bytes4 selector, uint160 value1, uint160 value2) internal pure {
62
+ assembly ("memory-safe") {
63
+ let fmp := mload(0x40)
64
+ mstore(fmp, selector)
65
+ mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
66
+ mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
67
+ revert(fmp, 0x44)
68
+ }
69
+ }
70
+
71
+ /// @dev Reverts with a custom error with two address arguments
72
+ function revertWith(bytes4 selector, address value1, address value2) internal pure {
73
+ assembly ("memory-safe") {
74
+ let fmp := mload(0x40)
75
+ mstore(fmp, selector)
76
+ mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
77
+ mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
78
+ revert(fmp, 0x44)
79
+ }
80
+ }
81
+
82
+ /// @notice bubble up the revert message returned by a call and revert with a wrapped ERC-7751 error
83
+ /// @dev this method can be vulnerable to revert data bombs
84
+ function bubbleUpAndRevertWith(address revertingContract, bytes4 revertingFunctionSelector, bytes4 additionalContext) internal pure {
85
+ bytes4 wrappedErrorSelector = WrappedError.selector;
86
+ assembly ("memory-safe") {
87
+ // Ensure the size of the revert data is a multiple of 32 bytes
88
+ let encodedDataSize := mul(div(add(returndatasize(), 31), 32), 32)
89
+
90
+ let fmp := mload(0x40)
91
+
92
+ // Encode wrapped error selector, address, function selector, offset, additional context, size, revert reason
93
+ mstore(fmp, wrappedErrorSelector)
94
+ mstore(add(fmp, 0x04), and(revertingContract, 0xffffffffffffffffffffffffffffffffffffffff))
95
+ mstore(add(fmp, 0x24), and(revertingFunctionSelector, 0xffffffff00000000000000000000000000000000000000000000000000000000))
96
+ // offset revert reason
97
+ mstore(add(fmp, 0x44), 0x80)
98
+ // offset additional context
99
+ mstore(add(fmp, 0x64), add(0xa0, encodedDataSize))
100
+ // size revert reason
101
+ mstore(add(fmp, 0x84), returndatasize())
102
+ // revert reason
103
+ returndatacopy(add(fmp, 0xa4), 0, returndatasize())
104
+ // size additional context
105
+ mstore(add(fmp, add(0xa4, encodedDataSize)), 0x04)
106
+ // additional context
107
+ mstore(add(fmp, add(0xc4, encodedDataSize)), and(additionalContext, 0xffffffff00000000000000000000000000000000000000000000000000000000))
108
+ revert(fmp, add(0xe4, encodedDataSize))
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /// @dev https://github.com/Uniswap/v4-core/blob/80311e34080fee64b6fc6c916e9a51a437d0e482/src/libraries/FixedPoint96.sol
5
+ /// @title FixedPoint96
6
+ /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
7
+ /// @dev Used in SqrtPriceMath.sol
8
+ library FixedPoint96 {
9
+ uint8 internal constant RESOLUTION = 96;
10
+ uint256 internal constant Q96 = 0x1000000000000000000000000;
11
+ }