@exponent-labs/market-three-math 0.9.13 → 0.9.14

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.
@@ -0,0 +1,63 @@
1
+ import type { LpPositionCLMM, Ticks } from "@exponent-labs/exponent-fetcher";
2
+ export type CrossingEqualizationDirection = "add" | "remove";
3
+ export type CrossingEqualizationPlanStep = {
4
+ shareIndex: number;
5
+ tickIdx: number;
6
+ direction: CrossingEqualizationDirection;
7
+ shareDeltaRaw: bigint;
8
+ ptDelta: bigint;
9
+ syDelta: bigint;
10
+ };
11
+ export type ExistingPositionEqualization = {
12
+ sySpent: bigint;
13
+ ptSpent: bigint;
14
+ syReleased: bigint;
15
+ ptReleased: bigint;
16
+ plan: CrossingEqualizationPlanStep[];
17
+ };
18
+ export type ExistingPositionBudgetEffect = {
19
+ userMaxSy: bigint;
20
+ userMaxPt: bigint;
21
+ effectiveMaxSy: bigint;
22
+ effectiveMaxPt: bigint;
23
+ fixedSySpent: bigint;
24
+ fixedPtSpent: bigint;
25
+ fixedSyReleased: bigint;
26
+ fixedPtReleased: bigint;
27
+ };
28
+ /**
29
+ * Mirrors `precalc_existing_position_add_liquidity` from the CLMM program.
30
+ *
31
+ * The returned `ptSpent` and `sySpent` are the exact token budget that must be
32
+ * reserved before adding more liquidity to a position with an active crossing split.
33
+ * All PreciseNumber operations are performed on raw fixed-point `bigint` values so
34
+ * the rounding matches the contract's `fast_floor_u128`, `fast_mul_ratio`, and
35
+ * `muldiv_ceil_u128` paths.
36
+ */
37
+ export declare function computeExistingPositionEqualization(ticks: Ticks, position: LpPositionCLMM): ExistingPositionEqualization;
38
+ /**
39
+ * Mirrors the contract's `compute_existing_position_budget_effect`.
40
+ *
41
+ * It throws the same invariant class when the supplied user budgets cannot cover
42
+ * the fixed equalization spend, for example `userMaxPt=0` while `ptSpent>0`.
43
+ */
44
+ export declare function computeExistingPositionBudgetEffect(params: {
45
+ ticks: Ticks;
46
+ position: LpPositionCLMM;
47
+ userMaxSy: bigint;
48
+ userMaxPt: bigint;
49
+ }): ExistingPositionBudgetEffect;
50
+ /**
51
+ * Mirrors `required_user_max_sy/pt`: expands a desired post-equalization budget
52
+ * into the user-facing max token amounts that should be passed to the instruction.
53
+ */
54
+ export declare function computeRequiredUserMaxForExistingPositionEqualization(params: {
55
+ ticks: Ticks;
56
+ position: LpPositionCLMM;
57
+ desiredEffectiveMaxSy: bigint;
58
+ desiredEffectiveMaxPt: bigint;
59
+ }): {
60
+ userMaxSy: bigint;
61
+ userMaxPt: bigint;
62
+ equalization: ExistingPositionEqualization;
63
+ };
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeExistingPositionEqualization = computeExistingPositionEqualization;
4
+ exports.computeExistingPositionBudgetEffect = computeExistingPositionBudgetEffect;
5
+ exports.computeRequiredUserMaxForExistingPositionEqualization = computeRequiredUserMaxForExistingPositionEqualization;
6
+ const SENTINEL_TICK_INDEX = 0xffffffff;
7
+ const PRECISE_NUMBER_DENOM = 1000000000000n;
8
+ const U64_MAX = (1n << 64n) - 1n;
9
+ const U128_MAX = (1n << 128n) - 1n;
10
+ const invariantError = (message) => new Error(`CrossingEqualizationInvariantViolated: ${message}`);
11
+ const assertU64 = (value, label) => {
12
+ if (value < 0n || value > U64_MAX) {
13
+ throw invariantError(`${label} does not fit in u64: ${value.toString()}`);
14
+ }
15
+ return value;
16
+ };
17
+ const assertU128 = (value, label) => {
18
+ if (value < 0n || value > U128_MAX) {
19
+ throw invariantError(`${label} does not fit in u128: ${value.toString()}`);
20
+ }
21
+ return value;
22
+ };
23
+ const checkedAddU64 = (left, right, label) => assertU64(left + right, label);
24
+ const checkedSubU64 = (left, right, label) => {
25
+ if (right > left) {
26
+ throw invariantError(`${label} underflow: ${left.toString()} - ${right.toString()}`);
27
+ }
28
+ return left - right;
29
+ };
30
+ const checkedMulU128 = (left, right, label) => assertU128(left * right, label);
31
+ const ceilMulDivU128 = (left, right, denominator, label) => {
32
+ if (denominator <= 0n) {
33
+ throw invariantError(`${label} division by zero`);
34
+ }
35
+ const product = checkedMulU128(left, right, `${label} product`);
36
+ return assertU128(product + denominator - 1n, `${label} ceil numerator`) / denominator;
37
+ };
38
+ const fastFloorU128 = (rawPreciseNumber) => assertU128(rawPreciseNumber, "precise number raw") / PRECISE_NUMBER_DENOM;
39
+ const fastMulRatioRaw = (rawPreciseNumber, numerator, denominator, label) => {
40
+ if (denominator <= 0n) {
41
+ throw invariantError(`${label} division by zero`);
42
+ }
43
+ const raw = assertU128(rawPreciseNumber, `${label} raw precise number`);
44
+ const product = checkedMulU128(raw, numerator, `${label} product`);
45
+ return product / denominator;
46
+ };
47
+ const getTickByIndex = (ticks, tickIdx) => {
48
+ const tick = ticks.ticksTree[tickIdx - 1];
49
+ if (!tick || tick.apyBasePoints <= 0) {
50
+ throw invariantError(`missing tick at index ${tickIdx}`);
51
+ }
52
+ return tick;
53
+ };
54
+ const getTickKey = (ticks, tickIdx) => getTickByIndex(ticks, tickIdx).apyBasePoints;
55
+ const successorIdx = (ticks, tickIdx) => {
56
+ const tickKey = getTickKey(ticks, tickIdx);
57
+ let best = null;
58
+ for (let index = 0; index < ticks.ticksTree.length; index++) {
59
+ const key = ticks.ticksTree[index].apyBasePoints;
60
+ if (key <= tickKey) {
61
+ continue;
62
+ }
63
+ if (!best || key < best.key) {
64
+ best = { tickIdx: index + 1, key };
65
+ }
66
+ }
67
+ return best?.tickIdx ?? null;
68
+ };
69
+ const isCrossingSplitActive = (position) => position.crossingSplit.crossLeftIdx !== SENTINEL_TICK_INDEX &&
70
+ position.crossingSplit.crossRightIdx !== SENTINEL_TICK_INDEX;
71
+ const shareIsWithinCrossingRange = (ticks, position, share) => {
72
+ const shareLeftKey = getTickKey(ticks, share.tickIdx);
73
+ let shareRightIdx = share.rightTickIdx;
74
+ if (share.rightTickIdx === SENTINEL_TICK_INDEX) {
75
+ shareRightIdx = successorIdx(ticks, share.tickIdx);
76
+ }
77
+ if (shareRightIdx == null) {
78
+ return false;
79
+ }
80
+ const shareRightKey = getTickKey(ticks, shareRightIdx);
81
+ const crossLeftKey = getTickKey(ticks, position.crossingSplit.crossLeftIdx);
82
+ const crossRightKey = getTickKey(ticks, position.crossingSplit.crossRightIdx);
83
+ return shareLeftKey >= crossLeftKey && shareRightKey <= crossRightKey;
84
+ };
85
+ const computeAddDeltas = (params) => {
86
+ const ptDelta = params.ownedPt > 0n
87
+ ? assertU64(ceilMulDivU128(params.ownedPt, params.liquidityDelta, params.lActual, "PT equalization in"), "PT equalization in")
88
+ : 0n;
89
+ const syDelta = params.ownedSy > 0n
90
+ ? assertU64(ceilMulDivU128(params.ownedSy, params.liquidityDelta, params.lActual, "SY equalization in"), "SY equalization in")
91
+ : 0n;
92
+ return [ptDelta, syDelta];
93
+ };
94
+ const computeRemoveDeltas = (params) => {
95
+ const burnShares = fastFloorU128(params.shareDeltaRaw);
96
+ const ptDelta = assertU64(checkedMulU128(burnShares, params.node.principalPt, "PT equalization out product") / params.supply, "PT equalization out");
97
+ const syDelta = assertU64(checkedMulU128(burnShares, params.node.principalSy, "SY equalization out product") / params.supply, "SY equalization out");
98
+ return [ptDelta, syDelta];
99
+ };
100
+ const summarizePlan = (plan) => {
101
+ let sySpent = 0n;
102
+ let ptSpent = 0n;
103
+ let syReleased = 0n;
104
+ let ptReleased = 0n;
105
+ for (const step of plan) {
106
+ if (step.direction === "add") {
107
+ sySpent = checkedAddU64(sySpent, step.syDelta, "equalization sy spent");
108
+ ptSpent = checkedAddU64(ptSpent, step.ptDelta, "equalization pt spent");
109
+ continue;
110
+ }
111
+ syReleased = checkedAddU64(syReleased, step.syDelta, "equalization sy released");
112
+ ptReleased = checkedAddU64(ptReleased, step.ptDelta, "equalization pt released");
113
+ }
114
+ return {
115
+ sySpent,
116
+ ptSpent,
117
+ syReleased,
118
+ ptReleased,
119
+ plan,
120
+ };
121
+ };
122
+ /**
123
+ * Mirrors `precalc_existing_position_add_liquidity` from the CLMM program.
124
+ *
125
+ * The returned `ptSpent` and `sySpent` are the exact token budget that must be
126
+ * reserved before adding more liquidity to a position with an active crossing split.
127
+ * All PreciseNumber operations are performed on raw fixed-point `bigint` values so
128
+ * the rounding matches the contract's `fast_floor_u128`, `fast_mul_ratio`, and
129
+ * `muldiv_ceil_u128` paths.
130
+ */
131
+ function computeExistingPositionEqualization(ticks, position) {
132
+ if (!isCrossingSplitActive(position)) {
133
+ return summarizePlan([]);
134
+ }
135
+ const lActual = assertU64(position.crossingSplit.lpBalanceCrossing, "lpBalanceCrossing");
136
+ const lpBalance = assertU64(position.lpBalance, "lpBalance");
137
+ if (lpBalance === lActual) {
138
+ return summarizePlan([]);
139
+ }
140
+ if (lActual === 0n) {
141
+ throw invariantError("active crossing split has zero actual liquidity");
142
+ }
143
+ const direction = lpBalance > lActual ? "add" : "remove";
144
+ const liquidityDelta = lpBalance > lActual ? lpBalance - lActual : lActual - lpBalance;
145
+ const plan = [];
146
+ let sawCrossingRangeShare = false;
147
+ let skippedCrossingShareDueToDust = false;
148
+ for (let shareIndex = 0; shareIndex < position.shareTrackers.length; shareIndex++) {
149
+ const share = position.shareTrackers[shareIndex];
150
+ if (!shareIsWithinCrossingRange(ticks, position, share)) {
151
+ continue;
152
+ }
153
+ sawCrossingRangeShare = true;
154
+ const node = getTickByIndex(ticks, share.tickIdx);
155
+ const supply = fastFloorU128(node.principalShareSupply);
156
+ if (supply === 0n) {
157
+ skippedCrossingShareDueToDust = true;
158
+ continue;
159
+ }
160
+ const shareFloor = fastFloorU128(share.lpShare);
161
+ if (shareFloor === 0n) {
162
+ skippedCrossingShareDueToDust = true;
163
+ continue;
164
+ }
165
+ const ownedPt = assertU64(checkedMulU128(shareFloor, node.principalPt, "owned PT product") / supply, "owned PT");
166
+ const ownedSy = assertU64(checkedMulU128(shareFloor, node.principalSy, "owned SY product") / supply, "owned SY");
167
+ const shareDeltaRaw = fastMulRatioRaw(share.lpShare, liquidityDelta, lActual, "share delta");
168
+ let deltas;
169
+ if (direction === "add") {
170
+ deltas = computeAddDeltas({
171
+ ownedPt,
172
+ ownedSy,
173
+ liquidityDelta,
174
+ lActual,
175
+ });
176
+ }
177
+ else {
178
+ deltas = computeRemoveDeltas({
179
+ shareDeltaRaw,
180
+ node,
181
+ supply,
182
+ });
183
+ }
184
+ const [ptDelta, syDelta] = deltas;
185
+ if (shareDeltaRaw === 0n && ptDelta === 0n && syDelta === 0n) {
186
+ skippedCrossingShareDueToDust = true;
187
+ continue;
188
+ }
189
+ plan.push({
190
+ shareIndex,
191
+ tickIdx: share.tickIdx,
192
+ direction,
193
+ shareDeltaRaw,
194
+ ptDelta,
195
+ syDelta,
196
+ });
197
+ }
198
+ if (plan.length === 0 && sawCrossingRangeShare && skippedCrossingShareDueToDust) {
199
+ return summarizePlan([]);
200
+ }
201
+ if (plan.length === 0) {
202
+ throw invariantError("active crossing split has no equalizable crossing-range shares");
203
+ }
204
+ return summarizePlan(plan);
205
+ }
206
+ /**
207
+ * Mirrors the contract's `compute_existing_position_budget_effect`.
208
+ *
209
+ * It throws the same invariant class when the supplied user budgets cannot cover
210
+ * the fixed equalization spend, for example `userMaxPt=0` while `ptSpent>0`.
211
+ */
212
+ function computeExistingPositionBudgetEffect(params) {
213
+ const equalization = computeExistingPositionEqualization(params.ticks, params.position);
214
+ const effectiveMaxSy = checkedAddU64(checkedSubU64(params.userMaxSy, equalization.sySpent, "effective max SY"), equalization.syReleased, "effective max SY");
215
+ const effectiveMaxPt = checkedAddU64(checkedSubU64(params.userMaxPt, equalization.ptSpent, "effective max PT"), equalization.ptReleased, "effective max PT");
216
+ return {
217
+ userMaxSy: params.userMaxSy,
218
+ userMaxPt: params.userMaxPt,
219
+ effectiveMaxSy,
220
+ effectiveMaxPt,
221
+ fixedSySpent: equalization.sySpent,
222
+ fixedPtSpent: equalization.ptSpent,
223
+ fixedSyReleased: equalization.syReleased,
224
+ fixedPtReleased: equalization.ptReleased,
225
+ };
226
+ }
227
+ /**
228
+ * Mirrors `required_user_max_sy/pt`: expands a desired post-equalization budget
229
+ * into the user-facing max token amounts that should be passed to the instruction.
230
+ */
231
+ function computeRequiredUserMaxForExistingPositionEqualization(params) {
232
+ const equalization = computeExistingPositionEqualization(params.ticks, params.position);
233
+ const userMaxSy = checkedSubU64(checkedAddU64(params.desiredEffectiveMaxSy, equalization.sySpent, "required user max SY"), equalization.syReleased, "required user max SY");
234
+ const userMaxPt = checkedSubU64(checkedAddU64(params.desiredEffectiveMaxPt, equalization.ptSpent, "required user max PT"), equalization.ptReleased, "required user max PT");
235
+ return {
236
+ userMaxSy,
237
+ userMaxPt,
238
+ equalization,
239
+ };
240
+ }
241
+ //# sourceMappingURL=existingPositionEqualization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"existingPositionEqualization.js","sourceRoot":"","sources":["../src/existingPositionEqualization.ts"],"names":[],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -17,6 +17,8 @@ export { EffSnap, TicksWrapper, calculateFeeRate, getFeeFromAmount } from "./uti
17
17
  export { simulateBuyYt, simulateSellYt, simulateBuyYtWithSyIn } from "./ytTrades";
