@alcorexchange/alcor-swap-sdk 1.0.395 → 1.0.397
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/entities/pool.js +14 -21
- package/build/entities/trade.js +32 -24
- package/build/utils/fullMath.d.ts +0 -3
- package/build/utils/fullMath.js +26 -7
- package/build/utils/getBestSwapRoute.js +77 -57
- package/build/utils/sqrtPriceMath.js +21 -14
- package/build/utils/swapMath.js +28 -28
- package/build/utils/tickList.js +16 -23
- package/package.json +1 -1
- package/src/entities/pool.ts +38 -94
- package/src/entities/trade.ts +45 -42
- package/src/utils/fullMath.ts +28 -8
- package/src/utils/getBestSwapRoute.ts +89 -94
- package/src/utils/sqrtPriceMath.ts +25 -65
- package/src/utils/swapMath.ts +57 -130
- package/src/utils/tickList.ts +19 -29
package/build/entities/pool.js
CHANGED
|
@@ -198,14 +198,9 @@ let Pool = exports.Pool = /*#__PURE__*/function () {
|
|
|
198
198
|
}, {
|
|
199
199
|
key: "swap",
|
|
200
200
|
value: function swap(zeroForOne, amountSpecified, sqrtPriceLimitX64) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
(0, _tinyInvariant.default)(sqrtPriceLimitX64 < this.sqrtPriceX64, "RATIO_CURRENT");
|
|
205
|
-
} else {
|
|
206
|
-
(0, _tinyInvariant.default)(sqrtPriceLimitX64 < _tickMath.TickMath.MAX_SQRT_RATIO, "RATIO_MAX");
|
|
207
|
-
(0, _tinyInvariant.default)(sqrtPriceLimitX64 > this.sqrtPriceX64, "RATIO_CURRENT");
|
|
208
|
-
}
|
|
201
|
+
const sqrtPriceLimit = sqrtPriceLimitX64 || (zeroForOne ? _tickMath.TickMath.MIN_SQRT_RATIO + _internalConstants.ONE : _tickMath.TickMath.MAX_SQRT_RATIO - _internalConstants.ONE);
|
|
202
|
+
|
|
203
|
+
// Убираем проверки invariant в продакшене, если данные гарантированно валидны
|
|
209
204
|
const exactInput = amountSpecified >= _internalConstants.ZERO;
|
|
210
205
|
const state = {
|
|
211
206
|
amountSpecifiedRemaining: amountSpecified,
|
|
@@ -214,17 +209,17 @@ let Pool = exports.Pool = /*#__PURE__*/function () {
|
|
|
214
209
|
tick: this.tickCurrent,
|
|
215
210
|
liquidity: this.liquidity
|
|
216
211
|
};
|
|
217
|
-
while (state.amountSpecifiedRemaining !== _internalConstants.ZERO && state.sqrtPriceX64
|
|
218
|
-
const step = {
|
|
219
|
-
|
|
212
|
+
while (state.amountSpecifiedRemaining !== _internalConstants.ZERO && state.sqrtPriceX64 !== sqrtPriceLimit) {
|
|
213
|
+
const step = {
|
|
214
|
+
sqrtPriceStartX64: state.sqrtPriceX64
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Оптимизация вызова nextInitializedTickWithinOneWord
|
|
220
218
|
[step.tickNext, step.initialized] = this.tickDataProvider.nextInitializedTickWithinOneWord(state.tick, zeroForOne, this.tickSpacing);
|
|
221
|
-
|
|
222
|
-
step.tickNext = _tickMath.TickMath.MIN_TICK;
|
|
223
|
-
} else if (step.tickNext > _tickMath.TickMath.MAX_TICK) {
|
|
224
|
-
step.tickNext = _tickMath.TickMath.MAX_TICK;
|
|
225
|
-
}
|
|
219
|
+
step.tickNext = Math.max(_tickMath.TickMath.MIN_TICK, Math.min(step.tickNext, _tickMath.TickMath.MAX_TICK));
|
|
226
220
|
step.sqrtPriceNextX64 = _tickMath.TickMath.getSqrtRatioAtTick(step.tickNext);
|
|
227
|
-
|
|
221
|
+
const targetPrice = (zeroForOne ? step.sqrtPriceNextX64 < sqrtPriceLimit : step.sqrtPriceNextX64 > sqrtPriceLimit) ? sqrtPriceLimit : step.sqrtPriceNextX64;
|
|
222
|
+
[state.sqrtPriceX64, step.amountIn, step.amountOut, step.feeAmount] = _swapMath.SwapMath.computeSwapStep(state.sqrtPriceX64, targetPrice, state.liquidity, state.amountSpecifiedRemaining, this.fee);
|
|
228
223
|
if (exactInput) {
|
|
229
224
|
state.amountSpecifiedRemaining = state.amountSpecifiedRemaining - (step.amountIn + step.feeAmount);
|
|
230
225
|
state.amountCalculated = state.amountCalculated - step.amountOut;
|
|
@@ -243,11 +238,9 @@ let Pool = exports.Pool = /*#__PURE__*/function () {
|
|
|
243
238
|
state.tick = _tickMath.TickMath.getTickAtSqrtRatio(state.sqrtPriceX64);
|
|
244
239
|
}
|
|
245
240
|
}
|
|
246
|
-
const amountA = zeroForOne == exactInput ? amountSpecified - state.amountSpecifiedRemaining : state.amountCalculated;
|
|
247
|
-
const amountB = zeroForOne == exactInput ? state.amountCalculated : amountSpecified - state.amountSpecifiedRemaining;
|
|
248
241
|
return {
|
|
249
|
-
amountA,
|
|
250
|
-
amountB,
|
|
242
|
+
amountA: zeroForOne === exactInput ? amountSpecified - state.amountSpecifiedRemaining : state.amountCalculated,
|
|
243
|
+
amountB: zeroForOne === exactInput ? state.amountCalculated : amountSpecified - state.amountSpecifiedRemaining,
|
|
251
244
|
sqrtPriceX64: state.sqrtPriceX64,
|
|
252
245
|
liquidity: state.liquidity,
|
|
253
246
|
tickCurrent: state.tick
|
package/build/entities/trade.js
CHANGED
|
@@ -320,25 +320,19 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
320
320
|
let inputAmount;
|
|
321
321
|
let outputAmount;
|
|
322
322
|
if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
|
|
323
|
-
|
|
324
|
-
amounts[0] = amount;
|
|
323
|
+
amounts[0] = amount; // Переиспользуем amount напрямую
|
|
325
324
|
for (let i = 0; i < route.tokenPath.length - 1; i++) {
|
|
326
|
-
|
|
327
|
-
const outputAmount = pool.getOutputAmount(amounts[i]);
|
|
328
|
-
amounts[i + 1] = outputAmount;
|
|
325
|
+
amounts[i + 1] = route.pools[i].getOutputAmount(amounts[i]);
|
|
329
326
|
}
|
|
330
|
-
inputAmount =
|
|
331
|
-
outputAmount =
|
|
327
|
+
inputAmount = amount; // Без создания нового объекта
|
|
328
|
+
outputAmount = amounts[amounts.length - 1];
|
|
332
329
|
} else {
|
|
333
|
-
(0, _tinyInvariant.default)(amount.currency.equals(route.output), 'OUTPUT');
|
|
334
330
|
amounts[amounts.length - 1] = amount;
|
|
335
331
|
for (let i = route.tokenPath.length - 1; i > 0; i--) {
|
|
336
|
-
|
|
337
|
-
const inputAmount = pool.getInputAmount(amounts[i]);
|
|
338
|
-
amounts[i - 1] = inputAmount;
|
|
332
|
+
amounts[i - 1] = route.pools[i - 1].getInputAmount(amounts[i]);
|
|
339
333
|
}
|
|
340
|
-
inputAmount =
|
|
341
|
-
outputAmount =
|
|
334
|
+
inputAmount = amounts[0];
|
|
335
|
+
outputAmount = amount;
|
|
342
336
|
}
|
|
343
337
|
return new Trade({
|
|
344
338
|
routes: [{
|
|
@@ -496,24 +490,32 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
496
490
|
minSplits: 1,
|
|
497
491
|
maxSplits: 10
|
|
498
492
|
};
|
|
499
|
-
// TODO Need rafactor
|
|
500
493
|
(0, _tinyInvariant.default)(_routes.length > 0, 'ROUTES');
|
|
501
494
|
(0, _tinyInvariant.default)(percents.length > 0, 'PERCENTS');
|
|
502
495
|
|
|
503
|
-
//
|
|
504
|
-
const
|
|
496
|
+
// Предварительно вычисляем splitAmount для всех процентов
|
|
497
|
+
const percentToAmount = new Map();
|
|
505
498
|
for (const percent of percents) {
|
|
506
|
-
|
|
507
|
-
|
|
499
|
+
percentToAmount.set(percent, amount.multiply(percent).divide(100));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Используем Map вместо объекта для лучшей производительности
|
|
503
|
+
const percentToTrades = new Map();
|
|
504
|
+
for (const percent of percents) {
|
|
505
|
+
percentToTrades.set(percent, []);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Оптимизируем внутренний цикл - группируем вычисления по маршрутам
|
|
509
|
+
for (const route of _routes) {
|
|
510
|
+
// Для каждого маршрута проходим по всем процентам
|
|
511
|
+
for (const percent of percents) {
|
|
512
|
+
const splitAmount = percentToAmount.get(percent);
|
|
508
513
|
try {
|
|
509
514
|
const trade = Trade.fromRoute(route, splitAmount, tradeType, percent);
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
percentToTrades[percent] = [];
|
|
515
|
+
if (trade.inputAmount.greaterThan(0) && trade.priceImpact.greaterThan(0)) {
|
|
516
|
+
percentToTrades.get(percent).push(trade);
|
|
513
517
|
}
|
|
514
|
-
percentToTrades[percent].push(trade);
|
|
515
518
|
} catch (error) {
|
|
516
|
-
// not enough liquidity in this pair
|
|
517
519
|
if (error.isInsufficientReservesError || error.isInsufficientInputAmountError) {
|
|
518
520
|
continue;
|
|
519
521
|
}
|
|
@@ -521,7 +523,13 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
521
523
|
}
|
|
522
524
|
}
|
|
523
525
|
}
|
|
524
|
-
|
|
526
|
+
|
|
527
|
+
// Преобразуем Map обратно в объект для совместимости с getBestSwapRoute
|
|
528
|
+
const percentToTradesObj = {};
|
|
529
|
+
percentToTrades.forEach((trades, percent) => {
|
|
530
|
+
percentToTradesObj[percent] = trades;
|
|
531
|
+
});
|
|
532
|
+
const bestTrades = (0, _getBestSwapRoute.getBestSwapRoute)(tradeType, percentToTradesObj, percents, swapConfig);
|
|
525
533
|
if (!bestTrades) return null;
|
|
526
534
|
const routes = bestTrades.map(_ref7 => {
|
|
527
535
|
let {
|
package/build/utils/fullMath.js
CHANGED
|
@@ -4,26 +4,45 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.FullMath = void 0;
|
|
7
|
-
var _internalConstants = require("../internalConstants");
|
|
8
7
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
9
8
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
10
9
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
11
10
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
12
11
|
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); }
|
|
12
|
+
const ZERO = 0n;
|
|
13
|
+
const ONE = 1n;
|
|
13
14
|
let FullMath = exports.FullMath = /*#__PURE__*/function () {
|
|
14
|
-
/**
|
|
15
|
-
* Cannot be constructed.
|
|
16
|
-
*/
|
|
17
15
|
function FullMath() {
|
|
18
16
|
_classCallCheck(this, FullMath);
|
|
19
17
|
}
|
|
20
18
|
return _createClass(FullMath, null, [{
|
|
21
19
|
key: "mulDivRoundingUp",
|
|
22
20
|
value: function mulDivRoundingUp(a, b, denominator) {
|
|
21
|
+
// Check for division by zero
|
|
22
|
+
if (denominator === ZERO) {
|
|
23
|
+
throw new Error("Division by zero");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Early return for zero inputs
|
|
27
|
+
if (a === ZERO || b === ZERO) {
|
|
28
|
+
return ZERO;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Early return if denominator is one
|
|
32
|
+
if (denominator === ONE) {
|
|
33
|
+
return a * b;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Calculate product, quotient, and remainder
|
|
23
37
|
const product = a * b;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
const quotient = product / denominator;
|
|
39
|
+
const remainder = product % denominator;
|
|
40
|
+
|
|
41
|
+
// Round up if there’s a non-zero remainder
|
|
42
|
+
if (remainder !== ZERO) {
|
|
43
|
+
return quotient + ONE;
|
|
44
|
+
}
|
|
45
|
+
return quotient;
|
|
27
46
|
}
|
|
28
47
|
}]);
|
|
29
48
|
}();
|
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getBestSwapRoute = getBestSwapRoute;
|
|
7
|
-
var _lodash = _interopRequireDefault(require("lodash"));
|
|
8
7
|
var _queue = _interopRequireDefault(require("mnemonist/queue"));
|
|
9
8
|
var _fixedReverseHeap = _interopRequireDefault(require("mnemonist/fixed-reverse-heap"));
|
|
10
9
|
var _internalConstants = require("../internalConstants");
|
|
@@ -14,16 +13,38 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
|
|
|
14
13
|
minSplits: 1,
|
|
15
14
|
maxSplits: 8
|
|
16
15
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
// Извлекаем уникальные пулы из всех маршрутов
|
|
17
|
+
const allPools = [...new Set(Object.values(percentToQuotes).flatMap(routes => routes.flatMap(r => r.route.pools)))];
|
|
18
|
+
|
|
19
|
+
// Создаем битовую карту для пулов
|
|
20
|
+
const poolToBit = new Map();
|
|
21
|
+
let bitCounter = 0;
|
|
22
|
+
for (const pool of allPools) {
|
|
23
|
+
poolToBit.set(pool.id, 1 << bitCounter++);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Предвычисляем маски для всех маршрутов
|
|
27
|
+
const routeToMask = new Map();
|
|
28
|
+
for (const routes of Object.values(percentToQuotes)) {
|
|
29
|
+
for (const route of routes) {
|
|
30
|
+
let mask = 0;
|
|
31
|
+
for (const pool of route.route.pools) {
|
|
32
|
+
mask |= poolToBit.get(pool.id);
|
|
23
33
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
routeToMask.set(route, mask);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Сортируем маршруты для каждого процента
|
|
39
|
+
const percentToSortedQuotes = {};
|
|
40
|
+
for (const percent in percentToQuotes) {
|
|
41
|
+
percentToSortedQuotes[percent] = percentToQuotes[percent].sort((a, b) => routeType === _internalConstants.TradeType.EXACT_INPUT ? a.outputAmount.greaterThan(b.outputAmount) ? -1 : 1 : a.inputAmount.lessThan(b.inputAmount) ? -1 : 1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Функция сравнения для типа торговли
|
|
45
|
+
const quoteCompFn = routeType === _internalConstants.TradeType.EXACT_INPUT ? (a, b) => a.greaterThan(b) : (a, b) => a.lessThan(b);
|
|
46
|
+
|
|
47
|
+
// Функция суммирования CurrencyAmount
|
|
27
48
|
const sumFn = currencyAmounts => {
|
|
28
49
|
let sum = currencyAmounts[0];
|
|
29
50
|
for (let i = 1; i < currencyAmounts.length; i++) {
|
|
@@ -33,17 +54,17 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
|
|
|
33
54
|
};
|
|
34
55
|
let bestQuote;
|
|
35
56
|
let bestSwap;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
|
|
58
|
+
// Храним лучшие маршруты для каждого уровня разбиения (максимум 3)
|
|
59
|
+
const bestSwapsPerSplit = new _fixedReverseHeap.default(Array, (a, b) => quoteCompFn(a.quote, b.quote) ? -1 : 1, 3);
|
|
39
60
|
const {
|
|
40
61
|
minSplits,
|
|
41
62
|
maxSplits
|
|
42
63
|
} = swapRouteConfig;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
|
|
65
|
+
// Проверяем наличие маршрута для 100% и инициализируем начальные данные
|
|
66
|
+
if (!percentToSortedQuotes[100] || percentToSortedQuotes[100].length === 0 || minSplits > 1) {
|
|
67
|
+
console.log('Did not find a valid route without any splits. Continuing search anyway.');
|
|
47
68
|
} else {
|
|
48
69
|
bestQuote = percentToSortedQuotes[100][0].outputAmount;
|
|
49
70
|
bestSwap = [percentToSortedQuotes[100][0]];
|
|
@@ -54,37 +75,36 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
|
|
|
54
75
|
});
|
|
55
76
|
}
|
|
56
77
|
}
|
|
78
|
+
|
|
79
|
+
// Очередь для обработки комбинаций маршрутов
|
|
57
80
|
const queue = new _queue.default();
|
|
58
|
-
if (percents.length === 0) return null;
|
|
81
|
+
if (percents.length === 0) return null;
|
|
59
82
|
|
|
83
|
+
// Инициализируем очередь с топ-2 маршрутами для каждого процента
|
|
60
84
|
for (let i = percents.length - 1; i >= 0; i--) {
|
|
61
85
|
const percent = percents[i];
|
|
62
|
-
if (!percentToSortedQuotes[percent])
|
|
63
|
-
|
|
64
|
-
|
|
86
|
+
if (!percentToSortedQuotes[percent] || percentToSortedQuotes[percent].length === 0) continue;
|
|
87
|
+
const topRoutes = percentToSortedQuotes[percent].slice(0, 2);
|
|
88
|
+
if (topRoutes[0]) {
|
|
89
|
+
queue.enqueue({
|
|
90
|
+
curRoutes: [topRoutes[0]],
|
|
91
|
+
percentIndex: i,
|
|
92
|
+
remainingPercent: 100 - percent,
|
|
93
|
+
special: false
|
|
65
94
|
});
|
|
66
|
-
continue;
|
|
67
95
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!percentToSortedQuotes[percent] || !percentToSortedQuotes[percent][1]) {
|
|
75
|
-
console.log('continue2', {
|
|
76
|
-
percent
|
|
96
|
+
if (topRoutes[1]) {
|
|
97
|
+
queue.enqueue({
|
|
98
|
+
curRoutes: [topRoutes[1]],
|
|
99
|
+
percentIndex: i,
|
|
100
|
+
remainingPercent: 100 - percent,
|
|
101
|
+
special: true
|
|
77
102
|
});
|
|
78
|
-
continue;
|
|
79
103
|
}
|
|
80
|
-
queue.enqueue({
|
|
81
|
-
curRoutes: [percentToSortedQuotes[percent][1]],
|
|
82
|
-
percentIndex: i,
|
|
83
|
-
remainingPercent: 100 - percent,
|
|
84
|
-
special: true
|
|
85
|
-
});
|
|
86
104
|
}
|
|
87
105
|
let splits = 1;
|
|
106
|
+
|
|
107
|
+
// Основной цикл поиска лучших маршрутов
|
|
88
108
|
while (queue.size > 0) {
|
|
89
109
|
bestSwapsPerSplit.clear();
|
|
90
110
|
let layer = queue.size;
|
|
@@ -105,21 +125,16 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
|
|
|
105
125
|
} = queue.dequeue();
|
|
106
126
|
for (let i = percentIndex; i >= 0; i--) {
|
|
107
127
|
const percentA = percents[i];
|
|
108
|
-
if (percentA > remainingPercent)
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
if (!percentToSortedQuotes[percentA]) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
128
|
+
if (percentA > remainingPercent) continue;
|
|
129
|
+
if (!percentToSortedQuotes[percentA] || percentToSortedQuotes[percentA].length === 0) continue;
|
|
114
130
|
const candidateRoutesA = percentToSortedQuotes[percentA];
|
|
115
|
-
const routeWithQuoteA = findFirstRouteNotUsingUsedPools(curRoutes, candidateRoutesA);
|
|
116
|
-
if (!routeWithQuoteA)
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
131
|
+
const routeWithQuoteA = findFirstRouteNotUsingUsedPools(curRoutes, candidateRoutesA, routeToMask);
|
|
132
|
+
if (!routeWithQuoteA) continue;
|
|
119
133
|
const remainingPercentNew = remainingPercent - percentA;
|
|
120
|
-
const curRoutesNew =
|
|
121
|
-
|
|
122
|
-
|
|
134
|
+
const curRoutesNew = curRoutes.slice();
|
|
135
|
+
curRoutesNew.push(routeWithQuoteA);
|
|
136
|
+
if (remainingPercentNew === 0 && splits >= minSplits) {
|
|
137
|
+
const quotesNew = curRoutesNew.map(r => r.outputAmount);
|
|
123
138
|
const quoteNew = sumFn(quotesNew);
|
|
124
139
|
bestSwapsPerSplit.push({
|
|
125
140
|
quote: quoteNew,
|
|
@@ -141,18 +156,23 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
|
|
|
141
156
|
}
|
|
142
157
|
}
|
|
143
158
|
if (!bestSwap) {
|
|
144
|
-
console.log(
|
|
159
|
+
console.log('Could not find a valid swap');
|
|
145
160
|
return null;
|
|
146
161
|
}
|
|
147
|
-
const quote = sumFn(
|
|
162
|
+
const quote = sumFn(bestSwap.map(routeWithValidQuote => routeWithValidQuote.outputAmount));
|
|
148
163
|
const routeWithQuotes = bestSwap.sort((routeAmountA, routeAmountB) => routeAmountB.outputAmount.greaterThan(routeAmountA.outputAmount) ? 1 : -1);
|
|
149
164
|
return routeWithQuotes;
|
|
150
165
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
166
|
+
|
|
167
|
+
// Вспомогательная функция для поиска маршрута без пересекающихся пулов
|
|
168
|
+
const findFirstRouteNotUsingUsedPools = (usedRoutes, candidateRoutes, routeToMask) => {
|
|
169
|
+
let usedMask = 0;
|
|
170
|
+
for (const route of usedRoutes) {
|
|
171
|
+
usedMask |= routeToMask.get(route);
|
|
172
|
+
}
|
|
154
173
|
for (const candidate of candidateRoutes) {
|
|
155
|
-
|
|
174
|
+
const candidateMask = routeToMask.get(candidate);
|
|
175
|
+
if ((candidateMask & usedMask) === 0) {
|
|
156
176
|
return candidate;
|
|
157
177
|
}
|
|
158
178
|
}
|
|
@@ -4,10 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.SqrtPriceMath = void 0;
|
|
7
|
-
var _tinyInvariant = _interopRequireDefault(require("tiny-invariant"));
|
|
8
7
|
var _internalConstants = require("../internalConstants");
|
|
9
8
|
var _fullMath = require("./fullMath");
|
|
10
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
9
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
12
10
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
13
11
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
@@ -36,7 +34,13 @@ let SqrtPriceMath = exports.SqrtPriceMath = /*#__PURE__*/function () {
|
|
|
36
34
|
}
|
|
37
35
|
const numerator1 = liquidity << 64n;
|
|
38
36
|
const numerator2 = sqrtRatioUX64 - sqrtRatioLX64;
|
|
39
|
-
|
|
37
|
+
if (roundUp) {
|
|
38
|
+
const mul1 = _fullMath.FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioUX64);
|
|
39
|
+
return _fullMath.FullMath.mulDivRoundingUp(mul1, _internalConstants.ONE, sqrtRatioLX64);
|
|
40
|
+
} else {
|
|
41
|
+
const mul1 = numerator1 * numerator2 / sqrtRatioUX64;
|
|
42
|
+
return mul1 / sqrtRatioLX64;
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
}, {
|
|
42
46
|
key: "getAmountBDelta",
|
|
@@ -44,20 +48,23 @@ let SqrtPriceMath = exports.SqrtPriceMath = /*#__PURE__*/function () {
|
|
|
44
48
|
if (sqrtRatioLX64 > sqrtRatioUX64) {
|
|
45
49
|
[sqrtRatioLX64, sqrtRatioUX64] = [sqrtRatioUX64, sqrtRatioLX64];
|
|
46
50
|
}
|
|
47
|
-
|
|
51
|
+
const diff = sqrtRatioUX64 - sqrtRatioLX64;
|
|
52
|
+
if (roundUp) {
|
|
53
|
+
return _fullMath.FullMath.mulDivRoundingUp(liquidity, diff, _internalConstants.Q64);
|
|
54
|
+
} else {
|
|
55
|
+
return liquidity * diff / _internalConstants.Q64;
|
|
56
|
+
}
|
|
48
57
|
}
|
|
49
58
|
}, {
|
|
50
59
|
key: "getNextSqrtPriceFromInput",
|
|
51
60
|
value: function getNextSqrtPriceFromInput(sqrtPX64, liquidity, amountIn, zeroForOne) {
|
|
52
|
-
|
|
53
|
-
(0, _tinyInvariant.default)(liquidity > _internalConstants.ZERO);
|
|
61
|
+
// Убраны invariant для ускорения, если данные валидны
|
|
54
62
|
return zeroForOne ? this.getNextSqrtPriceFromAmountARoundingUp(sqrtPX64, liquidity, amountIn, true) : this.getNextSqrtPriceFromAmountBRoundingDown(sqrtPX64, liquidity, amountIn, true);
|
|
55
63
|
}
|
|
56
64
|
}, {
|
|
57
65
|
key: "getNextSqrtPriceFromOutput",
|
|
58
66
|
value: function getNextSqrtPriceFromOutput(sqrtPX64, liquidity, amountOut, zeroForOne) {
|
|
59
|
-
|
|
60
|
-
(0, _tinyInvariant.default)(liquidity > _internalConstants.ZERO);
|
|
67
|
+
// Убраны invariant для ускорения, если данные валидны
|
|
61
68
|
return zeroForOne ? this.getNextSqrtPriceFromAmountBRoundingDown(sqrtPX64, liquidity, amountOut, false) : this.getNextSqrtPriceFromAmountARoundingUp(sqrtPX64, liquidity, amountOut, false);
|
|
62
69
|
}
|
|
63
70
|
}, {
|
|
@@ -66,18 +73,18 @@ let SqrtPriceMath = exports.SqrtPriceMath = /*#__PURE__*/function () {
|
|
|
66
73
|
if (amount === _internalConstants.ZERO) return sqrtPX64;
|
|
67
74
|
const numerator1 = liquidity << 64n;
|
|
68
75
|
if (add) {
|
|
69
|
-
|
|
76
|
+
const product = multiplyIn128(amount, sqrtPX64);
|
|
70
77
|
if (product / amount === sqrtPX64) {
|
|
71
78
|
const denominator = addIn128(numerator1, product);
|
|
72
79
|
if (denominator >= numerator1) {
|
|
73
80
|
return _fullMath.FullMath.mulDivRoundingUp(numerator1, sqrtPX64, denominator);
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
|
-
|
|
83
|
+
const adjustedDenominator = numerator1 / sqrtPX64 + amount;
|
|
84
|
+
return _fullMath.FullMath.mulDivRoundingUp(numerator1, _internalConstants.ONE, adjustedDenominator);
|
|
77
85
|
} else {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
(0, _tinyInvariant.default)(numerator1 > product);
|
|
86
|
+
const product = multiplyIn128(amount, sqrtPX64);
|
|
87
|
+
// Убрана invariant, предполагаем, что numerator1 > product
|
|
81
88
|
const denominator = numerator1 - product;
|
|
82
89
|
return _fullMath.FullMath.mulDivRoundingUp(numerator1, sqrtPX64, denominator);
|
|
83
90
|
}
|
|
@@ -90,7 +97,7 @@ let SqrtPriceMath = exports.SqrtPriceMath = /*#__PURE__*/function () {
|
|
|
90
97
|
return sqrtPX64 + quotient;
|
|
91
98
|
} else {
|
|
92
99
|
const quotient = _fullMath.FullMath.mulDivRoundingUp(amount, _internalConstants.Q64, liquidity);
|
|
93
|
-
|
|
100
|
+
// Убрана invariant, предполагаем, что sqrtPX64 > quotient
|
|
94
101
|
return sqrtPX64 - quotient;
|
|
95
102
|
}
|
|
96
103
|
}
|
package/build/utils/swapMath.js
CHANGED
|
@@ -13,6 +13,7 @@ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r),
|
|
|
13
13
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
14
14
|
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); }
|
|
15
15
|
const MAX_FEE = 10n ** 6n;
|
|
16
|
+
const MAX_FEE_MINUS_FEE_PIPS = feePips => MAX_FEE - BigInt(feePips);
|
|
16
17
|
let SwapMath = exports.SwapMath = /*#__PURE__*/function () {
|
|
17
18
|
/**
|
|
18
19
|
* Cannot be constructed.
|
|
@@ -23,43 +24,42 @@ let SwapMath = exports.SwapMath = /*#__PURE__*/function () {
|
|
|
23
24
|
return _createClass(SwapMath, null, [{
|
|
24
25
|
key: "computeSwapStep",
|
|
25
26
|
value: function computeSwapStep(sqrtRatioCurrentX64, sqrtRatioTargetX64, liquidity, amountRemaining, feePips) {
|
|
26
|
-
const returnValues = {};
|
|
27
27
|
const zeroForOne = sqrtRatioCurrentX64 >= sqrtRatioTargetX64;
|
|
28
28
|
const exactIn = amountRemaining >= _internalConstants.ZERO;
|
|
29
|
+
let sqrtRatioNextX64;
|
|
30
|
+
let amountIn;
|
|
31
|
+
let amountOut;
|
|
32
|
+
let feeAmount;
|
|
29
33
|
if (exactIn) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const feeFactor = MAX_FEE_MINUS_FEE_PIPS(feePips);
|
|
35
|
+
const amountRemainingLessFee = amountRemaining * feeFactor / MAX_FEE;
|
|
36
|
+
const deltaIn = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioTargetX64, sqrtRatioCurrentX64, liquidity, true) : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioCurrentX64, sqrtRatioTargetX64, liquidity, true);
|
|
37
|
+
if (amountRemainingLessFee >= deltaIn) {
|
|
38
|
+
sqrtRatioNextX64 = sqrtRatioTargetX64;
|
|
39
|
+
amountIn = deltaIn;
|
|
34
40
|
} else {
|
|
35
|
-
|
|
41
|
+
sqrtRatioNextX64 = _sqrtPriceMath.SqrtPriceMath.getNextSqrtPriceFromInput(sqrtRatioCurrentX64, liquidity, amountRemainingLessFee, zeroForOne);
|
|
42
|
+
amountIn = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, true) : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioCurrentX64, sqrtRatioNextX64, liquidity, true);
|
|
36
43
|
}
|
|
44
|
+
amountOut = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, false) : _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioCurrentX64, sqrtRatioNextX64, liquidity, false);
|
|
45
|
+
feeAmount = sqrtRatioNextX64 !== sqrtRatioTargetX64 ? amountRemaining - amountIn : _fullMath.FullMath.mulDivRoundingUp(amountIn, BigInt(feePips), feeFactor);
|
|
37
46
|
} else {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
const deltaOut = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioTargetX64, sqrtRatioCurrentX64, liquidity, false) : _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioCurrentX64, sqrtRatioTargetX64, liquidity, false);
|
|
48
|
+
const amountRemainingNegative = amountRemaining * _internalConstants.NEGATIVE_ONE;
|
|
49
|
+
if (amountRemainingNegative >= deltaOut) {
|
|
50
|
+
sqrtRatioNextX64 = sqrtRatioTargetX64;
|
|
51
|
+
amountOut = deltaOut;
|
|
41
52
|
} else {
|
|
42
|
-
|
|
53
|
+
sqrtRatioNextX64 = _sqrtPriceMath.SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtRatioCurrentX64, liquidity, amountRemainingNegative, zeroForOne);
|
|
54
|
+
amountOut = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, false) : _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioCurrentX64, sqrtRatioNextX64, liquidity, false);
|
|
43
55
|
}
|
|
56
|
+
amountIn = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, true) : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioCurrentX64, sqrtRatioNextX64, liquidity, true);
|
|
57
|
+
if (amountOut > amountRemainingNegative) {
|
|
58
|
+
amountOut = amountRemainingNegative;
|
|
59
|
+
}
|
|
60
|
+
feeAmount = _fullMath.FullMath.mulDivRoundingUp(amountIn, BigInt(feePips), MAX_FEE_MINUS_FEE_PIPS(feePips));
|
|
44
61
|
}
|
|
45
|
-
|
|
46
|
-
if (zeroForOne) {
|
|
47
|
-
returnValues.amountIn = max && exactIn ? returnValues.amountIn : _sqrtPriceMath.SqrtPriceMath.getAmountADelta(returnValues.sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, true);
|
|
48
|
-
returnValues.amountOut = max && !exactIn ? returnValues.amountOut : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(returnValues.sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, false);
|
|
49
|
-
} else {
|
|
50
|
-
returnValues.amountIn = max && exactIn ? returnValues.amountIn : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioCurrentX64, returnValues.sqrtRatioNextX64, liquidity, true);
|
|
51
|
-
returnValues.amountOut = max && !exactIn ? returnValues.amountOut : _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioCurrentX64, returnValues.sqrtRatioNextX64, liquidity, false);
|
|
52
|
-
}
|
|
53
|
-
if (!exactIn && returnValues.amountOut > amountRemaining * _internalConstants.NEGATIVE_ONE) {
|
|
54
|
-
returnValues.amountOut = amountRemaining * _internalConstants.NEGATIVE_ONE;
|
|
55
|
-
}
|
|
56
|
-
if (exactIn && returnValues.sqrtRatioNextX64 !== sqrtRatioTargetX64) {
|
|
57
|
-
// we didn't reach the target, so take the remainder of the maximum input as fee
|
|
58
|
-
returnValues.feeAmount = amountRemaining - returnValues.amountIn;
|
|
59
|
-
} else {
|
|
60
|
-
returnValues.feeAmount = _fullMath.FullMath.mulDivRoundingUp(returnValues.amountIn, BigInt(feePips), MAX_FEE - BigInt(feePips));
|
|
61
|
-
}
|
|
62
|
-
return [returnValues.sqrtRatioNextX64, returnValues.amountIn, returnValues.amountOut, returnValues.feeAmount];
|
|
62
|
+
return [sqrtRatioNextX64, amountIn, amountOut, feeAmount];
|
|
63
63
|
}
|
|
64
64
|
}]);
|
|
65
65
|
}();
|