@zoralabs/coins 0.9.0 → 1.0.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 +131 -114
- package/CHANGELOG.md +40 -0
- package/abis/BaseCoin.json +26 -118
- package/abis/BaseTest.json +47 -0
- package/abis/Coin.json +171 -63
- package/abis/CoinDopplerMultiCurve.json +38 -0
- package/abis/CoinRewardsV4.json +54 -0
- package/abis/CoinTest.json +53 -20
- package/abis/CoinUniV4Test.json +1053 -0
- package/abis/CoinV4.json +234 -211
- package/abis/DeployScript.json +47 -0
- package/abis/DeployedCoinVersionLookup.json +21 -0
- package/abis/DeployedCoinVersionLookupTest.json +716 -0
- package/abis/DifferentNamespaceVersionLookup.json +39 -0
- package/abis/DopplerUniswapV3Test.json +49 -93
- package/abis/ERC20.json +310 -0
- package/abis/FactoryTest.json +85 -7
- package/abis/FeeEstimatorHook.json +1528 -0
- package/abis/HooksDeployment.json +23 -0
- package/abis/HooksTest.json +47 -0
- package/abis/ICoin.json +40 -71
- package/abis/ICoinV3.json +879 -0
- package/abis/ICoinV4.json +915 -0
- package/abis/IDeployedCoinVersionLookup.json +21 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasPoolKey.json +42 -0
- package/abis/IHasRewardsRecipients.json +54 -0
- package/abis/IHasSwapPath.json +60 -0
- package/abis/IMsgSender.json +15 -0
- package/abis/IPoolConfigEncoding.json +46 -0
- package/abis/ISwapPathRouter.json +92 -0
- package/abis/IUniversalRouter.json +61 -0
- package/abis/IUnlockCallback.json +21 -0
- package/abis/IV4Quoter.json +310 -0
- package/abis/IZoraFactory.json +191 -11
- package/abis/IZoraV4CoinHook.json +348 -4
- package/abis/MockERC20.json +21 -0
- package/abis/MultiOwnableTest.json +47 -0
- package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
- package/abis/PrintUpgradeCommand.json +9 -0
- package/abis/ProxyShim.json +24 -0
- package/abis/StateLibrary.json +80 -0
- package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
- package/abis/TestV4Swap.json +9 -0
- package/abis/UpgradeCoinImpl.json +47 -0
- package/abis/UpgradesTest.json +67 -0
- package/abis/Vm.json +1482 -111
- package/abis/VmSafe.json +856 -32
- package/abis/ZoraFactoryImpl.json +339 -1
- package/abis/ZoraV4CoinHook.json +455 -5
- package/addresses/8453.json +8 -4
- package/addresses/84532.json +8 -4
- package/dist/index.cjs +1920 -169
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1916 -169
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +2599 -183
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +1928 -165
- package/package.json +8 -3
- package/remappings.txt +6 -1
- package/script/CoinsDeployerBase.sol +74 -11
- package/script/DeployDevFactory.s.sol +21 -0
- package/script/PrintUpgradeCommand.s.sol +13 -0
- package/script/Simulate.s.sol +1 -10
- package/script/TestBackingCoinSwap.s.sol +146 -0
- package/script/TestV4Swap.s.sol +136 -0
- package/script/UpgradeFactoryImpl.s.sol +1 -1
- package/src/BaseCoin.sol +176 -0
- package/src/Coin.sol +87 -202
- package/src/CoinV4.sol +121 -0
- package/src/ZoraFactoryImpl.sol +208 -36
- package/src/hooks/ZoraV4CoinHook.sol +195 -0
- package/src/hooks/{BaseCoinDeployHook.sol → deployment/BaseCoinDeployHook.sol} +3 -3
- package/src/hooks/{BuySupplyWithSwapRouterHook.sol → deployment/BuySupplyWithSwapRouterHook.sol} +7 -5
- package/src/interfaces/ICoin.sol +31 -39
- package/src/interfaces/ICoinV3.sol +71 -0
- package/src/interfaces/ICoinV4.sol +69 -0
- package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
- package/src/interfaces/IMsgSender.sol +9 -0
- package/src/interfaces/IPoolConfigEncoding.sol +14 -0
- package/src/interfaces/ISwapPathRouter.sol +14 -0
- package/src/interfaces/IZoraFactory.sol +65 -27
- package/src/interfaces/IZoraV4CoinHook.sol +116 -0
- package/src/libs/CoinCommon.sol +15 -0
- package/src/libs/CoinConfigurationVersions.sol +116 -1
- package/src/libs/CoinConstants.sol +5 -0
- package/src/libs/CoinDopplerMultiCurve.sol +134 -0
- package/src/libs/CoinDopplerUniV3.sol +19 -171
- package/src/libs/CoinRewards.sol +195 -0
- package/src/libs/CoinRewardsV4.sol +180 -0
- package/src/libs/CoinSetup.sol +57 -0
- package/src/libs/CoinSetupV3.sol +6 -67
- package/src/libs/DopplerMath.sol +156 -0
- package/src/libs/HooksDeployment.sol +84 -0
- package/src/libs/MarketConstants.sol +4 -0
- package/src/libs/PoolStateReader.sol +22 -0
- package/src/libs/UniV3BuySell.sol +74 -292
- package/src/libs/UniV4SwapHelper.sol +65 -0
- package/src/libs/UniV4SwapToCurrency.sol +109 -0
- package/src/libs/V4Liquidity.sol +129 -0
- package/src/types/PoolConfiguration.sol +15 -0
- package/src/utils/DeployedCoinVersionLookup.sol +52 -0
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +78 -88
- package/test/CoinDopplerUniV3.t.sol +32 -171
- package/test/CoinUniV4.t.sol +752 -0
- package/test/{Hooks.t.sol → DeploymentHooks.t.sol} +2 -6
- package/test/Factory.t.sol +80 -47
- package/test/MultiOwnable.t.sol +6 -3
- package/test/Upgrades.t.sol +6 -5
- package/test/mocks/MockERC20.sol +12 -0
- package/test/utils/BaseTest.sol +106 -56
- package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
- package/test/utils/FeeEstimatorHook.sol +84 -0
- package/test/utils/ProxyShim.sol +17 -0
- package/wagmi.config.ts +4 -0
- package/.env +0 -1
- package/.turbo/turbo-update-contract-version.log +0 -22
- package/abis/CoinSetupV3.json +0 -7
- package/abis/HookDeployer.json +0 -68
- package/abis/IHookDeployer.json +0 -42
- package/src/libs/CoinLegacy.sol +0 -48
- package/src/libs/CoinLegacyMarket.sol +0 -182
|
@@ -1,9 +1,124 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.23;
|
|
3
3
|
|
|
4
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
5
|
+
|
|
4
6
|
library CoinConfigurationVersions {
|
|
5
7
|
uint8 constant LEGACY_POOL_VERSION = 1;
|
|
6
8
|
uint8 constant DOPPLER_UNI_V3_POOL_VERSION = 2;
|
|
9
|
+
uint8 constant DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION = 4;
|
|
10
|
+
|
|
11
|
+
function getVersion(bytes memory poolConfig) internal pure returns (uint8 version) {
|
|
12
|
+
return (version) = abi.decode(poolConfig, (uint8));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isV3(uint8 version) internal pure returns (bool) {
|
|
16
|
+
return version == DOPPLER_UNI_V3_POOL_VERSION || version == LEGACY_POOL_VERSION;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isV4(uint8 version) internal pure returns (bool) {
|
|
20
|
+
return version == DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function decodeVersionAndCurrency(bytes memory poolConfig) internal pure returns (uint8 version, address currency) {
|
|
24
|
+
(version, currency) = abi.decode(poolConfig, (uint8, address));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function decodeDopplerUniV3(
|
|
28
|
+
bytes memory poolConfig
|
|
29
|
+
)
|
|
30
|
+
internal
|
|
31
|
+
pure
|
|
32
|
+
returns (uint8 version, address currency, int24 tickLower_, int24 tickUpper_, uint16 numDiscoveryPositions_, uint256 maxDiscoverySupplyShare_)
|
|
33
|
+
{
|
|
34
|
+
(version, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_) = abi.decode(
|
|
35
|
+
poolConfig,
|
|
36
|
+
(uint8, address, int24, int24, uint16, uint256)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function encodeDopplerUniV3(
|
|
41
|
+
address currency,
|
|
42
|
+
int24 tickLower_,
|
|
43
|
+
int24 tickUpper_,
|
|
44
|
+
uint16 numDiscoveryPositions_,
|
|
45
|
+
uint256 maxDiscoverySupplyShare_
|
|
46
|
+
) internal pure returns (bytes memory) {
|
|
47
|
+
return abi.encode(DOPPLER_UNI_V3_POOL_VERSION, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function decodeLegacy(bytes memory poolConfig) internal pure returns (uint8 version, address currency, int24 tickLower_) {
|
|
51
|
+
(version, currency, tickLower_) = abi.decode(poolConfig, (uint8, address, int24));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function decodeVanillaUniV4(bytes memory poolConfig) internal pure returns (uint8 version, address currency, int24 tickLower_) {
|
|
55
|
+
(version, currency, tickLower_) = abi.decode(poolConfig, (uint8, address, int24));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function encodeDopplerMultiCurveUniV4(
|
|
59
|
+
address currency,
|
|
60
|
+
int24[] memory tickLower_,
|
|
61
|
+
int24[] memory tickUpper_,
|
|
62
|
+
uint16[] memory numDiscoveryPositions_,
|
|
63
|
+
uint256[] memory maxDiscoverySupplyShare_
|
|
64
|
+
) internal pure returns (bytes memory) {
|
|
65
|
+
return abi.encode(DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function decodeDopplerMultiCurveUniV4(
|
|
69
|
+
bytes memory poolConfig
|
|
70
|
+
)
|
|
71
|
+
internal
|
|
72
|
+
pure
|
|
73
|
+
returns (
|
|
74
|
+
uint8 version,
|
|
75
|
+
address currency,
|
|
76
|
+
int24[] memory tickLower_,
|
|
77
|
+
int24[] memory tickUpper_,
|
|
78
|
+
uint16[] memory numDiscoveryPositions_,
|
|
79
|
+
uint256[] memory maxDiscoverySupplyShare_
|
|
80
|
+
)
|
|
81
|
+
{
|
|
82
|
+
(version, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_) = abi.decode(
|
|
83
|
+
poolConfig,
|
|
84
|
+
(uint8, address, int24[], int24[], uint16[], uint256[])
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function defaultDopplerUniV3(address currency) internal pure returns (bytes memory) {
|
|
89
|
+
return
|
|
90
|
+
encodeDopplerUniV3(
|
|
91
|
+
currency,
|
|
92
|
+
CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER,
|
|
93
|
+
CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER,
|
|
94
|
+
CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS,
|
|
95
|
+
CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function defaultDopplerMultiCurveUniV4(address currency) internal pure returns (bytes memory) {
|
|
100
|
+
int24[] memory tickLower = new int24[](2);
|
|
101
|
+
int24[] memory tickUpper = new int24[](2);
|
|
102
|
+
uint16[] memory numDiscoveryPositions = new uint16[](2);
|
|
103
|
+
uint256[] memory maxDiscoverySupplyShare = new uint256[](2);
|
|
104
|
+
|
|
105
|
+
// todo: configure defaults
|
|
106
|
+
// Curve 1
|
|
107
|
+
tickLower[0] = -328_000;
|
|
108
|
+
tickUpper[0] = -300_000;
|
|
109
|
+
numDiscoveryPositions[0] = 2;
|
|
110
|
+
maxDiscoverySupplyShare[0] = 0.1e18;
|
|
111
|
+
|
|
112
|
+
// Curve 2
|
|
113
|
+
tickLower[1] = -200_000;
|
|
114
|
+
tickUpper[1] = -100_000;
|
|
115
|
+
numDiscoveryPositions[1] = 2;
|
|
116
|
+
maxDiscoverySupplyShare[1] = 0.1e18;
|
|
117
|
+
|
|
118
|
+
return encodeDopplerMultiCurveUniV4(currency, tickLower, tickUpper, numDiscoveryPositions, maxDiscoverySupplyShare);
|
|
119
|
+
}
|
|
7
120
|
|
|
8
|
-
|
|
121
|
+
function defaultConfig(address currency) internal pure returns (bytes memory) {
|
|
122
|
+
return defaultDopplerUniV3(currency);
|
|
123
|
+
}
|
|
9
124
|
}
|
|
@@ -49,4 +49,9 @@ library CoinConstants {
|
|
|
49
49
|
/// @notice The percentage of the LP fee allocated to the Doppler protocol
|
|
50
50
|
/// @dev 500 basis points = 5% of the 1% LP FEE
|
|
51
51
|
uint256 public constant DOPPLER_MARKET_REWARD_BPS = 500;
|
|
52
|
+
|
|
53
|
+
int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = -777000;
|
|
54
|
+
int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = 222000;
|
|
55
|
+
uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = 10; // will be 11 total with tail position
|
|
56
|
+
uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = 0.495e18; // half of the 990m total pool supply
|
|
52
57
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {PoolConfiguration} from "../interfaces/ICoin.sol";
|
|
5
|
+
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
6
|
+
import {LpPosition} from "../types/LpPosition.sol";
|
|
7
|
+
import {MarketConstants} from "./MarketConstants.sol";
|
|
8
|
+
import {FullMath} from "../utils/uniswap/FullMath.sol";
|
|
9
|
+
import {TickMath} from "../utils/uniswap/TickMath.sol";
|
|
10
|
+
import {IDopplerErrors} from "../interfaces/IDopplerErrors.sol";
|
|
11
|
+
import {DopplerMath} from "./DopplerMath.sol";
|
|
12
|
+
|
|
13
|
+
library CoinDopplerMultiCurve {
|
|
14
|
+
error ArrayLengthMismatch();
|
|
15
|
+
error ZeroDiscoveryPositions();
|
|
16
|
+
error ZeroDiscoverySupplyShare();
|
|
17
|
+
error InvalidTickRangeMisordered(int24 tickLower, int24 tickUpper);
|
|
18
|
+
error ConfigTickLowerMustBeLessThanTickUpper();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @notice Configures multi-curve liquidity based on the provided parameters.
|
|
22
|
+
* @param isCoinToken0 A boolean indicating if the coin is token0 (true) or token1 (false) in the pair.
|
|
23
|
+
* This affects tick ordering and price calculations.
|
|
24
|
+
* @param poolConfig_ ABI encoded data containing the pool configuration parameters.
|
|
25
|
+
* It is expected to be encoded in the following order:
|
|
26
|
+
* - version (uint8): The version of the pool configuration.
|
|
27
|
+
* (e.g., 2 for UniswapV3, 4 for Doppler/Uniswap V4).
|
|
28
|
+
* - currency (address): The address of the currency token (e.g., WETH) paired with the coin.
|
|
29
|
+
* - tickLower (int24[]): An array of lower tick boundaries for each liquidity curve.
|
|
30
|
+
* - tickUpper (int24[]): An array of upper tick boundaries for each liquidity curve.
|
|
31
|
+
* - numDiscoveryPositions (uint16[]): An array specifying the number of discrete liquidity
|
|
32
|
+
* positions within each curve's discovery phase.
|
|
33
|
+
* - maxDiscoverySupplyShare (uint256[]): An array of WAD-scaled values (1e18) representing
|
|
34
|
+
* the maximum share of the coin's total supply
|
|
35
|
+
* allocated to each curve's discovery phase.
|
|
36
|
+
* @return sqrtPriceX96 The initial square root price of the pool, scaled to X96 format.
|
|
37
|
+
* @return poolConfiguration A struct containing the configured pool parameters,
|
|
38
|
+
* including version, number of positions, fee, tick spacing,
|
|
39
|
+
* and arrays for discovery positions, tick boundaries, and supply shares.
|
|
40
|
+
*/
|
|
41
|
+
function setupPool(bool isCoinToken0, bytes memory poolConfig_) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
|
|
42
|
+
(, , int24[] memory tickLower_, int24[] memory tickUpper_, uint16[] memory numDiscoveryPositions_, uint256[] memory maxDiscoverySupplyShare_) = abi
|
|
43
|
+
.decode(poolConfig_, (uint8, address, int24[], int24[], uint16[], uint256[]));
|
|
44
|
+
|
|
45
|
+
uint256 numCurves = tickLower_.length;
|
|
46
|
+
if (numCurves != tickUpper_.length || numCurves != numDiscoveryPositions_.length || numCurves != maxDiscoverySupplyShare_.length) {
|
|
47
|
+
revert ArrayLengthMismatch();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
uint256 totalDiscoverySupplyShare;
|
|
51
|
+
uint256 totalDiscoveryPositions;
|
|
52
|
+
|
|
53
|
+
int24 boundryTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MAX_TICK, MarketConstants.TICK_SPACING);
|
|
54
|
+
int24 boundryTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MIN_TICK, MarketConstants.TICK_SPACING);
|
|
55
|
+
|
|
56
|
+
// For each curve:
|
|
57
|
+
for (uint256 i; i < numCurves; i++) {
|
|
58
|
+
// Ensure a value is specified
|
|
59
|
+
require(numDiscoveryPositions_[i] > 0, ZeroDiscoveryPositions());
|
|
60
|
+
require(maxDiscoverySupplyShare_[i] > 0, ZeroDiscoverySupplyShare());
|
|
61
|
+
|
|
62
|
+
// Aggregate the total discovery positions and supply across curves
|
|
63
|
+
totalDiscoveryPositions += numDiscoveryPositions_[i];
|
|
64
|
+
totalDiscoverySupplyShare += maxDiscoverySupplyShare_[i];
|
|
65
|
+
|
|
66
|
+
int24 currentTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickLower_[i], MarketConstants.TICK_SPACING);
|
|
67
|
+
int24 currentTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickUpper_[i], MarketConstants.TICK_SPACING);
|
|
68
|
+
|
|
69
|
+
require(currentTickLower < currentTickUpper, ConfigTickLowerMustBeLessThanTickUpper());
|
|
70
|
+
|
|
71
|
+
// Sort the tick values based on token order
|
|
72
|
+
tickLower_[i] = isCoinToken0 ? currentTickLower : -currentTickUpper;
|
|
73
|
+
tickUpper_[i] = isCoinToken0 ? currentTickUpper : -currentTickLower;
|
|
74
|
+
|
|
75
|
+
boundryTickLower = boundryTickLower < tickLower_[i] ? boundryTickLower : tickLower_[i];
|
|
76
|
+
boundryTickUpper = boundryTickUpper > tickUpper_[i] ? boundryTickUpper : tickUpper_[i];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
require(boundryTickLower < boundryTickUpper, InvalidTickRangeMisordered(boundryTickLower, boundryTickUpper));
|
|
80
|
+
require(totalDiscoveryPositions > 1 && totalDiscoveryPositions <= 200, IDopplerErrors.NumDiscoveryPositionsOutOfRange());
|
|
81
|
+
require(totalDiscoverySupplyShare < MarketConstants.WAD, IDopplerErrors.MaxShareToBeSoldExceeded(totalDiscoverySupplyShare, MarketConstants.WAD));
|
|
82
|
+
|
|
83
|
+
sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? boundryTickLower : boundryTickUpper);
|
|
84
|
+
|
|
85
|
+
poolConfiguration = PoolConfiguration({
|
|
86
|
+
version: CoinConfigurationVersions.DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION,
|
|
87
|
+
numPositions: uint16(totalDiscoveryPositions + 1), // Add one for the final tail position
|
|
88
|
+
fee: MarketConstants.LP_FEE_V4,
|
|
89
|
+
tickSpacing: MarketConstants.TICK_SPACING,
|
|
90
|
+
numDiscoveryPositions: numDiscoveryPositions_,
|
|
91
|
+
tickLower: tickLower_,
|
|
92
|
+
tickUpper: tickUpper_,
|
|
93
|
+
maxDiscoverySupplyShare: maxDiscoverySupplyShare_
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// @notice Calculates the LP positions for a given multi-curve configuration
|
|
98
|
+
function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
|
|
99
|
+
positions = new LpPosition[](poolConfiguration.numPositions);
|
|
100
|
+
|
|
101
|
+
uint256 discoverySupply;
|
|
102
|
+
uint256 currentPositionOffset;
|
|
103
|
+
uint256 numCurves = poolConfiguration.tickLower.length;
|
|
104
|
+
|
|
105
|
+
for (uint256 i; i < numCurves; i++) {
|
|
106
|
+
uint256 curveSupply = FullMath.mulDiv(MarketConstants.POOL_LAUNCH_SUPPLY, poolConfiguration.maxDiscoverySupplyShare[i], MarketConstants.WAD);
|
|
107
|
+
|
|
108
|
+
(positions, curveSupply) = DopplerMath.calculateLogNormalDistribution(
|
|
109
|
+
poolConfiguration.tickLower[i],
|
|
110
|
+
poolConfiguration.tickUpper[i],
|
|
111
|
+
MarketConstants.TICK_SPACING,
|
|
112
|
+
isCoinToken0,
|
|
113
|
+
curveSupply,
|
|
114
|
+
poolConfiguration.numDiscoveryPositions[i],
|
|
115
|
+
positions,
|
|
116
|
+
currentPositionOffset
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
discoverySupply += curveSupply;
|
|
120
|
+
currentPositionOffset += poolConfiguration.numDiscoveryPositions[i];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
uint256 tailSupply = MarketConstants.POOL_LAUNCH_SUPPLY - discoverySupply;
|
|
124
|
+
|
|
125
|
+
// Calculate the tail position (the last position in the array)
|
|
126
|
+
positions[poolConfiguration.numPositions - 1] = DopplerMath.calculateLpTail(
|
|
127
|
+
poolConfiguration.tickLower[numCurves - 1],
|
|
128
|
+
poolConfiguration.tickUpper[numCurves - 1],
|
|
129
|
+
isCoinToken0,
|
|
130
|
+
tailSupply,
|
|
131
|
+
MarketConstants.TICK_SPACING
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.23;
|
|
3
3
|
|
|
4
|
-
import {PoolConfiguration} from "../interfaces/ICoin.sol";
|
|
5
4
|
import {TickMath} from "../utils/uniswap/TickMath.sol";
|
|
6
5
|
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
7
6
|
import {ICoin} from "../interfaces/ICoin.sol";
|
|
@@ -11,13 +10,13 @@ import {FullMath} from "../utils/uniswap/FullMath.sol";
|
|
|
11
10
|
import {SqrtPriceMath} from "../utils/uniswap/SqrtPriceMath.sol";
|
|
12
11
|
import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
|
|
13
12
|
import {IDopplerErrors} from "../interfaces/IDopplerErrors.sol";
|
|
13
|
+
import {DopplerMath} from "./DopplerMath.sol";
|
|
14
|
+
import {PoolConfiguration} from "../types/PoolConfiguration.sol";
|
|
14
15
|
|
|
15
16
|
library CoinDopplerUniV3 {
|
|
16
17
|
function setupPool(bool isCoinToken0, bytes memory poolConfig_) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
|
|
17
|
-
(, , int24 tickLower_, int24 tickUpper_, uint16 numDiscoveryPositions_, uint256 maxDiscoverySupplyShare_) =
|
|
18
|
-
poolConfig_
|
|
19
|
-
(uint8, address, int24, int24, uint16, uint256)
|
|
20
|
-
);
|
|
18
|
+
(, , int24 tickLower_, int24 tickUpper_, uint16 numDiscoveryPositions_, uint256 maxDiscoverySupplyShare_) = CoinConfigurationVersions
|
|
19
|
+
.decodeDopplerUniV3(poolConfig_);
|
|
21
20
|
|
|
22
21
|
require(numDiscoveryPositions_ > 1 && numDiscoveryPositions_ <= 200, IDopplerErrors.NumDiscoveryPositionsOutOfRange());
|
|
23
22
|
|
|
@@ -25,178 +24,27 @@ library CoinDopplerUniV3 {
|
|
|
25
24
|
revert IDopplerErrors.MaxShareToBeSoldExceeded(maxDiscoverySupplyShare_, MarketConstants.WAD);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
uint256[] memory maxDiscoverySupplyShare = new uint256[](1);
|
|
28
|
+
uint16[] memory numDiscoveryPositions = new uint16[](1);
|
|
29
|
+
int24[] memory savedTickLower = new int24[](1);
|
|
30
|
+
int24[] memory savedTickUpper = new int24[](1);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
maxDiscoverySupplyShare[0] = maxDiscoverySupplyShare_;
|
|
33
|
+
numDiscoveryPositions[0] = numDiscoveryPositions_;
|
|
34
|
+
savedTickLower[0] = isCoinToken0 ? tickLower_ : -tickUpper_;
|
|
35
|
+
savedTickUpper[0] = isCoinToken0 ? tickUpper_ : -tickLower_;
|
|
36
|
+
|
|
37
|
+
sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? savedTickLower[0] : savedTickUpper[0]);
|
|
32
38
|
|
|
33
39
|
poolConfiguration = PoolConfiguration({
|
|
34
40
|
version: CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION,
|
|
41
|
+
fee: MarketConstants.LP_FEE,
|
|
42
|
+
tickSpacing: MarketConstants.TICK_SPACING,
|
|
35
43
|
tickLower: savedTickLower,
|
|
36
44
|
tickUpper: savedTickUpper,
|
|
37
|
-
numPositions: numDiscoveryPositions_,
|
|
38
|
-
maxDiscoverySupplyShare:
|
|
45
|
+
numPositions: numDiscoveryPositions_ + 1, // Add one for the final tail position
|
|
46
|
+
maxDiscoverySupplyShare: maxDiscoverySupplyShare,
|
|
47
|
+
numDiscoveryPositions: numDiscoveryPositions
|
|
39
48
|
});
|
|
40
49
|
}
|
|
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
50
|
}
|