18
18
  export type { BuyYtSimulationArgs, BuyYtSimulationResult, SellYtSimulationArgs, SellYtSimulationResult, } from "./ytTrades";
19
19
  export { simulateAddLiquidity, computeLiquidityTargetAndTokenNeeds, computeTokenNeedsWithCrossing, getCrossingTickStateFromTicks, scaleCrossingTickInputs, calculateLpOut, estimateBalancedDeposit, calcDepositSyAndPtFromBaseAmount, simulateWrapperProvideLiquidity, simulateSwapAndSupply, } from "./addLiquidity";
20
+ export { computeExistingPositionBudgetEffect, computeExistingPositionEqualization, computeRequiredUserMaxForExistingPositionEqualization, } from "./existingPositionEqualization";
21
+ export type { CrossingEqualizationDirection, CrossingEqualizationPlanStep, ExistingPositionBudgetEffect, ExistingPositionEqualization, } from "./existingPositionEqualization";
20
22
  export { getPtAndSyOnWithdrawLiquidity } from "./withdrawLiquidity";
21
23
  export { bisectSearch2 } from "./bisect";
22
24
  export { buildLiquidityHistogram, buildLiquidityHistogramSimple } from "./liquidityHistogram";
package/build/index.js CHANGED
@@ -25,7 +25,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
25
25
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
26
26
  };
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.SwapDirection = exports.buildLiquidityHistogramSimple = exports.buildLiquidityHistogram = exports.bisectSearch2 = exports.getPtAndSyOnWithdrawLiquidity = exports.simulateSwapAndSupply = exports.simulateWrapperProvideLiquidity = exports.calcDepositSyAndPtFromBaseAmount = exports.estimateBalancedDeposit = exports.calculateLpOut = exports.scaleCrossingTickInputs = exports.getCrossingTickStateFromTicks = exports.computeTokenNeedsWithCrossing = exports.computeLiquidityTargetAndTokenNeeds = exports.simulateAddLiquidity = exports.simulateBuyYtWithSyIn = exports.simulateSellYt = exports.simulateBuyYt = exports.getFeeFromAmount = exports.calculateFeeRate = exports.TicksWrapper = exports.EffSnap = exports.QuoteDirection = exports.getSwapQuote = exports.simulateSwap = exports.getSuccessorTickIdxByIdx = exports.calcPtPriceInAsset = exports.convertApyBpToApy = exports.convertApyToApyBp = exports.findTickByKey = exports.getImpliedRate = exports.getPredecessorTickKey = exports.getSuccessorTickKey = exports.getActiveLiquidity = exports.normalizedTimeRemaining = void 0;
28
+ exports.SwapDirection = exports.buildLiquidityHistogramSimple = exports.buildLiquidityHistogram = exports.bisectSearch2 = exports.getPtAndSyOnWithdrawLiquidity = exports.computeRequiredUserMaxForExistingPositionEqualization = exports.computeExistingPositionEqualization = exports.computeExistingPositionBudgetEffect = exports.simulateSwapAndSupply = exports.simulateWrapperProvideLiquidity = exports.calcDepositSyAndPtFromBaseAmount = exports.estimateBalancedDeposit = exports.calculateLpOut = exports.scaleCrossingTickInputs = exports.getCrossingTickStateFromTicks = exports.computeTokenNeedsWithCrossing = exports.computeLiquidityTargetAndTokenNeeds = exports.simulateAddLiquidity = exports.simulateBuyYtWithSyIn = exports.simulateSellYt = exports.simulateBuyYt = exports.getFeeFromAmount = exports.calculateFeeRate = exports.TicksWrapper = exports.EffSnap = exports.QuoteDirection = exports.getSwapQuote = exports.simulateSwap = exports.getSuccessorTickIdxByIdx = exports.calcPtPriceInAsset = exports.convertApyBpToApy = exports.convertApyToApyBp = exports.findTickByKey = exports.getImpliedRate = exports.getPredecessorTickKey = exports.getSuccessorTickKey = exports.getActiveLiquidity = exports.normalizedTimeRemaining = void 0;
29
29
  // Export types
