@zoralabs/coins 0.7.1 → 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 +106 -84
- package/CHANGELOG.md +68 -0
- package/abis/BadImpl.json +15 -0
- package/abis/BalanceDeltaLibrary.json +15 -0
- package/abis/BaseCoin.json +1350 -0
- package/abis/BaseCoinDeployHook.json +78 -0
- package/abis/BaseHook.json +897 -0
- package/abis/BaseTest.json +60 -91
- package/abis/BeforeSwapDeltaLibrary.json +15 -0
- package/abis/BuySupplyWithSwapRouterHook.json +126 -0
- package/abis/Coin.json +214 -150
- package/abis/CoinConstants.json +65 -0
- package/abis/CoinDopplerMultiCurve.json +38 -0
- package/abis/CoinRewardsV4.json +54 -0
- package/abis/CoinTest.json +66 -111
- package/abis/CoinUniV4Test.json +1053 -0
- package/abis/CoinV4.json +1687 -0
- package/abis/CurrencyLibrary.json +25 -0
- package/abis/DeployHooks.json +9 -0
- 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 +62 -184
- package/abis/ERC20.json +310 -0
- package/abis/FactoryTest.json +98 -98
- package/abis/FakeHookNoInterface.json +21 -0
- package/abis/FeeEstimatorHook.json +1528 -0
- package/abis/Hooks.json +28 -0
- package/abis/HooksDeployment.json +23 -0
- package/abis/HooksTest.json +698 -0
- package/abis/IAllowanceTransfer.json +486 -0
- package/abis/ICoin.json +62 -69
- package/abis/ICoinDeployHook.json +31 -0
- package/abis/ICoinV3.json +879 -0
- package/abis/ICoinV4.json +915 -0
- package/abis/IContractMetadata.json +28 -0
- package/abis/IDeployedCoinVersionLookup.json +21 -0
- package/abis/IEIP712.json +15 -0
- package/abis/IEIP712_v4.json +15 -0
- package/abis/IERC20Minimal.json +172 -0
- package/abis/IERC6909Claims.json +288 -0
- package/abis/IERC721.json +36 -36
- package/abis/IERC721Permit_v4.json +88 -0
- package/abis/IExtsload.json +64 -0
- package/abis/IExttload.json +40 -0
- package/abis/IHasAfterCoinDeploy.json +31 -0
- package/abis/IHasContractName.json +15 -0
- package/abis/IHasPoolKey.json +42 -0
- package/abis/IHasRewardsRecipients.json +54 -0
- package/abis/IHasSwapPath.json +60 -0
- package/abis/IHooks.json +789 -0
- package/abis/IImmutableState.json +15 -0
- package/abis/IMsgSender.json +15 -0
- package/abis/IMulticall_v4.json +21 -0
- package/abis/INotifier.json +187 -0
- package/abis/IPermit2.json +865 -0
- package/abis/IPermit2Forwarder.json +138 -0
- package/abis/IPoolConfigEncoding.json +46 -0
- package/abis/IPoolInitializer_v4.json +53 -0
- package/abis/IPoolManager.json +1286 -0
- package/abis/IPositionManager.json +712 -0
- package/abis/IProtocolFees.json +174 -0
- package/abis/ISignatureTransfer.json +394 -0
- package/abis/ISubscriber.json +89 -0
- package/abis/ISwapPathRouter.json +92 -0
- package/abis/ISwapRouter.json +82 -0
- package/abis/IUniversalRouter.json +61 -0
- package/abis/IUnlockCallback.json +21 -0
- package/abis/IUnorderedNonce.json +44 -0
- package/abis/IV4Quoter.json +310 -0
- package/abis/IV4Router.json +47 -0
- package/abis/IZoraFactory.json +328 -4
- package/abis/IZoraV4CoinHook.json +427 -0
- package/abis/ImmutableState.json +36 -0
- package/abis/LPFeeLibrary.json +65 -0
- package/abis/MockERC20.json +21 -0
- package/abis/MultiOwnableTest.json +60 -91
- package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
- package/abis/PrintUpgradeCommand.json +9 -0
- package/abis/ProxyShim.json +24 -0
- package/abis/Simulate.json +0 -91
- package/abis/StateLibrary.json +80 -0
- package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
- package/abis/TestV4Swap.json +9 -0
- package/abis/{CoinSetup.json → UniV3BuySell.json} +5 -0
- package/abis/UniV3Errors.json +32 -0
- package/abis/UpgradeCoinImpl.json +47 -0
- package/abis/UpgradeFactoryImpl.json +9 -0
- package/abis/UpgradesTest.json +671 -0
- package/abis/Vm.json +1482 -111
- package/abis/VmSafe.json +856 -32
- package/abis/ZoraFactoryImpl.json +450 -1
- package/abis/ZoraV4CoinHook.json +1439 -0
- package/addresses/8453.json +8 -3
- package/addresses/84532.json +8 -3
- package/dist/index.cjs +1998 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1989 -178
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +2852 -688
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/wagmiGenerated.ts +1992 -173
- package/package.json +7 -2
- package/remappings.txt +6 -1
- package/script/CoinsDeployerBase.sol +105 -10
- package/script/DeployDevFactory.s.sol +21 -0
- package/script/DeployHooks.s.sol +22 -0
- package/script/PrintUpgradeCommand.s.sol +13 -0
- package/script/Simulate.s.sol +4 -12
- package/script/TestBackingCoinSwap.s.sol +146 -0
- package/script/TestV4Swap.s.sol +136 -0
- package/script/UpgradeCoinImpl.sol +2 -2
- package/script/UpgradeFactoryImpl.s.sol +23 -0
- package/src/BaseCoin.sol +176 -0
- package/src/Coin.sol +93 -515
- package/src/CoinV4.sol +121 -0
- package/src/ZoraFactoryImpl.sol +257 -57
- package/src/hooks/ZoraV4CoinHook.sol +195 -0
- package/src/hooks/deployment/BaseCoinDeployHook.sol +62 -0
- package/src/hooks/deployment/BuySupplyWithSwapRouterHook.sol +80 -0
- package/src/interfaces/ICoin.sol +35 -39
- package/src/interfaces/ICoinDeployHook.sol +8 -0
- 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/ISwapRouter.sol +1 -35
- package/src/interfaces/IZoraFactory.sol +97 -7
- package/src/interfaces/IZoraV4CoinHook.sol +116 -0
- package/src/libs/CoinCommon.sol +15 -0
- package/src/libs/CoinConfigurationVersions.sol +116 -1
- package/src/{utils → libs}/CoinConstants.sol +11 -6
- 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 +40 -20
- package/src/libs/CoinSetupV3.sol +50 -0
- 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 +231 -0
- package/src/libs/UniV3Errors.sol +11 -0
- 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 +94 -101
- package/test/CoinDopplerUniV3.t.sol +35 -184
- package/test/CoinUniV4.t.sol +752 -0
- package/test/DeploymentHooks.t.sol +270 -0
- package/test/Factory.t.sol +84 -50
- package/test/MultiOwnable.t.sol +6 -3
- package/test/Upgrades.t.sol +68 -0
- package/test/mocks/MockERC20.sol +12 -0
- package/test/utils/BaseTest.sol +124 -59
- 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 +10 -9
- package/src/libs/CoinLegacy.sol +0 -48
|
@@ -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
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
5
|
+
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
6
|
+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
7
|
+
import {IProtocolRewards} from "../interfaces/IProtocolRewards.sol";
|
|
8
|
+
import {ICoin} from "../interfaces/ICoin.sol";
|
|
9
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
10
|
+
import {IWETH} from "../interfaces/IWETH.sol";
|
|
11
|
+
|
|
12
|
+
struct CoinConfig {
|
|
13
|
+
address protocolRewardRecipient;
|
|
14
|
+
address platformReferrer;
|
|
15
|
+
address payoutRecipient;
|
|
16
|
+
address protocolRewards;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
library CoinRewards {
|
|
20
|
+
using SafeERC20 for IERC20;
|
|
21
|
+
|
|
22
|
+
/// @dev Handles sending ETH and ERC20 payouts and refunds to recipients
|
|
23
|
+
/// @param orderPayout The amount of currency to pay out
|
|
24
|
+
/// @param recipient The address to receive the payout
|
|
25
|
+
function handlePayout(uint256 orderPayout, address recipient, address currency, address weth) internal {
|
|
26
|
+
if (currency == weth) {
|
|
27
|
+
Address.sendValue(payable(recipient), orderPayout);
|
|
28
|
+
} else {
|
|
29
|
+
IERC20(currency).safeTransfer(recipient, orderPayout);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// @dev Handles calculating and depositing fees to an escrow protocol rewards contract
|
|
34
|
+
function handleTradeRewards(uint256 totalValue, address _tradeReferrer, CoinConfig memory coinConfig, address currency, IWETH weth) internal {
|
|
35
|
+
address protocolRewardRecipient = coinConfig.protocolRewardRecipient;
|
|
36
|
+
address platformReferrer = coinConfig.platformReferrer;
|
|
37
|
+
address payoutRecipient = coinConfig.payoutRecipient;
|
|
38
|
+
IProtocolRewards protocolRewards = IProtocolRewards(coinConfig.protocolRewards);
|
|
39
|
+
|
|
40
|
+
if (_tradeReferrer == address(0)) {
|
|
41
|
+
_tradeReferrer = protocolRewardRecipient;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
uint256 tokenCreatorFee = calculateReward(totalValue, CoinConstants.TOKEN_CREATOR_FEE_BPS);
|
|
45
|
+
uint256 platformReferrerFee = calculateReward(totalValue, CoinConstants.PLATFORM_REFERRER_FEE_BPS);
|
|
46
|
+
uint256 tradeReferrerFee = calculateReward(totalValue, CoinConstants.TRADE_REFERRER_FEE_BPS);
|
|
47
|
+
uint256 protocolFee = totalValue - tokenCreatorFee - platformReferrerFee - tradeReferrerFee;
|
|
48
|
+
|
|
49
|
+
if (currency == address(weth)) {
|
|
50
|
+
address[] memory recipients = new address[](4);
|
|
51
|
+
uint256[] memory amounts = new uint256[](4);
|
|
52
|
+
bytes4[] memory reasons = new bytes4[](4);
|
|
53
|
+
|
|
54
|
+
recipients[0] = payoutRecipient;
|
|
55
|
+
amounts[0] = tokenCreatorFee;
|
|
56
|
+
reasons[0] = bytes4(keccak256("COIN_CREATOR_REWARD"));
|
|
57
|
+
|
|
58
|
+
recipients[1] = platformReferrer;
|
|
59
|
+
amounts[1] = platformReferrerFee;
|
|
60
|
+
reasons[1] = bytes4(keccak256("COIN_PLATFORM_REFERRER_REWARD"));
|
|
61
|
+
|
|
62
|
+
recipients[2] = _tradeReferrer;
|
|
63
|
+
amounts[2] = tradeReferrerFee;
|
|
64
|
+
reasons[2] = bytes4(keccak256("COIN_TRADE_REFERRER_REWARD"));
|
|
65
|
+
|
|
66
|
+
recipients[3] = protocolRewardRecipient;
|
|
67
|
+
amounts[3] = protocolFee;
|
|
68
|
+
reasons[3] = bytes4(keccak256("COIN_PROTOCOL_REWARD"));
|
|
69
|
+
|
|
70
|
+
IProtocolRewards(protocolRewards).depositBatch{value: totalValue}(recipients, amounts, reasons, "");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (currency != address(weth)) {
|
|
74
|
+
IERC20(currency).safeTransfer(payoutRecipient, tokenCreatorFee);
|
|
75
|
+
IERC20(currency).safeTransfer(platformReferrer, platformReferrerFee);
|
|
76
|
+
IERC20(currency).safeTransfer(_tradeReferrer, tradeReferrerFee);
|
|
77
|
+
IERC20(currency).safeTransfer(protocolRewardRecipient, protocolFee);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
emit ICoin.CoinTradeRewards(
|
|
81
|
+
payoutRecipient,
|
|
82
|
+
platformReferrer,
|
|
83
|
+
_tradeReferrer,
|
|
84
|
+
protocolRewardRecipient,
|
|
85
|
+
tokenCreatorFee,
|
|
86
|
+
platformReferrerFee,
|
|
87
|
+
tradeReferrerFee,
|
|
88
|
+
protocolFee,
|
|
89
|
+
currency
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function calculateReward(uint256 amount, uint256 bps) internal pure returns (uint256) {
|
|
94
|
+
return (amount * bps) / 10_000;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function transferBothRewards(
|
|
98
|
+
address token0,
|
|
99
|
+
uint256 totalAmountToken0,
|
|
100
|
+
address token1,
|
|
101
|
+
uint256 totalAmountToken1,
|
|
102
|
+
address coin,
|
|
103
|
+
CoinConfig memory coinConfig,
|
|
104
|
+
address currency,
|
|
105
|
+
IWETH weth,
|
|
106
|
+
address doppler
|
|
107
|
+
) internal returns (ICoin.MarketRewards memory rewards) {
|
|
108
|
+
rewards = transferMarketRewards(token0, currency, totalAmountToken0, rewards, coin, coinConfig, weth, doppler);
|
|
109
|
+
rewards = transferMarketRewards(token1, currency, totalAmountToken1, rewards, coin, coinConfig, weth, doppler);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
struct Distribution {
|
|
113
|
+
bool isCurrency;
|
|
114
|
+
uint256 totalAmount;
|
|
115
|
+
uint256 creatorPayout;
|
|
116
|
+
uint256 platformReferrerPayout;
|
|
117
|
+
uint256 protocolPayout;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function transferMarketRewards(
|
|
121
|
+
address token,
|
|
122
|
+
address currency,
|
|
123
|
+
uint256 totalAmount,
|
|
124
|
+
ICoin.MarketRewards memory rewards,
|
|
125
|
+
address coin,
|
|
126
|
+
CoinConfig memory coinConfig,
|
|
127
|
+
IWETH weth,
|
|
128
|
+
address dopplerRecipient
|
|
129
|
+
) internal returns (ICoin.MarketRewards memory) {
|
|
130
|
+
address payoutRecipient = coinConfig.payoutRecipient;
|
|
131
|
+
address platformReferrer = coinConfig.platformReferrer;
|
|
132
|
+
address protocolRewardRecipient = coinConfig.protocolRewardRecipient;
|
|
133
|
+
address protocolRewards = coinConfig.protocolRewards;
|
|
134
|
+
|
|
135
|
+
if (totalAmount > 0) {
|
|
136
|
+
uint256 dopplerPayout = calculateReward(totalAmount, CoinConstants.DOPPLER_MARKET_REWARD_BPS);
|
|
137
|
+
uint256 creatorPayout = calculateReward(totalAmount, CoinConstants.CREATOR_MARKET_REWARD_BPS);
|
|
138
|
+
uint256 platformReferrerPayout = calculateReward(totalAmount, CoinConstants.PLATFORM_REFERRER_MARKET_REWARD_BPS);
|
|
139
|
+
uint256 protocolPayout = totalAmount - creatorPayout - platformReferrerPayout - dopplerPayout;
|
|
140
|
+
|
|
141
|
+
bool isCurrency = token == currency;
|
|
142
|
+
|
|
143
|
+
if (token == address(weth)) {
|
|
144
|
+
IWETH(weth).withdraw(totalAmount);
|
|
145
|
+
|
|
146
|
+
address[] memory recipients = new address[](4);
|
|
147
|
+
recipients[0] = payoutRecipient;
|
|
148
|
+
recipients[1] = platformReferrer;
|
|
149
|
+
recipients[2] = protocolRewardRecipient;
|
|
150
|
+
recipients[3] = dopplerRecipient;
|
|
151
|
+
|
|
152
|
+
uint256[] memory amounts = new uint256[](4);
|
|
153
|
+
amounts[0] = creatorPayout;
|
|
154
|
+
amounts[1] = platformReferrerPayout;
|
|
155
|
+
amounts[2] = protocolPayout;
|
|
156
|
+
amounts[3] = dopplerPayout;
|
|
157
|
+
|
|
158
|
+
bytes4[] memory reasons = new bytes4[](4);
|
|
159
|
+
reasons[0] = bytes4(keccak256("COIN_CREATOR_MARKET_REWARD"));
|
|
160
|
+
reasons[1] = bytes4(keccak256("COIN_PLATFORM_REFERRER_MARKET_REWARD"));
|
|
161
|
+
reasons[2] = bytes4(keccak256("COIN_PROTOCOL_MARKET_REWARD"));
|
|
162
|
+
reasons[3] = bytes4(keccak256("COIN_DOPPLER_MARKET_REWARD"));
|
|
163
|
+
|
|
164
|
+
IProtocolRewards(protocolRewards).depositBatch{value: totalAmount}(recipients, amounts, reasons, "");
|
|
165
|
+
IProtocolRewards(protocolRewards).withdrawFor(dopplerRecipient, dopplerPayout);
|
|
166
|
+
} else {
|
|
167
|
+
if (!isCurrency) {
|
|
168
|
+
IERC20(coin).safeTransfer(payoutRecipient, creatorPayout);
|
|
169
|
+
IERC20(coin).safeTransfer(platformReferrer, platformReferrerPayout);
|
|
170
|
+
IERC20(coin).safeTransfer(protocolRewardRecipient, protocolPayout);
|
|
171
|
+
IERC20(coin).safeTransfer(dopplerRecipient, dopplerPayout);
|
|
172
|
+
} else {
|
|
173
|
+
IERC20(currency).safeTransfer(payoutRecipient, creatorPayout);
|
|
174
|
+
IERC20(currency).safeTransfer(platformReferrer, platformReferrerPayout);
|
|
175
|
+
IERC20(currency).safeTransfer(protocolRewardRecipient, protocolPayout);
|
|
176
|
+
IERC20(currency).safeTransfer(dopplerRecipient, dopplerPayout);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (isCurrency) {
|
|
181
|
+
rewards.totalAmountCurrency = totalAmount;
|
|
182
|
+
rewards.creatorPayoutAmountCurrency = creatorPayout;
|
|
183
|
+
rewards.platformReferrerAmountCurrency = platformReferrerPayout;
|
|
184
|
+
rewards.protocolAmountCurrency = protocolPayout;
|
|
185
|
+
} else {
|
|
186
|
+
rewards.totalAmountCoin = totalAmount;
|
|
187
|
+
rewards.creatorPayoutAmountCoin = creatorPayout;
|
|
188
|
+
rewards.platformReferrerAmountCoin = platformReferrerPayout;
|
|
189
|
+
rewards.protocolAmountCoin = protocolPayout;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return rewards;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
5
|
+
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
6
|
+
import {LpPosition} from "../types/LpPosition.sol";
|
|
7
|
+
import {V4Liquidity} from "./V4Liquidity.sol";
|
|
8
|
+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
9
|
+
import {IHasRewardsRecipients} from "../interfaces/ICoin.sol";
|
|
10
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
11
|
+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
12
|
+
import {ICoin} from "../interfaces/ICoin.sol";
|
|
13
|
+
import {IZoraV4CoinHook} from "../interfaces/IZoraV4CoinHook.sol";
|
|
14
|
+
import {UniV4SwapToCurrency} from "./UniV4SwapToCurrency.sol";
|
|
15
|
+
import {IHasSwapPath} from "../interfaces/ICoinV4.sol";
|
|
16
|
+
|
|
17
|
+
library CoinRewardsV4 {
|
|
18
|
+
using SafeERC20 for IERC20;
|
|
19
|
+
|
|
20
|
+
// creator gets 50% of the total fee
|
|
21
|
+
uint256 public constant CREATOR_REWARD_BPS = 5000;
|
|
22
|
+
|
|
23
|
+
// create referrer gets 15% of the total fee
|
|
24
|
+
uint256 public constant CREATE_REFERRAL_REWARD_BPS = 1500;
|
|
25
|
+
|
|
26
|
+
// trade referrer gets 10% of the total fee
|
|
27
|
+
uint256 public constant TRADE_REFERRAL_REWARD_BPS = 1500;
|
|
28
|
+
|
|
29
|
+
// doppler gets 5% of the total fee
|
|
30
|
+
uint256 public constant DOPPLER_REWARD_BPS = 500;
|
|
31
|
+
|
|
32
|
+
function getTradeReferral(bytes calldata hookData) internal pure returns (address) {
|
|
33
|
+
return hookData.length > 0 ? abi.decode(hookData, (address)) : address(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// @notice Collects fees from LP positions, swaps them to target payout currency, and transfers to hook contract, so
|
|
37
|
+
/// that they can later be distributed as rewards.
|
|
38
|
+
/// @param poolManager The pool manager instance
|
|
39
|
+
/// @param key The pool key
|
|
40
|
+
/// @param positions The LP positions to collect fees from
|
|
41
|
+
/// @param payoutSwapPath The swap path to convert fees to target currency
|
|
42
|
+
/// @return fees0 The amount of fees collected in currency0
|
|
43
|
+
/// @return fees1 The amount of fees collected in currency1
|
|
44
|
+
/// @return receivedCurrency The final currency after swapping
|
|
45
|
+
/// @return receivedAmount The final amount after swapping
|
|
46
|
+
function collectFeesAndConvertToPayout(
|
|
47
|
+
IPoolManager poolManager,
|
|
48
|
+
PoolKey memory key,
|
|
49
|
+
LpPosition[] storage positions,
|
|
50
|
+
IHasSwapPath.PayoutSwapPath memory payoutSwapPath
|
|
51
|
+
) internal returns (int128 fees0, int128 fees1, Currency receivedCurrency, uint128 receivedAmount) {
|
|
52
|
+
// Step 1: Collect accrued fees from all LP positions in both token0 and token1
|
|
53
|
+
(fees0, fees1) = V4Liquidity.collectFees(poolManager, key, positions);
|
|
54
|
+
|
|
55
|
+
// Step 2: Swap the collected fees through the specified path to convert them to the target payout currency
|
|
56
|
+
// This handles multi-hop swaps if needed (e.g. coin -> backingCoin -> backingCoin's currency)
|
|
57
|
+
(receivedCurrency, receivedAmount) = UniV4SwapToCurrency.swapToPath(
|
|
58
|
+
poolManager,
|
|
59
|
+
uint128(fees0),
|
|
60
|
+
uint128(fees1),
|
|
61
|
+
payoutSwapPath.currencyIn,
|
|
62
|
+
payoutSwapPath.path
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Step 3: Transfer the final converted currency amount to this contract for distribution
|
|
66
|
+
// This makes the tokens available for the subsequent reward distribution
|
|
67
|
+
if (receivedAmount > 0) {
|
|
68
|
+
poolManager.take(receivedCurrency, address(this), receivedAmount);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// @notice Distributes collected market fees as rewards to various recipients including creator, referrers, protocol, and doppler
|
|
73
|
+
/// @dev Calculates reward amounts based on predefined basis points and transfers the specified currency to each recipient
|
|
74
|
+
/// @param currency The currency token to distribute as rewards (can be native ETH if address is zero)
|
|
75
|
+
/// @param fees The total amount of fees collected to be distributed
|
|
76
|
+
/// @param coin The coin contract instance that implements IHasRewardsRecipients to get recipient addresses
|
|
77
|
+
/// @param tradeReferrer The address of the trade referrer who should receive trade referral rewards (can be zero address)
|
|
78
|
+
function distributeMarketRewards(Currency currency, uint128 fees, IHasRewardsRecipients coin, address tradeReferrer) internal {
|
|
79
|
+
address payoutRecipient = coin.payoutRecipient();
|
|
80
|
+
address platformReferrer = coin.platformReferrer();
|
|
81
|
+
address protocolRewardRecipient = coin.protocolRewardRecipient();
|
|
82
|
+
address doppler = coin.dopplerFeeRecipient();
|
|
83
|
+
|
|
84
|
+
MarketRewards memory rewards = _distributeCurrencyRewards(
|
|
85
|
+
currency,
|
|
86
|
+
fees,
|
|
87
|
+
payoutRecipient,
|
|
88
|
+
platformReferrer,
|
|
89
|
+
protocolRewardRecipient,
|
|
90
|
+
doppler,
|
|
91
|
+
tradeReferrer
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
IZoraV4CoinHook.MarketRewardsV4 memory marketRewards = IZoraV4CoinHook.MarketRewardsV4({
|
|
95
|
+
creatorPayoutAmountCurrency: rewards.creatorAmount,
|
|
96
|
+
creatorPayoutAmountCoin: 0,
|
|
97
|
+
platformReferrerAmountCurrency: rewards.platformReferrerAmount,
|
|
98
|
+
platformReferrerAmountCoin: 0,
|
|
99
|
+
tradeReferrerAmountCurrency: rewards.tradeReferrerAmount,
|
|
100
|
+
tradeReferrerAmountCoin: 0,
|
|
101
|
+
protocolAmountCurrency: rewards.protocolAmount,
|
|
102
|
+
protocolAmountCoin: 0,
|
|
103
|
+
dopplerAmountCurrency: rewards.dopplerAmount,
|
|
104
|
+
dopplerAmountCoin: 0
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
emit IZoraV4CoinHook.CoinMarketRewardsV4(
|
|
108
|
+
address(coin),
|
|
109
|
+
Currency.unwrap(currency),
|
|
110
|
+
payoutRecipient,
|
|
111
|
+
platformReferrer,
|
|
112
|
+
tradeReferrer,
|
|
113
|
+
protocolRewardRecipient,
|
|
114
|
+
doppler,
|
|
115
|
+
marketRewards
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
struct MarketRewards {
|
|
120
|
+
uint256 platformReferrerAmount;
|
|
121
|
+
uint256 tradeReferrerAmount;
|
|
122
|
+
uint256 protocolAmount;
|
|
123
|
+
uint256 creatorAmount;
|
|
124
|
+
uint256 dopplerAmount;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function _distributeCurrencyRewards(
|
|
128
|
+
Currency currency,
|
|
129
|
+
uint128 fee,
|
|
130
|
+
address payoutRecipient,
|
|
131
|
+
address platformReferrer,
|
|
132
|
+
address protocolRewardRecipient,
|
|
133
|
+
address doppler,
|
|
134
|
+
address tradeReferral
|
|
135
|
+
) internal returns (MarketRewards memory rewards) {
|
|
136
|
+
rewards = _computeMarketRewards(fee, tradeReferral != address(0), platformReferrer != address(0));
|
|
137
|
+
|
|
138
|
+
if (platformReferrer != address(0)) {
|
|
139
|
+
_transferCurrency(currency, rewards.platformReferrerAmount, platformReferrer);
|
|
140
|
+
}
|
|
141
|
+
if (tradeReferral != address(0)) {
|
|
142
|
+
_transferCurrency(currency, rewards.tradeReferrerAmount, tradeReferral);
|
|
143
|
+
}
|
|
144
|
+
_transferCurrency(currency, rewards.creatorAmount, payoutRecipient);
|
|
145
|
+
_transferCurrency(currency, rewards.dopplerAmount, doppler);
|
|
146
|
+
_transferCurrency(currency, rewards.protocolAmount, protocolRewardRecipient);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function _transferCurrency(Currency currency, uint256 amount, address to) internal {
|
|
150
|
+
if (amount == 0) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (currency.isAddressZero()) {
|
|
155
|
+
(bool success, ) = payable(to).call{value: amount}("");
|
|
156
|
+
if (!success) {
|
|
157
|
+
revert ICoin.EthTransferFailed();
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
IERC20(Currency.unwrap(currency)).safeTransfer(to, amount);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function _computeMarketRewards(uint128 fee, bool hasTradeReferral, bool hasCreateReferral) internal pure returns (MarketRewards memory rewards) {
|
|
165
|
+
if (fee == 0) {
|
|
166
|
+
return rewards;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
uint256 totalAmount = uint256(fee);
|
|
170
|
+
rewards.platformReferrerAmount = hasCreateReferral ? calculateReward(totalAmount, CREATE_REFERRAL_REWARD_BPS) : 0;
|
|
171
|
+
rewards.tradeReferrerAmount = hasTradeReferral ? calculateReward(totalAmount, TRADE_REFERRAL_REWARD_BPS) : 0;
|
|
172
|
+
rewards.creatorAmount = calculateReward(totalAmount, CREATOR_REWARD_BPS);
|
|
173
|
+
rewards.dopplerAmount = calculateReward(totalAmount, DOPPLER_REWARD_BPS);
|
|
174
|
+
rewards.protocolAmount = totalAmount - rewards.platformReferrerAmount - rewards.tradeReferrerAmount - rewards.creatorAmount - rewards.dopplerAmount;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function calculateReward(uint256 amount, uint256 bps) internal pure returns (uint256) {
|
|
178
|
+
return (amount * bps) / 10_000;
|
|
179
|
+
}
|
|
180
|
+
}
|
package/src/libs/CoinSetup.sol
CHANGED
|
@@ -1,37 +1,57 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.23;
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {CoinLegacy} from "./CoinLegacy.sol";
|
|
6
|
-
import {CoinDopplerUniV3} from "./CoinDopplerUniV3.sol";
|
|
4
|
+
import {PoolConfigurationV4} from "../interfaces/ICoin.sol";
|
|
7
5
|
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
6
|
+
import {ICoin} from "../interfaces/ICoin.sol";
|
|
7
|
+
import {CoinCommon} from "./CoinCommon.sol";
|
|
8
|
+
import {MarketConstants} from "./MarketConstants.sol";
|
|
9
|
+
import {TickMath} from "../utils/uniswap/TickMath.sol";
|
|
10
|
+
import {IPoolManager, PoolKey, Currency, IHooks} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
11
|
+
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
|
12
|
+
import {IPositionManager} from "@uniswap/v4-periphery/src/interfaces/IPositionManager.sol";
|
|
13
|
+
import {MarketConstants} from "./MarketConstants.sol";
|
|
8
14
|
import {LpPosition} from "../types/LpPosition.sol";
|
|
15
|
+
import {CoinDopplerMultiCurve, PoolConfiguration} from "./CoinDopplerMultiCurve.sol";
|
|
16
|
+
import {CoinDopplerUniV3} from "./CoinDopplerUniV3.sol";
|
|
9
17
|
|
|
10
18
|
library CoinSetup {
|
|
11
|
-
|
|
19
|
+
function generatePoolConfig(
|
|
20
|
+
address coin,
|
|
21
|
+
bytes memory poolConfig_
|
|
22
|
+
) internal pure returns (uint8 version, address currency, uint160 sqrtPriceX96, bool isCoinToken0, PoolConfiguration memory poolConfiguration) {
|
|
23
|
+
// Extract version and currency from pool config
|
|
24
|
+
(version, currency) = CoinConfigurationVersions.decodeVersionAndCurrency(poolConfig_);
|
|
25
|
+
|
|
26
|
+
isCoinToken0 = CoinCommon.sortTokens(coin, currency);
|
|
27
|
+
|
|
28
|
+
(sqrtPriceX96, poolConfiguration) = setupPoolWithVersion(version, poolConfig_, isCoinToken0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildPoolKey(address coin, address currency, bool isCoinToken0, IHooks hooks) internal pure returns (PoolKey memory poolKey) {
|
|
32
|
+
Currency currency0 = isCoinToken0 ? Currency.wrap(coin) : Currency.wrap(currency);
|
|
33
|
+
Currency currency1 = isCoinToken0 ? Currency.wrap(currency) : Currency.wrap(coin);
|
|
34
|
+
|
|
35
|
+
poolKey = PoolKey({
|
|
36
|
+
currency0: currency0,
|
|
37
|
+
currency1: currency1,
|
|
38
|
+
fee: MarketConstants.LP_FEE_V4,
|
|
39
|
+
tickSpacing: MarketConstants.TICK_SPACING,
|
|
40
|
+
hooks: hooks
|
|
41
|
+
});
|
|
42
|
+
}
|
|
12
43
|
|
|
13
44
|
function setupPoolWithVersion(
|
|
14
45
|
uint8 version,
|
|
15
46
|
bytes memory poolConfig_,
|
|
16
|
-
bool isCoinToken0
|
|
17
|
-
address weth
|
|
47
|
+
bool isCoinToken0
|
|
18
48
|
) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
|
|
19
|
-
if (version == CoinConfigurationVersions.
|
|
20
|
-
(sqrtPriceX96, poolConfiguration) = CoinLegacy.setupPool(isCoinToken0, poolConfig_, weth);
|
|
21
|
-
} else if (version == CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION) {
|
|
49
|
+
if (version == CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION) {
|
|
22
50
|
(sqrtPriceX96, poolConfiguration) = CoinDopplerUniV3.setupPool(isCoinToken0, poolConfig_);
|
|
51
|
+
} else if (version == CoinConfigurationVersions.DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION) {
|
|
52
|
+
(sqrtPriceX96, poolConfiguration) = CoinDopplerMultiCurve.setupPool(isCoinToken0, poolConfig_);
|
|
23
53
|
} 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();
|
|
54
|
+
revert ICoin.InvalidPoolVersion();
|
|
35
55
|
}
|
|
36
56
|
}
|
|
37
57
|
}
|