@sundaeswap/math 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/PoolMath/index.ts"],"sourcesContent":["export * as ConcentratedLiquidityPool from \"./ConcentratedLiquidityPool.js\";\nexport * as ConstantProductPool from \"./ConstantProductPool.js\";\nexport * as ConstantSumPool from \"./ConstantSumPool.js\";\nexport * as StableSwapsPool from \"./StableSwapsPool.js\";\nexport * as SharedPoolMath from \"./SharedPoolMath.js\";\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/PoolMath/index.ts"],"sourcesContent":["export * as ConcentratedLiquidityPool from \"./ConcentratedLiquidityPool.js\";\nexport * as ConstantProductPool from \"./ConstantProductPool.js\";\nexport * as ConstantSumPool from \"./ConstantSumPool.js\";\nexport * as StableSwapsPool from \"./StableSwapsPool.js\";\nexport * as V4StableswapPool from \"./V4StableswapPool.js\";\nexport * as SharedPoolMath from \"./SharedPoolMath.js\";\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,495 @@
1
+ import { AssetAmount } from "@sundaeswap/asset";
2
+ import { Fraction } from "@sundaeswap/fraction";
3
+ import { SharedPoolMath } from "./index.js";
4
+
5
+ /**
6
+ * THE V4 STABLESWAP INVARIANT MODULE — NOT THE V3 "STABLESWAPS" CONTRACT.
7
+ *
8
+ * Two different things in this SDK carry the word "stableswap". Keep them
9
+ * apart:
10
+ *
11
+ * - `StableSwapsPool` / `EContractVersion.Stableswaps` is the **v3** stableswap
12
+ * contract. It is a whole contract version with its own datum builder and
13
+ * transaction builder. It stores `D` in the pool datum and has no per-asset
14
+ * rates.
15
+ * - `V4StableswapPool` / `EPoolCurve.V4Stableswap` (this file) is a **v4
16
+ * invariant module**. It is one curve among several a v4 pool can bind, next
17
+ * to constant product, constant sum and concentrated liquidity. It stores no
18
+ * `D` and carries per-asset integer `rates`.
19
+ *
20
+ * The amplification `A` is the ONE thing they share: the same parameter on the
21
+ * same scale, carried by the same `linearAmplificationFactor` field, with each
22
+ * implementation applying its own internal scaling. Everything else is
23
+ * different — do not pass one's reserves, rates or `D` to the other.
24
+ *
25
+ * The curve, on rated and scaled reserves `x = r_a·rate_a·P` and
26
+ * `y = r_b·rate_b·P` with `P = calc_precision = 10^12` and `A` the linear
27
+ * amplification:
28
+ *
29
+ * 4A(x + y) + D = 4AD + D³ / (4xy)
30
+ *
31
+ * `D` is the largest integer that satisfies it. Neither `D` nor a swap output
32
+ * can be computed on-chain, because both need Newton's method. The scooper
33
+ * computes them and the module checks that each supplied integer is the unique
34
+ * one that solves the invariant. This file is the off-chain twin of
35
+ * `lib/modules/ss_math.ak` and `lib/modules/ss_check.ak` in the sundae-v4
36
+ * repository — those are the source of truth; keep this in lockstep with them.
37
+ */
38
+
39
+ /** `ss_math.calc_precision`: the scale applied to reserves before they enter the invariant. */
40
+ export const CALC_PRECISION = 1_000_000_000_000n;
41
+
42
+ /** Newton iteration cap, matching the reference implementation. */
43
+ const NEWTON_MAX_ITERATIONS = 255;
44
+
45
+ /** Bound on the fix-up walk of the reverse swap. The closed form lands within a few units. */
46
+ const CORRECTION_MAX_STEPS = 256;
47
+
48
+ /**
49
+ * Holds the calculated outcome of a v4 stableswap swap. Mirrors
50
+ * {@link ConstantProductPool.TSwapOutcome} so it flows through the same generic
51
+ * swap-outcome consumers.
52
+ *
53
+ * `lpFee` is denominated in the **output** asset, not the input asset. The
54
+ * stableswap fee is taken off the gross output (`fee = ceil(gross · feeRate)`),
55
+ * unlike constant product and constant sum which take the fee off the input.
56
+ * The v3 `StableSwapsPool` does the same.
57
+ */
58
+
59
+ // ─── Integer helpers ────────────────────────────────────────────────────────
60
+
61
+ /** Plutus `divideInteger`: floor toward −∞. */
62
+ const floorDiv = (a, b) => {
63
+ const q = a / b;
64
+ const r = a % b;
65
+ return r !== 0n && r < 0n !== b < 0n ? q - 1n : q;
66
+ };
67
+
68
+ /** ceil(a / b) with the same sign convention as {@link floorDiv}. */
69
+ const ceilDiv = (a, b) => -floorDiv(-a, b);
70
+ const asFeeParts = fee => {
71
+ const f = Fraction.asFraction(fee);
72
+ if (f.lt(Fraction.ZERO) || f.gte(Fraction.ONE)) throw new Error("fee must be in [0, 1)");
73
+ return [BigInt(f.numerator), BigInt(f.denominator)];
74
+ };
75
+
76
+ // ─── Invariant polynomials (`ss_math.ak`) ───────────────────────────────────
77
+
78
+ /**
79
+ * `f(D)` from the on-chain `liquidity_invariant`. It is ≤ 0 when `D` is at or
80
+ * below the curve. All arguments are rated and scaled.
81
+ */
82
+ export const invariantF = (x, y, amp, d) => {
83
+ const sixteenAxy = 16n * amp * x * y;
84
+ return sixteenAxy * d + d * d * d - (sixteenAxy * (x + y) + 4n * x * y * d);
85
+ };
86
+
87
+ /**
88
+ * `g(y)` from the on-chain `exchange_invariant`. It is ≥ 0 when the pool keeps
89
+ * enough of the taken asset. `g` is symmetric in `x` and `y`, which is what
90
+ * lets {@link getSwapInput} reuse the same solver in the other direction.
91
+ */
92
+ export const invariantG = (x, y, amp, d) => {
93
+ const fourXy = 4n * x * y;
94
+ return fourXy * (4n * amp * (x + y) + d) - fourXy * 4n * amp * d - d * d * d;
95
+ };
96
+
97
+ // ─── D ──────────────────────────────────────────────────────────────────────
98
+
99
+ /** The ±1 fix-up that turns a Newton result into the exact largest `D`. */
100
+ const fixD = (x, y, amp, d0) => {
101
+ let d = d0;
102
+ while (invariantF(x, y, amp, d) > 0n) d -= 1n;
103
+ while (invariantF(x, y, amp, d + 1n) <= 0n) d += 1n;
104
+ return d;
105
+ };
106
+
107
+ /**
108
+ * The sum invariant `D` of a two-asset v4 stableswap pool: the largest integer
109
+ * that satisfies the curve on the rated, scaled reserves. Newton from
110
+ * `D = x + y`, then a ±1 fix-up.
111
+ *
112
+ * `D` carries the `rate · CALC_PRECISION` scale — it is not a token amount.
113
+ *
114
+ * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.
115
+ * @param aReserve The pool's reserve of asset A, in token units.
116
+ * @param bReserve The pool's reserve of asset B, in token units.
117
+ * @param rateA The pool's rate for asset A (from the stableswap config).
118
+ * @param rateB The pool's rate for asset B.
119
+ */
120
+ export const getD = (amp, aReserve, bReserve, rateA = 1n, rateB = 1n) => {
121
+ if (amp <= 0n) throw new Error("amplification must be positive");
122
+ if (aReserve < 0n || bReserve < 0n) throw new Error("reserves must be non-negative");
123
+ if (rateA <= 0n || rateB <= 0n) throw new Error("rates must be positive");
124
+ const xs = aReserve * rateA * CALC_PRECISION;
125
+ const ys = bReserve * rateB * CALC_PRECISION;
126
+ const sum = xs + ys;
127
+ // An empty pool has no invariant to solve; `D` is zero by definition.
128
+ if (sum === 0n) return 0n;
129
+ // Exactly one empty reserve is a different case, and it has no answer: the
130
+ // Newton step divides by `4·xs·ys`, which is zero here. The chain never
131
+ // reaches this state — Create requires both reserves positive, and the
132
+ // exchange invariant leaves at least one unit on the output side — but this
133
+ // is a client library, so it reports the bad input instead of faulting on a
134
+ // native division by zero.
135
+ if (xs === 0n || ys === 0n) {
136
+ throw new Error("getD: a stableswap pool cannot hold exactly one empty reserve; D is undefined there");
137
+ }
138
+ const ann = 4n * amp;
139
+ let d = sum;
140
+ for (let i = 0; i < NEWTON_MAX_ITERATIONS; i++) {
141
+ const dP = floorDiv(d * d * d, 4n * xs * ys);
142
+ const dNext = floorDiv((ann * sum + 2n * dP) * d, (ann - 1n) * d + 3n * dP);
143
+ const diff = dNext > d ? dNext - d : d - dNext;
144
+ d = dNext;
145
+ if (diff <= 1n) return fixD(xs, ys, amp, d);
146
+ }
147
+ throw new Error("getD: reached the Newton iteration cap");
148
+ };
149
+
150
+ // ─── The exchange solver ────────────────────────────────────────────────────
151
+
152
+ /**
153
+ * The smallest scaled `y` with `g(xs, y) ≥ 0` at the pre-swap `d`: the least
154
+ * the pool may keep of the counterparty asset and still sit on the curve.
155
+ * Newton on `y`, then a walk to the exact integer — the walk leaves `y` with
156
+ * `g(y) ≥ 0` and `g(y − 1) < 0`, so it is the boundary and not merely a valid
157
+ * point.
158
+ *
159
+ * Read the direction carefully, because the two framings invert. A caller
160
+ * turning this into an output computes `raw = Y − y`, so the SMALLEST `y` is
161
+ * the LARGEST output the invariant admits. It is still not generous: `y` is a
162
+ * ceiling on the exact real-valued requirement, so `raw` is at or below the
163
+ * exact output. Most the curve allows, never more than it allows.
164
+ *
165
+ * Both swap directions use this: `g` is symmetric, so solving for the
166
+ * counterparty reserve answers "what must the other side hold" whichever side
167
+ * is given.
168
+ */
169
+ const solveCounterparty = (amp, d, xs) => {
170
+ if (xs <= 0n) throw new Error("solveCounterparty: the given side must be positive");
171
+ const ann = 4n * amp;
172
+ // n = 2: c = D³ / (4·x·Ann), b = x + D / Ann. Evaluated in the same order as
173
+ // the Aiken source so the intermediate floors match.
174
+ const c = floorDiv(floorDiv(d * d, 2n * xs) * d, 2n * ann);
175
+ const b = xs + floorDiv(d, ann);
176
+ let y = d;
177
+ let converged = false;
178
+ for (let i = 0; i < NEWTON_MAX_ITERATIONS; i++) {
179
+ const yNext = floorDiv(y * y + c, 2n * y + b - d);
180
+ const diff = yNext > y ? yNext - y : y - yNext;
181
+ y = yNext;
182
+ if (diff <= 1n) {
183
+ converged = true;
184
+ break;
185
+ }
186
+ }
187
+ if (!converged) throw new Error("solveCounterparty: reached the Newton iteration cap");
188
+ while (invariantG(xs, y, amp, d) < 0n) y += 1n;
189
+ while (invariantG(xs, y - 1n, amp, d) >= 0n) y -= 1n;
190
+ return y;
191
+ };
192
+
193
+ /**
194
+ * The raw swap output: the scaled, pre-fee amount the curve releases when the
195
+ * given reserve becomes `inAfter` and the taken reserve starts at `outBefore`,
196
+ * at the pre-swap `d`. It is the largest such amount the exchange invariant
197
+ * admits — one unit more leaves the pool below the curve — and it is at or
198
+ * below the exact real-valued output, so it never over-quotes.
199
+ *
200
+ * The result carries the `rate · CALC_PRECISION` scale. Divide by
201
+ * `rateOut · CALC_PRECISION` to get the gross output in token units.
202
+ *
203
+ * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.
204
+ * @param d The pre-swap sum invariant, from {@link getD}.
205
+ * @param inAfter The given asset's reserve after the input arrives, in token units.
206
+ * @param outBefore The taken asset's reserve before the swap, in token units.
207
+ * @param rateIn The pool's rate for the given asset.
208
+ * @param rateOut The pool's rate for the taken asset.
209
+ */
210
+ export const getRawSwap = (amp, d, inAfter, outBefore, rateIn = 1n, rateOut = 1n) => {
211
+ if (amp <= 0n) throw new Error("amplification must be positive");
212
+ if (d <= 0n) throw new Error("the sum invariant must be positive");
213
+ if (inAfter <= 0n || outBefore <= 0n) throw new Error("reserves must be positive");
214
+ if (rateIn <= 0n || rateOut <= 0n) throw new Error("rates must be positive");
215
+ const xs = inAfter * rateIn * CALC_PRECISION;
216
+ return outBefore * rateOut * CALC_PRECISION - solveCounterparty(amp, d, xs);
217
+ };
218
+
219
+ // ─── Marginal price ─────────────────────────────────────────────────────────
220
+
221
+ /**
222
+ * The marginal price of the curve at the current reserves, as input units per
223
+ * output unit. Differentiating the invariant at fixed `D` gives
224
+ *
225
+ * −dy/dx = (16A·x²y² + D³·y) / (16A·x²y² + D³·x)
226
+ *
227
+ * in scaled value units, which converts to token units through the rates. At a
228
+ * balanced pool (`x = y`) it reduces to `rateOut / rateIn`, the par price.
229
+ */
230
+ const marginalInputPerOutput = (amp, xs, ys, d, rateIn, rateOut) => {
231
+ const common = 16n * amp * xs * xs * ys * ys;
232
+ const cube = d * d * d;
233
+ return new Fraction(rateOut * (common + cube * xs), rateIn * (common + cube * ys));
234
+ };
235
+
236
+ /**
237
+ * The pool's marginal price as raw units of asset A per raw unit of asset B.
238
+ * Use it where a constant-product pool would use the reserve ratio: for a
239
+ * stableswap pool the reserve ratio is not the price, because the curve holds
240
+ * the price near par across a wide band of ratios.
241
+ *
242
+ * The result is in raw (undecimalized) units. Adjust for the two assets'
243
+ * decimals before display.
244
+ */
245
+ export const getPrice = (amp, aReserve, bReserve, rateA = 1n, rateB = 1n) => {
246
+ if (aReserve <= 0n || bReserve <= 0n) throw new Error("reserves must be positive");
247
+ const d = getD(amp, aReserve, bReserve, rateA, rateB);
248
+ const xs = aReserve * rateA * CALC_PRECISION;
249
+ const ys = bReserve * rateB * CALC_PRECISION;
250
+ // A per B: asset B is the output side of the derivative.
251
+ return marginalInputPerOutput(amp, xs, ys, d, rateA, rateB);
252
+ };
253
+
254
+ // ─── Swap output ────────────────────────────────────────────────────────────
255
+
256
+ /**
257
+ * Calculate the swap outcome for a v4 stableswap pool.
258
+ *
259
+ * The sequence the chain enforces (`ss_check.ak`, tag 3):
260
+ *
261
+ * 1. `D` is derived from the pre-swap reserves at the pool's rates.
262
+ * 2. `raw` is the LARGEST scaled output the exchange invariant admits at that
263
+ * `D`, with the input already added to the given reserve. The solver pins
264
+ * the pool's remaining taken reserve `y` to the SMALLEST integer that still
265
+ * satisfies the invariant, and `raw = outBefore·rateOut·P − y`, so the two
266
+ * statements are the same one: least reserve kept, most output released.
267
+ * 3. `gross = floor(raw / (rateOut · CALC_PRECISION))` is the output in token units.
268
+ * 4. `fee = ceil(gross · feeRate)` stays in the pool.
269
+ * 5. The trader receives `gross − fee`.
270
+ *
271
+ * Every rounding step favours the pool, never the trader: `y` is a ceiling on
272
+ * the exact reserve the curve requires, `gross` floors to whole tokens, and the
273
+ * fee ceilings. A trader is never quoted more than the chain will pay.
274
+ *
275
+ * @param outputMetadata Metadata for the **taken** asset. The fee is denominated in it.
276
+ * @param input The amount of the given asset being swapped.
277
+ * @param inputReserve The pool's reserve of the given asset.
278
+ * @param outputReserve The pool's reserve of the taken asset.
279
+ * @param rateIn The pool's rate for the given asset (from the stableswap config).
280
+ * @param rateOut The pool's rate for the taken asset.
281
+ * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.
282
+ * @param fee The swap fee rate, applied to the gross output.
283
+ * @returns The swap details in the shared {@link TSwapOutcome} shape.
284
+ */
285
+ export const getSwapOutput = (outputMetadata, input, inputReserve, outputReserve, rateIn, rateOut, amp, fee) => {
286
+ if (input <= 0n || inputReserve <= 0n || outputReserve <= 0n) throw new Error("Input and reserves must be positive");
287
+ if (rateIn <= 0n || rateOut <= 0n) throw new Error("Rates must be positive");
288
+ if (amp <= 0n) throw new Error("Amplification must be positive");
289
+ const [feeNum, feeDen] = asFeeParts(fee);
290
+ const d = getD(amp, inputReserve, outputReserve, rateIn, rateOut);
291
+ const raw = getRawSwap(amp, d, inputReserve + input, outputReserve, rateIn, rateOut);
292
+ const gross = floorDiv(raw, rateOut * CALC_PRECISION);
293
+ const lpFee = ceilDiv(gross * feeNum, feeDen);
294
+ const output = gross - lpFee;
295
+ if (output <= 0n) throw new Error("Swap yields no output after the fee; raise the input");
296
+ const nextInputReserve = inputReserve + input;
297
+ const nextOutputReserve = outputReserve - output;
298
+
299
+ // Price impact against the pool's marginal price before the swap, the same
300
+ // definition concentrated liquidity uses. Constant sum measures against its
301
+ // par price instead, because a constant-sum pool has no other price. A
302
+ // stableswap pool does: its marginal price moves with the reserve ratio, so
303
+ // measuring against par would report the pool's standing imbalance as this
304
+ // trade's impact. The fee is excluded so the number is curve slippage only.
305
+ const xs = inputReserve * rateIn * CALC_PRECISION;
306
+ const ys = outputReserve * rateOut * CALC_PRECISION;
307
+ const idealPrice = marginalInputPerOutput(amp, xs, ys, d, rateIn, rateOut);
308
+ const actualPrice = new Fraction(input, gross);
309
+ const priceImpact = Fraction.ONE.subtract(idealPrice.divide(actualPrice));
310
+ return {
311
+ input,
312
+ output,
313
+ lpFee: new AssetAmount(lpFee, outputMetadata),
314
+ nextInputReserve,
315
+ nextOutputReserve,
316
+ priceImpact
317
+ };
318
+ };
319
+
320
+ // ─── Swap input (the reverse) ───────────────────────────────────────────────
321
+
322
+ /**
323
+ * Calculate the minimal input that buys a given output — the inverse of
324
+ * {@link getSwapOutput}.
325
+ *
326
+ * The steps:
327
+ *
328
+ * 1. Invert the fee: the smallest gross output that nets `output` after
329
+ * `ceil(gross · feeRate)` is `ceil(output · feeDen / (feeDen − feeNum))`.
330
+ * 2. Turn that gross into the scaled reserve the pool may keep of the taken
331
+ * asset.
332
+ * 3. Solve the exchange invariant in the other direction for the given
333
+ * reserve that supports it. `g` is symmetric, so this is the same solver.
334
+ * 4. Convert to token units and walk down to the minimum, which absorbs the
335
+ * integer rounding of step 3.
336
+ *
337
+ * @param outputMetadata Metadata for the **taken** asset. The fee is denominated in it.
338
+ * @param output The wanted amount of the taken asset.
339
+ * @param inputReserve The pool's reserve of the given asset.
340
+ * @param outputReserve The pool's reserve of the taken asset.
341
+ * @param rateIn The pool's rate for the given asset (from the stableswap config).
342
+ * @param rateOut The pool's rate for the taken asset.
343
+ * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.
344
+ * @param fee The swap fee rate, applied to the gross output.
345
+ * @returns The swap details in the shared {@link TSwapOutcome} shape.
346
+ */
347
+ export const getSwapInput = (outputMetadata, output, inputReserve, outputReserve, rateIn, rateOut, amp, fee) => {
348
+ if (output <= 0n || inputReserve <= 0n || outputReserve <= 0n) throw new Error("Output and reserves must be positive");
349
+ if (rateIn <= 0n || rateOut <= 0n) throw new Error("Rates must be positive");
350
+ if (amp <= 0n) throw new Error("Amplification must be positive");
351
+ if (output >= outputReserve) throw new Error("Output must be below the output reserve");
352
+ const [feeNum, feeDen] = asFeeParts(fee);
353
+
354
+ // 1. Smallest gross whose post-fee remainder reaches `output`.
355
+ // gross − ceil(gross·n/d) = floor(gross·(d−n)/d), which is ≥ output
356
+ // exactly when gross ≥ ceil(output·d / (d−n)).
357
+ const grossMin = ceilDiv(output * feeDen, feeDen - feeNum);
358
+ if (grossMin >= outputReserve) throw new Error("Output is not reachable: the fee pushes it past the reserve");
359
+ const d = getD(amp, inputReserve, outputReserve, rateIn, rateOut);
360
+ const scaleIn = rateIn * CALC_PRECISION;
361
+ const scaleOut = rateOut * CALC_PRECISION;
362
+
363
+ // 2. The most the pool may keep of the taken asset and still pay `grossMin`.
364
+ const yTarget = (outputReserve - grossMin) * scaleOut;
365
+ if (yTarget <= 0n) throw new Error("Output is not reachable within the pool's reserves");
366
+
367
+ // 3. + 4. The scaled given reserve that supports it, then token units.
368
+ const xNeeded = solveCounterparty(amp, d, yTarget);
369
+ const xBefore = inputReserve * scaleIn;
370
+ let input = ceilDiv(xNeeded - xBefore, scaleIn);
371
+ if (input < 1n) input = 1n;
372
+ const grossOf = dx => floorDiv(getRawSwap(amp, d, inputReserve + dx, outputReserve, rateIn, rateOut), scaleOut);
373
+ let steps = 0;
374
+ while (grossOf(input) < grossMin) {
375
+ input += 1n;
376
+ if (++steps > CORRECTION_MAX_STEPS) throw new Error("getSwapInput: the correction walk did not converge");
377
+ }
378
+ while (input > 1n && grossOf(input - 1n) >= grossMin) {
379
+ input -= 1n;
380
+ if (++steps > CORRECTION_MAX_STEPS) throw new Error("getSwapInput: the correction walk did not converge");
381
+ }
382
+ const gross = grossOf(input);
383
+ const lpFee = ceilDiv(gross * feeNum, feeDen);
384
+ const nextInputReserve = inputReserve + input;
385
+ const nextOutputReserve = outputReserve - output;
386
+ const xs = xBefore;
387
+ const ys = outputReserve * scaleOut;
388
+ const idealPrice = marginalInputPerOutput(amp, xs, ys, d, rateIn, rateOut);
389
+ const actualPrice = new Fraction(input, gross);
390
+ const priceImpact = Fraction.ONE.subtract(idealPrice.divide(actualPrice));
391
+ return {
392
+ input,
393
+ output,
394
+ lpFee: new AssetAmount(lpFee, outputMetadata),
395
+ nextInputReserve,
396
+ nextOutputReserve,
397
+ priceImpact
398
+ };
399
+ };
400
+
401
+ // ─── Deposits ───────────────────────────────────────────────────────────────
402
+
403
+ /**
404
+ * The deposit target a holder of `offered` can cover, and everything pinned to
405
+ * it. This is the target-pinned rule the v4 module enforces (`ss_check.ak`,
406
+ * tag 6), and it is the same shape constant sum uses, stated on `D` instead of
407
+ * on the value sum `V`:
408
+ *
409
+ * - the fill declares a `D` delta `t`;
410
+ * - every reserve moves by `ceil(r_i · t / D_before)`;
411
+ * - `total_lp` becomes `floor(lp_before · (D_before + t) / D_before)`.
412
+ *
413
+ * The largest `t` a holder can cover is capped by the scarcest offered asset,
414
+ * `t = min_i floor(offered_i · D_before / r_i)`. Anything above the pinned
415
+ * deltas is refunded. Every pool asset must be offered, or `t` is zero and the
416
+ * deposit can never fill.
417
+ *
418
+ * The rates do not enter the deltas: a proportional move is proportional in
419
+ * any units. They enter only through `D`.
420
+ */
421
+ export const calculatePinnedDeposit = (offered, reserves, totalLp, d) => {
422
+ if (offered.length !== reserves.length) throw new Error("offered and reserves must be aligned");
423
+ if (totalLp <= 0n) throw new Error("Not enough pool liquidity");
424
+ if (d <= 0n) throw new Error("Pool sum invariant is zero");
425
+ if (offered.some(o => o < 0n)) throw new Error("Deposit amounts must be non-negative");
426
+ let t;
427
+ for (let i = 0; i < offered.length; i++) {
428
+ if (reserves[i] <= 0n) continue;
429
+ const cap = floorDiv(offered[i] * d, reserves[i]);
430
+ t = t === undefined || cap < t ? cap : t;
431
+ }
432
+ if (t === undefined || t <= 0n) throw new Error("A stableswap deposit must offer every pool asset in proportion (asymmetric deposits are disallowed on-chain)");
433
+ const deltas = reserves.map(r => ceilDiv(r * t, d));
434
+ const nextTotalLp = floorDiv(totalLp * (d + t), d);
435
+ const generatedLp = nextTotalLp - totalLp;
436
+ if (generatedLp <= 0n) throw new Error("Deposit mints zero LP");
437
+ return {
438
+ targetDeltaD: t,
439
+ deltas,
440
+ generatedLp,
441
+ nextTotalLp,
442
+ shareAfterDeposit: SharedPoolMath.getShare(generatedLp, nextTotalLp)
443
+ };
444
+ };
445
+
446
+ /**
447
+ * Calculate the deposit parameters for a two-asset v4 stableswap pool through
448
+ * the target-pinned rule (see {@link calculatePinnedDeposit}). Surplus above
449
+ * the pinned deltas comes back as `aChange` / `bChange`, the way the scoop
450
+ * refunds it on-chain.
451
+ *
452
+ * @param a The amount of token A to deposit.
453
+ * @param b The amount of token B to deposit.
454
+ * @param aReserve The pool's reserve of token A.
455
+ * @param bReserve The pool's reserve of token B.
456
+ * @param totalLp The pool's total LP supply before the deposit.
457
+ * @param rateA The pool's rate for token A (from the stableswap config).
458
+ * @param rateB The pool's rate for token B.
459
+ * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.
460
+ */
461
+ export const calculateLiquidity = (a, b, aReserve, bReserve, totalLp, rateA, rateB, amp) => {
462
+ if (aReserve <= 0n || bReserve <= 0n) throw new Error("Not enough pool liquidity");
463
+ const d = getD(amp, aReserve, bReserve, rateA, rateB);
464
+ const {
465
+ deltas,
466
+ generatedLp,
467
+ nextTotalLp,
468
+ shareAfterDeposit
469
+ } = calculatePinnedDeposit([a, b], [aReserve, bReserve], totalLp, d);
470
+ return {
471
+ nextTotalLp,
472
+ generatedLp,
473
+ shareAfterDeposit,
474
+ aChange: a - deltas[0],
475
+ bChange: b - deltas[1],
476
+ actualDepositedA: deltas[0],
477
+ actualDepositedB: deltas[1]
478
+ };
479
+ };
480
+
481
+ /**
482
+ * The per-asset amounts a pinned deposit needs when the user anchors on one
483
+ * asset's amount: `t` from the anchor (`floor(anchor · D / r_anchor)`), every
484
+ * other delta ceil-pinned to it. Drives proportional auto-fill in deposit
485
+ * forms.
486
+ */
487
+ export const pinnedDepositFromAnchor = (anchorIndex, anchorAmount, reserves, d) => {
488
+ if (anchorIndex < 0 || anchorIndex >= reserves.length) throw new Error("anchor index is out of range");
489
+ if (reserves[anchorIndex] <= 0n) throw new Error("anchor asset has no reserve");
490
+ if (d <= 0n) throw new Error("Pool sum invariant is zero");
491
+ if (anchorAmount <= 0n) return reserves.map(() => 0n);
492
+ const t = floorDiv(anchorAmount * d, reserves[anchorIndex]);
493
+ return reserves.map((r, i) => i === anchorIndex ? anchorAmount : ceilDiv(r * t, d));
494
+ };
495
+ //# sourceMappingURL=V4StableswapPool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"V4StableswapPool.js","names":["AssetAmount","Fraction","SharedPoolMath","CALC_PRECISION","NEWTON_MAX_ITERATIONS","CORRECTION_MAX_STEPS","floorDiv","a","b","q","r","ceilDiv","asFeeParts","fee","f","asFraction","lt","ZERO","gte","ONE","Error","BigInt","numerator","denominator","invariantF","x","y","amp","d","sixteenAxy","invariantG","fourXy","fixD","d0","getD","aReserve","bReserve","rateA","rateB","xs","ys","sum","ann","i","dP","dNext","diff","solveCounterparty","c","converged","yNext","getRawSwap","inAfter","outBefore","rateIn","rateOut","marginalInputPerOutput","common","cube","getPrice","getSwapOutput","outputMetadata","input","inputReserve","outputReserve","feeNum","feeDen","raw","gross","lpFee","output","nextInputReserve","nextOutputReserve","idealPrice","actualPrice","priceImpact","subtract","divide","getSwapInput","grossMin","scaleIn","scaleOut","yTarget","xNeeded","xBefore","grossOf","dx","steps","calculatePinnedDeposit","offered","reserves","totalLp","length","some","o","t","cap","undefined","deltas","map","nextTotalLp","generatedLp","targetDeltaD","shareAfterDeposit","getShare","calculateLiquidity","aChange","bChange","actualDepositedA","actualDepositedB","pinnedDepositFromAnchor","anchorIndex","anchorAmount"],"sources":["../../../src/PoolMath/V4StableswapPool.ts"],"sourcesContent":["import { AssetAmount, IAssetAmountMetadata } from \"@sundaeswap/asset\";\nimport { Fraction, type TFractionLike } from \"@sundaeswap/fraction\";\n\nimport { SharedPoolMath } from \"./index.js\";\n\n/**\n * THE V4 STABLESWAP INVARIANT MODULE — NOT THE V3 \"STABLESWAPS\" CONTRACT.\n *\n * Two different things in this SDK carry the word \"stableswap\". Keep them\n * apart:\n *\n * - `StableSwapsPool` / `EContractVersion.Stableswaps` is the **v3** stableswap\n * contract. It is a whole contract version with its own datum builder and\n * transaction builder. It stores `D` in the pool datum and has no per-asset\n * rates.\n * - `V4StableswapPool` / `EPoolCurve.V4Stableswap` (this file) is a **v4\n * invariant module**. It is one curve among several a v4 pool can bind, next\n * to constant product, constant sum and concentrated liquidity. It stores no\n * `D` and carries per-asset integer `rates`.\n *\n * The amplification `A` is the ONE thing they share: the same parameter on the\n * same scale, carried by the same `linearAmplificationFactor` field, with each\n * implementation applying its own internal scaling. Everything else is\n * different — do not pass one's reserves, rates or `D` to the other.\n *\n * The curve, on rated and scaled reserves `x = r_a·rate_a·P` and\n * `y = r_b·rate_b·P` with `P = calc_precision = 10^12` and `A` the linear\n * amplification:\n *\n * 4A(x + y) + D = 4AD + D³ / (4xy)\n *\n * `D` is the largest integer that satisfies it. Neither `D` nor a swap output\n * can be computed on-chain, because both need Newton's method. The scooper\n * computes them and the module checks that each supplied integer is the unique\n * one that solves the invariant. This file is the off-chain twin of\n * `lib/modules/ss_math.ak` and `lib/modules/ss_check.ak` in the sundae-v4\n * repository — those are the source of truth; keep this in lockstep with them.\n */\n\n/** `ss_math.calc_precision`: the scale applied to reserves before they enter the invariant. */\nexport const CALC_PRECISION = 1_000_000_000_000n;\n\n/** Newton iteration cap, matching the reference implementation. */\nconst NEWTON_MAX_ITERATIONS = 255;\n\n/** Bound on the fix-up walk of the reverse swap. The closed form lands within a few units. */\nconst CORRECTION_MAX_STEPS = 256;\n\n/**\n * Holds the calculated outcome of a v4 stableswap swap. Mirrors\n * {@link ConstantProductPool.TSwapOutcome} so it flows through the same generic\n * swap-outcome consumers.\n *\n * `lpFee` is denominated in the **output** asset, not the input asset. The\n * stableswap fee is taken off the gross output (`fee = ceil(gross · feeRate)`),\n * unlike constant product and constant sum which take the fee off the input.\n * The v3 `StableSwapsPool` does the same.\n */\nexport type TSwapOutcome = {\n input: bigint;\n output: bigint;\n lpFee: AssetAmount<IAssetAmountMetadata>;\n nextInputReserve: bigint;\n nextOutputReserve: bigint;\n priceImpact: Fraction;\n};\n\n// ─── Integer helpers ────────────────────────────────────────────────────────\n\n/** Plutus `divideInteger`: floor toward −∞. */\nconst floorDiv = (a: bigint, b: bigint): bigint => {\n const q = a / b;\n const r = a % b;\n return r !== 0n && r < 0n !== b < 0n ? q - 1n : q;\n};\n\n/** ceil(a / b) with the same sign convention as {@link floorDiv}. */\nconst ceilDiv = (a: bigint, b: bigint): bigint => -floorDiv(-a, b);\n\nconst asFeeParts = (fee: TFractionLike): [bigint, bigint] => {\n const f = Fraction.asFraction(fee);\n if (f.lt(Fraction.ZERO) || f.gte(Fraction.ONE))\n throw new Error(\"fee must be in [0, 1)\");\n return [BigInt(f.numerator), BigInt(f.denominator)];\n};\n\n// ─── Invariant polynomials (`ss_math.ak`) ───────────────────────────────────\n\n/**\n * `f(D)` from the on-chain `liquidity_invariant`. It is ≤ 0 when `D` is at or\n * below the curve. All arguments are rated and scaled.\n */\nexport const invariantF = (\n x: bigint,\n y: bigint,\n amp: bigint,\n d: bigint,\n): bigint => {\n const sixteenAxy = 16n * amp * x * y;\n return sixteenAxy * d + d * d * d - (sixteenAxy * (x + y) + 4n * x * y * d);\n};\n\n/**\n * `g(y)` from the on-chain `exchange_invariant`. It is ≥ 0 when the pool keeps\n * enough of the taken asset. `g` is symmetric in `x` and `y`, which is what\n * lets {@link getSwapInput} reuse the same solver in the other direction.\n */\nexport const invariantG = (\n x: bigint,\n y: bigint,\n amp: bigint,\n d: bigint,\n): bigint => {\n const fourXy = 4n * x * y;\n return fourXy * (4n * amp * (x + y) + d) - fourXy * 4n * amp * d - d * d * d;\n};\n\n// ─── D ──────────────────────────────────────────────────────────────────────\n\n/** The ±1 fix-up that turns a Newton result into the exact largest `D`. */\nconst fixD = (x: bigint, y: bigint, amp: bigint, d0: bigint): bigint => {\n let d = d0;\n while (invariantF(x, y, amp, d) > 0n) d -= 1n;\n while (invariantF(x, y, amp, d + 1n) <= 0n) d += 1n;\n return d;\n};\n\n/**\n * The sum invariant `D` of a two-asset v4 stableswap pool: the largest integer\n * that satisfies the curve on the rated, scaled reserves. Newton from\n * `D = x + y`, then a ±1 fix-up.\n *\n * `D` carries the `rate · CALC_PRECISION` scale — it is not a token amount.\n *\n * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.\n * @param aReserve The pool's reserve of asset A, in token units.\n * @param bReserve The pool's reserve of asset B, in token units.\n * @param rateA The pool's rate for asset A (from the stableswap config).\n * @param rateB The pool's rate for asset B.\n */\nexport const getD = (\n amp: bigint,\n aReserve: bigint,\n bReserve: bigint,\n rateA = 1n,\n rateB = 1n,\n): bigint => {\n if (amp <= 0n) throw new Error(\"amplification must be positive\");\n if (aReserve < 0n || bReserve < 0n)\n throw new Error(\"reserves must be non-negative\");\n if (rateA <= 0n || rateB <= 0n) throw new Error(\"rates must be positive\");\n\n const xs = aReserve * rateA * CALC_PRECISION;\n const ys = bReserve * rateB * CALC_PRECISION;\n const sum = xs + ys;\n // An empty pool has no invariant to solve; `D` is zero by definition.\n if (sum === 0n) return 0n;\n // Exactly one empty reserve is a different case, and it has no answer: the\n // Newton step divides by `4·xs·ys`, which is zero here. The chain never\n // reaches this state — Create requires both reserves positive, and the\n // exchange invariant leaves at least one unit on the output side — but this\n // is a client library, so it reports the bad input instead of faulting on a\n // native division by zero.\n if (xs === 0n || ys === 0n) {\n throw new Error(\n \"getD: a stableswap pool cannot hold exactly one empty reserve; D is undefined there\",\n );\n }\n\n const ann = 4n * amp;\n let d = sum;\n for (let i = 0; i < NEWTON_MAX_ITERATIONS; i++) {\n const dP = floorDiv(d * d * d, 4n * xs * ys);\n const dNext = floorDiv((ann * sum + 2n * dP) * d, (ann - 1n) * d + 3n * dP);\n const diff = dNext > d ? dNext - d : d - dNext;\n d = dNext;\n if (diff <= 1n) return fixD(xs, ys, amp, d);\n }\n throw new Error(\"getD: reached the Newton iteration cap\");\n};\n\n// ─── The exchange solver ────────────────────────────────────────────────────\n\n/**\n * The smallest scaled `y` with `g(xs, y) ≥ 0` at the pre-swap `d`: the least\n * the pool may keep of the counterparty asset and still sit on the curve.\n * Newton on `y`, then a walk to the exact integer — the walk leaves `y` with\n * `g(y) ≥ 0` and `g(y − 1) < 0`, so it is the boundary and not merely a valid\n * point.\n *\n * Read the direction carefully, because the two framings invert. A caller\n * turning this into an output computes `raw = Y − y`, so the SMALLEST `y` is\n * the LARGEST output the invariant admits. It is still not generous: `y` is a\n * ceiling on the exact real-valued requirement, so `raw` is at or below the\n * exact output. Most the curve allows, never more than it allows.\n *\n * Both swap directions use this: `g` is symmetric, so solving for the\n * counterparty reserve answers \"what must the other side hold\" whichever side\n * is given.\n */\nconst solveCounterparty = (amp: bigint, d: bigint, xs: bigint): bigint => {\n if (xs <= 0n)\n throw new Error(\"solveCounterparty: the given side must be positive\");\n const ann = 4n * amp;\n // n = 2: c = D³ / (4·x·Ann), b = x + D / Ann. Evaluated in the same order as\n // the Aiken source so the intermediate floors match.\n const c = floorDiv(floorDiv(d * d, 2n * xs) * d, 2n * ann);\n const b = xs + floorDiv(d, ann);\n let y = d;\n let converged = false;\n for (let i = 0; i < NEWTON_MAX_ITERATIONS; i++) {\n const yNext = floorDiv(y * y + c, 2n * y + b - d);\n const diff = yNext > y ? yNext - y : y - yNext;\n y = yNext;\n if (diff <= 1n) {\n converged = true;\n break;\n }\n }\n if (!converged)\n throw new Error(\"solveCounterparty: reached the Newton iteration cap\");\n while (invariantG(xs, y, amp, d) < 0n) y += 1n;\n while (invariantG(xs, y - 1n, amp, d) >= 0n) y -= 1n;\n return y;\n};\n\n/**\n * The raw swap output: the scaled, pre-fee amount the curve releases when the\n * given reserve becomes `inAfter` and the taken reserve starts at `outBefore`,\n * at the pre-swap `d`. It is the largest such amount the exchange invariant\n * admits — one unit more leaves the pool below the curve — and it is at or\n * below the exact real-valued output, so it never over-quotes.\n *\n * The result carries the `rate · CALC_PRECISION` scale. Divide by\n * `rateOut · CALC_PRECISION` to get the gross output in token units.\n *\n * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.\n * @param d The pre-swap sum invariant, from {@link getD}.\n * @param inAfter The given asset's reserve after the input arrives, in token units.\n * @param outBefore The taken asset's reserve before the swap, in token units.\n * @param rateIn The pool's rate for the given asset.\n * @param rateOut The pool's rate for the taken asset.\n */\nexport const getRawSwap = (\n amp: bigint,\n d: bigint,\n inAfter: bigint,\n outBefore: bigint,\n rateIn = 1n,\n rateOut = 1n,\n): bigint => {\n if (amp <= 0n) throw new Error(\"amplification must be positive\");\n if (d <= 0n) throw new Error(\"the sum invariant must be positive\");\n if (inAfter <= 0n || outBefore <= 0n)\n throw new Error(\"reserves must be positive\");\n if (rateIn <= 0n || rateOut <= 0n) throw new Error(\"rates must be positive\");\n const xs = inAfter * rateIn * CALC_PRECISION;\n return outBefore * rateOut * CALC_PRECISION - solveCounterparty(amp, d, xs);\n};\n\n// ─── Marginal price ─────────────────────────────────────────────────────────\n\n/**\n * The marginal price of the curve at the current reserves, as input units per\n * output unit. Differentiating the invariant at fixed `D` gives\n *\n * −dy/dx = (16A·x²y² + D³·y) / (16A·x²y² + D³·x)\n *\n * in scaled value units, which converts to token units through the rates. At a\n * balanced pool (`x = y`) it reduces to `rateOut / rateIn`, the par price.\n */\nconst marginalInputPerOutput = (\n amp: bigint,\n xs: bigint,\n ys: bigint,\n d: bigint,\n rateIn: bigint,\n rateOut: bigint,\n): Fraction => {\n const common = 16n * amp * xs * xs * ys * ys;\n const cube = d * d * d;\n return new Fraction(\n rateOut * (common + cube * xs),\n rateIn * (common + cube * ys),\n );\n};\n\n/**\n * The pool's marginal price as raw units of asset A per raw unit of asset B.\n * Use it where a constant-product pool would use the reserve ratio: for a\n * stableswap pool the reserve ratio is not the price, because the curve holds\n * the price near par across a wide band of ratios.\n *\n * The result is in raw (undecimalized) units. Adjust for the two assets'\n * decimals before display.\n */\nexport const getPrice = (\n amp: bigint,\n aReserve: bigint,\n bReserve: bigint,\n rateA = 1n,\n rateB = 1n,\n): Fraction => {\n if (aReserve <= 0n || bReserve <= 0n)\n throw new Error(\"reserves must be positive\");\n const d = getD(amp, aReserve, bReserve, rateA, rateB);\n const xs = aReserve * rateA * CALC_PRECISION;\n const ys = bReserve * rateB * CALC_PRECISION;\n // A per B: asset B is the output side of the derivative.\n return marginalInputPerOutput(amp, xs, ys, d, rateA, rateB);\n};\n\n// ─── Swap output ────────────────────────────────────────────────────────────\n\n/**\n * Calculate the swap outcome for a v4 stableswap pool.\n *\n * The sequence the chain enforces (`ss_check.ak`, tag 3):\n *\n * 1. `D` is derived from the pre-swap reserves at the pool's rates.\n * 2. `raw` is the LARGEST scaled output the exchange invariant admits at that\n * `D`, with the input already added to the given reserve. The solver pins\n * the pool's remaining taken reserve `y` to the SMALLEST integer that still\n * satisfies the invariant, and `raw = outBefore·rateOut·P − y`, so the two\n * statements are the same one: least reserve kept, most output released.\n * 3. `gross = floor(raw / (rateOut · CALC_PRECISION))` is the output in token units.\n * 4. `fee = ceil(gross · feeRate)` stays in the pool.\n * 5. The trader receives `gross − fee`.\n *\n * Every rounding step favours the pool, never the trader: `y` is a ceiling on\n * the exact reserve the curve requires, `gross` floors to whole tokens, and the\n * fee ceilings. A trader is never quoted more than the chain will pay.\n *\n * @param outputMetadata Metadata for the **taken** asset. The fee is denominated in it.\n * @param input The amount of the given asset being swapped.\n * @param inputReserve The pool's reserve of the given asset.\n * @param outputReserve The pool's reserve of the taken asset.\n * @param rateIn The pool's rate for the given asset (from the stableswap config).\n * @param rateOut The pool's rate for the taken asset.\n * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.\n * @param fee The swap fee rate, applied to the gross output.\n * @returns The swap details in the shared {@link TSwapOutcome} shape.\n */\nexport const getSwapOutput = (\n outputMetadata: IAssetAmountMetadata,\n input: bigint,\n inputReserve: bigint,\n outputReserve: bigint,\n rateIn: bigint,\n rateOut: bigint,\n amp: bigint,\n fee: TFractionLike,\n): TSwapOutcome => {\n if (input <= 0n || inputReserve <= 0n || outputReserve <= 0n)\n throw new Error(\"Input and reserves must be positive\");\n if (rateIn <= 0n || rateOut <= 0n) throw new Error(\"Rates must be positive\");\n if (amp <= 0n) throw new Error(\"Amplification must be positive\");\n const [feeNum, feeDen] = asFeeParts(fee);\n\n const d = getD(amp, inputReserve, outputReserve, rateIn, rateOut);\n const raw = getRawSwap(\n amp,\n d,\n inputReserve + input,\n outputReserve,\n rateIn,\n rateOut,\n );\n const gross = floorDiv(raw, rateOut * CALC_PRECISION);\n const lpFee = ceilDiv(gross * feeNum, feeDen);\n const output = gross - lpFee;\n if (output <= 0n)\n throw new Error(\"Swap yields no output after the fee; raise the input\");\n\n const nextInputReserve = inputReserve + input;\n const nextOutputReserve = outputReserve - output;\n\n // Price impact against the pool's marginal price before the swap, the same\n // definition concentrated liquidity uses. Constant sum measures against its\n // par price instead, because a constant-sum pool has no other price. A\n // stableswap pool does: its marginal price moves with the reserve ratio, so\n // measuring against par would report the pool's standing imbalance as this\n // trade's impact. The fee is excluded so the number is curve slippage only.\n const xs = inputReserve * rateIn * CALC_PRECISION;\n const ys = outputReserve * rateOut * CALC_PRECISION;\n const idealPrice = marginalInputPerOutput(amp, xs, ys, d, rateIn, rateOut);\n const actualPrice = new Fraction(input, gross);\n const priceImpact = Fraction.ONE.subtract(idealPrice.divide(actualPrice));\n\n return {\n input,\n output,\n lpFee: new AssetAmount(lpFee, outputMetadata),\n nextInputReserve,\n nextOutputReserve,\n priceImpact,\n };\n};\n\n// ─── Swap input (the reverse) ───────────────────────────────────────────────\n\n/**\n * Calculate the minimal input that buys a given output — the inverse of\n * {@link getSwapOutput}.\n *\n * The steps:\n *\n * 1. Invert the fee: the smallest gross output that nets `output` after\n * `ceil(gross · feeRate)` is `ceil(output · feeDen / (feeDen − feeNum))`.\n * 2. Turn that gross into the scaled reserve the pool may keep of the taken\n * asset.\n * 3. Solve the exchange invariant in the other direction for the given\n * reserve that supports it. `g` is symmetric, so this is the same solver.\n * 4. Convert to token units and walk down to the minimum, which absorbs the\n * integer rounding of step 3.\n *\n * @param outputMetadata Metadata for the **taken** asset. The fee is denominated in it.\n * @param output The wanted amount of the taken asset.\n * @param inputReserve The pool's reserve of the given asset.\n * @param outputReserve The pool's reserve of the taken asset.\n * @param rateIn The pool's rate for the given asset (from the stableswap config).\n * @param rateOut The pool's rate for the taken asset.\n * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.\n * @param fee The swap fee rate, applied to the gross output.\n * @returns The swap details in the shared {@link TSwapOutcome} shape.\n */\nexport const getSwapInput = (\n outputMetadata: IAssetAmountMetadata,\n output: bigint,\n inputReserve: bigint,\n outputReserve: bigint,\n rateIn: bigint,\n rateOut: bigint,\n amp: bigint,\n fee: TFractionLike,\n): TSwapOutcome => {\n if (output <= 0n || inputReserve <= 0n || outputReserve <= 0n)\n throw new Error(\"Output and reserves must be positive\");\n if (rateIn <= 0n || rateOut <= 0n) throw new Error(\"Rates must be positive\");\n if (amp <= 0n) throw new Error(\"Amplification must be positive\");\n if (output >= outputReserve)\n throw new Error(\"Output must be below the output reserve\");\n const [feeNum, feeDen] = asFeeParts(fee);\n\n // 1. Smallest gross whose post-fee remainder reaches `output`.\n // gross − ceil(gross·n/d) = floor(gross·(d−n)/d), which is ≥ output\n // exactly when gross ≥ ceil(output·d / (d−n)).\n const grossMin = ceilDiv(output * feeDen, feeDen - feeNum);\n if (grossMin >= outputReserve)\n throw new Error(\n \"Output is not reachable: the fee pushes it past the reserve\",\n );\n\n const d = getD(amp, inputReserve, outputReserve, rateIn, rateOut);\n const scaleIn = rateIn * CALC_PRECISION;\n const scaleOut = rateOut * CALC_PRECISION;\n\n // 2. The most the pool may keep of the taken asset and still pay `grossMin`.\n const yTarget = (outputReserve - grossMin) * scaleOut;\n if (yTarget <= 0n)\n throw new Error(\"Output is not reachable within the pool's reserves\");\n\n // 3. + 4. The scaled given reserve that supports it, then token units.\n const xNeeded = solveCounterparty(amp, d, yTarget);\n const xBefore = inputReserve * scaleIn;\n let input = ceilDiv(xNeeded - xBefore, scaleIn);\n if (input < 1n) input = 1n;\n\n const grossOf = (dx: bigint): bigint =>\n floorDiv(\n getRawSwap(amp, d, inputReserve + dx, outputReserve, rateIn, rateOut),\n scaleOut,\n );\n\n let steps = 0;\n while (grossOf(input) < grossMin) {\n input += 1n;\n if (++steps > CORRECTION_MAX_STEPS)\n throw new Error(\"getSwapInput: the correction walk did not converge\");\n }\n while (input > 1n && grossOf(input - 1n) >= grossMin) {\n input -= 1n;\n if (++steps > CORRECTION_MAX_STEPS)\n throw new Error(\"getSwapInput: the correction walk did not converge\");\n }\n\n const gross = grossOf(input);\n const lpFee = ceilDiv(gross * feeNum, feeDen);\n\n const nextInputReserve = inputReserve + input;\n const nextOutputReserve = outputReserve - output;\n\n const xs = xBefore;\n const ys = outputReserve * scaleOut;\n const idealPrice = marginalInputPerOutput(amp, xs, ys, d, rateIn, rateOut);\n const actualPrice = new Fraction(input, gross);\n const priceImpact = Fraction.ONE.subtract(idealPrice.divide(actualPrice));\n\n return {\n input,\n output,\n lpFee: new AssetAmount(lpFee, outputMetadata),\n nextInputReserve,\n nextOutputReserve,\n priceImpact,\n };\n};\n\n// ─── Deposits ───────────────────────────────────────────────────────────────\n\n/**\n * The deposit target a holder of `offered` can cover, and everything pinned to\n * it. This is the target-pinned rule the v4 module enforces (`ss_check.ak`,\n * tag 6), and it is the same shape constant sum uses, stated on `D` instead of\n * on the value sum `V`:\n *\n * - the fill declares a `D` delta `t`;\n * - every reserve moves by `ceil(r_i · t / D_before)`;\n * - `total_lp` becomes `floor(lp_before · (D_before + t) / D_before)`.\n *\n * The largest `t` a holder can cover is capped by the scarcest offered asset,\n * `t = min_i floor(offered_i · D_before / r_i)`. Anything above the pinned\n * deltas is refunded. Every pool asset must be offered, or `t` is zero and the\n * deposit can never fill.\n *\n * The rates do not enter the deltas: a proportional move is proportional in\n * any units. They enter only through `D`.\n */\nexport const calculatePinnedDeposit = (\n offered: bigint[],\n reserves: bigint[],\n totalLp: bigint,\n d: bigint,\n): {\n targetDeltaD: bigint;\n deltas: bigint[];\n generatedLp: bigint;\n nextTotalLp: bigint;\n shareAfterDeposit: Fraction;\n} => {\n if (offered.length !== reserves.length)\n throw new Error(\"offered and reserves must be aligned\");\n if (totalLp <= 0n) throw new Error(\"Not enough pool liquidity\");\n if (d <= 0n) throw new Error(\"Pool sum invariant is zero\");\n if (offered.some((o) => o < 0n))\n throw new Error(\"Deposit amounts must be non-negative\");\n\n let t: bigint | undefined;\n for (let i = 0; i < offered.length; i++) {\n if (reserves[i] <= 0n) continue;\n const cap = floorDiv(offered[i] * d, reserves[i]);\n t = t === undefined || cap < t ? cap : t;\n }\n if (t === undefined || t <= 0n)\n throw new Error(\n \"A stableswap deposit must offer every pool asset in proportion (asymmetric deposits are disallowed on-chain)\",\n );\n\n const deltas = reserves.map((r) => ceilDiv(r * t, d));\n const nextTotalLp = floorDiv(totalLp * (d + t), d);\n const generatedLp = nextTotalLp - totalLp;\n if (generatedLp <= 0n) throw new Error(\"Deposit mints zero LP\");\n\n return {\n targetDeltaD: t,\n deltas,\n generatedLp,\n nextTotalLp,\n shareAfterDeposit: SharedPoolMath.getShare(generatedLp, nextTotalLp),\n };\n};\n\n/**\n * Calculate the deposit parameters for a two-asset v4 stableswap pool through\n * the target-pinned rule (see {@link calculatePinnedDeposit}). Surplus above\n * the pinned deltas comes back as `aChange` / `bChange`, the way the scoop\n * refunds it on-chain.\n *\n * @param a The amount of token A to deposit.\n * @param b The amount of token B to deposit.\n * @param aReserve The pool's reserve of token A.\n * @param bReserve The pool's reserve of token B.\n * @param totalLp The pool's total LP supply before the deposit.\n * @param rateA The pool's rate for token A (from the stableswap config).\n * @param rateB The pool's rate for token B.\n * @param amp The pool's `linear_amplification` (`A`), the raw stored integer.\n */\nexport const calculateLiquidity = (\n a: bigint,\n b: bigint,\n aReserve: bigint,\n bReserve: bigint,\n totalLp: bigint,\n rateA: bigint,\n rateB: bigint,\n amp: bigint,\n) => {\n if (aReserve <= 0n || bReserve <= 0n)\n throw new Error(\"Not enough pool liquidity\");\n const d = getD(amp, aReserve, bReserve, rateA, rateB);\n const { deltas, generatedLp, nextTotalLp, shareAfterDeposit } =\n calculatePinnedDeposit([a, b], [aReserve, bReserve], totalLp, d);\n\n return {\n nextTotalLp,\n generatedLp,\n shareAfterDeposit,\n aChange: a - deltas[0],\n bChange: b - deltas[1],\n actualDepositedA: deltas[0],\n actualDepositedB: deltas[1],\n };\n};\n\n/**\n * The per-asset amounts a pinned deposit needs when the user anchors on one\n * asset's amount: `t` from the anchor (`floor(anchor · D / r_anchor)`), every\n * other delta ceil-pinned to it. Drives proportional auto-fill in deposit\n * forms.\n */\nexport const pinnedDepositFromAnchor = (\n anchorIndex: number,\n anchorAmount: bigint,\n reserves: bigint[],\n d: bigint,\n): bigint[] => {\n if (anchorIndex < 0 || anchorIndex >= reserves.length)\n throw new Error(\"anchor index is out of range\");\n if (reserves[anchorIndex] <= 0n)\n throw new Error(\"anchor asset has no reserve\");\n if (d <= 0n) throw new Error(\"Pool sum invariant is zero\");\n if (anchorAmount <= 0n) return reserves.map(() => 0n);\n\n const t = floorDiv(anchorAmount * d, reserves[anchorIndex]);\n return reserves.map((r, i) =>\n i === anchorIndex ? anchorAmount : ceilDiv(r * t, d),\n );\n};\n"],"mappings":"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","ignoreList":[]}
@@ -2,5 +2,6 @@ export * as ConcentratedLiquidityPool from "./ConcentratedLiquidityPool.js";
2
2
  export * as ConstantProductPool from "./ConstantProductPool.js";
3
3
  export * as ConstantSumPool from "./ConstantSumPool.js";
4
4
  export * as StableSwapsPool from "./StableSwapsPool.js";
5
+ export * as V4StableswapPool from "./V4StableswapPool.js";
5
6
  export * as SharedPoolMath from "./SharedPoolMath.js";
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ConcentratedLiquidityPool","ConstantProductPool","ConstantSumPool","StableSwapsPool","SharedPoolMath"],"sources":["../../../src/PoolMath/index.ts"],"sourcesContent":["export * as ConcentratedLiquidityPool from \"./ConcentratedLiquidityPool.js\";\nexport * as ConstantProductPool from \"./ConstantProductPool.js\";\nexport * as ConstantSumPool from \"./ConstantSumPool.js\";\nexport * as StableSwapsPool from \"./StableSwapsPool.js\";\nexport * as SharedPoolMath from \"./SharedPoolMath.js\";\n"],"mappings":"AAAA,OAAO,KAAKA,yBAAyB,MAAM,gCAAgC;AAC3E,OAAO,KAAKC,mBAAmB,MAAM,0BAA0B;AAC/D,OAAO,KAAKC,eAAe,MAAM,sBAAsB;AACvD,OAAO,KAAKC,eAAe,MAAM,sBAAsB;AACvD,OAAO,KAAKC,cAAc,MAAM,qBAAqB","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["ConcentratedLiquidityPool","ConstantProductPool","ConstantSumPool","StableSwapsPool","V4StableswapPool","SharedPoolMath"],"sources":["../../../src/PoolMath/index.ts"],"sourcesContent":["export * as ConcentratedLiquidityPool from \"./ConcentratedLiquidityPool.js\";\nexport * as ConstantProductPool from \"./ConstantProductPool.js\";\nexport * as ConstantSumPool from \"./ConstantSumPool.js\";\nexport * as StableSwapsPool from \"./StableSwapsPool.js\";\nexport * as V4StableswapPool from \"./V4StableswapPool.js\";\nexport * as SharedPoolMath from \"./SharedPoolMath.js\";\n"],"mappings":"AAAA,OAAO,KAAKA,yBAAyB,MAAM,gCAAgC;AAC3E,OAAO,KAAKC,mBAAmB,MAAM,0BAA0B;AAC/D,OAAO,KAAKC,eAAe,MAAM,sBAAsB;AACvD,OAAO,KAAKC,eAAe,MAAM,sBAAsB;AACvD,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB;AACzD,OAAO,KAAKC,cAAc,MAAM,qBAAqB","ignoreList":[]}