@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.
- package/.turbo/turbo-build.log +69 -55
- package/CHANGELOG.md +12 -0
- package/abis/BaseTest.json +0 -23
- package/abis/Coin.json +186 -77
- package/abis/CoinConfigurationVersions.json +7 -0
- package/abis/CoinSetup.json +7 -0
- package/abis/CoinTest.json +5 -49
- package/abis/CustomRevert.json +28 -0
- package/abis/DopplerUniswapV3Test.json +891 -0
- package/abis/FactoryTest.json +7 -23
- package/abis/IAirlock.json +15 -0
- package/abis/ICoin.json +52 -34
- package/abis/IDopplerErrors.json +44 -0
- package/abis/INonfungiblePositionManager.json +13 -0
- package/abis/IUniswapV3Factory.json +198 -0
- package/abis/IUniswapV3Pool.json +135 -0
- package/abis/MultiOwnableTest.json +0 -23
- package/abis/SafeCast.json +7 -0
- package/abis/Simulate.json +120 -0
- package/abis/SqrtPriceMath.json +22 -0
- package/abis/TickMath.json +24 -0
- package/abis/ZoraFactoryImpl.json +59 -0
- package/addresses/8453.json +3 -3
- package/dist/index.cjs +160 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +160 -39
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +349 -67
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +161 -40
- package/package.json +3 -3
- package/script/CoinsDeployerBase.sol +1 -1
- package/script/Simulate.s.sol +67 -0
- package/src/Coin.sol +159 -90
- package/src/ZoraFactoryImpl.sol +47 -1
- package/src/interfaces/IAirlock.sol +6 -0
- package/src/interfaces/ICoin.sol +18 -2
- package/src/interfaces/IDopplerErrors.sol +14 -0
- package/src/interfaces/INonfungiblePositionManager.sol +2 -0
- package/src/interfaces/IUniswapV3Factory.sol +64 -0
- package/src/interfaces/IUniswapV3Pool.sol +48 -0
- package/src/libs/CoinConfigurationVersions.sol +9 -0
- package/src/libs/CoinDopplerUniV3.sol +202 -0
- package/src/libs/CoinLegacy.sol +48 -0
- package/src/libs/CoinSetup.sol +37 -0
- package/src/libs/MarketConstants.sol +25 -0
- package/src/types/LpPosition.sol +8 -0
- package/src/types/PoolState.sol +24 -0
- package/src/utils/CoinConstants.sol +5 -12
- package/src/utils/uniswap/BitMath.sol +55 -0
- package/src/utils/uniswap/CustomRevert.sol +111 -0
- package/src/utils/uniswap/FixedPoint96.sol +11 -0
- package/src/utils/uniswap/FullMath.sol +118 -0
- package/src/utils/uniswap/LiquidityAmounts.sol +117 -0
- package/src/utils/uniswap/SafeCast.sol +61 -0
- package/src/utils/uniswap/SqrtPriceMath.sol +249 -0
- package/src/utils/uniswap/TickMath.sol +244 -0
- package/src/utils/uniswap/UnsafeMath.sol +30 -0
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +65 -65
- package/test/CoinDopplerUniV3.t.sol +452 -0
- package/test/Factory.t.sol +49 -7
- package/test/utils/BaseTest.sol +26 -7
- package/wagmi.config.ts +1 -1
- package/abis/IERC721Receiver.json +0 -36
- 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,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,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
|
|
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
|
|
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
|
|
51
|
-
|
|
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
|
+
}
|