@alcorexchange/alcor-swap-sdk 1.1.3 → 1.1.5
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/trade.d.ts +2 -0
- package/build/entities/trade.js +63 -28
- package/build/esm/entities/trade.js +63 -28
- package/build/esm/utils/getBestSwapRoute.js +73 -104
- package/build/esm/utils/tradeCalculatorWASM.js +15 -15
- package/build/utils/getBestSwapRoute.d.ts +16 -5
- package/build/utils/getBestSwapRoute.js +73 -104
- package/build/utils/tradeCalculatorWASM.d.ts +1 -1
- package/build/utils/tradeCalculatorWASM.js +15 -15
- package/package.json +1 -1
|
@@ -217,5 +217,7 @@ export declare class Trade<TInput extends Currency, TOutput extends Currency, TT
|
|
|
217
217
|
static bestTradeWithSplit<TInput extends Currency, TOutput extends Currency>(_routes: Route<TInput, TOutput>[], amount: CurrencyAmount<Currency>, percents: number[], tradeType: TradeType, swapConfig?: {
|
|
218
218
|
minSplits: number;
|
|
219
219
|
maxSplits: number;
|
|
220
|
+
branchFactor?: number;
|
|
221
|
+
candidateLimit?: number;
|
|
220
222
|
}): Trade<Currency, Currency, TradeType> | null;
|
|
221
223
|
}
|
package/build/entities/trade.js
CHANGED
|
@@ -440,8 +440,14 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
440
440
|
value: function bestTradeExactIn(routes, currencyAmountIn, maxNumResults = 1) {
|
|
441
441
|
(0, _tinyInvariant.default)(routes.length > 0, 'ROUTES');
|
|
442
442
|
|
|
443
|
-
// Pre-filter:
|
|
444
|
-
|
|
443
|
+
// Pre-filter: drop routes touching empty pools (no positions at all).
|
|
444
|
+
// Use ticks presence, NOT current in-range liquidity: a pool can have
|
|
445
|
+
// liquidity === 0 at the current tick while all positions are out of range,
|
|
446
|
+
// yet still be tradeable (the swap crosses the gap into a position).
|
|
447
|
+
const validRoutes = routes.filter(route => route.pools.every(pool => {
|
|
448
|
+
var _ticks$length, _pool$tickDataProvide;
|
|
449
|
+
return pool.active && ((_ticks$length = (_pool$tickDataProvide = pool.tickDataProvider) === null || _pool$tickDataProvide === void 0 || (_pool$tickDataProvide = _pool$tickDataProvide.ticks) === null || _pool$tickDataProvide === void 0 ? void 0 : _pool$tickDataProvide.length) !== null && _ticks$length !== void 0 ? _ticks$length : 0) > 0;
|
|
450
|
+
}));
|
|
445
451
|
|
|
446
452
|
// Helper: compute min liquidity (no overflow)
|
|
447
453
|
const getMinLiquidity = route => {
|
|
@@ -493,8 +499,14 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
493
499
|
value: function bestTradeExactOut(routes, currencyAmountOut, maxNumResults = 1) {
|
|
494
500
|
(0, _tinyInvariant.default)(routes.length > 0, 'ROUTES');
|
|
495
501
|
|
|
496
|
-
// Pre-filter:
|
|
497
|
-
|
|
502
|
+
// Pre-filter: drop routes touching empty pools (no positions at all).
|
|
503
|
+
// Use ticks presence, NOT current in-range liquidity: a pool can have
|
|
504
|
+
// liquidity === 0 at the current tick while all positions are out of range,
|
|
505
|
+
// yet still be tradeable (the swap crosses the gap into a position).
|
|
506
|
+
const validRoutes = routes.filter(route => route.pools.every(pool => {
|
|
507
|
+
var _ticks$length2, _pool$tickDataProvide2;
|
|
508
|
+
return pool.active && ((_ticks$length2 = (_pool$tickDataProvide2 = pool.tickDataProvider) === null || _pool$tickDataProvide2 === void 0 || (_pool$tickDataProvide2 = _pool$tickDataProvide2.ticks) === null || _pool$tickDataProvide2 === void 0 ? void 0 : _pool$tickDataProvide2.length) !== null && _ticks$length2 !== void 0 ? _ticks$length2 : 0) > 0;
|
|
509
|
+
}));
|
|
498
510
|
|
|
499
511
|
// Helper: compute min liquidity (no overflow)
|
|
500
512
|
const getMinLiquidity = route => {
|
|
@@ -570,8 +582,14 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
570
582
|
(0, _tinyInvariant.default)(_routes.length > 0, 'ROUTES');
|
|
571
583
|
(0, _tinyInvariant.default)(percents.length > 0, 'PERCENTS');
|
|
572
584
|
|
|
573
|
-
// Pre-filter:
|
|
574
|
-
|
|
585
|
+
// Pre-filter: drop routes touching empty pools (no positions at all).
|
|
586
|
+
// Use ticks presence, NOT current in-range liquidity: a pool can have
|
|
587
|
+
// liquidity === 0 at the current tick while all positions are out of range,
|
|
588
|
+
// yet still be tradeable (the swap crosses the gap into a position).
|
|
589
|
+
const validRoutes = _routes.filter(route => route.pools.every(pool => {
|
|
590
|
+
var _ticks$length3, _pool$tickDataProvide3;
|
|
591
|
+
return pool.active && ((_ticks$length3 = (_pool$tickDataProvide3 = pool.tickDataProvider) === null || _pool$tickDataProvide3 === void 0 || (_pool$tickDataProvide3 = _pool$tickDataProvide3.ticks) === null || _pool$tickDataProvide3 === void 0 ? void 0 : _pool$tickDataProvide3.length) !== null && _ticks$length3 !== void 0 ? _ticks$length3 : 0) > 0;
|
|
592
|
+
}));
|
|
575
593
|
|
|
576
594
|
// Helper: compute min liquidity for a route (no overflow)
|
|
577
595
|
const getMinLiquidity = route => {
|
|
@@ -604,23 +622,46 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
604
622
|
for (const percent of percents) {
|
|
605
623
|
percentToAmount.set(percent, amount.multiply(percent).divide(100));
|
|
606
624
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
625
|
+
const quoteRoute = (route, splitAmount, percent) => {
|
|
626
|
+
const amounts = new Array(route.tokenPath.length);
|
|
627
|
+
let inputAmount;
|
|
628
|
+
let outputAmount;
|
|
629
|
+
if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
|
|
630
|
+
amounts[0] = splitAmount;
|
|
631
|
+
for (let i = 0; i < route.tokenPath.length - 1; i++) {
|
|
632
|
+
amounts[i + 1] = route.pools[i].getOutputAmount(amounts[i]);
|
|
633
|
+
}
|
|
634
|
+
inputAmount = splitAmount;
|
|
635
|
+
outputAmount = amounts[amounts.length - 1];
|
|
636
|
+
} else {
|
|
637
|
+
amounts[amounts.length - 1] = splitAmount;
|
|
638
|
+
for (let i = route.tokenPath.length - 1; i > 0; i--) {
|
|
639
|
+
amounts[i - 1] = route.pools[i - 1].getInputAmount(amounts[i]);
|
|
640
|
+
}
|
|
641
|
+
inputAmount = amounts[0];
|
|
642
|
+
outputAmount = splitAmount;
|
|
643
|
+
}
|
|
644
|
+
if (!outputAmount.greaterThan(0)) {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
percent,
|
|
649
|
+
route: route,
|
|
650
|
+
inputAmount,
|
|
651
|
+
outputAmount
|
|
652
|
+
};
|
|
653
|
+
};
|
|
654
|
+
const percentToQuotes = {};
|
|
610
655
|
for (const percent of percents) {
|
|
611
|
-
|
|
656
|
+
percentToQuotes[percent] = [];
|
|
612
657
|
}
|
|
613
|
-
|
|
614
|
-
// Оптимизируем внутренний цикл - группируем вычисления по маршрутам
|
|
615
658
|
for (const route of validRoutes) {
|
|
616
659
|
for (const percent of percents) {
|
|
617
660
|
const splitAmount = percentToAmount.get(percent);
|
|
618
661
|
try {
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
if (trade.outputAmount.greaterThan(0)) {
|
|
623
|
-
percentToTrades.get(percent).push(trade);
|
|
662
|
+
const quote = quoteRoute(route, splitAmount, percent);
|
|
663
|
+
if (quote) {
|
|
664
|
+
percentToQuotes[percent].push(quote);
|
|
624
665
|
}
|
|
625
666
|
} catch (error) {
|
|
626
667
|
if (error.isInsufficientReservesError || error.isInsufficientInputAmountError) {
|
|
@@ -630,25 +671,19 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
|
|
|
630
671
|
}
|
|
631
672
|
}
|
|
632
673
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
percentToTrades.forEach((trades, percent) => {
|
|
637
|
-
percentToTradesObj[percent] = trades;
|
|
638
|
-
});
|
|
639
|
-
const bestTrades = (0, _getBestSwapRoute.getBestSwapRoute)(tradeType, percentToTradesObj, percents, swapConfig);
|
|
640
|
-
if (!bestTrades) return null;
|
|
641
|
-
const routes = bestTrades.map(({
|
|
674
|
+
const bestQuotes = (0, _getBestSwapRoute.getBestSwapRoute)(tradeType, percentToQuotes, percents, swapConfig);
|
|
675
|
+
if (!bestQuotes) return null;
|
|
676
|
+
const routes = bestQuotes.map(({
|
|
642
677
|
inputAmount,
|
|
643
678
|
outputAmount,
|
|
644
679
|
route,
|
|
645
|
-
|
|
680
|
+
percent
|
|
646
681
|
}) => {
|
|
647
682
|
return {
|
|
648
683
|
inputAmount,
|
|
649
684
|
outputAmount,
|
|
650
685
|
route,
|
|
651
|
-
percent
|
|
686
|
+
percent
|
|
652
687
|
};
|
|
653
688
|
});
|
|
654
689
|
|
|
@@ -431,8 +431,14 @@ export let Trade = /*#__PURE__*/function () {
|
|
|
431
431
|
value: function bestTradeExactIn(routes, currencyAmountIn, maxNumResults = 1) {
|
|
432
432
|
invariant(routes.length > 0, 'ROUTES');
|
|
433
433
|
|
|
434
|
-
// Pre-filter:
|
|
435
|
-
|
|
434
|
+
// Pre-filter: drop routes touching empty pools (no positions at all).
|
|
435
|
+
// Use ticks presence, NOT current in-range liquidity: a pool can have
|
|
436
|
+
// liquidity === 0 at the current tick while all positions are out of range,
|
|
437
|
+
// yet still be tradeable (the swap crosses the gap into a position).
|
|
438
|
+
const validRoutes = routes.filter(route => route.pools.every(pool => {
|
|
439
|
+
var _ticks$length, _pool$tickDataProvide;
|
|
440
|
+
return pool.active && ((_ticks$length = (_pool$tickDataProvide = pool.tickDataProvider) === null || _pool$tickDataProvide === void 0 || (_pool$tickDataProvide = _pool$tickDataProvide.ticks) === null || _pool$tickDataProvide === void 0 ? void 0 : _pool$tickDataProvide.length) !== null && _ticks$length !== void 0 ? _ticks$length : 0) > 0;
|
|
441
|
+
}));
|
|
436
442
|
|
|
437
443
|
// Helper: compute min liquidity (no overflow)
|
|
438
444
|
const getMinLiquidity = route => {
|
|
@@ -484,8 +490,14 @@ export let Trade = /*#__PURE__*/function () {
|
|
|
484
490
|
value: function bestTradeExactOut(routes, currencyAmountOut, maxNumResults = 1) {
|
|
485
491
|
invariant(routes.length > 0, 'ROUTES');
|
|
486
492
|
|
|
487
|
-
// Pre-filter:
|
|
488
|
-
|
|
493
|
+
// Pre-filter: drop routes touching empty pools (no positions at all).
|
|
494
|
+
// Use ticks presence, NOT current in-range liquidity: a pool can have
|
|
495
|
+
// liquidity === 0 at the current tick while all positions are out of range,
|
|
496
|
+
// yet still be tradeable (the swap crosses the gap into a position).
|
|
497
|
+
const validRoutes = routes.filter(route => route.pools.every(pool => {
|
|
498
|
+
var _ticks$length2, _pool$tickDataProvide2;
|
|
499
|
+
return pool.active && ((_ticks$length2 = (_pool$tickDataProvide2 = pool.tickDataProvider) === null || _pool$tickDataProvide2 === void 0 || (_pool$tickDataProvide2 = _pool$tickDataProvide2.ticks) === null || _pool$tickDataProvide2 === void 0 ? void 0 : _pool$tickDataProvide2.length) !== null && _ticks$length2 !== void 0 ? _ticks$length2 : 0) > 0;
|
|
500
|
+
}));
|
|
489
501
|
|
|
490
502
|
// Helper: compute min liquidity (no overflow)
|
|
491
503
|
const getMinLiquidity = route => {
|
|
@@ -561,8 +573,14 @@ export let Trade = /*#__PURE__*/function () {
|
|
|
561
573
|
invariant(_routes.length > 0, 'ROUTES');
|
|
562
574
|
invariant(percents.length > 0, 'PERCENTS');
|
|
563
575
|
|
|
564
|
-
// Pre-filter:
|
|
565
|
-
|
|
576
|
+
// Pre-filter: drop routes touching empty pools (no positions at all).
|
|
577
|
+
// Use ticks presence, NOT current in-range liquidity: a pool can have
|
|
578
|
+
// liquidity === 0 at the current tick while all positions are out of range,
|
|
579
|
+
// yet still be tradeable (the swap crosses the gap into a position).
|
|
580
|
+
const validRoutes = _routes.filter(route => route.pools.every(pool => {
|
|
581
|
+
var _ticks$length3, _pool$tickDataProvide3;
|
|
582
|
+
return pool.active && ((_ticks$length3 = (_pool$tickDataProvide3 = pool.tickDataProvider) === null || _pool$tickDataProvide3 === void 0 || (_pool$tickDataProvide3 = _pool$tickDataProvide3.ticks) === null || _pool$tickDataProvide3 === void 0 ? void 0 : _pool$tickDataProvide3.length) !== null && _ticks$length3 !== void 0 ? _ticks$length3 : 0) > 0;
|
|
583
|
+
}));
|
|
566
584
|
|
|
567
585
|
// Helper: compute min liquidity for a route (no overflow)
|
|
568
586
|
const getMinLiquidity = route => {
|
|
@@ -595,23 +613,46 @@ export let Trade = /*#__PURE__*/function () {
|
|
|
595
613
|
for (const percent of percents) {
|
|
596
614
|
percentToAmount.set(percent, amount.multiply(percent).divide(100));
|
|
597
615
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
616
|
+
const quoteRoute = (route, splitAmount, percent) => {
|
|
617
|
+
const amounts = new Array(route.tokenPath.length);
|
|
618
|
+
let inputAmount;
|
|
619
|
+
let outputAmount;
|
|
620
|
+
if (tradeType === TradeType.EXACT_INPUT) {
|
|
621
|
+
amounts[0] = splitAmount;
|
|
622
|
+
for (let i = 0; i < route.tokenPath.length - 1; i++) {
|
|
623
|
+
amounts[i + 1] = route.pools[i].getOutputAmount(amounts[i]);
|
|
624
|
+
}
|
|
625
|
+
inputAmount = splitAmount;
|
|
626
|
+
outputAmount = amounts[amounts.length - 1];
|
|
627
|
+
} else {
|
|
628
|
+
amounts[amounts.length - 1] = splitAmount;
|
|
629
|
+
for (let i = route.tokenPath.length - 1; i > 0; i--) {
|
|
630
|
+
amounts[i - 1] = route.pools[i - 1].getInputAmount(amounts[i]);
|
|
631
|
+
}
|
|
632
|
+
inputAmount = amounts[0];
|
|
633
|
+
outputAmount = splitAmount;
|
|
634
|
+
}
|
|
635
|
+
if (!outputAmount.greaterThan(0)) {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
return {
|
|
639
|
+
percent,
|
|
640
|
+
route: route,
|
|
641
|
+
inputAmount,
|
|
642
|
+
outputAmount
|
|
643
|
+
};
|
|
644
|
+
};
|
|
645
|
+
const percentToQuotes = {};
|
|
601
646
|
for (const percent of percents) {
|
|
602
|
-
|
|
647
|
+
percentToQuotes[percent] = [];
|
|
603
648
|
}
|
|
604
|
-
|
|
605
|
-
// Оптимизируем внутренний цикл - группируем вычисления по маршрутам
|
|
606
649
|
for (const route of validRoutes) {
|
|
607
650
|
for (const percent of percents) {
|
|
608
651
|
const splitAmount = percentToAmount.get(percent);
|
|
609
652
|
try {
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (trade.outputAmount.greaterThan(0)) {
|
|
614
|
-
percentToTrades.get(percent).push(trade);
|
|
653
|
+
const quote = quoteRoute(route, splitAmount, percent);
|
|
654
|
+
if (quote) {
|
|
655
|
+
percentToQuotes[percent].push(quote);
|
|
615
656
|
}
|
|
616
657
|
} catch (error) {
|
|
617
658
|
if (error.isInsufficientReservesError || error.isInsufficientInputAmountError) {
|
|
@@ -621,25 +662,19 @@ export let Trade = /*#__PURE__*/function () {
|
|
|
621
662
|
}
|
|
622
663
|
}
|
|
623
664
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const
|
|
627
|
-
percentToTrades.forEach((trades, percent) => {
|
|
628
|
-
percentToTradesObj[percent] = trades;
|
|
629
|
-
});
|
|
630
|
-
const bestTrades = getBestSwapRoute(tradeType, percentToTradesObj, percents, swapConfig);
|
|
631
|
-
if (!bestTrades) return null;
|
|
632
|
-
const routes = bestTrades.map(({
|
|
665
|
+
const bestQuotes = getBestSwapRoute(tradeType, percentToQuotes, percents, swapConfig);
|
|
666
|
+
if (!bestQuotes) return null;
|
|
667
|
+
const routes = bestQuotes.map(({
|
|
633
668
|
inputAmount,
|
|
634
669
|
outputAmount,
|
|
635
670
|
route,
|
|
636
|
-
|
|
671
|
+
percent
|
|
637
672
|
}) => {
|
|
638
673
|
return {
|
|
639
674
|
inputAmount,
|
|
640
675
|
outputAmount,
|
|
641
676
|
route,
|
|
642
|
-
percent
|
|
677
|
+
percent
|
|
643
678
|
};
|
|
644
679
|
});
|
|
645
680
|
|
|
@@ -1,106 +1,78 @@
|
|
|
1
1
|
import Queue from 'mnemonist/queue';
|
|
2
|
-
import FixedReverseHeap from 'mnemonist/fixed-reverse-heap';
|
|
3
2
|
import { TradeType } from '../internalConstants';
|
|
4
3
|
export function getBestSwapRoute(routeType, percentToQuotes, percents, swapRouteConfig = {
|
|
5
4
|
minSplits: 1,
|
|
6
5
|
maxSplits: 8
|
|
7
6
|
}) {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const {
|
|
8
|
+
minSplits,
|
|
9
|
+
maxSplits,
|
|
10
|
+
branchFactor = 1,
|
|
11
|
+
candidateLimit = 0
|
|
12
|
+
} = swapRouteConfig;
|
|
13
|
+
const branchWidth = Math.max(1, branchFactor);
|
|
14
|
+
const quoteCompFn = routeType === TradeType.EXACT_INPUT ? (a, b) => a.greaterThan(b) : (a, b) => a.lessThan(b);
|
|
15
|
+
const quoteOf = routeType === TradeType.EXACT_INPUT ? q => q.outputAmount : q => q.inputAmount;
|
|
10
16
|
|
|
11
|
-
//
|
|
17
|
+
// Build pool bit ids without intermediate arrays.
|
|
12
18
|
const poolToBit = new Map();
|
|
13
19
|
let bitCounter = BigInt(0);
|
|
14
|
-
for (const
|
|
15
|
-
|
|
20
|
+
for (const quotes of Object.values(percentToQuotes)) {
|
|
21
|
+
for (const quote of quotes) {
|
|
22
|
+
for (const pool of quote.route.pools) {
|
|
23
|
+
if (!poolToBit.has(pool.id)) {
|
|
24
|
+
poolToBit.set(pool.id, BigInt(1) << bitCounter);
|
|
25
|
+
bitCounter += BigInt(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
16
29
|
}
|
|
17
|
-
|
|
18
|
-
// Предвычисляем маски для всех маршрутов
|
|
19
30
|
const routeToMask = new Map();
|
|
20
|
-
for (const
|
|
21
|
-
for (const
|
|
31
|
+
for (const quotes of Object.values(percentToQuotes)) {
|
|
32
|
+
for (const quote of quotes) {
|
|
22
33
|
let mask = BigInt(0);
|
|
23
|
-
for (const pool of
|
|
34
|
+
for (const pool of quote.route.pools) {
|
|
24
35
|
mask |= poolToBit.get(pool.id);
|
|
25
36
|
}
|
|
26
|
-
routeToMask.set(
|
|
37
|
+
routeToMask.set(quote, mask);
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
|
-
|
|
30
|
-
// Сортируем маршруты для каждого процента
|
|
31
40
|
const percentToSortedQuotes = {};
|
|
32
41
|
for (const percent in percentToQuotes) {
|
|
33
|
-
|
|
42
|
+
const sorted = percentToQuotes[percent].sort((a, b) => {
|
|
43
|
+
const qa = quoteOf(a);
|
|
44
|
+
const qb = quoteOf(b);
|
|
45
|
+
return quoteCompFn(qa, qb) ? -1 : 1;
|
|
46
|
+
});
|
|
47
|
+
percentToSortedQuotes[percent] = candidateLimit > 0 && sorted.length > candidateLimit ? sorted.slice(0, candidateLimit) : sorted;
|
|
34
48
|
}
|
|
35
|
-
|
|
36
|
-
// Функция сравнения для типа торговли
|
|
37
|
-
const quoteCompFn = routeType === TradeType.EXACT_INPUT ? (a, b) => a.greaterThan(b) : (a, b) => a.lessThan(b);
|
|
38
|
-
|
|
39
|
-
// Функция суммирования CurrencyAmount
|
|
40
|
-
const sumFn = currencyAmounts => {
|
|
41
|
-
let sum = currencyAmounts[0];
|
|
42
|
-
for (let i = 1; i < currencyAmounts.length; i++) {
|
|
43
|
-
sum = sum.add(currencyAmounts[i]);
|
|
44
|
-
}
|
|
45
|
-
return sum;
|
|
46
|
-
};
|
|
47
49
|
let bestQuote;
|
|
48
50
|
let bestSwap;
|
|
49
|
-
|
|
50
|
-
// Храним лучшие маршруты для каждого уровня разбиения (максимум 3)
|
|
51
|
-
const bestSwapsPerSplit = new FixedReverseHeap(Array, (a, b) => quoteCompFn(a.quote, b.quote) ? -1 : 1, 3);
|
|
52
|
-
const {
|
|
53
|
-
minSplits,
|
|
54
|
-
maxSplits
|
|
55
|
-
} = swapRouteConfig;
|
|
56
|
-
|
|
57
|
-
// Проверяем наличие маршрута для 100% и инициализируем начальные данные
|
|
58
|
-
if (!percentToSortedQuotes[100] || percentToSortedQuotes[100].length === 0 || minSplits > 1) {
|
|
51
|
+
if ((!percentToSortedQuotes[100] || percentToSortedQuotes[100].length === 0) && minSplits <= 1) {
|
|
59
52
|
console.log('Did not find a valid route without any splits. Continuing search anyway.');
|
|
60
|
-
} else {
|
|
61
|
-
bestQuote = percentToSortedQuotes[100][0].outputAmount;
|
|
53
|
+
} else if (minSplits <= 1 && percentToSortedQuotes[100] && percentToSortedQuotes[100][0]) {
|
|
62
54
|
bestSwap = [percentToSortedQuotes[100][0]];
|
|
63
|
-
|
|
64
|
-
bestSwapsPerSplit.push({
|
|
65
|
-
quote: routeWithQuote.outputAmount,
|
|
66
|
-
routes: [routeWithQuote]
|
|
67
|
-
});
|
|
68
|
-
}
|
|
55
|
+
bestQuote = quoteOf(percentToSortedQuotes[100][0]);
|
|
69
56
|
}
|
|
70
|
-
|
|
71
|
-
// Очередь для обработки комбинаций маршрутов (с usedMask для быстрой проверки overlap)
|
|
72
57
|
const queue = new Queue();
|
|
73
58
|
if (percents.length === 0) return null;
|
|
74
|
-
|
|
75
|
-
// Инициализируем очередь с топ-2 маршрутами для каждого процента
|
|
76
59
|
for (let i = percents.length - 1; i >= 0; i--) {
|
|
77
60
|
const percent = percents[i];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
61
|
+
const candidates = percentToSortedQuotes[percent];
|
|
62
|
+
if (!candidates || candidates.length === 0) continue;
|
|
63
|
+
const seeds = candidates.slice(0, branchWidth);
|
|
64
|
+
for (const seed of seeds) {
|
|
81
65
|
queue.enqueue({
|
|
82
|
-
curRoutes: [
|
|
66
|
+
curRoutes: [seed],
|
|
83
67
|
percentIndex: i,
|
|
84
68
|
remainingPercent: 100 - percent,
|
|
85
|
-
usedMask: routeToMask.get(
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
if (topRoutes[1]) {
|
|
90
|
-
queue.enqueue({
|
|
91
|
-
curRoutes: [topRoutes[1]],
|
|
92
|
-
percentIndex: i,
|
|
93
|
-
remainingPercent: 100 - percent,
|
|
94
|
-
usedMask: routeToMask.get(topRoutes[1]),
|
|
95
|
-
special: true
|
|
69
|
+
usedMask: routeToMask.get(seed),
|
|
70
|
+
quoteSoFar: quoteOf(seed)
|
|
96
71
|
});
|
|
97
72
|
}
|
|
98
73
|
}
|
|
99
74
|
let splits = 1;
|
|
100
|
-
|
|
101
|
-
// Основной цикл поиска лучших маршрутов
|
|
102
75
|
while (queue.size > 0) {
|
|
103
|
-
bestSwapsPerSplit.clear();
|
|
104
76
|
let layer = queue.size;
|
|
105
77
|
splits++;
|
|
106
78
|
if (splits >= 3 && bestSwap && bestSwap.length < splits - 1) {
|
|
@@ -116,38 +88,37 @@ export function getBestSwapRoute(routeType, percentToQuotes, percents, swapRoute
|
|
|
116
88
|
curRoutes,
|
|
117
89
|
percentIndex,
|
|
118
90
|
usedMask,
|
|
119
|
-
|
|
91
|
+
quoteSoFar
|
|
120
92
|
} = queue.dequeue();
|
|
121
93
|
for (let i = percentIndex; i >= 0; i--) {
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
94
|
+
const percent = percents[i];
|
|
95
|
+
if (percent > remainingPercent) continue;
|
|
96
|
+
const candidates = percentToSortedQuotes[percent];
|
|
97
|
+
if (!candidates || candidates.length === 0) continue;
|
|
98
|
+
const routeCandidates = findRoutesNotUsingUsedPools(usedMask, candidates, routeToMask, branchWidth);
|
|
99
|
+
if (routeCandidates.length === 0) continue;
|
|
100
|
+
for (const candidate of routeCandidates) {
|
|
101
|
+
const remainingPercentNew = remainingPercent - percent;
|
|
102
|
+
const usedMaskNew = usedMask | routeToMask.get(candidate);
|
|
103
|
+
const quoteNew = quoteSoFar.add(quoteOf(candidate));
|
|
104
|
+
if (remainingPercentNew === 0 && splits >= minSplits) {
|
|
105
|
+
const curRoutesNew = curRoutes.slice();
|
|
106
|
+
curRoutesNew.push(candidate);
|
|
107
|
+
if (!bestQuote || quoteCompFn(quoteNew, bestQuote)) {
|
|
108
|
+
bestQuote = quoteNew;
|
|
109
|
+
bestSwap = curRoutesNew;
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
const curRoutesNew = curRoutes.slice();
|
|
113
|
+
curRoutesNew.push(candidate);
|
|
114
|
+
queue.enqueue({
|
|
115
|
+
curRoutes: curRoutesNew,
|
|
116
|
+
remainingPercent: remainingPercentNew,
|
|
117
|
+
percentIndex: i,
|
|
118
|
+
usedMask: usedMaskNew,
|
|
119
|
+
quoteSoFar: quoteNew
|
|
120
|
+
});
|
|
142
121
|
}
|
|
143
|
-
} else {
|
|
144
|
-
queue.enqueue({
|
|
145
|
-
curRoutes: curRoutesNew,
|
|
146
|
-
remainingPercent: remainingPercentNew,
|
|
147
|
-
percentIndex: i,
|
|
148
|
-
usedMask: usedMaskNew,
|
|
149
|
-
special
|
|
150
|
-
});
|
|
151
122
|
}
|
|
152
123
|
}
|
|
153
124
|
}
|
|
@@ -156,18 +127,16 @@ export function getBestSwapRoute(routeType, percentToQuotes, percents, swapRoute
|
|
|
156
127
|
console.log('Could not find a valid swap');
|
|
157
128
|
return null;
|
|
158
129
|
}
|
|
159
|
-
|
|
160
|
-
const routeWithQuotes = bestSwap.sort((routeAmountA, routeAmountB) => routeAmountB.outputAmount.greaterThan(routeAmountA.outputAmount) ? 1 : -1);
|
|
161
|
-
return routeWithQuotes;
|
|
130
|
+
return bestSwap;
|
|
162
131
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const findFirstRouteNotUsingUsedPools = (usedMask, candidateRoutes, routeToMask) => {
|
|
132
|
+
const findRoutesNotUsingUsedPools = (usedMask, candidateRoutes, routeToMask, limit) => {
|
|
133
|
+
const result = [];
|
|
166
134
|
for (const candidate of candidateRoutes) {
|
|
167
135
|
const candidateMask = routeToMask.get(candidate);
|
|
168
136
|
if ((candidateMask & usedMask) === BigInt(0)) {
|
|
169
|
-
|
|
137
|
+
result.push(candidate);
|
|
138
|
+
if (result.length >= limit) return result;
|
|
170
139
|
}
|
|
171
140
|
}
|
|
172
|
-
return
|
|
141
|
+
return result;
|
|
173
142
|
};
|
|
@@ -7,6 +7,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
7
7
|
import { Trade } from '../entities';
|
|
8
8
|
import { CurrencyAmount } from '../entities/fractions';
|
|
9
9
|
import { TradeType } from '../internalConstants';
|
|
10
|
+
import { getBestSwapRoute } from './getBestSwapRoute';
|
|
10
11
|
|
|
11
12
|
// WASM module - lazy loaded
|
|
12
13
|
let wasmModule = null;
|
|
@@ -146,6 +147,8 @@ export let WASMTradeCalculator = /*#__PURE__*/function () {
|
|
|
146
147
|
amountOut: CurrencyAmount.fromRawAmount(route.output, BigInt(result.amountOut)),
|
|
147
148
|
priceImpact: result.priceImpact
|
|
148
149
|
});
|
|
150
|
+
} else {
|
|
151
|
+
trades.push(null);
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
154
|
}
|
|
@@ -212,37 +215,34 @@ export async function bestTradeWithSplitWASM(routes, amount, percents, tradeType
|
|
|
212
215
|
const allTrades = calculator.calculateTradesBatch(routes, splitAmounts);
|
|
213
216
|
|
|
214
217
|
// Group trades by percent
|
|
215
|
-
const
|
|
216
|
-
let tradeIdx = 0;
|
|
218
|
+
const quotesByPercent = {};
|
|
217
219
|
for (let i = 0; i < percents.length; i++) {
|
|
218
220
|
const percent = percents[i];
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
quotesByPercent[percent] = [];
|
|
222
|
+
}
|
|
223
|
+
const amountsPerRoute = splitAmounts.length;
|
|
224
|
+
for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {
|
|
225
|
+
for (let percentIndex = 0; percentIndex < percents.length; percentIndex++) {
|
|
226
|
+
const flatIndex = routeIndex * amountsPerRoute + percentIndex;
|
|
227
|
+
const tradeDat = allTrades[flatIndex];
|
|
222
228
|
if (tradeDat) {
|
|
223
|
-
const
|
|
229
|
+
const percent = percents[percentIndex];
|
|
230
|
+
quotesByPercent[percent].push({
|
|
224
231
|
route: tradeDat.route,
|
|
225
232
|
inputAmount: tradeDat.amountIn,
|
|
226
233
|
outputAmount: tradeDat.amountOut,
|
|
227
|
-
tradeType,
|
|
228
234
|
percent
|
|
229
235
|
});
|
|
230
|
-
tradesByPercent[percent].push(trade);
|
|
231
236
|
}
|
|
232
237
|
}
|
|
233
238
|
}
|
|
234
|
-
|
|
235
|
-
// Use existing getBestSwapRoute logic
|
|
236
|
-
const {
|
|
237
|
-
getBestSwapRoute
|
|
238
|
-
} = require('./getBestSwapRoute');
|
|
239
|
-
const bestTrades = getBestSwapRoute(tradeType, tradesByPercent, percents, swapConfig);
|
|
239
|
+
const bestTrades = getBestSwapRoute(tradeType, quotesByPercent, percents, swapConfig);
|
|
240
240
|
if (!bestTrades) return null;
|
|
241
241
|
const routeData = bestTrades.map(trade => ({
|
|
242
242
|
inputAmount: trade.inputAmount,
|
|
243
243
|
outputAmount: trade.outputAmount,
|
|
244
244
|
route: trade.route,
|
|
245
|
-
percent: trade.
|
|
245
|
+
percent: trade.percent
|
|
246
246
|
}));
|
|
247
247
|
return Trade.createUncheckedTradeWithMultipleRoutes({
|
|
248
248
|
routes: routeData,
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { Currency } from '../entities/currency';
|
|
2
|
-
import {
|
|
2
|
+
import { Route } from '../entities/route';
|
|
3
|
+
import { CurrencyAmount } from '../entities/fractions';
|
|
3
4
|
import { TradeType } from '../internalConstants';
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export interface SplitRouteQuote {
|
|
6
|
+
percent: number;
|
|
7
|
+
route: Route<Currency, Currency>;
|
|
8
|
+
inputAmount: CurrencyAmount<Currency>;
|
|
9
|
+
outputAmount: CurrencyAmount<Currency>;
|
|
10
|
+
}
|
|
11
|
+
interface SwapRouteConfig {
|
|
7
12
|
minSplits: number;
|
|
8
13
|
maxSplits: number;
|
|
9
|
-
|
|
14
|
+
branchFactor?: number;
|
|
15
|
+
candidateLimit?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function getBestSwapRoute(routeType: TradeType, percentToQuotes: {
|
|
18
|
+
[percent: number]: SplitRouteQuote[];
|
|
19
|
+
}, percents: number[], swapRouteConfig?: SwapRouteConfig): SplitRouteQuote[] | null;
|
|
20
|
+
export {};
|
|
@@ -5,109 +5,81 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.getBestSwapRoute = getBestSwapRoute;
|
|
7
7
|
var _queue = _interopRequireDefault(require("mnemonist/queue"));
|
|
8
|
-
var _fixedReverseHeap = _interopRequireDefault(require("mnemonist/fixed-reverse-heap"));
|
|
9
8
|
var _internalConstants = require("../internalConstants");
|
|
10
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
10
|
function getBestSwapRoute(routeType, percentToQuotes, percents, swapRouteConfig = {
|
|
12
11
|
minSplits: 1,
|
|
13
12
|
maxSplits: 8
|
|
14
13
|
}) {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const {
|
|
15
|
+
minSplits,
|
|
16
|
+
maxSplits,
|
|
17
|
+
branchFactor = 1,
|
|
18
|
+
candidateLimit = 0
|
|
19
|
+
} = swapRouteConfig;
|
|
20
|
+
const branchWidth = Math.max(1, branchFactor);
|
|
21
|
+
const quoteCompFn = routeType === _internalConstants.TradeType.EXACT_INPUT ? (a, b) => a.greaterThan(b) : (a, b) => a.lessThan(b);
|
|
22
|
+
const quoteOf = routeType === _internalConstants.TradeType.EXACT_INPUT ? q => q.outputAmount : q => q.inputAmount;
|
|
17
23
|
|
|
18
|
-
//
|
|
24
|
+
// Build pool bit ids without intermediate arrays.
|
|
19
25
|
const poolToBit = new Map();
|
|
20
26
|
let bitCounter = BigInt(0);
|
|
21
|
-
for (const
|
|
22
|
-
|
|
27
|
+
for (const quotes of Object.values(percentToQuotes)) {
|
|
28
|
+
for (const quote of quotes) {
|
|
29
|
+
for (const pool of quote.route.pools) {
|
|
30
|
+
if (!poolToBit.has(pool.id)) {
|
|
31
|
+
poolToBit.set(pool.id, BigInt(1) << bitCounter);
|
|
32
|
+
bitCounter += BigInt(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
23
36
|
}
|
|
24
|
-
|
|
25
|
-
// Предвычисляем маски для всех маршрутов
|
|
26
37
|
const routeToMask = new Map();
|
|
27
|
-
for (const
|
|
28
|
-
for (const
|
|
38
|
+
for (const quotes of Object.values(percentToQuotes)) {
|
|
39
|
+
for (const quote of quotes) {
|
|
29
40
|
let mask = BigInt(0);
|
|
30
|
-
for (const pool of
|
|
41
|
+
for (const pool of quote.route.pools) {
|
|
31
42
|
mask |= poolToBit.get(pool.id);
|
|
32
43
|
}
|
|
33
|
-
routeToMask.set(
|
|
44
|
+
routeToMask.set(quote, mask);
|
|
34
45
|
}
|
|
35
46
|
}
|
|
36
|
-
|
|
37
|
-
// Сортируем маршруты для каждого процента
|
|
38
47
|
const percentToSortedQuotes = {};
|
|
39
48
|
for (const percent in percentToQuotes) {
|
|
40
|
-
|
|
49
|
+
const sorted = percentToQuotes[percent].sort((a, b) => {
|
|
50
|
+
const qa = quoteOf(a);
|
|
51
|
+
const qb = quoteOf(b);
|
|
52
|
+
return quoteCompFn(qa, qb) ? -1 : 1;
|
|
53
|
+
});
|
|
54
|
+
percentToSortedQuotes[percent] = candidateLimit > 0 && sorted.length > candidateLimit ? sorted.slice(0, candidateLimit) : sorted;
|
|
41
55
|
}
|
|
42
|
-
|
|
43
|
-
// Функция сравнения для типа торговли
|
|
44
|
-
const quoteCompFn = routeType === _internalConstants.TradeType.EXACT_INPUT ? (a, b) => a.greaterThan(b) : (a, b) => a.lessThan(b);
|
|
45
|
-
|
|
46
|
-
// Функция суммирования CurrencyAmount
|
|
47
|
-
const sumFn = currencyAmounts => {
|
|
48
|
-
let sum = currencyAmounts[0];
|
|
49
|
-
for (let i = 1; i < currencyAmounts.length; i++) {
|
|
50
|
-
sum = sum.add(currencyAmounts[i]);
|
|
51
|
-
}
|
|
52
|
-
return sum;
|
|
53
|
-
};
|
|
54
56
|
let bestQuote;
|
|
55
57
|
let bestSwap;
|
|
56
|
-
|
|
57
|
-
// Храним лучшие маршруты для каждого уровня разбиения (максимум 3)
|
|
58
|
-
const bestSwapsPerSplit = new _fixedReverseHeap.default(Array, (a, b) => quoteCompFn(a.quote, b.quote) ? -1 : 1, 3);
|
|
59
|
-
const {
|
|
60
|
-
minSplits,
|
|
61
|
-
maxSplits
|
|
62
|
-
} = swapRouteConfig;
|
|
63
|
-
|
|
64
|
-
// Проверяем наличие маршрута для 100% и инициализируем начальные данные
|
|
65
|
-
if (!percentToSortedQuotes[100] || percentToSortedQuotes[100].length === 0 || minSplits > 1) {
|
|
58
|
+
if ((!percentToSortedQuotes[100] || percentToSortedQuotes[100].length === 0) && minSplits <= 1) {
|
|
66
59
|
console.log('Did not find a valid route without any splits. Continuing search anyway.');
|
|
67
|
-
} else {
|
|
68
|
-
bestQuote = percentToSortedQuotes[100][0].outputAmount;
|
|
60
|
+
} else if (minSplits <= 1 && percentToSortedQuotes[100] && percentToSortedQuotes[100][0]) {
|
|
69
61
|
bestSwap = [percentToSortedQuotes[100][0]];
|
|
70
|
-
|
|
71
|
-
bestSwapsPerSplit.push({
|
|
72
|
-
quote: routeWithQuote.outputAmount,
|
|
73
|
-
routes: [routeWithQuote]
|
|
74
|
-
});
|
|
75
|
-
}
|
|
62
|
+
bestQuote = quoteOf(percentToSortedQuotes[100][0]);
|
|
76
63
|
}
|
|
77
|
-
|
|
78
|
-
// Очередь для обработки комбинаций маршрутов (с usedMask для быстрой проверки overlap)
|
|
79
64
|
const queue = new _queue.default();
|
|
80
65
|
if (percents.length === 0) return null;
|
|
81
|
-
|
|
82
|
-
// Инициализируем очередь с топ-2 маршрутами для каждого процента
|
|
83
66
|
for (let i = percents.length - 1; i >= 0; i--) {
|
|
84
67
|
const percent = percents[i];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
const candidates = percentToSortedQuotes[percent];
|
|
69
|
+
if (!candidates || candidates.length === 0) continue;
|
|
70
|
+
const seeds = candidates.slice(0, branchWidth);
|
|
71
|
+
for (const seed of seeds) {
|
|
88
72
|
queue.enqueue({
|
|
89
|
-
curRoutes: [
|
|
73
|
+
curRoutes: [seed],
|
|
90
74
|
percentIndex: i,
|
|
91
75
|
remainingPercent: 100 - percent,
|
|
92
|
-
usedMask: routeToMask.get(
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
if (topRoutes[1]) {
|
|
97
|
-
queue.enqueue({
|
|
98
|
-
curRoutes: [topRoutes[1]],
|
|
99
|
-
percentIndex: i,
|
|
100
|
-
remainingPercent: 100 - percent,
|
|
101
|
-
usedMask: routeToMask.get(topRoutes[1]),
|
|
102
|
-
special: true
|
|
76
|
+
usedMask: routeToMask.get(seed),
|
|
77
|
+
quoteSoFar: quoteOf(seed)
|
|
103
78
|
});
|
|
104
79
|
}
|
|
105
80
|
}
|
|
106
81
|
let splits = 1;
|
|
107
|
-
|
|
108
|
-
// Основной цикл поиска лучших маршрутов
|
|
109
82
|
while (queue.size > 0) {
|
|
110
|
-
bestSwapsPerSplit.clear();
|
|
111
83
|
let layer = queue.size;
|
|
112
84
|
splits++;
|
|
113
85
|
if (splits >= 3 && bestSwap && bestSwap.length < splits - 1) {
|
|
@@ -123,38 +95,37 @@ function getBestSwapRoute(routeType, percentToQuotes, percents, swapRouteConfig
|
|
|
123
95
|
curRoutes,
|
|
124
96
|
percentIndex,
|
|
125
97
|
usedMask,
|
|
126
|
-
|
|
98
|
+
quoteSoFar
|
|
127
99
|
} = queue.dequeue();
|
|
128
100
|
for (let i = percentIndex; i >= 0; i--) {
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
101
|
+
const percent = percents[i];
|
|
102
|
+
if (percent > remainingPercent) continue;
|
|
103
|
+
const candidates = percentToSortedQuotes[percent];
|
|
104
|
+
if (!candidates || candidates.length === 0) continue;
|
|
105
|
+
const routeCandidates = findRoutesNotUsingUsedPools(usedMask, candidates, routeToMask, branchWidth);
|
|
106
|
+
if (routeCandidates.length === 0) continue;
|
|
107
|
+
for (const candidate of routeCandidates) {
|
|
108
|
+
const remainingPercentNew = remainingPercent - percent;
|
|
109
|
+
const usedMaskNew = usedMask | routeToMask.get(candidate);
|
|
110
|
+
const quoteNew = quoteSoFar.add(quoteOf(candidate));
|
|
111
|
+
if (remainingPercentNew === 0 && splits >= minSplits) {
|
|
112
|
+
const curRoutesNew = curRoutes.slice();
|
|
113
|
+
curRoutesNew.push(candidate);
|
|
114
|
+
if (!bestQuote || quoteCompFn(quoteNew, bestQuote)) {
|
|
115
|
+
bestQuote = quoteNew;
|
|
116
|
+
bestSwap = curRoutesNew;
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
const curRoutesNew = curRoutes.slice();
|
|
120
|
+
curRoutesNew.push(candidate);
|
|
121
|
+
queue.enqueue({
|
|
122
|
+
curRoutes: curRoutesNew,
|
|
123
|
+
remainingPercent: remainingPercentNew,
|
|
124
|
+
percentIndex: i,
|
|
125
|
+
usedMask: usedMaskNew,
|
|
126
|
+
quoteSoFar: quoteNew
|
|
127
|
+
});
|
|
149
128
|
}
|
|
150
|
-
} else {
|
|
151
|
-
queue.enqueue({
|
|
152
|
-
curRoutes: curRoutesNew,
|
|
153
|
-
remainingPercent: remainingPercentNew,
|
|
154
|
-
percentIndex: i,
|
|
155
|
-
usedMask: usedMaskNew,
|
|
156
|
-
special
|
|
157
|
-
});
|
|
158
129
|
}
|
|
159
130
|
}
|
|
160
131
|
}
|
|
@@ -163,18 +134,16 @@ function getBestSwapRoute(routeType, percentToQuotes, percents, swapRouteConfig
|
|
|
163
134
|
console.log('Could not find a valid swap');
|
|
164
135
|
return null;
|
|
165
136
|
}
|
|
166
|
-
|
|
167
|
-
const routeWithQuotes = bestSwap.sort((routeAmountA, routeAmountB) => routeAmountB.outputAmount.greaterThan(routeAmountA.outputAmount) ? 1 : -1);
|
|
168
|
-
return routeWithQuotes;
|
|
137
|
+
return bestSwap;
|
|
169
138
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const findFirstRouteNotUsingUsedPools = (usedMask, candidateRoutes, routeToMask) => {
|
|
139
|
+
const findRoutesNotUsingUsedPools = (usedMask, candidateRoutes, routeToMask, limit) => {
|
|
140
|
+
const result = [];
|
|
173
141
|
for (const candidate of candidateRoutes) {
|
|
174
142
|
const candidateMask = routeToMask.get(candidate);
|
|
175
143
|
if ((candidateMask & usedMask) === BigInt(0)) {
|
|
176
|
-
|
|
144
|
+
result.push(candidate);
|
|
145
|
+
if (result.length >= limit) return result;
|
|
177
146
|
}
|
|
178
147
|
}
|
|
179
|
-
return
|
|
148
|
+
return result;
|
|
180
149
|
};
|
|
@@ -9,6 +9,7 @@ exports.createTradeFromRouteWASM = createTradeFromRouteWASM;
|
|
|
9
9
|
var _entities = require("../entities");
|
|
10
10
|
var _fractions = require("../entities/fractions");
|
|
11
11
|
var _internalConstants = require("../internalConstants");
|
|
12
|
+
var _getBestSwapRoute = require("./getBestSwapRoute");
|
|
12
13
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
13
14
|
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); } }
|
|
14
15
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
@@ -153,6 +154,8 @@ let WASMTradeCalculator = exports.WASMTradeCalculator = /*#__PURE__*/function ()
|
|
|
153
154
|
amountOut: _fractions.CurrencyAmount.fromRawAmount(route.output, BigInt(result.amountOut)),
|
|
154
155
|
priceImpact: result.priceImpact
|
|
155
156
|
});
|
|
157
|
+
} else {
|
|
158
|
+
trades.push(null);
|
|
156
159
|
}
|
|
157
160
|
}
|
|
158
161
|
}
|
|
@@ -218,37 +221,34 @@ async function bestTradeWithSplitWASM(routes, amount, percents, tradeType, pools
|
|
|
218
221
|
const allTrades = calculator.calculateTradesBatch(routes, splitAmounts);
|
|
219
222
|
|
|
220
223
|
// Group trades by percent
|
|
221
|
-
const
|
|
222
|
-
let tradeIdx = 0;
|
|
224
|
+
const quotesByPercent = {};
|
|
223
225
|
for (let i = 0; i < percents.length; i++) {
|
|
224
226
|
const percent = percents[i];
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
quotesByPercent[percent] = [];
|
|
228
|
+
}
|
|
229
|
+
const amountsPerRoute = splitAmounts.length;
|
|
230
|
+
for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {
|
|
231
|
+
for (let percentIndex = 0; percentIndex < percents.length; percentIndex++) {
|
|
232
|
+
const flatIndex = routeIndex * amountsPerRoute + percentIndex;
|
|
233
|
+
const tradeDat = allTrades[flatIndex];
|
|
228
234
|
if (tradeDat) {
|
|
229
|
-
const
|
|
235
|
+
const percent = percents[percentIndex];
|
|
236
|
+
quotesByPercent[percent].push({
|
|
230
237
|
route: tradeDat.route,
|
|
231
238
|
inputAmount: tradeDat.amountIn,
|
|
232
239
|
outputAmount: tradeDat.amountOut,
|
|
233
|
-
tradeType,
|
|
234
240
|
percent
|
|
235
241
|
});
|
|
236
|
-
tradesByPercent[percent].push(trade);
|
|
237
242
|
}
|
|
238
243
|
}
|
|
239
244
|
}
|
|
240
|
-
|
|
241
|
-
// Use existing getBestSwapRoute logic
|
|
242
|
-
const {
|
|
243
|
-
getBestSwapRoute
|
|
244
|
-
} = require('./getBestSwapRoute');
|
|
245
|
-
const bestTrades = getBestSwapRoute(tradeType, tradesByPercent, percents, swapConfig);
|
|
245
|
+
const bestTrades = (0, _getBestSwapRoute.getBestSwapRoute)(tradeType, quotesByPercent, percents, swapConfig);
|
|
246
246
|
if (!bestTrades) return null;
|
|
247
247
|
const routeData = bestTrades.map(trade => ({
|
|
248
248
|
inputAmount: trade.inputAmount,
|
|
249
249
|
outputAmount: trade.outputAmount,
|
|
250
250
|
route: trade.route,
|
|
251
|
-
percent: trade.
|
|
251
|
+
percent: trade.percent
|
|
252
252
|
}));
|
|
253
253
|
return _entities.Trade.createUncheckedTradeWithMultipleRoutes({
|
|
254
254
|
routes: routeData,
|