@exponent-labs/market-three-math 0.9.13 → 0.9.15
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/build/addLiquidity.js +11 -10
- package/build/addLiquidity.js.map +1 -1
- package/build/bisect.js +2 -1
- package/build/bisect.js.map +1 -1
- package/build/existingPositionEqualization.d.ts +63 -0
- package/build/existingPositionEqualization.js +242 -0
- package/build/existingPositionEqualization.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +5 -1
- package/build/index.js.map +1 -1
- package/build/liquidityHistogram.js +3 -2
- package/build/liquidityHistogram.js.map +1 -1
- package/build/quote.js +2 -2
- package/build/quote.js.map +1 -1
- package/build/swap.js +2 -1
- package/build/swap.js.map +1 -1
- package/build/swapLegacy.d.ts +16 -0
- package/build/swapLegacy.js +229 -0
- package/build/swapLegacy.js.map +1 -0
- package/build/swapV2.js +3 -2
- package/build/swapV2.js.map +1 -1
- package/build/utils.d.ts +2 -2
- package/build/utils.js +22 -21
- package/build/utils.js.map +1 -1
- package/build/utilsV2.js +4 -4
- package/build/utilsV2.js.map +1 -1
- package/build/withdrawLiquidity.js +2 -1
- package/build/withdrawLiquidity.js.map +1 -1
- package/build/ytTrades.js +4 -3
- package/build/ytTrades.js.map +1 -1
- package/build/ytTradesLegacy.js +4 -3
- package/build/ytTradesLegacy.js.map +1 -1
- package/jest.config.ts +9 -0
- package/package.json +2 -2
- package/src/existingPositionEqualization.test.ts +162 -0
- package/src/existingPositionEqualization.ts +367 -0
- package/src/index.ts +11 -0
- package/src/utils.ts +6 -4
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import type { LpPositionCLMM, Tick, Ticks } from "@exponent-labs/exponent-fetcher"
|
|
2
|
+
|
|
3
|
+
const SENTINEL_TICK_INDEX = 0xffffffff
|
|
4
|
+
const PRECISE_NUMBER_DENOM = 1_000_000_000_000n
|
|
5
|
+
const U64_MAX = (1n << 64n) - 1n
|
|
6
|
+
const U128_MAX = (1n << 128n) - 1n
|
|
7
|
+
|
|
8
|
+
export type CrossingEqualizationDirection = "add" | "remove"
|
|
9
|
+
|
|
10
|
+
export type CrossingEqualizationPlanStep = {
|
|
11
|
+
shareIndex: number
|
|
12
|
+
tickIdx: number
|
|
13
|
+
direction: CrossingEqualizationDirection
|
|
14
|
+
shareDeltaRaw: bigint
|
|
15
|
+
ptDelta: bigint
|
|
16
|
+
syDelta: bigint
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ExistingPositionEqualization = {
|
|
20
|
+
sySpent: bigint
|
|
21
|
+
ptSpent: bigint
|
|
22
|
+
syReleased: bigint
|
|
23
|
+
ptReleased: bigint
|
|
24
|
+
plan: CrossingEqualizationPlanStep[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ExistingPositionBudgetEffect = {
|
|
28
|
+
userMaxSy: bigint
|
|
29
|
+
userMaxPt: bigint
|
|
30
|
+
effectiveMaxSy: bigint
|
|
31
|
+
effectiveMaxPt: bigint
|
|
32
|
+
fixedSySpent: bigint
|
|
33
|
+
fixedPtSpent: bigint
|
|
34
|
+
fixedSyReleased: bigint
|
|
35
|
+
fixedPtReleased: bigint
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const invariantError = (message: string) => new Error(`CrossingEqualizationInvariantViolated: ${message}`)
|
|
39
|
+
|
|
40
|
+
const assertU64 = (value: bigint, label: string): bigint => {
|
|
41
|
+
if (value < 0n || value > U64_MAX) {
|
|
42
|
+
throw invariantError(`${label} does not fit in u64: ${value.toString()}`)
|
|
43
|
+
}
|
|
44
|
+
return value
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const assertU128 = (value: bigint, label: string): bigint => {
|
|
48
|
+
if (value < 0n || value > U128_MAX) {
|
|
49
|
+
throw invariantError(`${label} does not fit in u128: ${value.toString()}`)
|
|
50
|
+
}
|
|
51
|
+
return value
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const checkedAddU64 = (left: bigint, right: bigint, label: string) => assertU64(left + right, label)
|
|
55
|
+
|
|
56
|
+
const checkedSubU64 = (left: bigint, right: bigint, label: string) => {
|
|
57
|
+
if (right > left) {
|
|
58
|
+
throw invariantError(`${label} underflow: ${left.toString()} - ${right.toString()}`)
|
|
59
|
+
}
|
|
60
|
+
return left - right
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const checkedMulU128 = (left: bigint, right: bigint, label: string) => assertU128(left * right, label)
|
|
64
|
+
|
|
65
|
+
const ceilMulDivU128 = (left: bigint, right: bigint, denominator: bigint, label: string) => {
|
|
66
|
+
if (denominator <= 0n) {
|
|
67
|
+
throw invariantError(`${label} division by zero`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const product = checkedMulU128(left, right, `${label} product`)
|
|
71
|
+
return assertU128(product + denominator - 1n, `${label} ceil numerator`) / denominator
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const fastFloorU128 = (rawPreciseNumber: bigint) =>
|
|
75
|
+
assertU128(rawPreciseNumber, "precise number raw") / PRECISE_NUMBER_DENOM
|
|
76
|
+
|
|
77
|
+
const fastMulRatioRaw = (rawPreciseNumber: bigint, numerator: bigint, denominator: bigint, label: string) => {
|
|
78
|
+
if (denominator <= 0n) {
|
|
79
|
+
throw invariantError(`${label} division by zero`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const raw = assertU128(rawPreciseNumber, `${label} raw precise number`)
|
|
83
|
+
const product = checkedMulU128(raw, numerator, `${label} product`)
|
|
84
|
+
return product / denominator
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const getTickByIndex = (ticks: Ticks, tickIdx: number): Tick => {
|
|
88
|
+
const tick = ticks.ticksTree[tickIdx - 1]
|
|
89
|
+
if (!tick || tick.apyBasePoints <= 0) {
|
|
90
|
+
throw invariantError(`missing tick at index ${tickIdx}`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return tick
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const getTickKey = (ticks: Ticks, tickIdx: number) => getTickByIndex(ticks, tickIdx).apyBasePoints
|
|
97
|
+
|
|
98
|
+
const successorIdx = (ticks: Ticks, tickIdx: number): number | null => {
|
|
99
|
+
const tickKey = getTickKey(ticks, tickIdx)
|
|
100
|
+
let best: { tickIdx: number; key: number } | null = null
|
|
101
|
+
|
|
102
|
+
for (let index = 0; index < ticks.ticksTree.length; index++) {
|
|
103
|
+
const key = ticks.ticksTree[index].apyBasePoints
|
|
104
|
+
if (key <= tickKey) {
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
if (!best || key < best.key) {
|
|
108
|
+
best = { tickIdx: index + 1, key }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return best?.tickIdx ?? null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const isCrossingSplitActive = (position: LpPositionCLMM) =>
|
|
116
|
+
position.crossingSplit.crossLeftIdx !== SENTINEL_TICK_INDEX &&
|
|
117
|
+
position.crossingSplit.crossRightIdx !== SENTINEL_TICK_INDEX
|
|
118
|
+
|
|
119
|
+
const shareIsWithinCrossingRange = (
|
|
120
|
+
ticks: Ticks,
|
|
121
|
+
position: LpPositionCLMM,
|
|
122
|
+
share: LpPositionCLMM["shareTrackers"][number],
|
|
123
|
+
): boolean => {
|
|
124
|
+
const shareLeftKey = getTickKey(ticks, share.tickIdx)
|
|
125
|
+
let shareRightIdx: number | null = share.rightTickIdx
|
|
126
|
+
if (share.rightTickIdx === SENTINEL_TICK_INDEX) {
|
|
127
|
+
shareRightIdx = successorIdx(ticks, share.tickIdx)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (shareRightIdx == null) {
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const shareRightKey = getTickKey(ticks, shareRightIdx)
|
|
135
|
+
const crossLeftKey = getTickKey(ticks, position.crossingSplit.crossLeftIdx)
|
|
136
|
+
const crossRightKey = getTickKey(ticks, position.crossingSplit.crossRightIdx)
|
|
137
|
+
|
|
138
|
+
return shareLeftKey >= crossLeftKey && shareRightKey <= crossRightKey
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const computeAddDeltas = (params: {
|
|
142
|
+
ownedPt: bigint
|
|
143
|
+
ownedSy: bigint
|
|
144
|
+
liquidityDelta: bigint
|
|
145
|
+
lActual: bigint
|
|
146
|
+
}): [bigint, bigint] => {
|
|
147
|
+
const ptDelta =
|
|
148
|
+
params.ownedPt > 0n
|
|
149
|
+
? assertU64(
|
|
150
|
+
ceilMulDivU128(params.ownedPt, params.liquidityDelta, params.lActual, "PT equalization in"),
|
|
151
|
+
"PT equalization in",
|
|
152
|
+
)
|
|
153
|
+
: 0n
|
|
154
|
+
const syDelta =
|
|
155
|
+
params.ownedSy > 0n
|
|
156
|
+
? assertU64(
|
|
157
|
+
ceilMulDivU128(params.ownedSy, params.liquidityDelta, params.lActual, "SY equalization in"),
|
|
158
|
+
"SY equalization in",
|
|
159
|
+
)
|
|
160
|
+
: 0n
|
|
161
|
+
|
|
162
|
+
return [ptDelta, syDelta]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const computeRemoveDeltas = (params: { shareDeltaRaw: bigint; node: Tick; supply: bigint }): [bigint, bigint] => {
|
|
166
|
+
const burnShares = fastFloorU128(params.shareDeltaRaw)
|
|
167
|
+
const ptDelta = assertU64(
|
|
168
|
+
checkedMulU128(burnShares, params.node.principalPt, "PT equalization out product") / params.supply,
|
|
169
|
+
"PT equalization out",
|
|
170
|
+
)
|
|
171
|
+
const syDelta = assertU64(
|
|
172
|
+
checkedMulU128(burnShares, params.node.principalSy, "SY equalization out product") / params.supply,
|
|
173
|
+
"SY equalization out",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return [ptDelta, syDelta]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const summarizePlan = (plan: CrossingEqualizationPlanStep[]): ExistingPositionEqualization => {
|
|
180
|
+
let sySpent = 0n
|
|
181
|
+
let ptSpent = 0n
|
|
182
|
+
let syReleased = 0n
|
|
183
|
+
let ptReleased = 0n
|
|
184
|
+
|
|
185
|
+
for (const step of plan) {
|
|
186
|
+
if (step.direction === "add") {
|
|
187
|
+
sySpent = checkedAddU64(sySpent, step.syDelta, "equalization sy spent")
|
|
188
|
+
ptSpent = checkedAddU64(ptSpent, step.ptDelta, "equalization pt spent")
|
|
189
|
+
continue
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
syReleased = checkedAddU64(syReleased, step.syDelta, "equalization sy released")
|
|
193
|
+
ptReleased = checkedAddU64(ptReleased, step.ptDelta, "equalization pt released")
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
sySpent,
|
|
198
|
+
ptSpent,
|
|
199
|
+
syReleased,
|
|
200
|
+
ptReleased,
|
|
201
|
+
plan,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Mirrors `precalc_existing_position_add_liquidity` from the CLMM program.
|
|
207
|
+
*
|
|
208
|
+
* The returned `ptSpent` and `sySpent` are the exact token budget that must be
|
|
209
|
+
* reserved before adding more liquidity to a position with an active crossing split.
|
|
210
|
+
* All PreciseNumber operations are performed on raw fixed-point `bigint` values so
|
|
211
|
+
* the rounding matches the contract's `fast_floor_u128`, `fast_mul_ratio`, and
|
|
212
|
+
* `muldiv_ceil_u128` paths.
|
|
213
|
+
*/
|
|
214
|
+
export function computeExistingPositionEqualization(
|
|
215
|
+
ticks: Ticks,
|
|
216
|
+
position: LpPositionCLMM,
|
|
217
|
+
): ExistingPositionEqualization {
|
|
218
|
+
if (!isCrossingSplitActive(position)) {
|
|
219
|
+
return summarizePlan([])
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const lActual = assertU64(position.crossingSplit.lpBalanceCrossing, "lpBalanceCrossing")
|
|
223
|
+
const lpBalance = assertU64(position.lpBalance, "lpBalance")
|
|
224
|
+
|
|
225
|
+
if (lpBalance === lActual) {
|
|
226
|
+
return summarizePlan([])
|
|
227
|
+
}
|
|
228
|
+
if (lActual === 0n) {
|
|
229
|
+
throw invariantError("active crossing split has zero actual liquidity")
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const direction: CrossingEqualizationDirection = lpBalance > lActual ? "add" : "remove"
|
|
233
|
+
const liquidityDelta = lpBalance > lActual ? lpBalance - lActual : lActual - lpBalance
|
|
234
|
+
const plan: CrossingEqualizationPlanStep[] = []
|
|
235
|
+
let sawCrossingRangeShare = false
|
|
236
|
+
let skippedCrossingShareDueToDust = false
|
|
237
|
+
|
|
238
|
+
for (let shareIndex = 0; shareIndex < position.shareTrackers.length; shareIndex++) {
|
|
239
|
+
const share = position.shareTrackers[shareIndex]
|
|
240
|
+
if (!shareIsWithinCrossingRange(ticks, position, share)) {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
sawCrossingRangeShare = true
|
|
245
|
+
const node = getTickByIndex(ticks, share.tickIdx)
|
|
246
|
+
const supply = fastFloorU128(node.principalShareSupply)
|
|
247
|
+
if (supply === 0n) {
|
|
248
|
+
skippedCrossingShareDueToDust = true
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const shareFloor = fastFloorU128(share.lpShare)
|
|
253
|
+
if (shareFloor === 0n) {
|
|
254
|
+
skippedCrossingShareDueToDust = true
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const ownedPt = assertU64(checkedMulU128(shareFloor, node.principalPt, "owned PT product") / supply, "owned PT")
|
|
259
|
+
const ownedSy = assertU64(checkedMulU128(shareFloor, node.principalSy, "owned SY product") / supply, "owned SY")
|
|
260
|
+
const shareDeltaRaw = fastMulRatioRaw(share.lpShare, liquidityDelta, lActual, "share delta")
|
|
261
|
+
|
|
262
|
+
let deltas: [bigint, bigint]
|
|
263
|
+
if (direction === "add") {
|
|
264
|
+
deltas = computeAddDeltas({
|
|
265
|
+
ownedPt,
|
|
266
|
+
ownedSy,
|
|
267
|
+
liquidityDelta,
|
|
268
|
+
lActual,
|
|
269
|
+
})
|
|
270
|
+
} else {
|
|
271
|
+
deltas = computeRemoveDeltas({
|
|
272
|
+
shareDeltaRaw,
|
|
273
|
+
node,
|
|
274
|
+
supply,
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
const [ptDelta, syDelta] = deltas
|
|
278
|
+
|
|
279
|
+
if (shareDeltaRaw === 0n && ptDelta === 0n && syDelta === 0n) {
|
|
280
|
+
skippedCrossingShareDueToDust = true
|
|
281
|
+
continue
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
plan.push({
|
|
285
|
+
shareIndex,
|
|
286
|
+
tickIdx: share.tickIdx,
|
|
287
|
+
direction,
|
|
288
|
+
shareDeltaRaw,
|
|
289
|
+
ptDelta,
|
|
290
|
+
syDelta,
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (plan.length === 0 && sawCrossingRangeShare && skippedCrossingShareDueToDust) {
|
|
295
|
+
return summarizePlan([])
|
|
296
|
+
}
|
|
297
|
+
if (plan.length === 0) {
|
|
298
|
+
throw invariantError("active crossing split has no equalizable crossing-range shares")
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return summarizePlan(plan)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Mirrors the contract's `compute_existing_position_budget_effect`.
|
|
306
|
+
*
|
|
307
|
+
* It throws the same invariant class when the supplied user budgets cannot cover
|
|
308
|
+
* the fixed equalization spend, for example `userMaxPt=0` while `ptSpent>0`.
|
|
309
|
+
*/
|
|
310
|
+
export function computeExistingPositionBudgetEffect(params: {
|
|
311
|
+
ticks: Ticks
|
|
312
|
+
position: LpPositionCLMM
|
|
313
|
+
userMaxSy: bigint
|
|
314
|
+
userMaxPt: bigint
|
|
315
|
+
}): ExistingPositionBudgetEffect {
|
|
316
|
+
const equalization = computeExistingPositionEqualization(params.ticks, params.position)
|
|
317
|
+
const effectiveMaxSy = checkedAddU64(
|
|
318
|
+
checkedSubU64(params.userMaxSy, equalization.sySpent, "effective max SY"),
|
|
319
|
+
equalization.syReleased,
|
|
320
|
+
"effective max SY",
|
|
321
|
+
)
|
|
322
|
+
const effectiveMaxPt = checkedAddU64(
|
|
323
|
+
checkedSubU64(params.userMaxPt, equalization.ptSpent, "effective max PT"),
|
|
324
|
+
equalization.ptReleased,
|
|
325
|
+
"effective max PT",
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
userMaxSy: params.userMaxSy,
|
|
330
|
+
userMaxPt: params.userMaxPt,
|
|
331
|
+
effectiveMaxSy,
|
|
332
|
+
effectiveMaxPt,
|
|
333
|
+
fixedSySpent: equalization.sySpent,
|
|
334
|
+
fixedPtSpent: equalization.ptSpent,
|
|
335
|
+
fixedSyReleased: equalization.syReleased,
|
|
336
|
+
fixedPtReleased: equalization.ptReleased,
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Mirrors `required_user_max_sy/pt`: expands a desired post-equalization budget
|
|
342
|
+
* into the user-facing max token amounts that should be passed to the instruction.
|
|
343
|
+
*/
|
|
344
|
+
export function computeRequiredUserMaxForExistingPositionEqualization(params: {
|
|
345
|
+
ticks: Ticks
|
|
346
|
+
position: LpPositionCLMM
|
|
347
|
+
desiredEffectiveMaxSy: bigint
|
|
348
|
+
desiredEffectiveMaxPt: bigint
|
|
349
|
+
}): { userMaxSy: bigint; userMaxPt: bigint; equalization: ExistingPositionEqualization } {
|
|
350
|
+
const equalization = computeExistingPositionEqualization(params.ticks, params.position)
|
|
351
|
+
const userMaxSy = checkedSubU64(
|
|
352
|
+
checkedAddU64(params.desiredEffectiveMaxSy, equalization.sySpent, "required user max SY"),
|
|
353
|
+
equalization.syReleased,
|
|
354
|
+
"required user max SY",
|
|
355
|
+
)
|
|
356
|
+
const userMaxPt = checkedSubU64(
|
|
357
|
+
checkedAddU64(params.desiredEffectiveMaxPt, equalization.ptSpent, "required user max PT"),
|
|
358
|
+
equalization.ptReleased,
|
|
359
|
+
"required user max PT",
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
userMaxSy,
|
|
364
|
+
userMaxPt,
|
|
365
|
+
equalization,
|
|
366
|
+
}
|
|
367
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -56,6 +56,17 @@ export {
|
|
|
56
56
|
simulateWrapperProvideLiquidity,
|
|
57
57
|
simulateSwapAndSupply,
|
|
58
58
|
} from "./addLiquidity"
|
|
59
|
+
export {
|
|
60
|
+
computeExistingPositionBudgetEffect,
|
|
61
|
+
computeExistingPositionEqualization,
|
|
62
|
+
computeRequiredUserMaxForExistingPositionEqualization,
|
|
63
|
+
} from "./existingPositionEqualization"
|
|
64
|
+
export type {
|
|
65
|
+
CrossingEqualizationDirection,
|
|
66
|
+
CrossingEqualizationPlanStep,
|
|
67
|
+
ExistingPositionBudgetEffect,
|
|
68
|
+
ExistingPositionEqualization,
|
|
69
|
+
} from "./existingPositionEqualization"
|
|
59
70
|
|
|
60
71
|
export { getPtAndSyOnWithdrawLiquidity } from "./withdrawLiquidity"
|
|
61
72
|
|
package/src/utils.ts
CHANGED
|
@@ -219,18 +219,20 @@ export function findTickByIndex(ticks: Ticks, index: number): Tick {
|
|
|
219
219
|
return ticks.ticksTree.at(index - 1) ?? null
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
const CLMM_TICK_KEY_SCALE = 10_000
|
|
223
|
+
|
|
222
224
|
/**
|
|
223
|
-
* Convert APY percentage to
|
|
225
|
+
* Convert APY percentage to CLMM tick-key units.
|
|
224
226
|
*/
|
|
225
227
|
export function convertApyToApyBp(apyPercent: number): number {
|
|
226
|
-
return Math.round(apyPercent *
|
|
228
|
+
return Math.round(apyPercent * CLMM_TICK_KEY_SCALE)
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
/**
|
|
230
|
-
* Convert
|
|
232
|
+
* Convert CLMM tick-key units to APY percentage.
|
|
231
233
|
*/
|
|
232
234
|
export function convertApyBpToApy(apyBp: number): number {
|
|
233
|
-
return apyBp /
|
|
235
|
+
return apyBp / CLMM_TICK_KEY_SCALE
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
export function bigIntMax(...args: bigint[]): bigint {
|