@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,214 +1,709 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
7
|
-
exports.getSwapOutput = exports.
|
|
6
|
+
exports.upperEdge = exports.uniformLadder = exports.swapSteps = exports.shapedLadder = exports.reservesAt = exports.prefixes = exports.nextBand = exports.minCreateCounter = exports.maxDxInBand = exports.liquidityOf = exports.indexLadder = exports.getSwapOutput = exports.findWitnessInBand = exports.findWitness = exports.feeFor = exports.delta = exports.coeffB = exports.coeffA = exports.buildIndex = exports.bandWeights = exports.bandView = exports.bandStates = exports.bandProof = exports.bandOutput = exports.bandOf = exports.bandFee = exports.bandCapacity = exports.bSat = exports.aSat = exports.MIN_COUNTER_PER_WEIGHT = exports.FIXED_ONE = void 0;
|
|
8
7
|
var _asset = require("@sundaeswap/asset");
|
|
9
8
|
var _fraction = require("@sundaeswap/fraction");
|
|
10
|
-
|
|
11
|
-
function
|
|
12
|
-
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
13
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
9
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
10
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
14
11
|
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
15
12
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
16
|
-
function
|
|
17
|
-
function
|
|
13
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
16
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
17
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
18
18
|
/**
|
|
19
|
-
* Holds the calculated outcome of a concentrated-liquidity swap.
|
|
20
|
-
*
|
|
21
|
-
* swap-outcome consumers.
|
|
19
|
+
* Holds the calculated outcome of a concentrated-liquidity swap. It has the
|
|
20
|
+
* same shape as `ConstantProductPool.TSwapOutcome`, so the generic
|
|
21
|
+
* swap-outcome consumers accept it.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
/** A sqrt-price bound from the CL config, as an exact rational `[num, den]`. */
|
|
25
|
-
|
|
26
24
|
/**
|
|
27
|
-
* Concentrated liquidity
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* pool's liquidity `
|
|
25
|
+
* Concentrated liquidity: Sundae v4's ladder invariant.
|
|
26
|
+
*
|
|
27
|
+
* A pool is a ladder of bands in ascending sqrt-price order. Each band is a
|
|
28
|
+
* concentrated-liquidity arc (`curve` 0) or a constant-sum bin (`curve` 1)
|
|
29
|
+
* with its own share of the pool's liquidity (`weight / weightTotal`) and
|
|
30
|
+
* its own two fees. LP is one fungible token for the whole ladder; a
|
|
31
|
+
* deposit or withdrawal scales every band.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* The chain never stores the price. It stores reserves, and the state is a
|
|
34
|
+
* WITNESS `(X, k)`: the ladder counter `X` (the pool's `total_lp`, spec V12)
|
|
35
|
+
* and the active band `k`, which the reserves must prove. Everything here
|
|
36
|
+
* mirrors `lib/modules/cl_check.ak` and the scooper's ladder
|
|
37
|
+
* math, rounding included; where they disagree the chain wins
|
|
38
|
+
* and the transaction fails. The rounding is not negotiable:
|
|
35
39
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* - `L_k` floors; saturation ceils, in ONE division;
|
|
41
|
+
* - the band fee ceils, per step, on the step's real input;
|
|
42
|
+
* - the prefix sums come from the LADDER INDEX (cumulative coefficients
|
|
43
|
+
* in fixed point at 2^64, spec §2.3), multiplied by the counter and
|
|
44
|
+
* ceiled ONCE. A per-band sum of ceilings is a different number and the
|
|
45
|
+
* chain rejects it.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/** An exact rational `num / den`. */
|
|
49
|
+
|
|
50
|
+
/** One band of the ladder. */
|
|
51
|
+
|
|
52
|
+
/** A ladder: bands, the top edge, and the sum of the weights. */
|
|
53
|
+
|
|
54
|
+
/** The witness the chain checks: the ladder counter and the active band. */
|
|
55
|
+
|
|
56
|
+
/** 2^64, the fixed-point scale of the ladder index. */
|
|
57
|
+
var FIXED_ONE = exports.FIXED_ONE = 1n << 64n;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The least counter per unit of weight a ladder may be created with.
|
|
61
|
+
* Mirrors `cl_check.min_counter_per_weight` (sundae-v4 #229, SV4-007):
|
|
62
|
+
* Create requires `total_lp >= MIN_COUNTER_PER_WEIGHT * weightTotal`.
|
|
63
|
+
*/
|
|
64
|
+
var MIN_COUNTER_PER_WEIGHT = exports.MIN_COUNTER_PER_WEIGHT = 1000n;
|
|
65
|
+
|
|
66
|
+
/** The least counter (initial `total_lp`) the chain accepts at Create for `ladder`. */
|
|
67
|
+
var minCreateCounter = exports.minCreateCounter = function minCreateCounter(ladder) {
|
|
68
|
+
return MIN_COUNTER_PER_WEIGHT * ladder.weightTotal;
|
|
69
|
+
};
|
|
70
|
+
var ceilDiv = function ceilDiv(n, d) {
|
|
71
|
+
return (n + d - 1n) / d;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** Band i's upper sqrt-price edge. */
|
|
75
|
+
var upperEdge = exports.upperEdge = function upperEdge(l, i) {
|
|
76
|
+
return i + 1 < l.bands.length ? l.bands[i + 1].start : l.closing;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** `D_i = hi.num·lo.den − lo.num·hi.den`: positive exactly when the edges increase. */
|
|
80
|
+
var delta = exports.delta = function delta(lo, hi) {
|
|
81
|
+
return hi.num * lo.den - lo.num * hi.den;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** `a_sat_i = ceil(w_i·X·D_i / (W·lo.num·hi.num))`: the A a band above the price holds. */
|
|
85
|
+
var aSat = exports.aSat = function aSat(l, x, i) {
|
|
86
|
+
var lo = l.bands[i].start;
|
|
87
|
+
var hi = upperEdge(l, i);
|
|
88
|
+
return ceilDiv(l.bands[i].weight * x * delta(lo, hi), l.weightTotal * lo.num * hi.num);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** `b_sat_i = ceil(w_i·X·D_i / (W·lo.den·hi.den))`: the B a band below the price holds. */
|
|
92
|
+
var bSat = exports.bSat = function bSat(l, x, i) {
|
|
93
|
+
var lo = l.bands[i].start;
|
|
94
|
+
var hi = upperEdge(l, i);
|
|
95
|
+
return ceilDiv(l.bands[i].weight * x * delta(lo, hi), l.weightTotal * lo.den * hi.den);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** `L_k = floor(w_k·X / W)`. */
|
|
99
|
+
var liquidityOf = exports.liquidityOf = function liquidityOf(l, x, k) {
|
|
100
|
+
return l.bands[k].weight * x / l.weightTotal;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** Band i's A-side saturation coefficient: the part of `a_sat_i` independent of X, rounded up. */
|
|
104
|
+
var coeffA = exports.coeffA = function coeffA(l, i) {
|
|
105
|
+
var lo = l.bands[i].start;
|
|
106
|
+
var hi = upperEdge(l, i);
|
|
107
|
+
return ceilDiv(l.bands[i].weight * delta(lo, hi) * FIXED_ONE, l.weightTotal * lo.num * hi.num);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** The same on the B side. */
|
|
111
|
+
var coeffB = exports.coeffB = function coeffB(l, i) {
|
|
112
|
+
var lo = l.bands[i].start;
|
|
113
|
+
var hi = upperEdge(l, i);
|
|
114
|
+
return ceilDiv(l.bands[i].weight * delta(lo, hi) * FIXED_ONE, l.weightTotal * lo.den * hi.den);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/** One index entry: the cumulative coefficients above and below band k. */
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The ladder index `Create` pins and every spend reads: entry k is the pair
|
|
121
|
+
* of cumulative coefficients for the bands strictly above and strictly
|
|
122
|
+
* below band k. `cl_check.build_index`, to the digit.
|
|
40
123
|
*/
|
|
124
|
+
var buildIndex = exports.buildIndex = function buildIndex(l) {
|
|
125
|
+
var n = l.bands.length;
|
|
126
|
+
var a = Array.from({
|
|
127
|
+
length: n
|
|
128
|
+
}, function (_, i) {
|
|
129
|
+
return coeffA(l, i);
|
|
130
|
+
});
|
|
131
|
+
var b = Array.from({
|
|
132
|
+
length: n
|
|
133
|
+
}, function (_, i) {
|
|
134
|
+
return coeffB(l, i);
|
|
135
|
+
});
|
|
136
|
+
var out = [];
|
|
137
|
+
for (var k = 0; k < n; k++) {
|
|
138
|
+
var ca = 0n;
|
|
139
|
+
var cb = 0n;
|
|
140
|
+
for (var i = 0; i < n; i++) {
|
|
141
|
+
if (i > k) ca += a[i];
|
|
142
|
+
if (i < k) cb += b[i];
|
|
143
|
+
}
|
|
144
|
+
out.push({
|
|
145
|
+
ca: ca,
|
|
146
|
+
cb: cb
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A ladder with its index built once. Every probe of the witness search
|
|
154
|
+
* reads the index for every band, and rebuilding it per probe is most of
|
|
155
|
+
* the cost of a quote.
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
var indexLadder = exports.indexLadder = function indexLadder(l) {
|
|
159
|
+
return "index" in l && Array.isArray(l.index) ? l : _objectSpread(_objectSpread({}, l), {}, {
|
|
160
|
+
index: buildIndex(l)
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/** `C_A`, `C_B` at (X, k) from the index: one ceiling after the multiply by X. */
|
|
165
|
+
var prefixes = exports.prefixes = function prefixes(l, x, k) {
|
|
166
|
+
var e = l.index[k];
|
|
167
|
+
return {
|
|
168
|
+
ca: ceilDiv(e.ca * x, FIXED_ONE),
|
|
169
|
+
cb: ceilDiv(e.cb * x, FIXED_ONE)
|
|
170
|
+
};
|
|
171
|
+
};
|
|
41
172
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return [BigInt(f.numerator), BigInt(f.denominator)];
|
|
173
|
+
/** `cl_check.f_at_raw`: the CL arc, cross-multiplied. */
|
|
174
|
+
var fAt = function fAt(a, b, L, lo, hi) {
|
|
175
|
+
return (a * hi.num + L * hi.den) * (b * lo.den + L * lo.num) - L * L * hi.num * lo.den;
|
|
46
176
|
};
|
|
47
177
|
|
|
178
|
+
/** G at a witness, on whichever curve the active band carries. */
|
|
179
|
+
var gAt = function gAt(l, A, B, x, k) {
|
|
180
|
+
var _prefixes = prefixes(l, x, k),
|
|
181
|
+
ca = _prefixes.ca,
|
|
182
|
+
cb = _prefixes.cb;
|
|
183
|
+
var lo = l.bands[k].start;
|
|
184
|
+
var hi = upperEdge(l, k);
|
|
185
|
+
var ra = A - ca;
|
|
186
|
+
var rb = B - cb;
|
|
187
|
+
var L = liquidityOf(l, x, k);
|
|
188
|
+
return l.bands[k].curve === 0 ? fAt(ra, rb, L, lo, hi) : ra * (lo.num * hi.num) + rb * (lo.den * hi.den) - L * delta(lo, hi);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** The band proof, P1 through P7. The chain runs exactly this. */
|
|
192
|
+
var bandProof = exports.bandProof = function bandProof(l, A, B, x, k) {
|
|
193
|
+
if (x < l.weightTotal) return false; // P1
|
|
194
|
+
var _prefixes2 = prefixes(l, x, k),
|
|
195
|
+
ca = _prefixes2.ca,
|
|
196
|
+
cb = _prefixes2.cb;
|
|
197
|
+
var ra = A - ca;
|
|
198
|
+
var rb = B - cb;
|
|
199
|
+
if (ra < 0n || rb < 0n) return false; // P2, P3
|
|
200
|
+
if (ra > aSat(l, x, k) || rb > bSat(l, x, k)) return false; // P4, P5
|
|
201
|
+
if (gAt(l, A, B, x, k) < 0n) return false; // P6
|
|
202
|
+
return gAt(l, A, B, x + 1n, k) < 0n; // P7
|
|
203
|
+
};
|
|
204
|
+
var SCAN = 400n;
|
|
205
|
+
var CAP = 1n << 80n;
|
|
206
|
+
|
|
48
207
|
/**
|
|
49
|
-
* The
|
|
50
|
-
*
|
|
51
|
-
*
|
|
208
|
+
* The witness in band k, seeded at `hint` when given: the counter only
|
|
209
|
+
* grows along a swap, so the doubling search starts from the previous
|
|
210
|
+
* witness. `lo` is the largest x with non-negative residuals and G ≥ 0,
|
|
211
|
+
* and a witness is such an x whose successor fails G, so the scan walks
|
|
212
|
+
* DOWN from `lo` first. Mirrors the scooper's `find_witness_in_band_from`.
|
|
52
213
|
*/
|
|
53
|
-
var
|
|
54
|
-
var
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
214
|
+
var findWitnessInBand = exports.findWitnessInBand = function findWitnessInBand(l, A, B, k, hint) {
|
|
215
|
+
var ok = function ok(x) {
|
|
216
|
+
var _prefixes3 = prefixes(l, x, k),
|
|
217
|
+
ca = _prefixes3.ca,
|
|
218
|
+
cb = _prefixes3.cb;
|
|
219
|
+
return A - ca >= 0n && B - cb >= 0n && gAt(l, A, B, x, k) >= 0n;
|
|
220
|
+
};
|
|
221
|
+
var search = function search(start) {
|
|
222
|
+
var lo = start;
|
|
223
|
+
var span = 1n;
|
|
224
|
+
var hi = lo + span;
|
|
225
|
+
while (ok(hi) && hi < CAP) {
|
|
226
|
+
lo = hi;
|
|
227
|
+
span *= 2n;
|
|
228
|
+
hi = lo + span;
|
|
229
|
+
}
|
|
230
|
+
while (lo + 1n < hi) {
|
|
231
|
+
var m = (lo + hi) / 2n;
|
|
232
|
+
if (ok(m)) lo = m;else hi = m;
|
|
233
|
+
}
|
|
234
|
+
var from = lo > SCAN ? lo - SCAN : 1n;
|
|
235
|
+
for (var x = lo; x >= from; x--) {
|
|
236
|
+
if (bandProof(l, A, B, x, k)) return {
|
|
237
|
+
x: x,
|
|
238
|
+
k: k
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
for (var _x = lo + 1n; _x <= lo + SCAN; _x++) {
|
|
242
|
+
if (bandProof(l, A, B, _x, k)) return {
|
|
243
|
+
x: _x,
|
|
244
|
+
k: k
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
};
|
|
249
|
+
if (hint !== undefined && hint > 0n && ok(hint)) {
|
|
250
|
+
var w = search(hint);
|
|
251
|
+
if (w) return w;
|
|
63
252
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (denom <= 0n) return 0n;
|
|
67
|
-
return va0 * dvbEff / denom;
|
|
253
|
+
if (!ok(1n)) return null;
|
|
254
|
+
return search(1n);
|
|
68
255
|
};
|
|
69
256
|
|
|
70
257
|
/**
|
|
71
|
-
*
|
|
258
|
+
* The witness `(X, k)` for a pool's reserves. A hint (the indexed
|
|
259
|
+
* `bandCounter` / `activeBand`) is checked first and seeds the search in
|
|
260
|
+
* its band; a stale hint falls back to every band. Null when the reserves
|
|
261
|
+
* sit on no point of the ladder — the chain would reject them too.
|
|
262
|
+
*/
|
|
263
|
+
var findWitness = exports.findWitness = function findWitness(ladder, A, B, hint) {
|
|
264
|
+
var l = indexLadder(ladder);
|
|
265
|
+
if (hint && hint.k >= 0 && hint.k < l.bands.length) {
|
|
266
|
+
if (bandProof(l, A, B, hint.x, hint.k)) return hint;
|
|
267
|
+
var w = findWitnessInBand(l, A, B, hint.k, hint.x);
|
|
268
|
+
if (w) return w;
|
|
269
|
+
}
|
|
270
|
+
for (var k = 0; k < l.bands.length; k++) {
|
|
271
|
+
var _w = findWitnessInBand(l, A, B, k);
|
|
272
|
+
if (_w) return _w;
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/** The active band's view at a witness: its residual reserves and liquidity. */
|
|
278
|
+
|
|
279
|
+
var bandView = exports.bandView = function bandView(l, A, B, w) {
|
|
280
|
+
var _prefixes4 = prefixes(l, w.x, w.k),
|
|
281
|
+
ca = _prefixes4.ca,
|
|
282
|
+
cb = _prefixes4.cb;
|
|
283
|
+
var band = l.bands[w.k];
|
|
284
|
+
return {
|
|
285
|
+
ra: A - ca,
|
|
286
|
+
rb: B - cb,
|
|
287
|
+
l: liquidityOf(l, w.x, w.k),
|
|
288
|
+
lo: band.start,
|
|
289
|
+
hi: upperEdge(l, w.k),
|
|
290
|
+
curve: band.curve,
|
|
291
|
+
feeBuy: band.feeBuy,
|
|
292
|
+
feeSell: band.feeSell
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/** The band's fee for a direction: `feeSell` when A is sold, `feeBuy` when A is bought. */
|
|
297
|
+
var feeFor = exports.feeFor = function feeFor(v, isAInput) {
|
|
298
|
+
return isAInput ? v.feeSell : v.feeBuy;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* The band fee on a step's real input `dx`: `ceil(dx * num / den)`.
|
|
303
|
+
* Mirrors `cl_check.band_fee` (sundae-v4 #228, SV4-006). The chain charges
|
|
304
|
+
* it per transcript step and rounds it UP, so a step below `den / num`
|
|
305
|
+
* units still pays one unit. A zero rate charges nothing.
|
|
306
|
+
*/
|
|
307
|
+
var bandFee = exports.bandFee = function bandFee(dx, fee) {
|
|
308
|
+
return (dx * fee.num + fee.den - 1n) / fee.den;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/** The output one band pays for `dx` within itself. Mirrors the scooper's `band_output`. */
|
|
312
|
+
var bandOutput = exports.bandOutput = function bandOutput(v, isAInput, dx) {
|
|
313
|
+
var fee = feeFor(v, isAInput);
|
|
314
|
+
if (fee.den <= 0n) return 0n;
|
|
315
|
+
var dxEff = dx - bandFee(dx, fee);
|
|
316
|
+
if (dxEff <= 0n) return 0n;
|
|
317
|
+
if (v.curve === 0) {
|
|
318
|
+
var va0 = v.ra * v.hi.num + v.l * v.hi.den;
|
|
319
|
+
var vb0 = v.rb * v.lo.den + v.l * v.lo.num;
|
|
320
|
+
if (isAInput) {
|
|
321
|
+
var dva = dxEff * v.hi.num;
|
|
322
|
+
var _den = (va0 + dva) * v.lo.den;
|
|
323
|
+
return _den === 0n ? 0n : vb0 * dva / _den;
|
|
324
|
+
}
|
|
325
|
+
var dvb = dxEff * v.lo.den;
|
|
326
|
+
var den = (vb0 + dvb) * v.hi.num;
|
|
327
|
+
return den === 0n ? 0n : va0 * dvb / den;
|
|
328
|
+
}
|
|
329
|
+
var pn = v.lo.num * v.hi.num;
|
|
330
|
+
var pd = v.lo.den * v.hi.den;
|
|
331
|
+
return isAInput ? dxEff * pn / pd : dxEff * pd / pn;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/** What the band can pay out in a direction: its residual of the output asset. */
|
|
335
|
+
var bandCapacity = exports.bandCapacity = function bandCapacity(v, isAInput) {
|
|
336
|
+
return isAInput ? v.rb : v.ra;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/** The largest input whose output stays within the band. */
|
|
340
|
+
var maxDxInBand = exports.maxDxInBand = function maxDxInBand(v, isAInput) {
|
|
341
|
+
var cap = bandCapacity(v, isAInput);
|
|
342
|
+
if (cap <= 0n) return 0n;
|
|
343
|
+
var fits = function fits(dx) {
|
|
344
|
+
return bandOutput(v, isAInput, dx) <= cap;
|
|
345
|
+
};
|
|
346
|
+
var hi = 1n;
|
|
347
|
+
for (var guard = 0; fits(hi); guard++) {
|
|
348
|
+
hi *= 2n;
|
|
349
|
+
if (guard > 200) return hi;
|
|
350
|
+
}
|
|
351
|
+
var lo = 0n;
|
|
352
|
+
while (lo + 1n < hi) {
|
|
353
|
+
var m = (lo + hi) / 2n;
|
|
354
|
+
if (fits(m)) lo = m;else hi = m;
|
|
355
|
+
}
|
|
356
|
+
return lo;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/** The next band a fill enters: selling A walks down the ladder, buying A walks up. */
|
|
360
|
+
var nextBand = exports.nextBand = function nextBand(k, isAInput, n) {
|
|
361
|
+
return isAInput ? k === 0 ? null : k - 1 : k + 1 >= n ? null : k + 1;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/** One band of a fill: what it paid, and the state it left. */
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Price `dx` across as many bands as it takes, as the scooper's
|
|
368
|
+
* `swap_steps` does: fill the active band, cross to the next, repeat.
|
|
369
|
+
* Returns the steps, or null when the ladder cannot absorb `dx` (the
|
|
370
|
+
* direction is exhausted, or the remainder pays nothing).
|
|
371
|
+
*/
|
|
372
|
+
var swapSteps = exports.swapSteps = function swapSteps(ladder, A, B, isAInput, dx, hint) {
|
|
373
|
+
var l = indexLadder(ladder);
|
|
374
|
+
var n = l.bands.length;
|
|
375
|
+
var ca = A;
|
|
376
|
+
var cb = B;
|
|
377
|
+
var remaining = dx;
|
|
378
|
+
var steps = [];
|
|
379
|
+
var known = findWitness(l, A, B, hint);
|
|
380
|
+
if (!known) return null;
|
|
381
|
+
var prefer = null;
|
|
382
|
+
var hintX;
|
|
383
|
+
for (var guard = 0; remaining > 0n; guard++) {
|
|
384
|
+
if (guard > 2 * n + 2) return null;
|
|
385
|
+
var before = known;
|
|
386
|
+
known = null;
|
|
387
|
+
if (!before) {
|
|
388
|
+
if (prefer !== null) before = findWitnessInBand(l, ca, cb, prefer, hintX);
|
|
389
|
+
if (!before) before = findWitness(l, ca, cb);
|
|
390
|
+
if (!before) return null;
|
|
391
|
+
}
|
|
392
|
+
var view = bandView(l, ca, cb, before);
|
|
393
|
+
var capDx = maxDxInBand(view, isAInput);
|
|
394
|
+
var stepDx = capDx < remaining ? capDx : remaining;
|
|
395
|
+
var dy = stepDx > 0n ? bandOutput(view, isAInput, stepDx) : 0n;
|
|
396
|
+
if (dy <= 0n) {
|
|
397
|
+
var kNext = nextBand(before.k, isAInput, n);
|
|
398
|
+
if (kNext === null || prefer === kNext) return null;
|
|
399
|
+
prefer = kNext;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
var na = isAInput ? ca + stepDx : ca - dy;
|
|
403
|
+
var nb = isAInput ? cb - dy : cb + stepDx;
|
|
404
|
+
var crossing = stepDx < remaining;
|
|
405
|
+
var after = null;
|
|
406
|
+
if (crossing) {
|
|
407
|
+
var _kNext = nextBand(before.k, isAInput, n);
|
|
408
|
+
if (_kNext !== null) after = findWitnessInBand(l, na, nb, _kNext, before.x);
|
|
409
|
+
} else {
|
|
410
|
+
after = findWitnessInBand(l, na, nb, before.k, before.x);
|
|
411
|
+
}
|
|
412
|
+
if (!after) after = findWitness(l, na, nb);
|
|
413
|
+
if (!after || after.x < before.x) return null;
|
|
414
|
+
steps.push({
|
|
415
|
+
band: before.k,
|
|
416
|
+
input: stepDx,
|
|
417
|
+
output: dy,
|
|
418
|
+
after: after
|
|
419
|
+
});
|
|
420
|
+
remaining -= stepDx;
|
|
421
|
+
ca = na;
|
|
422
|
+
cb = nb;
|
|
423
|
+
prefer = after.k;
|
|
424
|
+
hintX = after.x;
|
|
425
|
+
known = after;
|
|
426
|
+
}
|
|
427
|
+
return steps;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* The swap output of a concentrated-liquidity pool for `input` of one asset, crossing
|
|
432
|
+
* bands as needed. `aReserve`/`bReserve` are the pool's reserves in
|
|
433
|
+
* canonical order; `hint` is the indexed `bandCounter` / `activeBand`,
|
|
434
|
+
* which seeds the witness search and may be stale.
|
|
72
435
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* @param bReserve The pool's raw reserve of asset B.
|
|
77
|
-
* @param totalLp The pool's total LP supply (the virtual liquidity `L`).
|
|
78
|
-
* @param sqrtPriceA The lower sqrt-price bound `[num, den]` from the CL config.
|
|
79
|
-
* @param sqrtPriceB The upper sqrt-price bound `[num, den]` from the CL config.
|
|
80
|
-
* @param fee The liquidity-provider fee (plus protocol fee) as a fraction.
|
|
81
|
-
* @param isAInput True when asset A is the supplied asset (A→B swap).
|
|
82
|
-
* @returns The swap details in the shared {@link TSwapOutcome} shape.
|
|
436
|
+
* The `lpFee` is the fee the bands kept of the input, summed across the
|
|
437
|
+
* steps. `priceImpact` is against the active band's opening marginal
|
|
438
|
+
* price. Throws when the ladder cannot absorb the input.
|
|
83
439
|
*/
|
|
84
|
-
var getSwapOutput = exports.getSwapOutput = function getSwapOutput(inputMetadata, input, aReserve, bReserve,
|
|
85
|
-
if (input <= 0n || aReserve
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
440
|
+
var getSwapOutput = exports.getSwapOutput = function getSwapOutput(inputMetadata, input, aReserve, bReserve, ladder, isAInput, hint) {
|
|
441
|
+
if (input <= 0n || aReserve < 0n || bReserve < 0n) throw new Error("Input must be positive and reserves non-negative");
|
|
442
|
+
if (ladder.bands.length === 0 || ladder.weightTotal <= 0n) throw new Error("The ladder has no bands");
|
|
443
|
+
var l = indexLadder(ladder);
|
|
444
|
+
var steps = swapSteps(l, aReserve, bReserve, isAInput, input, hint);
|
|
445
|
+
if (!steps || steps.length === 0) throw new Error("The ladder cannot absorb this input in this direction");
|
|
446
|
+
var output = 0n;
|
|
447
|
+
var lpFee = 0n;
|
|
448
|
+
var _iterator = _createForOfIteratorHelper(steps),
|
|
449
|
+
_step;
|
|
450
|
+
try {
|
|
451
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
452
|
+
var s = _step.value;
|
|
453
|
+
output += s.output;
|
|
454
|
+
var band = l.bands[s.band];
|
|
455
|
+
var fee = isAInput ? band.feeSell : band.feeBuy;
|
|
456
|
+
lpFee += bandFee(s.input, fee);
|
|
457
|
+
}
|
|
458
|
+
} catch (err) {
|
|
459
|
+
_iterator.e(err);
|
|
460
|
+
} finally {
|
|
461
|
+
_iterator.f();
|
|
462
|
+
}
|
|
97
463
|
var inputReserve = isAInput ? aReserve : bReserve;
|
|
98
464
|
var outputReserve = isAInput ? bReserve : aReserve;
|
|
99
|
-
var output = rawSwapOutput(input, aReserve, bReserve, totalLp, spaNum, spaDen, spbNum, spbDen, feeNum, feeDen, isAInput);
|
|
100
|
-
// Can't take more than the pool actually holds (virtual reserves can
|
|
101
|
-
// otherwise let the formula exceed the real reserve near the boundary).
|
|
102
465
|
if (output > outputReserve) output = outputReserve;
|
|
103
|
-
|
|
104
|
-
var
|
|
105
|
-
var
|
|
106
|
-
|
|
107
|
-
// Price impact vs the marginal (dx→0) rate. Marginal output-per-effective-
|
|
108
|
-
// input is vb0·spbNum/(va0·spaDen) for A→B (symmetric for B→A), so the ideal
|
|
109
|
-
// input-per-output is its reciprocal. Display-only.
|
|
110
|
-
var va0 = aReserve * spbNum + totalLp * spbDen;
|
|
111
|
-
var vb0 = bReserve * spaDen + totalLp * spaNum;
|
|
466
|
+
// Price impact against the opening marginal of the first band traded.
|
|
467
|
+
var w0 = findWitness(l, aReserve, bReserve, hint);
|
|
468
|
+
var v0 = bandView(l, aReserve, bReserve, w0);
|
|
112
469
|
var priceImpact = _fraction.Fraction.ZERO;
|
|
113
470
|
if (output > 0n) {
|
|
114
|
-
var
|
|
115
|
-
|
|
116
|
-
|
|
471
|
+
var ideal;
|
|
472
|
+
if (v0.curve === 1) {
|
|
473
|
+
var pn = v0.lo.num * v0.hi.num;
|
|
474
|
+
var pd = v0.lo.den * v0.hi.den;
|
|
475
|
+
ideal = isAInput ? new _fraction.Fraction(pd, pn) : new _fraction.Fraction(pn, pd);
|
|
476
|
+
} else {
|
|
477
|
+
var va0 = v0.ra * v0.hi.num + v0.l * v0.hi.den;
|
|
478
|
+
var vb0 = v0.rb * v0.lo.den + v0.l * v0.lo.num;
|
|
479
|
+
ideal = isAInput ? new _fraction.Fraction(va0 * v0.lo.den, vb0 * v0.hi.num) : new _fraction.Fraction(vb0 * v0.hi.num, va0 * v0.lo.den);
|
|
480
|
+
}
|
|
481
|
+
var actual = new _fraction.Fraction(input - lpFee, output);
|
|
482
|
+
priceImpact = _fraction.Fraction.ONE.subtract(ideal.divide(actual));
|
|
117
483
|
}
|
|
118
484
|
return {
|
|
119
485
|
input: input,
|
|
120
486
|
output: output,
|
|
121
|
-
lpFee: new _asset.AssetAmount(
|
|
122
|
-
nextInputReserve:
|
|
123
|
-
nextOutputReserve:
|
|
124
|
-
priceImpact: priceImpact
|
|
487
|
+
lpFee: new _asset.AssetAmount(lpFee, inputMetadata),
|
|
488
|
+
nextInputReserve: inputReserve + input,
|
|
489
|
+
nextOutputReserve: outputReserve - output,
|
|
490
|
+
priceImpact: priceImpact,
|
|
491
|
+
steps: steps
|
|
125
492
|
};
|
|
126
493
|
};
|
|
127
494
|
|
|
495
|
+
/** What each band holds at the pool's reserves. */
|
|
496
|
+
|
|
128
497
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* absorbs the fee floor and integer rounding so the result is the exact minimal
|
|
133
|
-
* input whose forward swap yields at least `output`.
|
|
498
|
+
* Every band's state at the pool's reserves: a band below the price holds
|
|
499
|
+
* only B, a band above it only A, the active band some of both. Null when
|
|
500
|
+
* the reserves sit on no point of the ladder.
|
|
134
501
|
*/
|
|
135
|
-
var
|
|
136
|
-
|
|
137
|
-
var
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
var
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
502
|
+
var bandStates = exports.bandStates = function bandStates(ladder, aReserve, bReserve, hint) {
|
|
503
|
+
var l = indexLadder(ladder);
|
|
504
|
+
var w = findWitness(l, aReserve, bReserve, hint);
|
|
505
|
+
if (!w) return null;
|
|
506
|
+
var out = [];
|
|
507
|
+
var restA = 0n;
|
|
508
|
+
var restB = 0n;
|
|
509
|
+
for (var i = 0; i < l.bands.length; i++) {
|
|
510
|
+
var liquidity = liquidityOf(l, w.x, i);
|
|
511
|
+
if (i < w.k) {
|
|
512
|
+
var b = bSat(l, w.x, i);
|
|
513
|
+
restB += b;
|
|
514
|
+
out.push({
|
|
515
|
+
quantities: [0n, b],
|
|
516
|
+
liquidity: liquidity,
|
|
517
|
+
active: false
|
|
518
|
+
});
|
|
519
|
+
} else if (i > w.k) {
|
|
520
|
+
var a = aSat(l, w.x, i);
|
|
521
|
+
restA += a;
|
|
522
|
+
out.push({
|
|
523
|
+
quantities: [a, 0n],
|
|
524
|
+
liquidity: liquidity,
|
|
525
|
+
active: false
|
|
526
|
+
});
|
|
527
|
+
} else {
|
|
528
|
+
out.push({
|
|
529
|
+
quantities: [0n, 0n],
|
|
530
|
+
liquidity: liquidity,
|
|
531
|
+
active: true
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
var ra = aReserve - restA;
|
|
536
|
+
var rb = bReserve - restB;
|
|
537
|
+
out[w.k].quantities = [ra < 0n ? 0n : ra, rb < 0n ? 0n : rb];
|
|
538
|
+
return out;
|
|
539
|
+
};
|
|
156
540
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (denom <= 0n) throw new Error("Output not reachable within the pool's range");
|
|
163
|
-
var dvaEffMin = ceilDiv(output * spaDen * va0, denom);
|
|
164
|
-
effMin = ceilDiv(dvaEffMin, spbNum);
|
|
165
|
-
} else {
|
|
166
|
-
var _denom2 = va0 - output * spbNum;
|
|
167
|
-
if (_denom2 <= 0n) throw new Error("Output not reachable within the pool's range");
|
|
168
|
-
var dvbEffMin = ceilDiv(output * spbNum * vb0, _denom2);
|
|
169
|
-
effMin = ceilDiv(dvbEffMin, spaNum);
|
|
541
|
+
/** Which band holds a sqrt-price. */
|
|
542
|
+
var bandOf = exports.bandOf = function bandOf(l, s) {
|
|
543
|
+
for (var i = 0; i < l.bands.length; i++) {
|
|
544
|
+
var hi = upperEdge(l, i);
|
|
545
|
+
if (s.num * hi.den < hi.num * s.den) return i;
|
|
170
546
|
}
|
|
171
|
-
|
|
547
|
+
return l.bands.length - 1;
|
|
548
|
+
};
|
|
172
549
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
550
|
+
/**
|
|
551
|
+
* The exact reserves a ladder holds at counter `x` with the price at
|
|
552
|
+
* sqrt-price `s`, which must lie in band `k`: what a pool creator deposits
|
|
553
|
+
* for a chosen shape, scale and launch price. The result is floored, so
|
|
554
|
+
* `findWitness` must still be run on it to learn the counter the chain
|
|
555
|
+
* derives — usually `x`, but rounding can move it.
|
|
556
|
+
*/
|
|
557
|
+
var reservesAt = exports.reservesAt = function reservesAt(l, x, k, s) {
|
|
558
|
+
var A = 0n;
|
|
559
|
+
var B = 0n;
|
|
560
|
+
for (var i = 0; i < l.bands.length; i++) {
|
|
561
|
+
if (i > k) {
|
|
562
|
+
A += aSat(l, x, i);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (i < k) {
|
|
566
|
+
B += bSat(l, x, i);
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
var lo = l.bands[i].start;
|
|
570
|
+
var hi = upperEdge(l, i);
|
|
571
|
+
var L = liquidityOf(l, x, i);
|
|
572
|
+
if (l.bands[i].curve === 1) {
|
|
573
|
+
// A constant-sum bin prices at the geometric mean; `s` only says how
|
|
574
|
+
// far through the bin the conversion has gone.
|
|
575
|
+
var K = L * delta(lo, hi);
|
|
576
|
+
var frac = (s.num * lo.den - lo.num * s.den) * hi.den;
|
|
577
|
+
var span = (hi.num * lo.den - lo.num * hi.den) * s.den;
|
|
578
|
+
var rb = K * frac / (span * (lo.den * hi.den));
|
|
579
|
+
A += (K - rb * (lo.den * hi.den)) / (lo.num * hi.num);
|
|
580
|
+
B += rb;
|
|
581
|
+
} else {
|
|
582
|
+
// a = L·(1/s − 1/hi), b = L·(s − lo), in exact rationals.
|
|
583
|
+
A += L * (s.den * hi.num - s.num * hi.den) / (s.num * hi.num);
|
|
584
|
+
B += L * (s.num * lo.den - lo.num * s.den) / (s.den * lo.den);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
A: A,
|
|
589
|
+
B: B
|
|
177
590
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
var
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/** A fraction in lowest terms. */
|
|
594
|
+
var reduceFrac = function reduceFrac(f) {
|
|
595
|
+
var _gcd = function gcd(a, b) {
|
|
596
|
+
return b === 0n ? a : _gcd(b, a % b);
|
|
597
|
+
};
|
|
598
|
+
var g = _gcd(f.num < 0n ? -f.num : f.num, f.den);
|
|
599
|
+
return g > 1n ? {
|
|
600
|
+
num: f.num / g,
|
|
601
|
+
den: f.den / g
|
|
602
|
+
} : f;
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* An equal-weight ladder of `n` bands from `lo` to `hi`, every band a CL
|
|
607
|
+
* arc at `fee` unless named in `curves`. The edges are built over a common
|
|
608
|
+
* denominator and reduced: every rational component of a concentrated-liquidity config
|
|
609
|
+
* must stay below 2^64 (spec V9).
|
|
610
|
+
*/
|
|
611
|
+
var uniformLadder = exports.uniformLadder = function uniformLadder(n, lo, hi, fee) {
|
|
612
|
+
var curves = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
|
|
613
|
+
if (n < 1) throw new Error("A ladder needs at least one band");
|
|
614
|
+
if (lo.num * hi.den >= hi.num * lo.den) throw new Error("The lower edge must be strictly below the upper edge");
|
|
615
|
+
var span = hi.num * lo.den - lo.num * hi.den;
|
|
616
|
+
var commonDen = lo.den * hi.den * BigInt(n);
|
|
617
|
+
var bands = [];
|
|
618
|
+
for (var i = 0; i < n; i++) {
|
|
619
|
+
var _curves$i;
|
|
620
|
+
bands.push({
|
|
621
|
+
start: reduceFrac({
|
|
622
|
+
num: lo.num * hi.den * BigInt(n) + span * BigInt(i),
|
|
623
|
+
den: commonDen
|
|
624
|
+
}),
|
|
625
|
+
weight: 1n,
|
|
626
|
+
curve: (_curves$i = curves[i]) !== null && _curves$i !== void 0 ? _curves$i : 0,
|
|
627
|
+
feeBuy: fee,
|
|
628
|
+
feeSell: fee
|
|
629
|
+
});
|
|
190
630
|
}
|
|
191
631
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
632
|
+
bands: bands,
|
|
633
|
+
closing: reduceFrac({
|
|
634
|
+
num: lo.num * hi.den * BigInt(n) + span * BigInt(n),
|
|
635
|
+
den: commonDen
|
|
636
|
+
}),
|
|
637
|
+
weightTotal: BigInt(n)
|
|
198
638
|
};
|
|
199
639
|
};
|
|
200
640
|
|
|
641
|
+
/** Band weight profiles. Every weight is at least 1, as `Create` demands. */
|
|
642
|
+
|
|
643
|
+
var bandWeights = exports.bandWeights = function bandWeights(n) {
|
|
644
|
+
var _opts$peak, _opts$tail, _opts$centre;
|
|
645
|
+
var shape = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "concentrated";
|
|
646
|
+
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
647
|
+
if (shape === "flat") return Array.from({
|
|
648
|
+
length: n
|
|
649
|
+
}, function () {
|
|
650
|
+
return 1n;
|
|
651
|
+
});
|
|
652
|
+
var peak = Number((_opts$peak = opts.peak) !== null && _opts$peak !== void 0 ? _opts$peak : 64n);
|
|
653
|
+
var tail = Number((_opts$tail = opts.tail) !== null && _opts$tail !== void 0 ? _opts$tail : 1n);
|
|
654
|
+
var centre = (_opts$centre = opts.centre) !== null && _opts$centre !== void 0 ? _opts$centre : (n - 1) / 2;
|
|
655
|
+
var sigma = Math.max(n / 5, 0.75);
|
|
656
|
+
var bell = function bell(i, c) {
|
|
657
|
+
return Math.exp(-Math.pow((i - c) / sigma, 2));
|
|
658
|
+
};
|
|
659
|
+
var profile = function profile(i) {
|
|
660
|
+
switch (shape) {
|
|
661
|
+
case "concentrated":
|
|
662
|
+
return bell(i, centre);
|
|
663
|
+
case "skewed-low":
|
|
664
|
+
return Math.exp(-i / Math.max(n / 3, 1));
|
|
665
|
+
case "skewed-high":
|
|
666
|
+
return Math.exp(-(n - 1 - i) / Math.max(n / 3, 1));
|
|
667
|
+
case "bimodal":
|
|
668
|
+
return Math.max(bell(i, (n - 1) / 4), bell(i, 3 * (n - 1) / 4));
|
|
669
|
+
default:
|
|
670
|
+
return 1;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
return Array.from({
|
|
674
|
+
length: n
|
|
675
|
+
}, function (_, i) {
|
|
676
|
+
var w = Math.round(tail + (peak - tail) * profile(i));
|
|
677
|
+
return BigInt(Math.max(1, w));
|
|
678
|
+
});
|
|
679
|
+
};
|
|
680
|
+
|
|
201
681
|
/**
|
|
202
|
-
*
|
|
203
|
-
* constant-
|
|
204
|
-
*
|
|
205
|
-
* `a1·L0 ≥ a0·L1` and `b1·L0 ≥ b0·L1` — and those two bounds multiply to
|
|
206
|
-
* exactly the on-chain CL non-swap invariant `va1·vb1·L0² ≥ va0·vb0·L1²`
|
|
207
|
-
* (`cl_check.ak`). So the proportional deposit is always chain-valid; excess of
|
|
208
|
-
* either asset is refunded via aChange/bChange. This mirrors the scooper's
|
|
209
|
-
* `resolve_cl_deposit` (which likewise reuses the CP pinning).
|
|
682
|
+
* A ladder of `n` bands from `lo` to `hi` with a weight shape, optional
|
|
683
|
+
* constant-sum bins and an optional per-band fee. The edges are
|
|
684
|
+
* `uniformLadder`'s exactly; only the weights, curves and fees differ.
|
|
210
685
|
*/
|
|
211
|
-
var
|
|
212
|
-
|
|
686
|
+
var shapedLadder = exports.shapedLadder = function shapedLadder(n, lo, hi, fee) {
|
|
687
|
+
var _opts$shape, _opts$csBands;
|
|
688
|
+
var opts = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
|
|
689
|
+
var base = uniformLadder(n, lo, hi, fee);
|
|
690
|
+
var weights = bandWeights(n, (_opts$shape = opts.shape) !== null && _opts$shape !== void 0 ? _opts$shape : "concentrated", opts);
|
|
691
|
+
var cs = new Set((_opts$csBands = opts.csBands) !== null && _opts$csBands !== void 0 ? _opts$csBands : []);
|
|
692
|
+
var bands = base.bands.map(function (b, i) {
|
|
693
|
+
var f = opts.feeAt ? opts.feeAt(i) : fee;
|
|
694
|
+
return _objectSpread(_objectSpread({}, b), {}, {
|
|
695
|
+
weight: weights[i],
|
|
696
|
+
curve: cs.has(i) ? 1 : 0,
|
|
697
|
+
feeBuy: f,
|
|
698
|
+
feeSell: f
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
return {
|
|
702
|
+
bands: bands,
|
|
703
|
+
closing: base.closing,
|
|
704
|
+
weightTotal: weights.reduce(function (s, w) {
|
|
705
|
+
return s + w;
|
|
706
|
+
}, 0n)
|
|
707
|
+
};
|
|
213
708
|
};
|
|
214
709
|
//# sourceMappingURL=ConcentratedLiquidityPool.js.map
|