@nradko/metric-omm-sdk-v1 0.0.16
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/README.md +111 -0
- package/dist/abis/MetricOmmPool.d.ts +784 -0
- package/dist/abis/MetricOmmPool.d.ts.map +1 -0
- package/dist/abis/MetricOmmPool.js +1008 -0
- package/dist/abis/MetricOmmPool.js.map +1 -0
- package/dist/abis/MetricOmmPoolFactory.d.ts +1365 -0
- package/dist/abis/MetricOmmPoolFactory.d.ts.map +1 -0
- package/dist/abis/MetricOmmPoolFactory.js +1754 -0
- package/dist/abis/MetricOmmPoolFactory.js.map +1 -0
- package/dist/abis/MetricOmmPoolLiquidityAdder.d.ts +308 -0
- package/dist/abis/MetricOmmPoolLiquidityAdder.d.ts.map +1 -0
- package/dist/abis/MetricOmmPoolLiquidityAdder.js +401 -0
- package/dist/abis/MetricOmmPoolLiquidityAdder.js.map +1 -0
- package/dist/abis/MetricOmmPoolStateView.d.ts +538 -0
- package/dist/abis/MetricOmmPoolStateView.d.ts.map +1 -0
- package/dist/abis/MetricOmmPoolStateView.js +717 -0
- package/dist/abis/MetricOmmPoolStateView.js.map +1 -0
- package/dist/abis/MetricOmmPoolSwapper.d.ts +557 -0
- package/dist/abis/MetricOmmPoolSwapper.d.ts.map +1 -0
- package/dist/abis/MetricOmmPoolSwapper.js +723 -0
- package/dist/abis/MetricOmmPoolSwapper.js.map +1 -0
- package/dist/abis/Multicall3.d.ts +333 -0
- package/dist/abis/Multicall3.d.ts.map +1 -0
- package/dist/abis/Multicall3.js +441 -0
- package/dist/abis/Multicall3.js.map +1 -0
- package/dist/abis/PriceProvider.d.ts +142 -0
- package/dist/abis/PriceProvider.d.ts.map +1 -0
- package/dist/abis/PriceProvider.js +184 -0
- package/dist/abis/PriceProvider.js.map +1 -0
- package/dist/abis/PriceProviderUi.d.ts +433 -0
- package/dist/abis/PriceProviderUi.d.ts.map +1 -0
- package/dist/abis/PriceProviderUi.js +318 -0
- package/dist/abis/PriceProviderUi.js.map +1 -0
- package/dist/abis/index.d.ts +11 -0
- package/dist/abis/index.d.ts.map +1 -0
- package/dist/abis/index.js +11 -0
- package/dist/abis/index.js.map +1 -0
- package/dist/addresses.d.ts +52 -0
- package/dist/addresses.d.ts.map +1 -0
- package/dist/addresses.js +58 -0
- package/dist/addresses.js.map +1 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +26 -0
- package/dist/constants.js.map +1 -0
- package/dist/factory/collectFees.d.ts +41 -0
- package/dist/factory/collectFees.d.ts.map +1 -0
- package/dist/factory/collectFees.js +120 -0
- package/dist/factory/collectFees.js.map +1 -0
- package/dist/factory/index.d.ts +5 -0
- package/dist/factory/index.d.ts.map +1 -0
- package/dist/factory/index.js +5 -0
- package/dist/factory/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/pool/create.d.ts +80 -0
- package/dist/pool/create.d.ts.map +1 -0
- package/dist/pool/create.js +80 -0
- package/dist/pool/create.js.map +1 -0
- package/dist/pool/index.d.ts +7 -0
- package/dist/pool/index.d.ts.map +1 -0
- package/dist/pool/index.js +10 -0
- package/dist/pool/index.js.map +1 -0
- package/dist/pool/liquidity.d.ts +165 -0
- package/dist/pool/liquidity.d.ts.map +1 -0
- package/dist/pool/liquidity.js +508 -0
- package/dist/pool/liquidity.js.map +1 -0
- package/dist/pool/read.d.ts +22 -0
- package/dist/pool/read.d.ts.map +1 -0
- package/dist/pool/read.js +88 -0
- package/dist/pool/read.js.map +1 -0
- package/dist/router/index.d.ts +5 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +5 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/swap.d.ts +117 -0
- package/dist/router/swap.d.ts.map +1 -0
- package/dist/router/swap.js +298 -0
- package/dist/router/swap.js.map +1 -0
- package/dist/stateView/index.d.ts +5 -0
- package/dist/stateView/index.d.ts.map +1 -0
- package/dist/stateView/index.js +5 -0
- package/dist/stateView/index.js.map +1 -0
- package/dist/stateView/read.d.ts +80 -0
- package/dist/stateView/read.d.ts.map +1 -0
- package/dist/stateView/read.js +249 -0
- package/dist/stateView/read.js.map +1 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/binData.d.ts +65 -0
- package/dist/utils/binData.d.ts.map +1 -0
- package/dist/utils/binData.js +109 -0
- package/dist/utils/binData.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/liquidityMath.d.ts +10 -0
- package/dist/utils/liquidityMath.d.ts.map +1 -0
- package/dist/utils/liquidityMath.js +31 -0
- package/dist/utils/liquidityMath.js.map +1 -0
- package/dist/utils/price.d.ts +13 -0
- package/dist/utils/price.d.ts.map +1 -0
- package/dist/utils/price.js +21 -0
- package/dist/utils/price.js.map +1 -0
- package/package.json +74 -0
- package/src/abis/MetricOmmPool.ts +1007 -0
- package/src/abis/MetricOmmPoolFactory.ts +1753 -0
- package/src/abis/MetricOmmPoolLiquidityAdder.ts +400 -0
- package/src/abis/MetricOmmPoolStateView.ts +716 -0
- package/src/abis/MetricOmmPoolSwapper.ts +722 -0
- package/src/abis/Multicall3.ts +440 -0
- package/src/abis/PriceProvider.ts +183 -0
- package/src/abis/PriceProviderUi.ts +317 -0
- package/src/abis/index.ts +11 -0
- package/src/addresses.ts +100 -0
- package/src/constants.ts +35 -0
- package/src/factory/collectFees.ts +197 -0
- package/src/factory/index.ts +12 -0
- package/src/index.ts +157 -0
- package/src/pool/create.ts +158 -0
- package/src/pool/index.ts +47 -0
- package/src/pool/liquidity.ts +839 -0
- package/src/pool/read.ts +131 -0
- package/src/router/index.ts +27 -0
- package/src/router/swap.ts +507 -0
- package/src/stateView/index.ts +18 -0
- package/src/stateView/read.ts +355 -0
- package/src/types.ts +162 -0
- package/src/utils/binData.ts +127 -0
- package/src/utils/index.ts +26 -0
- package/src/utils/liquidityMath.ts +47 -0
- package/src/utils/price.ts +23 -0
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetricAMM SDK - Liquidity
|
|
3
|
+
* Unified liquidity calculations and position operations.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Address, PublicClient, Hex } from "viem";
|
|
7
|
+
import { encodeFunctionData } from "viem";
|
|
8
|
+
import { Q64, MAX_UINT104, MAX_INT104, MAX_INT128, LIQUIDITY_ADDER_ABI } from "../constants.js";
|
|
9
|
+
import { MetricOmmPoolAbi } from "../abis/MetricOmmPool.js";
|
|
10
|
+
import type { LiquidityDelta, LiquidityBinValueInput, PoolLiquidityDelta } from "../types.js";
|
|
11
|
+
import { getPoolImmutables } from "./read.js";
|
|
12
|
+
import {
|
|
13
|
+
getSlot0,
|
|
14
|
+
getBinStatesScaled,
|
|
15
|
+
getPositionBinSharesRange,
|
|
16
|
+
getPositionBinSharesForBins,
|
|
17
|
+
} from "../stateView/read.js";
|
|
18
|
+
import {
|
|
19
|
+
ONE_E18,
|
|
20
|
+
ceilDiv,
|
|
21
|
+
toPriceE18,
|
|
22
|
+
convertToken0ToToken1,
|
|
23
|
+
convertToken1ToToken0,
|
|
24
|
+
scaledPositiveDeltaToExternal,
|
|
25
|
+
} from "../utils/liquidityMath.js";
|
|
26
|
+
export { convertToken0ToToken1, convertToken1ToToken0 };
|
|
27
|
+
export const getPositionShares = getPositionBinSharesRange;
|
|
28
|
+
export const getPositionSharesForBins = getPositionBinSharesForBins;
|
|
29
|
+
|
|
30
|
+
export interface RemoveLiquidityParams {
|
|
31
|
+
percentageToRemove: number;
|
|
32
|
+
bins?: number[];
|
|
33
|
+
lowerBin?: number;
|
|
34
|
+
upperBin?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BinRemovalSpec {
|
|
38
|
+
bin: number;
|
|
39
|
+
percentageToRemove?: number;
|
|
40
|
+
sharesToRemove?: bigint;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const NO_SLIPPAGE_LIMIT = MAX_INT128;
|
|
44
|
+
|
|
45
|
+
type ModifyLiquidityParams = {
|
|
46
|
+
salt: bigint;
|
|
47
|
+
deltas: LiquidityDelta[];
|
|
48
|
+
specAmount0: bigint;
|
|
49
|
+
specAmount1: bigint;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ModifyLiquidityArgs = readonly [bigint, LiquidityDelta[], bigint, bigint];
|
|
53
|
+
|
|
54
|
+
export interface BuildModifyLiquidityArgsBase {
|
|
55
|
+
publicClient: PublicClient;
|
|
56
|
+
/** Factory that deployed the pool (used to read `poolImmutables`). */
|
|
57
|
+
factoryAddress: Address;
|
|
58
|
+
stateViewAddress: Address;
|
|
59
|
+
poolAddress: Address;
|
|
60
|
+
specAmountBufferPercent?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface BuildModifyLiquidityArgsForAdditionParams extends BuildModifyLiquidityArgsBase {
|
|
64
|
+
bins: LiquidityBinValueInput[];
|
|
65
|
+
currentPrice: number;
|
|
66
|
+
salt?: bigint;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface BuildModifyLiquidityArgsForUniformAdditionParams extends BuildModifyLiquidityArgsBase {
|
|
70
|
+
amountInTokensPerBin: bigint;
|
|
71
|
+
amountIsInToken0: boolean;
|
|
72
|
+
currentPrice: number;
|
|
73
|
+
lowerBin: number;
|
|
74
|
+
upperBin: number;
|
|
75
|
+
salt?: bigint;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface BuildModifyLiquidityArgsForUniformAdditionWithTotalTokenAmountParams extends BuildModifyLiquidityArgsBase {
|
|
79
|
+
bins: number[];
|
|
80
|
+
weights?: number[];
|
|
81
|
+
totalValueToAddInToken: bigint;
|
|
82
|
+
totalValueInToken0: boolean;
|
|
83
|
+
currentPriceX64: bigint;
|
|
84
|
+
salt?: bigint;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface BuildModifyLiquidityArgsForPercentageRemovalParams extends BuildModifyLiquidityArgsBase {
|
|
88
|
+
owner: Address;
|
|
89
|
+
salt: bigint;
|
|
90
|
+
percentageToRemove: number;
|
|
91
|
+
bins?: number[];
|
|
92
|
+
lowerBin?: number;
|
|
93
|
+
upperBin?: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface BuildModifyLiquidityArgsForRemovalParams extends BuildModifyLiquidityArgsBase {
|
|
97
|
+
owner: Address;
|
|
98
|
+
salt: bigint;
|
|
99
|
+
specs: BinRemovalSpec[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Prepare uniform liquidity distribution across a bin range.
|
|
104
|
+
*/
|
|
105
|
+
export async function buildModifyLiquidityArgsForUniformAddition({
|
|
106
|
+
publicClient,
|
|
107
|
+
factoryAddress,
|
|
108
|
+
stateViewAddress,
|
|
109
|
+
poolAddress,
|
|
110
|
+
amountInTokensPerBin,
|
|
111
|
+
amountIsInToken0,
|
|
112
|
+
currentPrice,
|
|
113
|
+
lowerBin,
|
|
114
|
+
upperBin,
|
|
115
|
+
salt = 0n,
|
|
116
|
+
specAmountBufferPercent = 0,
|
|
117
|
+
}: BuildModifyLiquidityArgsForUniformAdditionParams): Promise<ModifyLiquidityArgs> {
|
|
118
|
+
if (lowerBin > upperBin) {
|
|
119
|
+
throw new Error("lowerBin must be <= upperBin");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const bins: LiquidityBinValueInput[] = [];
|
|
123
|
+
for (let bin = lowerBin; bin <= upperBin; bin++) {
|
|
124
|
+
bins.push({
|
|
125
|
+
bin,
|
|
126
|
+
targetValueInToken: amountInTokensPerBin,
|
|
127
|
+
targetValueInToken0: amountIsInToken0,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return buildModifyLiquidityArgsForAddition({
|
|
132
|
+
publicClient,
|
|
133
|
+
factoryAddress,
|
|
134
|
+
stateViewAddress,
|
|
135
|
+
poolAddress,
|
|
136
|
+
bins,
|
|
137
|
+
currentPrice,
|
|
138
|
+
salt,
|
|
139
|
+
specAmountBufferPercent,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Prepare uniform liquidity across arbitrary bins from a TOTAL value budget.
|
|
145
|
+
*
|
|
146
|
+
* Algorithm:
|
|
147
|
+
* 1. Calibrate by assigning weighted 1e18 targets per bin with zero buffer.
|
|
148
|
+
* 2. Compute linear scale so selected specAmount token matches totalValueToAddInToken.
|
|
149
|
+
* 3. Rebuild with scaled per-bin value and caller-provided specAmountBufferPercent.
|
|
150
|
+
*/
|
|
151
|
+
export async function buildModifyLiquidityArgsForUniformAdditionWithTotalTokenAmount({
|
|
152
|
+
publicClient,
|
|
153
|
+
factoryAddress,
|
|
154
|
+
stateViewAddress,
|
|
155
|
+
poolAddress,
|
|
156
|
+
bins,
|
|
157
|
+
weights,
|
|
158
|
+
totalValueToAddInToken,
|
|
159
|
+
totalValueInToken0,
|
|
160
|
+
currentPriceX64,
|
|
161
|
+
salt = 0n,
|
|
162
|
+
specAmountBufferPercent = 0,
|
|
163
|
+
}: BuildModifyLiquidityArgsForUniformAdditionWithTotalTokenAmountParams): Promise<ModifyLiquidityArgs> {
|
|
164
|
+
validateSpecAmountBufferPercent(specAmountBufferPercent);
|
|
165
|
+
|
|
166
|
+
if (!Array.isArray(bins) || bins.length === 0) {
|
|
167
|
+
throw new Error("bins must be a non-empty array");
|
|
168
|
+
}
|
|
169
|
+
if (weights != null) {
|
|
170
|
+
if (!Array.isArray(weights) || weights.length !== bins.length) {
|
|
171
|
+
throw new Error("weights must be an array with the same length as bins");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const uniqueBins = new Set(bins);
|
|
175
|
+
if (uniqueBins.size !== bins.length) {
|
|
176
|
+
throw new Error("bins must not contain duplicates");
|
|
177
|
+
}
|
|
178
|
+
if (totalValueToAddInToken <= 0n) {
|
|
179
|
+
throw new Error("totalValueToAddInToken must be > 0");
|
|
180
|
+
}
|
|
181
|
+
if (currentPriceX64 <= 0n) {
|
|
182
|
+
throw new Error("currentPriceX64 must be > 0");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const currentPrice = Number(currentPriceX64.toString()) / Number(Q64.toString());
|
|
186
|
+
if (!Number.isFinite(currentPrice) || currentPrice <= 0) {
|
|
187
|
+
throw new Error("currentPriceX64 is out of supported numeric range");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const calibrationValue = totalValueToAddInToken / BigInt(bins.length) + 1n;
|
|
191
|
+
const calibrationTargets: bigint[] = weights
|
|
192
|
+
? weights.map((w) => (calibrationValue * BigInt(Math.ceil(w * 1e6) + 1)) / 1_000_000n)
|
|
193
|
+
: Array(bins.length).fill(calibrationValue);
|
|
194
|
+
|
|
195
|
+
const calibrationBins: LiquidityBinValueInput[] = bins.map((bin, i) => ({
|
|
196
|
+
bin,
|
|
197
|
+
targetValueInToken: calibrationTargets[i],
|
|
198
|
+
targetValueInToken0: totalValueInToken0,
|
|
199
|
+
}));
|
|
200
|
+
|
|
201
|
+
const [, , calibrationSpecAmount0, calibrationSpecAmount1] =
|
|
202
|
+
await buildModifyLiquidityArgsForAddition({
|
|
203
|
+
publicClient,
|
|
204
|
+
factoryAddress,
|
|
205
|
+
stateViewAddress,
|
|
206
|
+
poolAddress,
|
|
207
|
+
bins: calibrationBins,
|
|
208
|
+
currentPrice,
|
|
209
|
+
salt,
|
|
210
|
+
specAmountBufferPercent: 0,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const calibrationSpecAmount = totalValueInToken0
|
|
214
|
+
? calibrationSpecAmount0
|
|
215
|
+
: calibrationSpecAmount1;
|
|
216
|
+
|
|
217
|
+
if (calibrationSpecAmount <= 0n) {
|
|
218
|
+
throw new Error("failed to calibrate uniform total amount scaling: specAmount is zero");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const scaledBins: LiquidityBinValueInput[] = bins.map((bin, i) => ({
|
|
222
|
+
bin,
|
|
223
|
+
targetValueInToken: (calibrationTargets[i] * totalValueToAddInToken) / calibrationSpecAmount,
|
|
224
|
+
targetValueInToken0: totalValueInToken0,
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
return buildModifyLiquidityArgsForAddition({
|
|
228
|
+
publicClient,
|
|
229
|
+
factoryAddress,
|
|
230
|
+
stateViewAddress,
|
|
231
|
+
poolAddress,
|
|
232
|
+
bins: scaledBins,
|
|
233
|
+
currentPrice,
|
|
234
|
+
salt,
|
|
235
|
+
specAmountBufferPercent,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Prepare per-bin liquidity distribution from arbitrary per-bin amounts.
|
|
241
|
+
*
|
|
242
|
+
* Each input entry defines a single bin and a value expressed in token0 or token1.
|
|
243
|
+
* The function computes `deltaShares` for every bin and totals `specAmount0/specAmount1`
|
|
244
|
+
* needed to execute `modifyLiquidity` safely.
|
|
245
|
+
*
|
|
246
|
+
* ## Algorithm Overview
|
|
247
|
+
*
|
|
248
|
+
* This is the core distribution engine used by both:
|
|
249
|
+
* - direct per-bin plans (computeModifyLiquidityForAddition)
|
|
250
|
+
* - uniform plans via adapter (computeModifyLiquidityForUniformAddition)
|
|
251
|
+
*
|
|
252
|
+
* ## Steps
|
|
253
|
+
*
|
|
254
|
+
* 1. Validate inputs, price, bin range, and reject duplicate bins.
|
|
255
|
+
* 2. Convert each bin target into both token0/token1 amounts using current price.
|
|
256
|
+
* 3. Fetch slot0 and bin states, then classify each bin as below/current/above.
|
|
257
|
+
* 4. Compute shares to add using ceiling division to avoid under-provisioning:
|
|
258
|
+
* - Existing bins: proportional to current scaled balances and total shares.
|
|
259
|
+
* - Empty bins: derived from initial scaled per-share constants.
|
|
260
|
+
* 5. Convert scaled amounts back to external token amounts and accumulate
|
|
261
|
+
* `specAmount0/specAmount1` with overflow checks.
|
|
262
|
+
*/
|
|
263
|
+
export async function buildModifyLiquidityArgsForAddition({
|
|
264
|
+
publicClient,
|
|
265
|
+
factoryAddress,
|
|
266
|
+
stateViewAddress,
|
|
267
|
+
poolAddress,
|
|
268
|
+
bins,
|
|
269
|
+
currentPrice,
|
|
270
|
+
salt = 0n,
|
|
271
|
+
specAmountBufferPercent = 0,
|
|
272
|
+
}: BuildModifyLiquidityArgsForAdditionParams): Promise<ModifyLiquidityArgs> {
|
|
273
|
+
validateSpecAmountBufferPercent(specAmountBufferPercent);
|
|
274
|
+
|
|
275
|
+
if (!Array.isArray(bins)) {
|
|
276
|
+
throw new Error("bins must be an array");
|
|
277
|
+
}
|
|
278
|
+
if (bins.length === 0) {
|
|
279
|
+
return [salt, [], 0n, 0n];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const immutables = await getPoolImmutables(publicClient, factoryAddress, poolAddress);
|
|
283
|
+
const priceE18 = toPriceE18(currentPrice);
|
|
284
|
+
|
|
285
|
+
const targetsByBin = new Map<
|
|
286
|
+
number,
|
|
287
|
+
{
|
|
288
|
+
targetAmount0: bigint;
|
|
289
|
+
targetAmount1: bigint;
|
|
290
|
+
targetValueInToken1: bigint;
|
|
291
|
+
}
|
|
292
|
+
>();
|
|
293
|
+
|
|
294
|
+
for (const entry of bins) {
|
|
295
|
+
if (!Number.isInteger(entry.bin)) {
|
|
296
|
+
throw new Error(`bin must be an integer, got ${entry.bin}`);
|
|
297
|
+
}
|
|
298
|
+
if (entry.bin < immutables.lowestBin || entry.bin > immutables.highestBin) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`bin ${entry.bin} is out of range [${immutables.lowestBin}, ${immutables.highestBin}]`,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
if (entry.targetValueInToken <= 0n) {
|
|
304
|
+
throw new Error(`amountInToken for bin ${entry.bin} must be > 0`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const amount0 = entry.targetValueInToken0
|
|
308
|
+
? entry.targetValueInToken
|
|
309
|
+
: convertToken1ToToken0(
|
|
310
|
+
entry.targetValueInToken,
|
|
311
|
+
immutables.token0ScaleMultiplier,
|
|
312
|
+
immutables.token1ScaleMultiplier,
|
|
313
|
+
priceE18,
|
|
314
|
+
);
|
|
315
|
+
const amount1 = entry.targetValueInToken0
|
|
316
|
+
? convertToken0ToToken1(
|
|
317
|
+
entry.targetValueInToken,
|
|
318
|
+
immutables.token0ScaleMultiplier,
|
|
319
|
+
immutables.token1ScaleMultiplier,
|
|
320
|
+
priceE18,
|
|
321
|
+
)
|
|
322
|
+
: entry.targetValueInToken;
|
|
323
|
+
const targetValueInToken1 = entry.targetValueInToken0
|
|
324
|
+
? convertToken0ToToken1(
|
|
325
|
+
entry.targetValueInToken,
|
|
326
|
+
immutables.token0ScaleMultiplier,
|
|
327
|
+
immutables.token1ScaleMultiplier,
|
|
328
|
+
priceE18,
|
|
329
|
+
)
|
|
330
|
+
: entry.targetValueInToken;
|
|
331
|
+
|
|
332
|
+
if (targetsByBin.has(entry.bin)) {
|
|
333
|
+
throw new Error(`duplicate bin ${entry.bin} is not supported`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
targetsByBin.set(entry.bin, {
|
|
337
|
+
targetAmount0: amount0,
|
|
338
|
+
targetAmount1: amount1,
|
|
339
|
+
targetValueInToken1,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const binIndices = Array.from(targetsByBin.keys()).sort((a, b) => a - b);
|
|
344
|
+
const [slot0, binStates] = await Promise.all([
|
|
345
|
+
getSlot0(publicClient, stateViewAddress, poolAddress),
|
|
346
|
+
getBinStatesScaled(publicClient, stateViewAddress, poolAddress, binIndices),
|
|
347
|
+
]);
|
|
348
|
+
|
|
349
|
+
const deltas: LiquidityDelta[] = [];
|
|
350
|
+
let specAmount0 = 0n;
|
|
351
|
+
let specAmount1 = 0n;
|
|
352
|
+
|
|
353
|
+
const curBinIdx = slot0.curBinIdx;
|
|
354
|
+
const curPosInBin = slot0.curPosInBin;
|
|
355
|
+
|
|
356
|
+
for (let i = 0; i < binIndices.length; i++) {
|
|
357
|
+
const binIdx = binIndices[i];
|
|
358
|
+
const target = targetsByBin.get(binIdx);
|
|
359
|
+
if (!target) {
|
|
360
|
+
throw new Error(`internal error: missing target for bin ${binIdx}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const targetAmount0Scaled = target.targetAmount0 * immutables.token0ScaleMultiplier;
|
|
364
|
+
const targetAmount1Scaled = target.targetAmount1 * immutables.token1ScaleMultiplier;
|
|
365
|
+
|
|
366
|
+
const targetValueInToken1Scaled = target.targetValueInToken1 * immutables.token1ScaleMultiplier;
|
|
367
|
+
|
|
368
|
+
const token0BalanceScaled = binStates.token0BalancesScaled[i];
|
|
369
|
+
const token1BalanceScaled = binStates.token1BalancesScaled[i];
|
|
370
|
+
const totalShares = binStates.totalShares[i];
|
|
371
|
+
|
|
372
|
+
const isBelowBin = binIdx < curBinIdx || (binIdx === curBinIdx && curPosInBin === MAX_UINT104);
|
|
373
|
+
const isAboveBin = binIdx > curBinIdx || (binIdx === curBinIdx && curPosInBin === 0n);
|
|
374
|
+
|
|
375
|
+
let sharesToAdd: bigint;
|
|
376
|
+
|
|
377
|
+
if (isBelowBin) {
|
|
378
|
+
if (targetAmount1Scaled <= 0n) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
`amount for bin ${binIdx} is too small after conversion and results in 0 token1 target`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (totalShares > 0n) {
|
|
384
|
+
if (token1BalanceScaled <= 0n) {
|
|
385
|
+
throw new Error(`bin ${binIdx} has zero token1 balance with non-zero shares`);
|
|
386
|
+
}
|
|
387
|
+
sharesToAdd = ceilDiv(targetAmount1Scaled * totalShares, token1BalanceScaled);
|
|
388
|
+
} else {
|
|
389
|
+
sharesToAdd = ceilDiv(
|
|
390
|
+
targetAmount1Scaled * ONE_E18,
|
|
391
|
+
immutables.initialScaledToken1PerShareE18,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
} else if (isAboveBin) {
|
|
395
|
+
if (targetAmount0Scaled <= 0n) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`amount for bin ${binIdx} is too small after conversion and results in 0 token0 target`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
if (totalShares > 0n) {
|
|
401
|
+
if (token0BalanceScaled <= 0n) {
|
|
402
|
+
throw new Error(`bin ${binIdx} has zero token0 balance with non-zero shares`);
|
|
403
|
+
}
|
|
404
|
+
sharesToAdd = ceilDiv(targetAmount0Scaled * totalShares, token0BalanceScaled);
|
|
405
|
+
} else {
|
|
406
|
+
sharesToAdd = ceilDiv(
|
|
407
|
+
targetAmount0Scaled * ONE_E18,
|
|
408
|
+
immutables.initialScaledToken0PerShareE18,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
if (totalShares > 0n) {
|
|
413
|
+
const totalValueInToken1Scaled =
|
|
414
|
+
token1BalanceScaled + convertToken0ToToken1(token0BalanceScaled, 1n, 1n, priceE18);
|
|
415
|
+
|
|
416
|
+
sharesToAdd = (targetValueInToken1Scaled * totalShares) / totalValueInToken1Scaled;
|
|
417
|
+
} else {
|
|
418
|
+
const token0Portion = MAX_UINT104 - curPosInBin;
|
|
419
|
+
const token1Portion = curPosInBin;
|
|
420
|
+
|
|
421
|
+
sharesToAdd =
|
|
422
|
+
(targetValueInToken1Scaled * ONE_E18) /
|
|
423
|
+
((immutables.initialScaledToken1PerShareE18 * token1Portion +
|
|
424
|
+
(immutables.initialScaledToken0PerShareE18 * token0Portion * priceE18) / ONE_E18) /
|
|
425
|
+
MAX_UINT104);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (sharesToAdd === 0n) {
|
|
430
|
+
sharesToAdd = 1n;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (sharesToAdd > MAX_INT104) {
|
|
434
|
+
throw new Error(`sharesToAdd (${sharesToAdd}) for bin ${binIdx} exceeds int104 max`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const [amount0ScaledToAdd, amount1ScaledToAdd] =
|
|
438
|
+
totalShares > 0n
|
|
439
|
+
? [
|
|
440
|
+
ceilDiv(sharesToAdd * token0BalanceScaled, totalShares),
|
|
441
|
+
ceilDiv(sharesToAdd * token1BalanceScaled, totalShares),
|
|
442
|
+
]
|
|
443
|
+
: isBelowBin
|
|
444
|
+
? [0n, ceilDiv(sharesToAdd * immutables.initialScaledToken1PerShareE18, ONE_E18)]
|
|
445
|
+
: isAboveBin
|
|
446
|
+
? [ceilDiv(sharesToAdd * immutables.initialScaledToken0PerShareE18, ONE_E18), 0n]
|
|
447
|
+
: (() => {
|
|
448
|
+
const token0Portion = MAX_UINT104 - curPosInBin;
|
|
449
|
+
const token1Portion = curPosInBin;
|
|
450
|
+
return [
|
|
451
|
+
ceilDiv(
|
|
452
|
+
sharesToAdd * immutables.initialScaledToken0PerShareE18 * token0Portion,
|
|
453
|
+
ONE_E18 * MAX_UINT104,
|
|
454
|
+
),
|
|
455
|
+
ceilDiv(
|
|
456
|
+
sharesToAdd * immutables.initialScaledToken1PerShareE18 * token1Portion,
|
|
457
|
+
ONE_E18 * MAX_UINT104,
|
|
458
|
+
),
|
|
459
|
+
] as const;
|
|
460
|
+
})();
|
|
461
|
+
|
|
462
|
+
const amount0External = scaledPositiveDeltaToExternal(
|
|
463
|
+
amount0ScaledToAdd,
|
|
464
|
+
immutables.token0ScaleMultiplier,
|
|
465
|
+
);
|
|
466
|
+
const amount1External = scaledPositiveDeltaToExternal(
|
|
467
|
+
amount1ScaledToAdd,
|
|
468
|
+
immutables.token1ScaleMultiplier,
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
specAmount0 += amount0External;
|
|
472
|
+
specAmount1 += amount1External;
|
|
473
|
+
|
|
474
|
+
if (specAmount0 > MAX_INT128 || specAmount1 > MAX_INT128) {
|
|
475
|
+
throw new Error("specAmount exceeds int128 max");
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
deltas.push({
|
|
479
|
+
bin: binIdx,
|
|
480
|
+
deltaShares: sharesToAdd,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const computed: ModifyLiquidityParams = {
|
|
485
|
+
salt,
|
|
486
|
+
deltas,
|
|
487
|
+
specAmount0: applySpecAmountBuffer(specAmount0, specAmountBufferPercent),
|
|
488
|
+
specAmount1: applySpecAmountBuffer(specAmount1, specAmountBufferPercent),
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
return toModifyLiquidityArgs(computed);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export async function buildModifyLiquidityArgsForRemoval(
|
|
495
|
+
params: BuildModifyLiquidityArgsForRemovalParams,
|
|
496
|
+
): Promise<ModifyLiquidityArgs> {
|
|
497
|
+
const {
|
|
498
|
+
publicClient,
|
|
499
|
+
stateViewAddress,
|
|
500
|
+
poolAddress,
|
|
501
|
+
owner,
|
|
502
|
+
salt,
|
|
503
|
+
specs,
|
|
504
|
+
specAmountBufferPercent = 0,
|
|
505
|
+
} = params;
|
|
506
|
+
validateSpecAmountBufferPercent(specAmountBufferPercent);
|
|
507
|
+
|
|
508
|
+
if (specs.length === 0) {
|
|
509
|
+
return [salt, [], NO_SLIPPAGE_LIMIT, NO_SLIPPAGE_LIMIT];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
for (const spec of specs) {
|
|
513
|
+
if (spec.percentageToRemove != null && spec.sharesToRemove != null) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`Bin ${spec.bin}: specify either percentageToRemove or sharesToRemove, not both`,
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
if (spec.percentageToRemove == null && spec.sharesToRemove == null) {
|
|
519
|
+
throw new Error(`Bin ${spec.bin}: must specify either percentageToRemove or sharesToRemove`);
|
|
520
|
+
}
|
|
521
|
+
if (
|
|
522
|
+
spec.percentageToRemove != null &&
|
|
523
|
+
(spec.percentageToRemove <= 0 || spec.percentageToRemove > 100)
|
|
524
|
+
) {
|
|
525
|
+
throw new Error(`Bin ${spec.bin}: percentageToRemove must be between 0 and 100`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const binIndices = specs.map((s) => s.bin);
|
|
530
|
+
const positionShares = await getPositionBinSharesForBins(
|
|
531
|
+
publicClient,
|
|
532
|
+
stateViewAddress,
|
|
533
|
+
poolAddress,
|
|
534
|
+
owner,
|
|
535
|
+
salt,
|
|
536
|
+
binIndices,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
const sharesMap = new Map(positionShares.map((p) => [p.binIdx, p.shares]));
|
|
540
|
+
const deltas: LiquidityDelta[] = [];
|
|
541
|
+
|
|
542
|
+
for (const spec of specs) {
|
|
543
|
+
const currentShares = sharesMap.get(spec.bin) ?? 0n;
|
|
544
|
+
if (currentShares === 0n) continue;
|
|
545
|
+
|
|
546
|
+
let sharesToRemove: bigint;
|
|
547
|
+
|
|
548
|
+
if (spec.sharesToRemove != null) {
|
|
549
|
+
sharesToRemove = spec.sharesToRemove > currentShares ? currentShares : spec.sharesToRemove;
|
|
550
|
+
} else {
|
|
551
|
+
sharesToRemove =
|
|
552
|
+
spec.percentageToRemove === 100
|
|
553
|
+
? currentShares
|
|
554
|
+
: (currentShares * BigInt(Math.floor(spec.percentageToRemove! * 100))) / 10000n;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (sharesToRemove > 0n) {
|
|
558
|
+
deltas.push({
|
|
559
|
+
bin: spec.bin,
|
|
560
|
+
deltaShares: -sharesToRemove,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const computed: ModifyLiquidityParams = {
|
|
566
|
+
salt,
|
|
567
|
+
deltas,
|
|
568
|
+
specAmount0: applySpecAmountBuffer(NO_SLIPPAGE_LIMIT, specAmountBufferPercent),
|
|
569
|
+
specAmount1: applySpecAmountBuffer(NO_SLIPPAGE_LIMIT, specAmountBufferPercent),
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
return toModifyLiquidityArgs(computed);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export async function buildModifyLiquidityArgsForPercentageRemoval({
|
|
576
|
+
publicClient,
|
|
577
|
+
factoryAddress,
|
|
578
|
+
stateViewAddress,
|
|
579
|
+
poolAddress,
|
|
580
|
+
owner,
|
|
581
|
+
salt,
|
|
582
|
+
percentageToRemove,
|
|
583
|
+
bins,
|
|
584
|
+
lowerBin = -10,
|
|
585
|
+
upperBin = 10,
|
|
586
|
+
specAmountBufferPercent = 0,
|
|
587
|
+
}: BuildModifyLiquidityArgsForPercentageRemovalParams): Promise<ModifyLiquidityArgs> {
|
|
588
|
+
validateSpecAmountBufferPercent(specAmountBufferPercent);
|
|
589
|
+
|
|
590
|
+
if (percentageToRemove <= 0 || percentageToRemove > 100) {
|
|
591
|
+
throw new Error("percentageToRemove must be between 0 and 100");
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const binsWithShares = bins
|
|
595
|
+
? bins
|
|
596
|
+
: (
|
|
597
|
+
await getPositionBinSharesRange(
|
|
598
|
+
publicClient,
|
|
599
|
+
stateViewAddress,
|
|
600
|
+
poolAddress,
|
|
601
|
+
owner,
|
|
602
|
+
salt,
|
|
603
|
+
lowerBin,
|
|
604
|
+
upperBin,
|
|
605
|
+
)
|
|
606
|
+
).map((p) => p.binIdx);
|
|
607
|
+
|
|
608
|
+
if (binsWithShares.length === 0) {
|
|
609
|
+
return [salt, [], NO_SLIPPAGE_LIMIT, NO_SLIPPAGE_LIMIT];
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const specs: BinRemovalSpec[] = binsWithShares.map((bin) => ({
|
|
613
|
+
bin,
|
|
614
|
+
percentageToRemove,
|
|
615
|
+
}));
|
|
616
|
+
|
|
617
|
+
return buildModifyLiquidityArgsForRemoval({
|
|
618
|
+
publicClient,
|
|
619
|
+
factoryAddress,
|
|
620
|
+
stateViewAddress,
|
|
621
|
+
poolAddress,
|
|
622
|
+
owner,
|
|
623
|
+
salt,
|
|
624
|
+
specs,
|
|
625
|
+
specAmountBufferPercent,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export function additionPlanToPoolLiquidityDelta(
|
|
630
|
+
deltas: readonly LiquidityDelta[],
|
|
631
|
+
): PoolLiquidityDelta {
|
|
632
|
+
const pos = deltas.filter((d) => d.deltaShares > 0n).sort((a, b) => a.bin - b.bin);
|
|
633
|
+
return {
|
|
634
|
+
binIdxs: pos.map((d) => BigInt(d.bin)),
|
|
635
|
+
shares: pos.map((d) => d.deltaShares),
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
export function removalPlanToPoolLiquidityDelta(
|
|
640
|
+
deltas: readonly LiquidityDelta[],
|
|
641
|
+
): PoolLiquidityDelta {
|
|
642
|
+
const burn = deltas.filter((d) => d.deltaShares < 0n).sort((a, b) => a.bin - b.bin);
|
|
643
|
+
return {
|
|
644
|
+
binIdxs: burn.map((d) => BigInt(d.bin)),
|
|
645
|
+
shares: burn.map((d) => -d.deltaShares),
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const WAD = 10n ** 18n;
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Matches `MetricOmmPoolLiquidityAdder._scaleWeightsToShares`: given probe token needs from a weighted
|
|
653
|
+
* probe with the same weight vector, scale integer shares so neither token exceeds the caller caps.
|
|
654
|
+
*/
|
|
655
|
+
export function scaleWeightedPoolDeltasToFitCaps(params: {
|
|
656
|
+
weights: PoolLiquidityDelta;
|
|
657
|
+
probeNeed0: bigint;
|
|
658
|
+
probeNeed1: bigint;
|
|
659
|
+
maxAmountToken0: bigint;
|
|
660
|
+
maxAmountToken1: bigint;
|
|
661
|
+
}): PoolLiquidityDelta {
|
|
662
|
+
const { weights, probeNeed0, probeNeed1, maxAmountToken0, maxAmountToken1 } = params;
|
|
663
|
+
const maxU = 2n ** 256n - 2n;
|
|
664
|
+
const scaleWad0 = probeNeed0 === 0n ? maxU : (maxAmountToken0 * WAD) / probeNeed0;
|
|
665
|
+
const scaleWad1 = probeNeed1 === 0n ? maxU : (maxAmountToken1 * WAD) / probeNeed1;
|
|
666
|
+
const scaleWad = scaleWad0 < scaleWad1 ? scaleWad0 : scaleWad1;
|
|
667
|
+
const shares = weights.shares.map((w, i) => {
|
|
668
|
+
const scaled = (w * scaleWad) / WAD;
|
|
669
|
+
if (w !== 0n && scaled === 0n) {
|
|
670
|
+
throw new Error(`shares rounded to zero at weight index ${i}`);
|
|
671
|
+
}
|
|
672
|
+
return scaled;
|
|
673
|
+
});
|
|
674
|
+
return { binIdxs: weights.binIdxs, shares };
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
export async function buildAddLiquidityExactSharesParamsFromBinTargets(
|
|
678
|
+
params: BuildModifyLiquidityArgsForAdditionParams,
|
|
679
|
+
): Promise<{
|
|
680
|
+
salt: bigint;
|
|
681
|
+
poolLiquidityDelta: PoolLiquidityDelta;
|
|
682
|
+
maxAmountToken0: bigint;
|
|
683
|
+
maxAmountToken1: bigint;
|
|
684
|
+
}> {
|
|
685
|
+
const [salt, deltas, max0, max1] = await buildModifyLiquidityArgsForAddition(params);
|
|
686
|
+
return {
|
|
687
|
+
salt,
|
|
688
|
+
poolLiquidityDelta: additionPlanToPoolLiquidityDelta(deltas),
|
|
689
|
+
maxAmountToken0: max0,
|
|
690
|
+
maxAmountToken1: max1,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
export function encodeAddLiquidityExactSharesSelfCalldata(params: {
|
|
695
|
+
pool: Address;
|
|
696
|
+
salt: bigint;
|
|
697
|
+
deltas: PoolLiquidityDelta;
|
|
698
|
+
maxAmountToken0: bigint;
|
|
699
|
+
maxAmountToken1: bigint;
|
|
700
|
+
}): Hex {
|
|
701
|
+
return encodeFunctionData({
|
|
702
|
+
abi: LIQUIDITY_ADDER_ABI,
|
|
703
|
+
functionName: "addLiquidityExactShares",
|
|
704
|
+
args: [
|
|
705
|
+
params.pool,
|
|
706
|
+
params.salt,
|
|
707
|
+
{ binIdxs: [...params.deltas.binIdxs], shares: [...params.deltas.shares] },
|
|
708
|
+
params.maxAmountToken0,
|
|
709
|
+
params.maxAmountToken1,
|
|
710
|
+
],
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export function encodeAddLiquidityExactSharesWithOwnerCalldata(params: {
|
|
715
|
+
pool: Address;
|
|
716
|
+
owner: Address;
|
|
717
|
+
salt: bigint;
|
|
718
|
+
deltas: PoolLiquidityDelta;
|
|
719
|
+
maxAmountToken0: bigint;
|
|
720
|
+
maxAmountToken1: bigint;
|
|
721
|
+
}): Hex {
|
|
722
|
+
return encodeFunctionData({
|
|
723
|
+
abi: LIQUIDITY_ADDER_ABI,
|
|
724
|
+
functionName: "addLiquidityExactShares",
|
|
725
|
+
args: [
|
|
726
|
+
params.pool,
|
|
727
|
+
params.owner,
|
|
728
|
+
params.salt,
|
|
729
|
+
{ binIdxs: [...params.deltas.binIdxs], shares: [...params.deltas.shares] },
|
|
730
|
+
params.maxAmountToken0,
|
|
731
|
+
params.maxAmountToken1,
|
|
732
|
+
],
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
export function encodeAddLiquidityWeightedSelfCalldata(params: {
|
|
737
|
+
pool: Address;
|
|
738
|
+
salt: bigint;
|
|
739
|
+
weightDeltas: PoolLiquidityDelta;
|
|
740
|
+
maxAmountToken0: bigint;
|
|
741
|
+
maxAmountToken1: bigint;
|
|
742
|
+
}): Hex {
|
|
743
|
+
return encodeFunctionData({
|
|
744
|
+
abi: LIQUIDITY_ADDER_ABI,
|
|
745
|
+
functionName: "addLiquidityWeighted",
|
|
746
|
+
args: [
|
|
747
|
+
params.pool,
|
|
748
|
+
params.salt,
|
|
749
|
+
{ binIdxs: [...params.weightDeltas.binIdxs], shares: [...params.weightDeltas.shares] },
|
|
750
|
+
params.maxAmountToken0,
|
|
751
|
+
params.maxAmountToken1,
|
|
752
|
+
],
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function encodeAddLiquidityWeightedWithOwnerCalldata(params: {
|
|
757
|
+
pool: Address;
|
|
758
|
+
owner: Address;
|
|
759
|
+
salt: bigint;
|
|
760
|
+
weightDeltas: PoolLiquidityDelta;
|
|
761
|
+
maxAmountToken0: bigint;
|
|
762
|
+
maxAmountToken1: bigint;
|
|
763
|
+
}): Hex {
|
|
764
|
+
return encodeFunctionData({
|
|
765
|
+
abi: LIQUIDITY_ADDER_ABI,
|
|
766
|
+
functionName: "addLiquidityWeighted",
|
|
767
|
+
args: [
|
|
768
|
+
params.pool,
|
|
769
|
+
params.owner,
|
|
770
|
+
params.salt,
|
|
771
|
+
{ binIdxs: [...params.weightDeltas.binIdxs], shares: [...params.weightDeltas.shares] },
|
|
772
|
+
params.maxAmountToken0,
|
|
773
|
+
params.maxAmountToken1,
|
|
774
|
+
],
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
export function encodeRemoveLiquidityCalldata(params: {
|
|
779
|
+
owner: Address;
|
|
780
|
+
salt: bigint;
|
|
781
|
+
deltas: PoolLiquidityDelta;
|
|
782
|
+
}): Hex {
|
|
783
|
+
return encodeFunctionData({
|
|
784
|
+
abi: MetricOmmPoolAbi,
|
|
785
|
+
functionName: "removeLiquidity",
|
|
786
|
+
args: [
|
|
787
|
+
params.owner,
|
|
788
|
+
params.salt,
|
|
789
|
+
{ binIdxs: [...params.deltas.binIdxs], shares: [...params.deltas.shares] },
|
|
790
|
+
],
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export async function buildRemoveLiquidityPoolDelta(
|
|
795
|
+
params: BuildModifyLiquidityArgsForRemovalParams,
|
|
796
|
+
): Promise<{ salt: bigint; poolLiquidityDelta: PoolLiquidityDelta }> {
|
|
797
|
+
const [salt, deltas] = await buildModifyLiquidityArgsForRemoval(params);
|
|
798
|
+
return { salt, poolLiquidityDelta: removalPlanToPoolLiquidityDelta(deltas) };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function validateSpecAmountBufferPercent(specAmountBufferPercent: number): void {
|
|
802
|
+
if (!Number.isFinite(specAmountBufferPercent) || specAmountBufferPercent < 0) {
|
|
803
|
+
throw new Error("specAmountBufferPercent must be a non-negative percent number");
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function applySpecAmountBuffer(specAmount: bigint, specAmountBufferPercent: number): bigint {
|
|
808
|
+
if (specAmountBufferPercent === 0 || specAmount === NO_SLIPPAGE_LIMIT) {
|
|
809
|
+
return specAmount;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const bufferRatio = specAmountBufferPercent / 100;
|
|
813
|
+
|
|
814
|
+
if (specAmount > 0n) {
|
|
815
|
+
const multiplier = BigInt(Math.floor((1 + bufferRatio) * 1e18));
|
|
816
|
+
return (specAmount * multiplier) / BigInt(1e18);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (specAmount < 0n) {
|
|
820
|
+
const multiplier = BigInt(Math.floor((1 - bufferRatio) * 1e18));
|
|
821
|
+
if (multiplier <= 0n) {
|
|
822
|
+
return 0n;
|
|
823
|
+
}
|
|
824
|
+
return (specAmount * multiplier) / BigInt(1e18);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
return specAmount;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function toModifyLiquidityArgs(
|
|
831
|
+
preparedModifyLiquidity: ModifyLiquidityParams,
|
|
832
|
+
): ModifyLiquidityArgs {
|
|
833
|
+
return [
|
|
834
|
+
preparedModifyLiquidity.salt,
|
|
835
|
+
preparedModifyLiquidity.deltas,
|
|
836
|
+
preparedModifyLiquidity.specAmount0,
|
|
837
|
+
preparedModifyLiquidity.specAmount1,
|
|
838
|
+
];
|
|
839
|
+
}
|