@sundaeswap/math 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/PoolMath/ConcentratedLiquidityPool.js +655 -160
- package/dist/cjs/PoolMath/ConcentratedLiquidityPool.js.map +1 -1
- package/dist/esm/PoolMath/ConcentratedLiquidityPool.js +605 -137
- package/dist/esm/PoolMath/ConcentratedLiquidityPool.js.map +1 -1
- package/dist/types/PoolMath/ConcentratedLiquidityPool.d.ts +231 -45
- package/dist/types/PoolMath/ConcentratedLiquidityPool.d.ts.map +1 -1
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/PoolMath/ConcentratedLiquidityPool.ts +712 -212
- package/src/PoolMath/__tests__/ConcentratedLiquidityPool.test.ts +189 -196
|
@@ -1,225 +1,218 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { IAssetAmountMetadata } from "@sundaeswap/asset";
|
|
3
|
-
import { Fraction } from "@sundaeswap/fraction";
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
aSat,
|
|
6
|
+
bandOf,
|
|
7
|
+
bandOutput,
|
|
8
|
+
bandProof,
|
|
9
|
+
bandStates,
|
|
10
|
+
bandView,
|
|
11
|
+
buildIndex,
|
|
12
|
+
findWitness,
|
|
8
13
|
getSwapOutput,
|
|
9
|
-
|
|
14
|
+
indexLadder,
|
|
15
|
+
liquidityOf,
|
|
16
|
+
prefixes,
|
|
17
|
+
reservesAt,
|
|
18
|
+
shapedLadder,
|
|
19
|
+
swapSteps,
|
|
20
|
+
type TLadder,
|
|
21
|
+
uniformLadder,
|
|
10
22
|
} from "../ConcentratedLiquidityPool.js";
|
|
11
23
|
|
|
12
|
-
const
|
|
13
|
-
assetId: "09169bb6f5ff5b246d65d65935b2222cc53b5e677d7ed22771878972.
|
|
24
|
+
const tokenB: IAssetAmountMetadata = {
|
|
25
|
+
assetId: "09169bb6f5ff5b246d65d65935b2222cc53b5e677d7ed22771878972.744f4b454e42",
|
|
14
26
|
decimals: 0,
|
|
15
27
|
};
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
feeNum: bigint,
|
|
32
|
-
feeDen: bigint,
|
|
33
|
-
): bigint => {
|
|
34
|
-
const fee = (dx * feeNum) / feeDen;
|
|
35
|
-
const dxEff = dx - fee;
|
|
36
|
-
const va0 = a * spb[0] + l * spb[1];
|
|
37
|
-
const vb0 = b * spa[1] + l * spa[0];
|
|
38
|
-
if (isAInput) {
|
|
39
|
-
const dvaEff = dxEff * spb[0];
|
|
40
|
-
return (vb0 * dvaEff) / ((va0 + dvaEff) * spa[1]);
|
|
41
|
-
}
|
|
42
|
-
const dvbEff = dxEff * spa[0];
|
|
43
|
-
return (va0 * dvbEff) / ((vb0 + dvbEff) * spb[0]);
|
|
29
|
+
// The eight equal-weight ladder of sundae-v4's lib/tests/unit/cl_check_unit.ak
|
|
30
|
+
// (edges 1.00..1.08 at 1e6, fee 3/1000).
|
|
31
|
+
const fee = { num: 3n, den: 1000n };
|
|
32
|
+
const den = 1_000_000n;
|
|
33
|
+
const eq8: TLadder = {
|
|
34
|
+
bands: Array.from({ length: 8 }, (_, i) => ({
|
|
35
|
+
start: { num: 1_000_000n + 10_000n * BigInt(i), den },
|
|
36
|
+
weight: 1n,
|
|
37
|
+
curve: 0 as const,
|
|
38
|
+
feeBuy: fee,
|
|
39
|
+
feeSell: fee,
|
|
40
|
+
})),
|
|
41
|
+
closing: { num: 1_080_000n, den },
|
|
42
|
+
weightTotal: 8n,
|
|
44
43
|
};
|
|
44
|
+
const A = 8_637_368n;
|
|
45
|
+
const B = 624_999n;
|
|
45
46
|
|
|
46
|
-
describe("ConcentratedLiquidityPool
|
|
47
|
-
it("
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
true,
|
|
60
|
-
);
|
|
61
|
-
expect(output).toEqual(93n);
|
|
47
|
+
describe("ConcentratedLiquidityPool: the on-chain vectors", () => {
|
|
48
|
+
it("finds the witness the Aiken test names", () => {
|
|
49
|
+
const w = findWitness(eq8, A, B)!;
|
|
50
|
+
expect(w.x).toBe(999_999_813n);
|
|
51
|
+
expect(w.k).toBe(0);
|
|
52
|
+
const l = indexLadder(eq8);
|
|
53
|
+
const p = prefixes(l, w.x, w.k);
|
|
54
|
+
expect(p.ca).toBe(8_021_634n);
|
|
55
|
+
expect(p.cb).toBe(0n);
|
|
56
|
+
expect(liquidityOf(l, w.x, w.k)).toBe(124_999_976n);
|
|
57
|
+
expect(aSat(l, w.x, w.k)).toBe(1_237_624n);
|
|
58
|
+
expect(bandProof(l, A, B, 999_999_813n, 0)).toBe(true);
|
|
59
|
+
expect(bandProof(l, A, B, 71_428_457n, 7)).toBe(false);
|
|
62
60
|
});
|
|
63
61
|
|
|
64
|
-
it("
|
|
65
|
-
|
|
66
|
-
const { output } = getSwapOutput(
|
|
67
|
-
tokenA,
|
|
68
|
-
100n,
|
|
69
|
-
1000n,
|
|
70
|
-
1000n,
|
|
71
|
-
1000n,
|
|
72
|
-
spa,
|
|
73
|
-
spb,
|
|
74
|
-
zeroFee,
|
|
75
|
-
false,
|
|
76
|
-
);
|
|
77
|
-
expect(output).toEqual(48n);
|
|
62
|
+
it("finds the post-swap witness", () => {
|
|
63
|
+
expect(findWitness(eq8, 8_636_752n, 625_623n)!.x).toBe(999_999_938n);
|
|
78
64
|
});
|
|
79
65
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
spb,
|
|
91
|
-
threePct,
|
|
92
|
-
isAInput,
|
|
93
|
-
);
|
|
94
|
-
expect(output).toEqual(
|
|
95
|
-
refOutput(dx, 1_000_000n, 1_000_000n, 1_000_000n, isAInput, 3n, 1000n),
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
66
|
+
// sundae-v4 #228 (SV4-006): the band fee rounds UP, so 624 B pays a fee
|
|
67
|
+
// of ceil(624 * 3 / 1000) = 2 and the pair admits 615 A. With the floored
|
|
68
|
+
// fee of 1 it admitted 616. lib/tests/unit/cl_check_unit.ak.
|
|
69
|
+
it("pays 615 A for 624 B, the band fee rounded up to 2", () => {
|
|
70
|
+
const out = getSwapOutput(tokenB, 624n, A, B, eq8, false);
|
|
71
|
+
expect(out.output).toBe(615n);
|
|
72
|
+
expect(out.lpFee.amount).toBe(2n);
|
|
73
|
+
expect(out.steps.length).toBe(1);
|
|
74
|
+
expect(out.steps[0]!.after.x).toBe(1_000_000_062n);
|
|
75
|
+
expect(findWitness(eq8, A - 615n, B + 624n)!.x).toBe(1_000_000_062n);
|
|
99
76
|
});
|
|
100
77
|
|
|
101
|
-
it("
|
|
102
|
-
const {
|
|
103
|
-
|
|
104
|
-
10_000_000n,
|
|
105
|
-
100n,
|
|
106
|
-
5n,
|
|
107
|
-
1_000_000n,
|
|
108
|
-
spa,
|
|
109
|
-
spb,
|
|
110
|
-
zeroFee,
|
|
111
|
-
true,
|
|
112
|
-
);
|
|
113
|
-
expect(output).toBeLessThanOrEqual(5n);
|
|
78
|
+
it("recovers from a stale hint", () => {
|
|
79
|
+
const out = getSwapOutput(tokenB, 624n, A, B, eq8, false, { x: 123n, k: 5 });
|
|
80
|
+
expect(out.output).toBe(615n);
|
|
114
81
|
});
|
|
115
82
|
|
|
116
|
-
it("
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
).
|
|
120
|
-
expect(()
|
|
121
|
-
|
|
122
|
-
|
|
83
|
+
it("charges a fee of one unit on an input below den / num", () => {
|
|
84
|
+
const l = indexLadder(eq8);
|
|
85
|
+
const v = bandView(l, A, B, findWitness(l, A, B)!);
|
|
86
|
+
// ceil(1 * 3 / 1000) = 1, so one unit of input has no effective input.
|
|
87
|
+
expect(bandOutput(v, false, 1n)).toBe(0n);
|
|
88
|
+
// ceil(333 * 3 / 1000) = 1: dx_eff = 332.
|
|
89
|
+
const free = { ...v, feeBuy: { num: 0n, den: 1n } };
|
|
90
|
+
expect(bandOutput(v, false, 333n)).toBe(bandOutput(free, false, 332n));
|
|
91
|
+
// ceil(1000 * 3 / 1000) = 3 exactly: no extra unit when it divides.
|
|
92
|
+
expect(bandOutput(v, false, 1_000n)).toBe(bandOutput(free, false, 997n));
|
|
93
|
+
// A zero rate still charges nothing, whatever its denominator.
|
|
94
|
+
const zero = { ...v, feeBuy: { num: 0n, den: 1_000n } };
|
|
95
|
+
expect(bandOutput(zero, false, 1_000n)).toBe(bandOutput(free, false, 1_000n));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("crosses bands for an input larger than the active band", () => {
|
|
99
|
+
const l = indexLadder(eq8);
|
|
100
|
+
const w = findWitness(l, A, B)!;
|
|
101
|
+
// Band 0 holds a_sat_0 of A; buying more than that crosses into band 1.
|
|
102
|
+
const steps = swapSteps(l, A, B, false, 2_000_000n, w)!;
|
|
103
|
+
expect(steps.length).toBeGreaterThan(1);
|
|
104
|
+
expect(steps[0]!.band).toBe(0);
|
|
105
|
+
expect(steps[1]!.band).toBe(1);
|
|
106
|
+
const total = steps.reduce((s, st) => s + st.output, 0n);
|
|
107
|
+
expect(total).toBeGreaterThan(1_237_624n);
|
|
108
|
+
expect(total).toBeLessThan(A);
|
|
123
109
|
});
|
|
124
|
-
});
|
|
125
110
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
targetOut,
|
|
133
|
-
1_000_000n,
|
|
134
|
-
1_000_000n,
|
|
135
|
-
1_000_000n,
|
|
136
|
-
spa,
|
|
137
|
-
spb,
|
|
138
|
-
threePct,
|
|
139
|
-
isAInput,
|
|
140
|
-
);
|
|
141
|
-
const got = getSwapOutput(
|
|
142
|
-
tokenA,
|
|
143
|
-
input,
|
|
144
|
-
1_000_000n,
|
|
145
|
-
1_000_000n,
|
|
146
|
-
1_000_000n,
|
|
147
|
-
spa,
|
|
148
|
-
spb,
|
|
149
|
-
threePct,
|
|
150
|
-
isAInput,
|
|
151
|
-
).output;
|
|
152
|
-
// Reaches the target...
|
|
153
|
-
expect(got).toBeGreaterThanOrEqual(targetOut);
|
|
154
|
-
// ...and is minimal: one unit less falls short.
|
|
155
|
-
const less = getSwapOutput(
|
|
156
|
-
tokenA,
|
|
157
|
-
input - 1n,
|
|
158
|
-
1_000_000n,
|
|
159
|
-
1_000_000n,
|
|
160
|
-
1_000_000n,
|
|
161
|
-
spa,
|
|
162
|
-
spb,
|
|
163
|
-
threePct,
|
|
164
|
-
isAInput,
|
|
165
|
-
).output;
|
|
166
|
-
expect(less).toBeLessThan(targetOut);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
111
|
+
it("refuses an input the ladder cannot absorb", () => {
|
|
112
|
+
// The price sits in band 0: selling A pays out band 0's B residual and
|
|
113
|
+
// then has no band below to walk into.
|
|
114
|
+
const small = getSwapOutput(tokenB, 1_000n, A, B, eq8, true);
|
|
115
|
+
expect(small.output).toBeGreaterThan(0n);
|
|
116
|
+
expect(() => getSwapOutput(tokenB, 1_000_000_000n, A, B, eq8, true)).toThrow();
|
|
169
117
|
});
|
|
170
118
|
|
|
171
|
-
it("
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
).
|
|
119
|
+
it("splits the reserves across the bands exactly", () => {
|
|
120
|
+
const states = bandStates(eq8, A, B)!;
|
|
121
|
+
expect(states.length).toBe(8);
|
|
122
|
+
expect(states[0]!.active).toBe(true);
|
|
123
|
+
const sumA = states.reduce((s, b) => s + b.quantities[0], 0n);
|
|
124
|
+
const sumB = states.reduce((s, b) => s + b.quantities[1], 0n);
|
|
125
|
+
expect(sumA).toBe(A);
|
|
126
|
+
expect(sumB).toBe(B);
|
|
127
|
+
for (let i = 1; i < 8; i++) expect(states[i]!.quantities[1]).toBe(0n);
|
|
175
128
|
});
|
|
176
129
|
});
|
|
177
130
|
|
|
178
|
-
describe("ConcentratedLiquidityPool
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// [offeredA, offeredB, reserveA, reserveB, totalLp]
|
|
201
|
-
[100n, 100n, 1000n, 1000n, 1000n],
|
|
202
|
-
[100n, 250n, 1000n, 1000n, 1000n], // B surplus
|
|
203
|
-
[500n, 100n, 3333n, 991n, 1777n], // coprime-ish reserves
|
|
204
|
-
[12345n, 67890n, 1_000_000n, 5_000_000n, 2_000_000n],
|
|
205
|
-
];
|
|
206
|
-
for (const [a, b, ra, rb, lp] of cases) {
|
|
207
|
-
const res = calculateLiquidity(a, b, ra, rb, lp);
|
|
208
|
-
expect(res.generatedLp).toBeGreaterThan(0n);
|
|
209
|
-
// Never consumes more than offered.
|
|
210
|
-
expect(res.actualDepositedA).toBeLessThanOrEqual(a);
|
|
211
|
-
expect(res.actualDepositedB).toBeLessThanOrEqual(b);
|
|
212
|
-
// The resulting deposit is chain-valid under the CL invariant.
|
|
213
|
-
expect(
|
|
214
|
-
invariantHolds(
|
|
215
|
-
ra,
|
|
216
|
-
rb,
|
|
217
|
-
lp,
|
|
218
|
-
res.actualDepositedA,
|
|
219
|
-
res.actualDepositedB,
|
|
220
|
-
res.generatedLp,
|
|
221
|
-
),
|
|
222
|
-
).toBe(true);
|
|
223
|
-
}
|
|
131
|
+
describe("ConcentratedLiquidityPool: building a ladder", () => {
|
|
132
|
+
it("derives reserves at a launch price that the ladder then witnesses", () => {
|
|
133
|
+
const ladder = shapedLadder(8, { num: 95n, den: 100n }, { num: 105n, den: 100n }, fee, {
|
|
134
|
+
shape: "concentrated",
|
|
135
|
+
});
|
|
136
|
+
expect(ladder.weightTotal).toBe(ladder.bands.reduce((s, b) => s + b.weight, 0n));
|
|
137
|
+
const launch = { num: 1n, den: 1n };
|
|
138
|
+
const k = bandOf(ladder, launch);
|
|
139
|
+
const r = reservesAt(ladder, 1_000_000_000n, k, launch);
|
|
140
|
+
const w = findWitness(ladder, r.A, r.B)!;
|
|
141
|
+
expect(w).not.toBeNull();
|
|
142
|
+
// The reserves are floored, so the band the chain derives can sit one
|
|
143
|
+
// below the one asked for; a creator takes the witness's band.
|
|
144
|
+
expect(Math.abs(w.k - k)).toBeLessThanOrEqual(1);
|
|
145
|
+
expect(buildIndex(ladder).length).toBe(8);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("keeps uniform edges in lowest terms and refuses an inverted range", () => {
|
|
149
|
+
const u = uniformLadder(4, { num: 1n, den: 2n }, { num: 2n, den: 1n }, fee);
|
|
150
|
+
expect(u.bands[0]!.start).toEqual({ num: 1n, den: 2n });
|
|
151
|
+
expect(u.closing).toEqual({ num: 2n, den: 1n });
|
|
152
|
+
expect(() => uniformLadder(4, { num: 2n, den: 1n }, { num: 1n, den: 2n }, fee)).toThrow();
|
|
224
153
|
});
|
|
225
154
|
});
|
|
155
|
+
|
|
156
|
+
// The scaling bench of sundae-v4 validators/benches/scaling/cl.ak after #228
|
|
157
|
+
// (the band fee rounded up): for N equal-weight bands with edges
|
|
158
|
+
// 1.00 + 0.01*j and fee 3/1000, a B-in swap of 0.1% of the B reserve.
|
|
159
|
+
// Each row is (N, A, B, X, k, A', B', X', budget); the chain admits A - A'.
|
|
160
|
+
const benchRows: [number, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint][] = [
|
|
161
|
+
[1, 4925865n, 4999999n, 999999826n, 0n, 4920931n, 5004998n, 1000001381n, 1555n],
|
|
162
|
+
[2, 2414759n, 7500000n, 999999999n, 1n, 2407502n, 7507500n, 1000002313n, 2314n],
|
|
163
|
+
[3, 4782629n, 5000000n, 999999899n, 1n, 4777791n, 5005000n, 1000001399n, 1500n],
|
|
164
|
+
[4, 3517823n, 6249999n, 999999799n, 2n, 3511894n, 6256248n, 1000001620n, 1821n],
|
|
165
|
+
[5, 4645760n, 4999999n, 999999749n, 2n, 4641017n, 5004998n, 1000001269n, 1520n],
|
|
166
|
+
[6, 3797891n, 5833333n, 999999799n, 3n, 3792463n, 5839166n, 1000001701n, 1902n],
|
|
167
|
+
[7, 4514876n, 5000000n, 999999766n, 3n, 4510223n, 5005000n, 1000001333n, 1567n],
|
|
168
|
+
[8, 3876484n, 5624999n, 999999799n, 4n, 3871350n, 5630623n, 1000001551n, 1752n],
|
|
169
|
+
[9, 4389622n, 4999999n, 999999674n, 4n, 4385059n, 5004998n, 1000001303n, 1629n],
|
|
170
|
+
[10, 3877638n, 5500000n, 999999786n, 5n, 3872713n, 5505500n, 1000001599n, 1813n],
|
|
171
|
+
[11, 4269672n, 5000000n, 999999879n, 5n, 4265194n, 5005000n, 1000001446n, 1567n],
|
|
172
|
+
[12, 3842499n, 5416666n, 999999799n, 6n, 3837740n, 5422082n, 1000001599n, 1800n],
|
|
173
|
+
[13, 4154721n, 4999999n, 999999649n, 6n, 4150328n, 5004998n, 1000001383n, 1734n],
|
|
174
|
+
[14, 3788541n, 5357142n, 999999799n, 7n, 3783921n, 5362499n, 1000001599n, 1800n],
|
|
175
|
+
[15, 4044489n, 5000000n, 999999848n, 7n, 4040176n, 5005000n, 1000001445n, 1597n],
|
|
176
|
+
[16, 3724376n, 5312499n, 999999799n, 8n, 3719878n, 5317811n, 1000001399n, 1600n],
|
|
177
|
+
[17, 3938713n, 4999999n, 999999712n, 8n, 3934480n, 5004998n, 1000001387n, 1675n],
|
|
178
|
+
[18, 3654687n, 5277777n, 999999643n, 9n, 3650300n, 5283054n, 1000001395n, 1752n],
|
|
179
|
+
[19, 3837151n, 5000000n, 999999903n, 9n, 3832994n, 5005000n, 1000001455n, 1552n],
|
|
180
|
+
[20, 3582202n, 5249999n, 999999799n, 10n, 3577917n, 5255248n, 1000001399n, 1600n],
|
|
181
|
+
[21, 3739575n, 4999999n, 999999692n, 10n, 3735494n, 5004998n, 1000001309n, 1617n],
|
|
182
|
+
[22, 3508584n, 5227272n, 999999799n, 11n, 3504393n, 5232499n, 1000001399n, 1600n],
|
|
183
|
+
[23, 3645776n, 5000000n, 999999854n, 11n, 3641767n, 5005000n, 1000001380n, 1526n],
|
|
184
|
+
[24, 3434886n, 5208333n, 999999754n, 12n, 3430785n, 5213541n, 1000001588n, 1834n],
|
|
185
|
+
[25, 3555555n, 5000000n, 999999791n, 12n, 3551617n, 5005000n, 1000001445n, 1654n],
|
|
186
|
+
[26, 3361787n, 5192307n, 999999655n, 13n, 3357770n, 5197499n, 1000001522n, 1867n],
|
|
187
|
+
[27, 3468729n, 4999999n, 999999792n, 13n, 3464861n, 5004998n, 1000001295n, 1503n],
|
|
188
|
+
[28, 3289730n, 5178571n, 999999799n, 14n, 3285794n, 5183749n, 1000001614n, 1815n],
|
|
189
|
+
[29, 3385125n, 4999999n, 999999735n, 14n, 3381324n, 5004998n, 1000001272n, 1537n],
|
|
190
|
+
[30, 3219003n, 5166666n, 999999799n, 15n, 3215144n, 5171832n, 1000001599n, 1800n],
|
|
191
|
+
[31, 3304583n, 4999999n, 999999653n, 15n, 3300848n, 5004998n, 1000001389n, 1736n],
|
|
192
|
+
[32, 3149791n, 5156250n, 999999799n, 16n, 3146005n, 5161406n, 1000001553n, 1754n],
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
const benchLadder = (n: number): TLadder => ({
|
|
196
|
+
bands: Array.from({ length: n }, (_, i) => ({
|
|
197
|
+
start: { num: 1_000_000n + 10_000n * BigInt(i), den },
|
|
198
|
+
weight: 1n,
|
|
199
|
+
curve: 0 as const,
|
|
200
|
+
feeBuy: fee,
|
|
201
|
+
feeSell: fee,
|
|
202
|
+
})),
|
|
203
|
+
closing: { num: 1_000_000n + 10_000n * BigInt(n), den },
|
|
204
|
+
weightTotal: BigInt(n),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe("ConcentratedLiquidityPool: the scaling bench vectors", () => {
|
|
208
|
+
for (const [n, a, b, x, k, a1, b1] of benchRows) {
|
|
209
|
+
it(`pays ${a - a1} A for ${b1 - b} B on ${n} bands`, () => {
|
|
210
|
+
const ladder = benchLadder(n);
|
|
211
|
+
const w = findWitness(ladder, a, b)!;
|
|
212
|
+
expect(w.x).toBe(x);
|
|
213
|
+
expect(BigInt(w.k)).toBe(k);
|
|
214
|
+
const out = getSwapOutput(tokenB, b1 - b, a, b, ladder, false, w);
|
|
215
|
+
expect(out.output).toBe(a - a1);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|