30
30
  __exportStar(require("./types"), exports);
31
31
  // Export utility functions
@@ -69,6 +69,10 @@ Object.defineProperty(exports, "estimateBalancedDeposit", { enumerable: true, ge
69
69
  Object.defineProperty(exports, "calcDepositSyAndPtFromBaseAmount", { enumerable: true, get: function () { return addLiquidity_1.calcDepositSyAndPtFromBaseAmount; } });
70
70
  Object.defineProperty(exports, "simulateWrapperProvideLiquidity", { enumerable: true, get: function () { return addLiquidity_1.simulateWrapperProvideLiquidity; } });
71
71
  Object.defineProperty(exports, "simulateSwapAndSupply", { enumerable: true, get: function () { return addLiquidity_1.simulateSwapAndSupply; } });
72
+ var existingPositionEqualization_1 = require("./existingPositionEqualization");
73
+ Object.defineProperty(exports, "computeExistingPositionBudgetEffect", { enumerable: true, get: function () { return existingPositionEqualization_1.computeExistingPositionBudgetEffect; } });
74
+ Object.defineProperty(exports, "computeExistingPositionEqualization", { enumerable: true, get: function () { return existingPositionEqualization_1.computeExistingPositionEqualization; } });
75
+ Object.defineProperty(exports, "computeRequiredUserMaxForExistingPositionEqualization", { enumerable: true, get: function () { return existingPositionEqualization_1.computeRequiredUserMaxForExistingPositionEqualization; } });
72
76
  var withdrawLiquidity_1 = require("./withdrawLiquidity");
73
77
  Object.defineProperty(exports, "getPtAndSyOnWithdrawLiquidity", { enumerable: true, get: function () { return withdrawLiquidity_1.getPtAndSyOnWithdrawLiquidity; } });
74
78
  // Export bisect search utility
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;AAEH,eAAe;AACf,0CAAuB;AAEvB,2BAA2B;AAC3B,iCAWgB;AAVd,gHAAA,uBAAuB,OAAA;AACvB,2GAAA,kBAAkB,OAAA;AAClB,4GAAA,mBAAmB,OAAA;AACnB,8GAAA,qBAAqB,OAAA;AACrB,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AACjB,0GAAA,iBAAiB,OAAA;AACjB,2GAAA,kBAAkB,OAAA;AAClB,iHAAA,wBAAwB,OAAA;AAG1B,wBAAwB;AACxB,mCAAuC;AAA9B,sGAAA,YAAY,OAAA;AACrB,iCAAsD;AAA7C,qGAAA,YAAY,OAAA;AAAE,uGAAA,cAAc,OAAA;AAErC,sBAAsB;AACtB,qCAAqF;AAA5E,kGAAA,OAAO,OAAA;AAAE,uGAAA,YAAY,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAElE,4BAA4B;AAC5B,uCAAiF;AAAxE,yGAAA,aAAa,OAAA;AAAE,0GAAA,cAAc,OAAA;AAAE,iHAAA,qBAAqB,OAAA;AAQ7D,iCAAiC;AACjC,+CAWuB;AAVrB,oHAAA,oBAAoB,OAAA;AACpB,mIAAA,mCAAmC,OAAA;AACnC,6HAAA,6BAA6B,OAAA;AAC7B,6HAAA,6BAA6B,OAAA;AAC7B,uHAAA,uBAAuB,OAAA;AACvB,8GAAA,cAAc,OAAA;AACd,uHAAA,uBAAuB,OAAA;AACvB,gIAAA,gCAAgC,OAAA;AAChC,+HAAA,+BAA+B,OAAA;AAC/B,qHAAA,qBAAqB,OAAA;AAGvB,yDAAmE;AAA1D,kIAAA,6BAA6B,OAAA;AAEtC,+BAA+B;AAC/B,mCAAwC;AAA/B,uGAAA,aAAa,OAAA;AAEtB,uCAAuC;AACvC,2DAA6F;AAApF,6HAAA,uBAAuB,OAAA;AAAE,mIAAA,6BAA6B,OAAA;AAc/D,iCAAuC;AAA9B,sGAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;AAEH,eAAe;AACf,0CAAuB;AAEvB,2BAA2B;AAC3B,iCAWgB;AAVd,gHAAA,uBAAuB,OAAA;AACvB,2GAAA,kBAAkB,OAAA;AAClB,4GAAA,mBAAmB,OAAA;AACnB,8GAAA,qBAAqB,OAAA;AACrB,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AACjB,0GAAA,iBAAiB,OAAA;AACjB,2GAAA,kBAAkB,OAAA;AAClB,iHAAA,wBAAwB,OAAA;AAG1B,wBAAwB;AACxB,mCAAuC;AAA9B,sGAAA,YAAY,OAAA;AACrB,iCAAsD;AAA7C,qGAAA,YAAY,OAAA;AAAE,uGAAA,cAAc,OAAA;AAErC,sBAAsB;AACtB,qCAAqF;AAA5E,kGAAA,OAAO,OAAA;AAAE,uGAAA,YAAY,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAElE,4BAA4B;AAC5B,uCAAiF;AAAxE,yGAAA,aAAa,OAAA;AAAE,0GAAA,cAAc,OAAA;AAAE,iHAAA,qBAAqB,OAAA;AAQ7D,iCAAiC;AACjC,+CAWuB;AAVrB,oHAAA,oBAAoB,OAAA;AACpB,mIAAA,mCAAmC,OAAA;AACnC,6HAAA,6BAA6B,OAAA;AAC7B,6HAAA,6BAA6B,OAAA;AAC7B,uHAAA,uBAAuB,OAAA;AACvB,8GAAA,cAAc,OAAA;AACd,uHAAA,uBAAuB,OAAA;AACvB,gIAAA,gCAAgC,OAAA;AAChC,+HAAA,+BAA+B,OAAA;AAC/B,qHAAA,qBAAqB,OAAA;AAEvB,+EAIuC;AAHrC,mJAAA,mCAAmC,OAAA;AACnC,mJAAA,mCAAmC,OAAA;AACnC,qKAAA,qDAAqD,OAAA;AASvD,yDAAmE;AAA1D,kIAAA,6BAA6B,OAAA;AAEtC,+BAA+B;AAC/B,mCAAwC;AAA/B,uGAAA,aAAa,OAAA;AAEtB,uCAAuC;AACvC,2DAA6F;AAApF,6HAAA,uBAAuB,OAAA;AAAE,mIAAA,6BAA6B,OAAA;AAc/D,iCAAuC;AAA9B,sGAAA,aAAa,OAAA"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CLMM Swap simulation
3
+ * Ported from exponent_clmm/src/state/market_three/helpers/swap.rs
4
+ */
5
+ import { MarketThreeState, SwapArgs, SwapDirection, SwapOutcome } from "./types";
6
+ /**
7
+ * Simulate a swap on the CLMM market
8
+ * This is a pure function that does not mutate the market state
9
+ * Returns the swap outcome including amounts and final state
10
+ */
11
+ export declare function simulateSwap(marketState: MarketThreeState, args: SwapArgs): SwapOutcome;
12
+ /**
13
+ * Calculate the expected output for a given input amount
14
+ * This is a convenience wrapper around simulateSwap
15
+ */
16
+ export declare function getSwapQuote(marketState: MarketThreeState, amountIn: number, direction: SwapDirection): SwapOutcome;
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSwapQuote = exports.simulateSwap = void 0;
4
+ /**
5
+ * CLMM Swap simulation
6
+ * Ported from exponent_clmm/src/state/market_three/helpers/swap.rs
7
+ */
8
+ const types_1 = require("./types");
9
+ const utils_1 = require("./utils");
10
+ const BASE_POINTS = 10000;
11
+ /**
12
+ * Simulate a swap on the CLMM market
13
+ * This is a pure function that does not mutate the market state
14
+ * Returns the swap outcome including amounts and final state
15
+ */
16
+ function simulateSwap(marketState, args) {
17
+ const { financials, configurationOptions, ticks } = marketState;
18
+ const secondsRemaining = Math.max(0, Number(financials.expirationTs) - Date.now() / 1000);
19
+ // Create effective price snapshot
20
+ const snapshot = new utils_1.EffSnap((0, utils_1.normalizedTimeRemaining)(secondsRemaining), args.syExchangeRate);
21
+ // Current state
22
+ let currentPriceSpot = ticks.currentSpotPrice;
23
+ let currentLeftBoundaryIndex = ticks.currentTick;
24
+ // Use currentPrefixSum if available, otherwise fall back to calculating it
25
+ let activeLiquidityU64 = ticks.currentPrefixSum ?? 0n;
26
+ let activeLiquidityF64 = Number(activeLiquidityU64);
27
+ // Fees
28
+ const lpFeeRate = (0, utils_1.calculateFeeRate)(configurationOptions.lnFeeRateRoot, secondsRemaining);
29
+ const protocolFeeBps = configurationOptions.treasuryFeeBps;
30
+ // Check price limits
31
+ if (args.priceSpotLimit !== undefined) {
32
+ if (args.direction === types_1.SwapDirection.PtToSy) {
33
+ if (args.priceSpotLimit < currentPriceSpot) {
34
+ throw new Error("Price limit violated: limit must be >= current price for PtToSy");
35
+ }
36
+ }
37
+ else {
38
+ if (args.priceSpotLimit > currentPriceSpot) {
39
+ throw new Error("Price limit violated: limit must be <= current price for SyToPt");
40
+ }
41
+ }
42
+ }
43
+ // Accumulators
44
+ let amountOutNet = 0;
45
+ let feeLpOut = 0;
46
+ let feeProtocolOut = 0;
47
+ let amountInLeft = args.amountIn;
48
+ // Main loop across contiguous intervals
49
+ let iterations = 0;
50
+ const maxIterations = 1000;
51
+ while (amountInLeft > 0 && iterations < maxIterations) {
52
+ iterations++;
53
+ // Get right boundary of current interval
54
+ const rightBoundaryIndexOpt = (0, utils_1.getSuccessorTickKey)(ticks, currentLeftBoundaryIndex);
55
+ if (rightBoundaryIndexOpt === null) {
56
+ if (args.direction === types_1.SwapDirection.SyToPt) {
57
+ // Cross to create a new interval
58
+ const predecessor = (0, utils_1.getPredecessorTickKey)(ticks, currentLeftBoundaryIndex);
59
+ if (predecessor === null)
60
+ break;
61
+ // When crossing downward (SyToPt), update state
62
+ currentPriceSpot = (0, utils_1.getImpliedRate)(currentLeftBoundaryIndex); // Boundary we're crossing
63
+ currentLeftBoundaryIndex = predecessor; // New left boundary
64
+ // Update active liquidity by subtracting liquidity_net at boundary
65
+ const boundaryTick = (0, utils_1.findTickByKey)(ticks, predecessor);
66
+ if (boundaryTick) {
67
+ activeLiquidityU64 = (0, utils_1.bigIntMax)(0n, activeLiquidityU64 - boundaryTick.tick.liquidityNet);
68
+ activeLiquidityF64 = Number(activeLiquidityU64);
69
+ }
70
+ continue;
71
+ }
72
+ else {
73
+ // No more liquidity available
74
+ break;
75
+ }
76
+ }
77
+ const rightBoundaryIndex = rightBoundaryIndexOpt;
78
+ // Get anchor prices for interval boundaries
79
+ const anchorULeft = (0, utils_1.getImpliedRate)(currentLeftBoundaryIndex);
80
+ const anchorURight = (0, utils_1.getImpliedRate)(rightBoundaryIndex);
81
+ // Effective price at current spot
82
+ const cEffOld = snapshot.getEffectivePrice(currentPriceSpot);
83
+ // Get principal ledgers for the interval
84
+ const currentTickData = (0, utils_1.findTickByKey)(ticks, currentLeftBoundaryIndex);
85
+ const principalPt = currentTickData?.tick.principalPt ?? 0n;
86
+ const principalSy = currentTickData?.tick.principalSy ?? 0n;
87
+ const eps = configurationOptions.epsilonClamp;
88
+ // Calculate kappa (scaling factor based on available principal)
89
+ // Y_max = (L/τ) * (C(u_old) - C(u_right))
90
+ const cEffAtBoundary = snapshot.getEffectivePrice(anchorURight);
91
+ const yMaxToBoundaryF = (Number(activeLiquidityF64) / snapshot.timeFactor) * (cEffOld - cEffAtBoundary);
92
+ const kappaSy = yMaxToBoundaryF > 0 ? Number(principalSy) / Number(yMaxToBoundaryF) : 0;
93
+ const duToLeft = currentPriceSpot - anchorULeft;
94
+ const ptMaxToLeftF = Number(activeLiquidityF64) * duToLeft;
95
+ const kappaPt = ptMaxToLeftF > 0 ? Number(principalPt) / ptMaxToLeftF : 0;
96
+ const kappa = Math.min(kappaPt, kappaSy, 1.0);
97
+ const lTradeF64 = Number(activeLiquidityF64) * kappa;
98
+ if (args.direction === types_1.SwapDirection.PtToSy) {
99
+ // PT -> SY swap (buying SY with PT)
100
+ const duByInput = lTradeF64 > 0 ? amountInLeft / lTradeF64 : 0;
101
+ const duToBoundary = anchorURight - currentPriceSpot;
102
+ const duActual = Math.min(duByInput, duToBoundary);
103
+ if (duToBoundary <= eps) {
104
+ // Cross boundary
105
+ const boundaryTick = (0, utils_1.findTickByKey)(ticks, rightBoundaryIndex);
106
+ if (boundaryTick) {
107
+ activeLiquidityU64 += boundaryTick.tick.liquidityNet;
108
+ activeLiquidityF64 = Number(activeLiquidityU64);
109
+ }
110
+ currentLeftBoundaryIndex = rightBoundaryIndex;
111
+ currentPriceSpot = anchorURight;
112
+ continue;
113
+ }
114
+ // Token flows for this segment
115
+ const ptInSegment = Math.floor(lTradeF64 * duActual);
116
+ const anchorUNew = currentPriceSpot + duActual;
117
+ const cEffNew = snapshot.getEffectivePrice(anchorUNew);
118
+ const syOutGross = Math.floor((lTradeF64 / snapshot.timeFactor) * (cEffOld - cEffNew));
119
+ const syOutGrossClamped = Math.min(syOutGross, Number(principalSy));
120
+ if (syOutGrossClamped > 0) {
121
+ const totalFeeOut = (0, utils_1.getFeeFromAmount)(syOutGrossClamped, lpFeeRate);
122
+ const protocolFeeOut = Math.floor((totalFeeOut * protocolFeeBps) / BASE_POINTS);
123
+ const lpFeeOut = totalFeeOut - protocolFeeOut;
124
+ const syOutNet = syOutGrossClamped - totalFeeOut;
125
+ amountOutNet += syOutNet;
126
+ feeLpOut += lpFeeOut;
127
+ feeProtocolOut += protocolFeeOut;
128
+ }
129
+ amountInLeft -= ptInSegment;
130
+ currentPriceSpot = anchorUNew;
131
+ }
132
+ else {
133
+ // SY -> PT swap (buying PT with SY)
134
+ const cEffLeft = snapshot.getEffectivePrice(anchorULeft);
135
+ const deltaCByInput = lTradeF64 > 0 ? (snapshot.timeFactor / lTradeF64) * amountInLeft : 0;
136
+ const deltaCToLeftBoundary = Math.max(0, cEffLeft - cEffOld);
137
+ const deltaCActual = Math.min(deltaCByInput, deltaCToLeftBoundary);
138
+ if (deltaCToLeftBoundary <= eps) {
139
+ // Cross boundary to the left
140
+ const predecessor = (0, utils_1.getPredecessorTickKey)(ticks, currentLeftBoundaryIndex);
141
+ if (predecessor === null)
142
+ break;
143
+ // Update active liquidity
144
+ const boundaryTick = (0, utils_1.findTickByKey)(ticks, currentLeftBoundaryIndex);
145
+ if (boundaryTick) {
146
+ activeLiquidityU64 = (0, utils_1.bigIntMax)(0n, activeLiquidityU64 - boundaryTick.tick.liquidityNet);
147
+ activeLiquidityF64 = Number(activeLiquidityU64);
148
+ }
149
+ currentPriceSpot = (0, utils_1.getImpliedRate)(currentLeftBoundaryIndex);
150
+ currentLeftBoundaryIndex = predecessor;
151
+ continue;
152
+ }
153
+ // New effective price and spot price after consuming ΔC
154
+ const cEffNew = cEffOld + deltaCActual;
155
+ const spotPriceNew = snapshot.spotPriceFromEffectivePrice(cEffNew);
156
+ // Token flows
157
+ const syInSegmentF = (lTradeF64 / snapshot.timeFactor) * (cEffNew - cEffOld);
158
+ const duAbs = currentPriceSpot - spotPriceNew;
159
+ const ptOutGrossF = lTradeF64 * duAbs;
160
+ // Clamp gross PT by available principal
161
+ const ptOutGrossU64 = (0, utils_1.bigIntMin)(BigInt(Math.floor(ptOutGrossF)), principalPt);
162
+ const syInSegmentU64 = BigInt(Math.floor(syInSegmentF));
163
+ if (ptOutGrossU64 === 0n) {
164
+ // Nothing to pay out; try to cross
165
+ const predecessor = (0, utils_1.getPredecessorTickKey)(ticks, currentLeftBoundaryIndex);
166
+ if (predecessor === null)
167
+ break;
168
+ // Update active liquidity
169
+ const boundaryTick = (0, utils_1.findTickByKey)(ticks, currentLeftBoundaryIndex);
170
+ if (boundaryTick) {
171
+ activeLiquidityU64 = (0, utils_1.bigIntMax)(0n, activeLiquidityU64 - boundaryTick.tick.liquidityNet);
172
+ activeLiquidityF64 = Number(activeLiquidityU64);
173
+ }
174
+ currentPriceSpot = (0, utils_1.getImpliedRate)(currentLeftBoundaryIndex);
175
+ currentLeftBoundaryIndex = predecessor;
176
+ continue;
177
+ }
178
+ // Fees in token_out (PT)
179
+ const totalFeeOut = (0, utils_1.getFeeFromAmount)(Number(ptOutGrossU64), lpFeeRate);
180
+ const protocolFeeOut = Math.floor((totalFeeOut * protocolFeeBps) / BASE_POINTS);
181
+ const lpFeeOut = totalFeeOut - protocolFeeOut;
182
+ const ptOutNet = Number(ptOutGrossU64) - totalFeeOut;
183
+ // Accumulate to user
184
+ amountOutNet += ptOutNet;
185
+ feeLpOut += lpFeeOut;
186
+ feeProtocolOut += protocolFeeOut;
187
+ // Consume input and advance state
188
+ amountInLeft -= Number(syInSegmentU64);
189
+ currentPriceSpot = spotPriceNew;
190
+ // If we hit boundary, cross
191
+ if (Math.abs(currentPriceSpot - anchorULeft) <= eps && amountInLeft > 0) {
192
+ const predecessor = (0, utils_1.getPredecessorTickKey)(ticks, currentLeftBoundaryIndex);
193
+ if (predecessor === null)
194
+ break;
195
+ // Update active liquidity
196
+ const boundaryTick = (0, utils_1.findTickByKey)(ticks, currentLeftBoundaryIndex);
197
+ if (boundaryTick) {
198
+ activeLiquidityU64 = (0, utils_1.bigIntMax)(0n, activeLiquidityU64 - boundaryTick.tick.liquidityNet);
199
+ activeLiquidityF64 = Number(activeLiquidityU64);
200
+ }
201
+ currentPriceSpot = (0, utils_1.getImpliedRate)(currentLeftBoundaryIndex);
202
+ currentLeftBoundaryIndex = predecessor;
203
+ }
204
+ }
205
+ }
206
+ return {
207
+ amountInConsumed: args.amountIn - amountInLeft,
208
+ amountOut: amountOutNet,
209
+ lpFeeChargedOutToken: feeLpOut,
210
+ protocolFeeChargedOutToken: feeProtocolOut,
211
+ finalSpotPrice: currentPriceSpot,
212
+ finalTickIndex: currentLeftBoundaryIndex,
213
+ };
214
+ }
215
+ exports.simulateSwap = simulateSwap;
216
+ /**
217
+ * Calculate the expected output for a given input amount
218
+ * This is a convenience wrapper around simulateSwap
219
+ */
220
+ function getSwapQuote(marketState, amountIn, direction) {
221
+ return simulateSwap(marketState, {
222
+ direction,
223
+ amountIn,
224
+ syExchangeRate: marketState.currentSyExchangeRate,
225
+ isCurrentFlashSwap: false,
226
+ });
227
+ }
228
+ exports.getSwapQuote = getSwapQuote;
229
+ //# sourceMappingURL=swapLegacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swapLegacy.js","sourceRoot":"","sources":["../src/swapLegacy.ts"],"names":[],"mappings":"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"}
package/build/utils.d.ts CHANGED
@@ -107,11 +107,11 @@ export declare function findTickByKey(ticks: Ticks, tickKey: number): {
107
107
  */
108
108
  export declare function findTickByIndex(ticks: Ticks, index: number): Tick;
109
109
  /**
110
- * Convert APY percentage to basis points
110
+ * Convert APY percentage to CLMM tick-key units.
111
111
  */
112
112
  export declare function convertApyToApyBp(apyPercent: number): number;
113
113
  /**
114
- * Convert basis points to APY percentage
114
+ * Convert CLMM tick-key units to APY percentage.
115
115
  */
116
116
  export declare function convertApyBpToApy(apyBp: number): number;
117
117
  export declare function bigIntMax(...args: bigint[]): bigint;
package/build/utils.js CHANGED
@@ -203,17 +203,18 @@ function findTickByKey(ticks, tickKey) {
203
203
  function findTickByIndex(ticks, index) {
204
204
  return ticks.ticksTree.at(index - 1) ?? null;
205
205
  }
206
+ const CLMM_TICK_KEY_SCALE = 10_000;
206
207
  /**
207
- * Convert APY percentage to basis points
208
+ * Convert APY percentage to CLMM tick-key units.
208
209
  */
209
210
  function convertApyToApyBp(apyPercent) {
210
- return Math.round(apyPercent * 100);
211
+ return Math.round(apyPercent * CLMM_TICK_KEY_SCALE);
211
212
  }
212
213
  /**
213
- * Convert basis points to APY percentage
214
+ * Convert CLMM tick-key units to APY percentage.
214
215
  */
215
216
  function convertApyBpToApy(apyBp) {
216
- return apyBp / 100;
217
+ return apyBp / CLMM_TICK_KEY_SCALE;
217
218
  }
218
219
  function bigIntMax(...args) {
219
220
  return args.reduce((m, e) => (e > m ? e : m));
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/jest.config.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { Config } from "@jest/types"
2
+
3
+ const config: Config.InitialOptions = {
4
+ preset: "ts-jest",
5
+ testEnvironment: "node",
6
+ testMatch: ["**/*.test.ts"],
7
+ }
8
+
9
+ export default config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exponent-labs/market-three-math",
3
- "version": "0.9.13",
3
+ "version": "0.9.14",
4
4
  "main": "build/index.js",
5
5
  "types": "build/index.d.ts",
6
6
  "license": "AGPL-3.0",
@@ -10,7 +10,7 @@
10
10
  "test:live": "ts-node examples/test-live-market.ts"
11
11
  },
12
12
  "dependencies": {
13
- "@exponent-labs/exponent-fetcher": "0.9.13",
13
+ "@exponent-labs/exponent-fetcher": "0.9.14",
14
14
  "@solana/web3.js": "^1.95.8"
15
15
  },
16
16
  "devDependencies": {
@@ -0,0 +1,162 @@
1
+ import { PublicKey } from "@solana/web3.js"
2
+
3
+ import type { LpPositionCLMM, Ticks } from "@exponent-labs/exponent-fetcher"
4
+
5
+ import {
6
+ computeExistingPositionBudgetEffect,
7
+ computeExistingPositionEqualization,
8
+ computeRequiredUserMaxForExistingPositionEqualization,
9
+ } from "./existingPositionEqualization"
10
+
11
+ const PRECISE_NUMBER_DENOM = 1_000_000_000_000n
12
+ const SENTINEL_TICK_INDEX = 0xffffffff
13
+ const PUBLIC_KEY = PublicKey.default
14
+
15
+ const preciseRaw = (value: bigint) => value * PRECISE_NUMBER_DENOM
16
+
17
+ const makeTicks = (): Ticks => ({
18
+ currentTick: 1,
19
+ ticksTree: [
20
+ {
21
+ apyBasePoints: 63_000,
22
+ impliedRate: 1.063,
23
+ principalPt: 500n,
24
+ principalSy: 700n,
25
+ principalShareSupply: preciseRaw(100n),
26
+ liquidityNet: 0n,
27
+ liquidityGross: 0n,
28
+ feeGrowthOutsidePt: 0n,
29
+ feeGrowthOutsideSy: 0n,
30
+ farms: [],
31
+ emissions: [],
32
+ lastSplitEpoch: 0n,
33
+ frozenLiquidity: 0n,
34
+ },
35
+ {
36
+ apyBasePoints: 65_000,
37
+ impliedRate: 1.065,
38
+ principalPt: 0n,
39
+ principalSy: 0n,
40
+ principalShareSupply: 0n,
41
+ liquidityNet: 0n,
42
+ liquidityGross: 0n,
43
+ feeGrowthOutsidePt: 0n,
44
+ feeGrowthOutsideSy: 0n,
45
+ farms: [],
46
+ emissions: [],
47
+ lastSplitEpoch: 0n,
48
+ frozenLiquidity: 0n,
49
+ },
50
+ ],
51
+ market: PUBLIC_KEY,
52
+ feeGrowthIndexGlobalPt: 0n,
53
+ feeGrowthIndexGlobalSy: 0n,
54
+ currentPrefixSum: 0n,
55
+ currentSpotPrice: 1.062,
56
+ })
57
+
58
+ const makePosition = (): LpPositionCLMM => ({
59
+ owner: PUBLIC_KEY,
60
+ market: PUBLIC_KEY,
61
+ feeInsideLastPt: 0n,
62
+ feeInsideLastSy: 0n,
63
+ lpBalance: 200n,
64
+ tokensOwedSy: 0n,
65
+ tokensOwedPt: 0n,
66
+ lowerTickIdx: 1,
67
+ upperTickIdx: 2,
68
+ farms: [],
69
+ shareTrackers: [
70
+ {
71
+ tickIdx: 1,
72
+ rightTickIdx: 2,
73
+ splitEpoch: 0n,
74
+ lpShare: preciseRaw(100n),
75
+ emissions: [],
76
+ },
77
+ ],
78
+ crossingSplit: {
79
+ crossLeftIdx: 1,
80
+ crossRightIdx: 2,
81
+ lpBalanceCrossing: 100n,
82
+ isActive: true,
83
+ },
84
+ })
85
+
86
+ describe("existing position equalization", () => {
87
+ it("matches the contract add-direction budget math with integer rounding", () => {
88
+ const equalization = computeExistingPositionEqualization(makeTicks(), makePosition())
89
+
90
+ expect(equalization.sySpent).toBe(700n)
91
+ expect(equalization.ptSpent).toBe(500n)
92
+ expect(equalization.syReleased).toBe(0n)
93
+ expect(equalization.ptReleased).toBe(0n)
94
+ expect(equalization.plan).toEqual([
95
+ {
96
+ shareIndex: 0,
97
+ tickIdx: 1,
98
+ direction: "add",
99
+ shareDeltaRaw: preciseRaw(100n),
100
+ ptDelta: 500n,
101
+ syDelta: 700n,
102
+ },
103
+ ])
104
+ })
105
+
106
+ it("throws when user budgets cannot cover fixed equalization spend", () => {
107
+ expect(() =>
108
+ computeExistingPositionBudgetEffect({
109
+ ticks: makeTicks(),
110
+ position: makePosition(),
111
+ userMaxSy: 700n,
112
+ userMaxPt: 499n,
113
+ }),
114
+ ).toThrow("CrossingEqualizationInvariantViolated")
115
+
116
+ expect(
117
+ computeExistingPositionBudgetEffect({
118
+ ticks: makeTicks(),
119
+ position: makePosition(),
120
+ userMaxSy: 700n,
121
+ userMaxPt: 500n,
122
+ }),
123
+ ).toMatchObject({
124
+ effectiveMaxSy: 0n,
125
+ effectiveMaxPt: 0n,
126
+ fixedSySpent: 700n,
127
+ fixedPtSpent: 500n,
128
+ })
129
+ })
130
+
131
+ it("expands desired effective budgets to user-facing instruction inputs", () => {
132
+ expect(
133
+ computeRequiredUserMaxForExistingPositionEqualization({
134
+ ticks: makeTicks(),
135
+ position: makePosition(),
136
+ desiredEffectiveMaxSy: 10n,
137
+ desiredEffectiveMaxPt: 20n,
138
+ }),
139
+ ).toMatchObject({
140
+ userMaxSy: 710n,
141
+ userMaxPt: 520n,
142
+ })
143
+ })
144
+
145
+ it("returns a no-op for inactive crossing splits", () => {
146
+ const position = makePosition()
147
+ position.crossingSplit = {
148
+ crossLeftIdx: SENTINEL_TICK_INDEX,
149
+ crossRightIdx: SENTINEL_TICK_INDEX,
150
+ lpBalanceCrossing: 0n,
151
+ isActive: false,
152
+ }
153
+
154
+ expect(computeExistingPositionEqualization(makeTicks(), position)).toMatchObject({
155
+ sySpent: 0n,
156
+ ptSpent: 0n,
157
+ syReleased: 0n,
158
+ ptReleased: 0n,
159
+ plan: [],
160
+ })
161
+ })
162
+ })
@@ -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 basis points
225
+ * Convert APY percentage to CLMM tick-key units.
224
226
  */
225
227
  export function convertApyToApyBp(apyPercent: number): number {
226
- return Math.round(apyPercent * 100)
228
+ return Math.round(apyPercent * CLMM_TICK_KEY_SCALE)
227
229
  }
228
230
 
229
231
  /**
230
- * Convert basis points to APY percentage
232
+ * Convert CLMM tick-key units to APY percentage.
231
233
  */
232
234
  export function convertApyBpToApy(apyBp: number): number {
233
- return apyBp / 100
235
+ return apyBp / CLMM_TICK_KEY_SCALE
234
236
  }
235
237
 
236
238
  export function bigIntMax(...args: bigint[]): bigint {