@alcorexchange/alcor-swap-sdk 1.0.402 → 1.0.403

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.
@@ -202,48 +202,65 @@ let Pool = exports.Pool = /*#__PURE__*/function () {
202
202
 
203
203
  // Убираем проверки invariant в продакшене, если данные гарантированно валидны
204
204
  const exactInput = amountSpecified >= _internalConstants.ZERO;
205
- const state = {
206
- amountSpecifiedRemaining: amountSpecified,
207
- amountCalculated: _internalConstants.ZERO,
208
- sqrtPriceX64: this.sqrtPriceX64,
209
- tick: this.tickCurrent,
210
- liquidity: this.liquidity
211
- };
212
- while (state.amountSpecifiedRemaining !== _internalConstants.ZERO && state.sqrtPriceX64 !== sqrtPriceLimit) {
213
- const step = {
214
- sqrtPriceStartX64: state.sqrtPriceX64
215
- };
216
205
 
217
- // Оптимизация вызова nextInitializedTickWithinOneWord
218
- [step.tickNext, step.initialized] = this.tickDataProvider.nextInitializedTickWithinOneWord(state.tick, zeroForOne, this.tickSpacing);
219
- step.tickNext = Math.max(_tickMath.TickMath.MIN_TICK, Math.min(step.tickNext, _tickMath.TickMath.MAX_TICK));
220
- step.sqrtPriceNextX64 = _tickMath.TickMath.getSqrtRatioAtTick(step.tickNext);
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);
206
+ // Cache tickSpacing to avoid getter call each iteration
207
+ const tickSpacing = this.tickSpacing;
208
+
209
+ // Reset cursor for sequential tick access optimization
210
+ const provider = this.tickDataProvider;
211
+ const useCursor = typeof provider.nextInitializedTickWithinOneWordWithCursor === 'function';
212
+ if (useCursor) provider.resetCursor();
213
+
214
+ // State variables
215
+ let amountSpecifiedRemaining = amountSpecified;
216
+ let amountCalculated = _internalConstants.ZERO;
217
+ let sqrtPriceX64 = this.sqrtPriceX64;
218
+ let tick = this.tickCurrent;
219
+ let liquidity = this.liquidity;
220
+
221
+ // Step variables (reused each iteration - no object allocation)
222
+ let sqrtPriceStartX64;
223
+ let tickNext;
224
+ let initialized;
225
+ let sqrtPriceNextX64;
226
+ let amountIn;
227
+ let amountOut;
228
+ let feeAmount;
229
+ while (amountSpecifiedRemaining !== _internalConstants.ZERO && sqrtPriceX64 !== sqrtPriceLimit) {
230
+ sqrtPriceStartX64 = sqrtPriceX64;
231
+
232
+ // Use cursor-optimized version if available (O(1) vs O(log n))
233
+ [tickNext, initialized] = useCursor ? provider.nextInitializedTickWithinOneWordWithCursor(tick, zeroForOne, tickSpacing) : this.tickDataProvider.nextInitializedTickWithinOneWord(tick, zeroForOne, tickSpacing);
234
+
235
+ // Clamp tickNext to valid range
236
+ if (tickNext < _tickMath.TickMath.MIN_TICK) tickNext = _tickMath.TickMath.MIN_TICK;else if (tickNext > _tickMath.TickMath.MAX_TICK) tickNext = _tickMath.TickMath.MAX_TICK;
237
+ sqrtPriceNextX64 = _tickMath.TickMath.getSqrtRatioAtTick(tickNext);
238
+ const targetPrice = (zeroForOne ? sqrtPriceNextX64 < sqrtPriceLimit : sqrtPriceNextX64 > sqrtPriceLimit) ? sqrtPriceLimit : sqrtPriceNextX64;
239
+ [sqrtPriceX64, amountIn, amountOut, feeAmount] = _swapMath.SwapMath.computeSwapStep(sqrtPriceX64, targetPrice, liquidity, amountSpecifiedRemaining, this.fee);
223
240
  if (exactInput) {
224
- state.amountSpecifiedRemaining = state.amountSpecifiedRemaining - (step.amountIn + step.feeAmount);
225
- state.amountCalculated = state.amountCalculated - step.amountOut;
241
+ amountSpecifiedRemaining = amountSpecifiedRemaining - (amountIn + feeAmount);
242
+ amountCalculated = amountCalculated - amountOut;
226
243
  } else {
227
- state.amountSpecifiedRemaining = state.amountSpecifiedRemaining + step.amountOut;
228
- state.amountCalculated = state.amountCalculated + (step.amountIn + step.feeAmount);
244
+ amountSpecifiedRemaining = amountSpecifiedRemaining + amountOut;
245
+ amountCalculated = amountCalculated + (amountIn + feeAmount);
229
246
  }
230
- if (state.sqrtPriceX64 === step.sqrtPriceNextX64) {
231
- if (step.initialized) {
232
- let liquidityNet = BigInt(this.tickDataProvider.getTick(step.tickNext).liquidityNet);
247
+ if (sqrtPriceX64 === sqrtPriceNextX64) {
248
+ if (initialized) {
249
+ let liquidityNet = this.tickDataProvider.getTick(tickNext).liquidityNet;
233
250
  if (zeroForOne) liquidityNet = liquidityNet * _internalConstants.NEGATIVE_ONE;
234
- state.liquidity = _liquidityMath.LiquidityMath.addDelta(state.liquidity, liquidityNet);
251
+ liquidity = _liquidityMath.LiquidityMath.addDelta(liquidity, liquidityNet);
235
252
  }
236
- state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
237
- } else if (state.sqrtPriceX64 !== step.sqrtPriceStartX64) {
238
- state.tick = _tickMath.TickMath.getTickAtSqrtRatio(state.sqrtPriceX64);
253
+ tick = zeroForOne ? tickNext - 1 : tickNext;
254
+ } else if (sqrtPriceX64 !== sqrtPriceStartX64) {
255
+ tick = _tickMath.TickMath.getTickAtSqrtRatio(sqrtPriceX64);
239
256
  }
240
257
  }
241
258
  return {
242
- amountA: zeroForOne === exactInput ? amountSpecified - state.amountSpecifiedRemaining : state.amountCalculated,
243
- amountB: zeroForOne === exactInput ? state.amountCalculated : amountSpecified - state.amountSpecifiedRemaining,
244
- sqrtPriceX64: state.sqrtPriceX64,
245
- liquidity: state.liquidity,
246
- tickCurrent: state.tick
259
+ amountA: zeroForOne === exactInput ? amountSpecified - amountSpecifiedRemaining : amountCalculated,
260
+ amountB: zeroForOne === exactInput ? amountCalculated : amountSpecified - amountSpecifiedRemaining,
261
+ sqrtPriceX64,
262
+ liquidity,
263
+ tickCurrent: tick
247
264
  };
248
265
  }
249
266
  }, {
@@ -5,9 +5,14 @@ import { TickDataProvider } from "./tickDataProvider";
5
5
  */
6
6
  export declare class TickListDataProvider implements TickDataProvider {
7
7
  ticks: readonly Tick[];
8
+ private _cursorIndex;
8
9
  constructor(ticks: (Tick | TickConstructorArgs)[], tickSpacing: number);
9
10
  getTick(tick: number): Tick;
11
+ /** Reset cursor for new swap */
12
+ resetCursor(): void;
10
13
  nextInitializedTickWithinOneWord(tick: number, lte: boolean, tickSpacing: number): [number, boolean];
14
+ /** Optimized version with cursor - O(1) for sequential access */
15
+ nextInitializedTickWithinOneWordWithCursor(tick: number, lte: boolean, tickSpacing: number): [number, boolean];
11
16
  static toJSON(ticks: Tick[]): object;
12
17
  static fromJSON(ticksArray: any): TickListDataProvider;
13
18
  }
@@ -19,6 +19,7 @@ let TickListDataProvider = exports.TickListDataProvider = /*#__PURE__*/function
19
19
  function TickListDataProvider(ticks, tickSpacing) {
20
20
  _classCallCheck(this, TickListDataProvider);
21
21
  _defineProperty(this, "ticks", void 0);
22
+ _defineProperty(this, "_cursorIndex", -1);
22
23
  const ticksMapped = ticks.map(t => t instanceof _tick.Tick ? t : new _tick.Tick(t));
23
24
  _tickList.TickList.validateList(ticksMapped, tickSpacing);
24
25
  this.ticks = ticksMapped;
@@ -28,11 +29,27 @@ let TickListDataProvider = exports.TickListDataProvider = /*#__PURE__*/function
28
29
  value: function getTick(tick) {
29
30
  return _tickList.TickList.getTick(this.ticks, tick);
30
31
  }
32
+
33
+ /** Reset cursor for new swap */
34
+ }, {
35
+ key: "resetCursor",
36
+ value: function resetCursor() {
37
+ this._cursorIndex = -1;
38
+ }
31
39
  }, {
32
40
  key: "nextInitializedTickWithinOneWord",
33
41
  value: function nextInitializedTickWithinOneWord(tick, lte, tickSpacing) {
34
42
  return _tickList.TickList.nextInitializedTickWithinOneWord(this.ticks, tick, lte, tickSpacing);
35
43
  }
44
+
45
+ /** Optimized version with cursor - O(1) for sequential access */
46
+ }, {
47
+ key: "nextInitializedTickWithinOneWordWithCursor",
48
+ value: function nextInitializedTickWithinOneWordWithCursor(tick, lte, tickSpacing) {
49
+ const [tickNext, initialized, newCursor] = _tickList.TickList.nextInitializedTickWithinOneWordWithCursor(this.ticks, tick, lte, tickSpacing, this._cursorIndex);
50
+ this._cursorIndex = newCursor;
51
+ return [tickNext, initialized];
52
+ }
36
53
  }], [{
37
54
  key: "toJSON",
38
55
  value: function toJSON(ticks) {
@@ -2,6 +2,7 @@ import { Currency } from './currency';
2
2
  import { Percent, Price, CurrencyAmount } from './fractions';
3
3
  import { TradeType } from '../internalConstants';
4
4
  import { Route } from './route';
5
+ import { Pool } from './pool';
5
6
  /**
6
7
  * Trades comparator, an extension of the input output comparator that also considers other dimensions of the trade in ranking them
7
8
  * @template TInput The input token, either Ether or an ERC-20
@@ -105,6 +106,14 @@ export declare class Trade<TInput extends Currency, TOutput extends Currency, TT
105
106
  * @returns The exact out trade
106
107
  */
107
108
  static exactOut<TInput extends Currency, TOutput extends Currency>(route: Route<TInput, TOutput>, amountOut: CurrencyAmount<TOutput>): Trade<TInput, TOutput, TradeType.EXACT_OUTPUT>;
109
+ /**
110
+ * WASM-accelerated version of fromRoute (for EXACT_INPUT only)
111
+ * @param route route to swap through
112
+ * @param amount the input amount
113
+ * @param pools All pools for calculation
114
+ * @returns Promise of the trade
115
+ */
116
+ static fromRouteWASM<TInput extends Currency, TOutput extends Currency>(route: Route<TInput, TOutput>, amount: CurrencyAmount<TInput>, pools: Pool[]): Promise<Trade<TInput, TOutput, TradeType.EXACT_INPUT>>;
108
117
  /**
109
118
  * Constructs a trade by simulating swaps through the given route
110
119
  * @template TInput The input token, either Ether or an ERC-20.
@@ -191,6 +200,20 @@ export declare class Trade<TInput extends Currency, TOutput extends Currency, TT
191
200
  worstExecutionPrice(slippageTolerance: Percent): Price<TInput, TOutput>;
192
201
  static bestTradeExactIn<TInput extends Currency, TOutput extends Currency>(routes: Route<TInput, TOutput>[], currencyAmountIn: CurrencyAmount<TInput>, maxNumResults?: number): Trade<TInput, TOutput, TradeType.EXACT_INPUT>[];
193
202
  static bestTradeExactOut<TInput extends Currency, TOutput extends Currency>(routes: Route<TInput, TOutput>[], currencyAmountOut: CurrencyAmount<TOutput>, maxNumResults?: number): Trade<TInput, TOutput, TradeType.EXACT_OUTPUT>[];
203
+ /**
204
+ * WASM-accelerated version of bestTradeWithSplit
205
+ * @param _routes Routes to consider
206
+ * @param amount Amount to swap
207
+ * @param percents Percentages to split
208
+ * @param tradeType Type of trade
209
+ * @param pools All pools for trade calculation
210
+ * @param swapConfig Configuration for splits
211
+ * @returns Best trade or null
212
+ */
213
+ static bestTradeWithSplitWASM<TInput extends Currency, TOutput extends Currency>(_routes: Route<TInput, TOutput>[], amount: CurrencyAmount<Currency>, percents: number[], tradeType: TradeType, pools: Pool[], swapConfig?: {
214
+ minSplits: number;
215
+ maxSplits: number;
216
+ }): Promise<Trade<Currency, Currency, TradeType> | null>;
194
217
  static bestTradeWithSplit<TInput extends Currency, TOutput extends Currency>(_routes: Route<TInput, TOutput>[], amount: CurrencyAmount<Currency>, percents: number[], tradeType: TradeType, swapConfig?: {
195
218
  minSplits: number;
196
219
  maxSplits: number;
@@ -14,6 +14,8 @@ var _getBestSwapRoute = require("../utils/getBestSwapRoute");
14
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
15
  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; }
16
16
  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; }
17
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
18
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
19
  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
18
20
  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); } }
19
21
  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
@@ -302,6 +304,22 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
302
304
  return Trade.fromRoute(route, amountOut, _internalConstants.TradeType.EXACT_OUTPUT);
303
305
  }
304
306
 
307
+ /**
308
+ * WASM-accelerated version of fromRoute (for EXACT_INPUT only)
309
+ * @param route route to swap through
310
+ * @param amount the input amount
311
+ * @param pools All pools for calculation
312
+ * @returns Promise of the trade
313
+ */
314
+ }, {
315
+ key: "fromRouteWASM",
316
+ value: async function fromRouteWASM(route, amount, pools) {
317
+ const {
318
+ createTradeFromRouteWASM
319
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('../utils/tradeCalculatorWASM')));
320
+ return createTradeFromRouteWASM(route, amount, _internalConstants.TradeType.EXACT_INPUT, pools);
321
+ }
322
+
305
323
  /**
306
324
  * Constructs a trade by simulating swaps through the given route
307
325
  * @template TInput The input token, either Ether or an ERC-20.
@@ -442,8 +460,38 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
442
460
  value: function bestTradeExactIn(routes, currencyAmountIn) {
443
461
  let maxNumResults = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
444
462
  (0, _tinyInvariant.default)(routes.length > 0, 'ROUTES');
463
+
464
+ // Pre-filter: remove routes with zero-liquidity pools
465
+ const validRoutes = routes.filter(route => route.pools.every(pool => pool.active && pool.liquidity > _internalConstants.ZERO));
466
+
467
+ // Helper: compute min liquidity using JSBI (no overflow)
468
+ const getMinLiquidity = route => {
469
+ let min = route.pools[0].liquidity;
470
+ for (let i = 1; i < route.pools.length; i++) {
471
+ if (route.pools[i].liquidity < min) {
472
+ min = route.pools[i].liquidity;
473
+ }
474
+ }
475
+ return min;
476
+ };
477
+
478
+ // Precompute min liquidity for sorting
479
+ const routeMinLiq = new Map();
480
+ for (const route of validRoutes) {
481
+ routeMinLiq.set(route, getMinLiquidity(route));
482
+ }
483
+
484
+ // Sort routes: fewer hops first, then by min liquidity desc
485
+ validRoutes.sort((a, b) => {
486
+ if (a.pools.length !== b.pools.length) return a.pools.length - b.pools.length;
487
+ const minLiqA = routeMinLiq.get(a);
488
+ const minLiqB = routeMinLiq.get(b);
489
+ if (minLiqA > minLiqB) return -1;
490
+ if (minLiqA < minLiqB) return 1;
491
+ return 0;
492
+ });
445
493
  const bestTrades = [];
446
- for (const route of routes) {
494
+ for (const route of validRoutes) {
447
495
  let trade;
448
496
  try {
449
497
  trade = Trade.fromRoute(route, currencyAmountIn, _internalConstants.TradeType.EXACT_INPUT);
@@ -455,8 +503,8 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
455
503
  throw error;
456
504
  }
457
505
 
458
- // FIXME! Sorting bug multiple pools
459
- if (!trade.inputAmount.greaterThan(0) || !trade.priceImpact.greaterThan(0)) continue;
506
+ // Only check outputAmount > 0, skip expensive priceImpact calculation
507
+ if (!trade.outputAmount.greaterThan(0)) continue;
460
508
  (0, _utils.sortedInsert)(bestTrades, trade, maxNumResults, tradeComparator);
461
509
  }
462
510
  return bestTrades;
@@ -483,6 +531,29 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
483
531
  }
484
532
  return bestTrades;
485
533
  }
534
+
535
+ /**
536
+ * WASM-accelerated version of bestTradeWithSplit
537
+ * @param _routes Routes to consider
538
+ * @param amount Amount to swap
539
+ * @param percents Percentages to split
540
+ * @param tradeType Type of trade
541
+ * @param pools All pools for trade calculation
542
+ * @param swapConfig Configuration for splits
543
+ * @returns Best trade or null
544
+ */
545
+ }, {
546
+ key: "bestTradeWithSplitWASM",
547
+ value: async function bestTradeWithSplitWASM(_routes, amount, percents, tradeType, pools) {
548
+ let swapConfig = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {
549
+ minSplits: 1,
550
+ maxSplits: 10
551
+ };
552
+ const {
553
+ bestTradeWithSplitWASM
554
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('../utils/tradeCalculatorWASM')));
555
+ return bestTradeWithSplitWASM(_routes, amount, percents, tradeType, pools, swapConfig);
556
+ }
486
557
  }, {
487
558
  key: "bestTradeWithSplit",
488
559
  value: function bestTradeWithSplit(_routes, amount, percents, tradeType) {
@@ -493,6 +564,35 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
493
564
  (0, _tinyInvariant.default)(_routes.length > 0, 'ROUTES');
494
565
  (0, _tinyInvariant.default)(percents.length > 0, 'PERCENTS');
495
566
 
567
+ // Pre-filter: remove routes with zero-liquidity or inactive pools
568
+ const validRoutes = _routes.filter(route => route.pools.every(pool => pool.active && pool.liquidity > _internalConstants.ZERO));
569
+
570
+ // Helper: compute min liquidity for a route using JSBI (no overflow)
571
+ const getMinLiquidity = route => {
572
+ let min = route.pools[0].liquidity;
573
+ for (let i = 1; i < route.pools.length; i++) {
574
+ if (route.pools[i].liquidity < min) {
575
+ min = route.pools[i].liquidity;
576
+ }
577
+ }
578
+ return min;
579
+ };
580
+
581
+ // Precompute min liquidity for sorting (avoid recalculating)
582
+ const routeMinLiq = new Map();
583
+ for (const route of validRoutes) {
584
+ routeMinLiq.set(route, getMinLiquidity(route));
585
+ }
586
+
587
+ // Sort routes by min liquidity (descending) - no hop preference
588
+ validRoutes.sort((a, b) => {
589
+ const minLiqA = routeMinLiq.get(a);
590
+ const minLiqB = routeMinLiq.get(b);
591
+ if (minLiqA > minLiqB) return -1;
592
+ if (minLiqA < minLiqB) return 1;
593
+ return 0;
594
+ });
595
+
496
596
  // Предварительно вычисляем splitAmount для всех процентов
497
597
  const percentToAmount = new Map();
498
598
  for (const percent of percents) {
@@ -506,13 +606,14 @@ let Trade = exports.Trade = /*#__PURE__*/function () {
506
606
  }
507
607
 
508
608
  // Оптимизируем внутренний цикл - группируем вычисления по маршрутам
509
- for (const route of _routes) {
510
- // Для каждого маршрута проходим по всем процентам
609
+ for (const route of validRoutes) {
511
610
  for (const percent of percents) {
512
611
  const splitAmount = percentToAmount.get(percent);
513
612
  try {
514
613
  const trade = Trade.fromRoute(route, splitAmount, tradeType, percent);
515
- if (trade.inputAmount.greaterThan(0) && trade.priceImpact.greaterThan(0)) {
614
+
615
+ // Only check outputAmount > 0, skip expensive priceImpact calculation
616
+ if (trade.outputAmount.greaterThan(0)) {
516
617
  percentToTrades.get(percent).push(trade);
517
618
  }
518
619
  } catch (error) {
@@ -18,28 +18,18 @@ let FullMath = exports.FullMath = /*#__PURE__*/function () {
18
18
  return _createClass(FullMath, null, [{
19
19
  key: "mulDivRoundingUp",
20
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
21
+ // Early return for zero inputs (most common fast path)
27
22
  if (a === ZERO || b === ZERO) {
28
23
  return ZERO;
29
24
  }
30
25
 
31
- // Early return if denominator is one
32
- if (denominator === ONE) {
33
- return a * b;
34
- }
35
-
36
- // Calculate product, quotient, and remainder
26
+ // Calculate product and quotient (1 division instead of 2)
37
27
  const product = a * b;
38
28
  const quotient = product / denominator;
39
- const remainder = product % denominator;
40
29
 
41
- // Round up if theres a non-zero remainder
42
- if (remainder !== ZERO) {
30
+ // Round up if there's remainder: check if product !== quotient * denominator
31
+ // This avoids expensive % operation (which is another division internally)
32
+ if (product !== quotient * denominator) {
43
33
  return quotient + ONE;
44
34
  }
45
35
  return quotient;
@@ -16,18 +16,18 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
16
16
  // Извлекаем уникальные пулы из всех маршрутов
17
17
  const allPools = [...new Set(Object.values(percentToQuotes).flatMap(routes => routes.flatMap(r => r.route.pools)))];
18
18
 
19
- // Создаем битовую карту для пулов
19
+ // Создаем битовую карту для пулов (bigint для поддержки >31 пулов)
20
20
  const poolToBit = new Map();
21
- let bitCounter = 0;
21
+ let bitCounter = BigInt(0);
22
22
  for (const pool of allPools) {
23
- poolToBit.set(pool.id, 1 << bitCounter++);
23
+ poolToBit.set(pool.id, BigInt(1) << bitCounter++);
24
24
  }
25
25
 
26
26
  // Предвычисляем маски для всех маршрутов
27
27
  const routeToMask = new Map();
28
28
  for (const routes of Object.values(percentToQuotes)) {
29
29
  for (const route of routes) {
30
- let mask = 0;
30
+ let mask = BigInt(0);
31
31
  for (const pool of route.route.pools) {
32
32
  mask |= poolToBit.get(pool.id);
33
33
  }
@@ -76,7 +76,7 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
76
76
  }
77
77
  }
78
78
 
79
- // Очередь для обработки комбинаций маршрутов
79
+ // Очередь для обработки комбинаций маршрутов (с usedMask для быстрой проверки overlap)
80
80
  const queue = new _queue.default();
81
81
  if (percents.length === 0) return null;
82
82
 
@@ -90,6 +90,7 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
90
90
  curRoutes: [topRoutes[0]],
91
91
  percentIndex: i,
92
92
  remainingPercent: 100 - percent,
93
+ usedMask: routeToMask.get(topRoutes[0]),
93
94
  special: false
94
95
  });
95
96
  }
@@ -98,6 +99,7 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
98
99
  curRoutes: [topRoutes[1]],
99
100
  percentIndex: i,
100
101
  remainingPercent: 100 - percent,
102
+ usedMask: routeToMask.get(topRoutes[1]),
101
103
  special: true
102
104
  });
103
105
  }
@@ -121,6 +123,7 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
121
123
  remainingPercent,
122
124
  curRoutes,
123
125
  percentIndex,
126
+ usedMask,
124
127
  special
125
128
  } = queue.dequeue();
126
129
  for (let i = percentIndex; i >= 0; i--) {
@@ -128,11 +131,12 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
128
131
  if (percentA > remainingPercent) continue;
129
132
  if (!percentToSortedQuotes[percentA] || percentToSortedQuotes[percentA].length === 0) continue;
130
133
  const candidateRoutesA = percentToSortedQuotes[percentA];
131
- const routeWithQuoteA = findFirstRouteNotUsingUsedPools(curRoutes, candidateRoutesA, routeToMask);
134
+ const routeWithQuoteA = findFirstRouteNotUsingUsedPools(usedMask, candidateRoutesA, routeToMask);
132
135
  if (!routeWithQuoteA) continue;
133
136
  const remainingPercentNew = remainingPercent - percentA;
134
137
  const curRoutesNew = curRoutes.slice();
135
138
  curRoutesNew.push(routeWithQuoteA);
139
+ const usedMaskNew = usedMask | routeToMask.get(routeWithQuoteA);
136
140
  if (remainingPercentNew === 0 && splits >= minSplits) {
137
141
  const quotesNew = curRoutesNew.map(r => r.outputAmount);
138
142
  const quoteNew = sumFn(quotesNew);
@@ -149,6 +153,7 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
149
153
  curRoutes: curRoutesNew,
150
154
  remainingPercent: remainingPercentNew,
151
155
  percentIndex: i,
156
+ usedMask: usedMaskNew,
152
157
  special
153
158
  });
154
159
  }
@@ -165,14 +170,10 @@ function getBestSwapRoute(routeType, percentToQuotes, percents) {
165
170
  }
166
171
 
167
172
  // Вспомогательная функция для поиска маршрута без пересекающихся пулов
168
- const findFirstRouteNotUsingUsedPools = (usedRoutes, candidateRoutes, routeToMask) => {
169
- let usedMask = 0;
170
- for (const route of usedRoutes) {
171
- usedMask |= routeToMask.get(route);
172
- }
173
+ const findFirstRouteNotUsingUsedPools = (usedMask, candidateRoutes, routeToMask) => {
173
174
  for (const candidate of candidateRoutes) {
174
175
  const candidateMask = routeToMask.get(candidate);
175
- if ((candidateMask & usedMask) === 0) {
176
+ if ((candidateMask & usedMask) === BigInt(0)) {
176
177
  return candidate;
177
178
  }
178
179
  }
@@ -13,7 +13,26 @@ 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
+ // Cache for fee factors - feePips values are limited (100, 500, 3000, 10000)
18
+ const feeFactorCache = new Map();
19
+ const feePipsCache = new Map();
20
+ function getFeeFactor(feePips) {
21
+ let cached = feeFactorCache.get(feePips);
22
+ if (cached === undefined) {
23
+ cached = MAX_FEE - getFeePipsBigInt(feePips);
24
+ feeFactorCache.set(feePips, cached);
25
+ }
26
+ return cached;
27
+ }
28
+ function getFeePipsBigInt(feePips) {
29
+ let cached = feePipsCache.get(feePips);
30
+ if (cached === undefined) {
31
+ cached = BigInt(feePips);
32
+ feePipsCache.set(feePips, cached);
33
+ }
34
+ return cached;
35
+ }
17
36
  let SwapMath = exports.SwapMath = /*#__PURE__*/function () {
18
37
  /**
19
38
  * Cannot be constructed.
@@ -31,7 +50,7 @@ let SwapMath = exports.SwapMath = /*#__PURE__*/function () {
31
50
  let amountOut;
32
51
  let feeAmount;
33
52
  if (exactIn) {
34
- const feeFactor = MAX_FEE_MINUS_FEE_PIPS(feePips);
53
+ const feeFactor = getFeeFactor(feePips);
35
54
  const amountRemainingLessFee = amountRemaining * feeFactor / MAX_FEE;
36
55
  const deltaIn = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioTargetX64, sqrtRatioCurrentX64, liquidity, true) : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioCurrentX64, sqrtRatioTargetX64, liquidity, true);
37
56
  if (amountRemainingLessFee >= deltaIn) {
@@ -42,7 +61,7 @@ let SwapMath = exports.SwapMath = /*#__PURE__*/function () {
42
61
  amountIn = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioNextX64, sqrtRatioCurrentX64, liquidity, true) : _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioCurrentX64, sqrtRatioNextX64, liquidity, true);
43
62
  }
44
63
  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);
64
+ feeAmount = sqrtRatioNextX64 !== sqrtRatioTargetX64 ? amountRemaining - amountIn : _fullMath.FullMath.mulDivRoundingUp(amountIn, getFeePipsBigInt(feePips), feeFactor);
46
65
  } else {
47
66
  const deltaOut = zeroForOne ? _sqrtPriceMath.SqrtPriceMath.getAmountBDelta(sqrtRatioTargetX64, sqrtRatioCurrentX64, liquidity, false) : _sqrtPriceMath.SqrtPriceMath.getAmountADelta(sqrtRatioCurrentX64, sqrtRatioTargetX64, liquidity, false);
48
67
  const amountRemainingNegative = amountRemaining * _internalConstants.NEGATIVE_ONE;
@@ -57,7 +76,7 @@ let SwapMath = exports.SwapMath = /*#__PURE__*/function () {
57
76
  if (amountOut > amountRemainingNegative) {
58
77
  amountOut = amountRemainingNegative;
59
78
  }
60
- feeAmount = _fullMath.FullMath.mulDivRoundingUp(amountIn, BigInt(feePips), MAX_FEE_MINUS_FEE_PIPS(feePips));
79
+ feeAmount = _fullMath.FullMath.mulDivRoundingUp(amountIn, getFeePipsBigInt(feePips), getFeeFactor(feePips));
61
80
  }
62
81
  return [sqrtRatioNextX64, amountIn, amountOut, feeAmount];
63
82
  }
@@ -20,4 +20,10 @@ export declare abstract class TickList {
20
20
  private static binarySearch;
21
21
  static nextInitializedTick(ticks: readonly Tick[], tick: number, lte: boolean): Tick;
22
22
  static nextInitializedTickWithinOneWord(ticks: readonly Tick[], tick: number, lte: boolean, tickSpacing: number): [number, boolean];
23
+ /**
24
+ * Optimized version with cursor hint for sequential access (swap loops)
25
+ * @param cursorHint - last known index position, -1 if unknown
26
+ * @returns [tickId, initialized, newCursorIndex]
27
+ */
28
+ static nextInitializedTickWithinOneWordWithCursor(ticks: readonly Tick[], tick: number, lte: boolean, tickSpacing: number, cursorHint: number): [number, boolean, number];
23
29
  }
@@ -144,5 +144,61 @@ let TickList = exports.TickList = /*#__PURE__*/function () {
144
144
  return [nextInitializedTick, nextInitializedTick === ticks[id + 1].id];
145
145
  }
146
146
  }
147
+
148
+ /**
149
+ * Optimized version with cursor hint for sequential access (swap loops)
150
+ * @param cursorHint - last known index position, -1 if unknown
151
+ * @returns [tickId, initialized, newCursorIndex]
152
+ */
153
+ }, {
154
+ key: "nextInitializedTickWithinOneWordWithCursor",
155
+ value: function nextInitializedTickWithinOneWordWithCursor(ticks, tick, lte, tickSpacing, cursorHint) {
156
+ const compressed = Math.floor(tick / tickSpacing);
157
+ const tickCount = ticks.length;
158
+ if (lte) {
159
+ const wordPos = compressed >> 7;
160
+ const minimum = (wordPos << 7) * tickSpacing;
161
+ if (tick < ticks[0].id) return [minimum, false, -1];
162
+ if (tick >= ticks[tickCount - 1].id) return [ticks[tickCount - 1].id, true, tickCount - 1];
163
+
164
+ // Use cursor hint for O(1) lookup if valid
165
+ let id;
166
+ if (cursorHint >= 0 && cursorHint < tickCount) {
167
+ // Linear scan from hint (swap moves monotonically)
168
+ if (ticks[cursorHint].id <= tick && (cursorHint === tickCount - 1 || ticks[cursorHint + 1].id > tick)) {
169
+ id = cursorHint;
170
+ } else if (cursorHint > 0 && ticks[cursorHint - 1].id <= tick && ticks[cursorHint].id > tick) {
171
+ id = cursorHint - 1;
172
+ } else {
173
+ id = this.binarySearch(ticks, tick);
174
+ }
175
+ } else {
176
+ id = this.binarySearch(ticks, tick);
177
+ }
178
+ const nextInitializedTick = Math.max(minimum, ticks[id].id);
179
+ return [nextInitializedTick, nextInitializedTick === ticks[id].id, id];
180
+ } else {
181
+ const wordPos = compressed + 1 >> 7;
182
+ const maximum = ((wordPos + 1 << 7) - 1) * tickSpacing;
183
+ if (tick >= ticks[tickCount - 1].id) return [maximum, false, tickCount - 1];
184
+ if (tick < ticks[0].id) return [ticks[0].id, true, 0];
185
+
186
+ // Use cursor hint for O(1) lookup if valid
187
+ let id;
188
+ if (cursorHint >= 0 && cursorHint < tickCount - 1) {
189
+ if (ticks[cursorHint].id <= tick && ticks[cursorHint + 1].id > tick) {
190
+ id = cursorHint;
191
+ } else if (cursorHint < tickCount - 2 && ticks[cursorHint + 1].id <= tick && ticks[cursorHint + 2].id > tick) {
192
+ id = cursorHint + 1;
193
+ } else {
194
+ id = this.binarySearch(ticks, tick);
195
+ }
196
+ } else {
197
+ id = this.binarySearch(ticks, tick);
198
+ }
199
+ const nextInitializedTick = Math.min(maximum, ticks[id + 1].id);
200
+ return [nextInitializedTick, nextInitializedTick === ticks[id + 1].id, id];
201
+ }
202
+ }
147
203
  }]);
148
204
  }();
@@ -16,6 +16,10 @@ export declare abstract class TickMath {
16
16
  * The sqrt ratio corresponding to the maximum tick that could be used on any pool.
17
17
  */
18
18
  static MAX_SQRT_RATIO: JSBI;
19
+ /**
20
+ * Clears all caches. Call this if memory usage is a concern.
21
+ */
22
+ static clearCache(): void;
19
23
  /**
20
24
  * Returns the sqrt ratio as a Q64.96 for the given tick. The sqrt ratio is computed as sqrt(1.0001)^tick
21
25
  * @param tick the tick for which to compute the sqrt ratio
@@ -18,18 +18,37 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
18
18
  function mulShift(val, mulBy) {
19
19
  return val * BigInt(mulBy) >> 128n;
20
20
  }
21
+
22
+ // Memoization caches
23
+ const sqrtRatioCache = new Map();
24
+ const tickAtSqrtRatioCache = new Map();
21
25
  let TickMath = exports.TickMath = /*#__PURE__*/function () {
22
26
  function TickMath() {
23
27
  _classCallCheck(this, TickMath);
24
28
  }
25
29
  return _createClass(TickMath, null, [{
26
- key: "getSqrtRatioAtTick",
30
+ key: "clearCache",
27
31
  value:
32
+ /**
33
+ * Clears all caches. Call this if memory usage is a concern.
34
+ */
35
+ function clearCache() {
36
+ sqrtRatioCache.clear();
37
+ tickAtSqrtRatioCache.clear();
38
+ }
39
+
28
40
  /**
29
41
  * Returns the sqrt ratio as a Q64.96 for the given tick. The sqrt ratio is computed as sqrt(1.0001)^tick
30
42
  * @param tick the tick for which to compute the sqrt ratio
31
43
  */
32
- function getSqrtRatioAtTick(tick) {
44
+ }, {
45
+ key: "getSqrtRatioAtTick",
46
+ value: function getSqrtRatioAtTick(tick) {
47
+ // Check cache first
48
+ const cached = sqrtRatioCache.get(tick);
49
+ if (cached !== undefined) {
50
+ return cached;
51
+ }
33
52
  (0, _tinyInvariant.default)(tick >= TickMath.MIN_TICK && tick <= TickMath.MAX_TICK && Number.isInteger(tick), "TICK");
34
53
  const absTick = tick < 0 ? tick * -1 : tick;
35
54
  let ratio = (absTick & 0x1) != 0 ? BigInt("0xfffcb933bd6fad37aa2d162d1a594001") : BigInt("0x100000000000000000000000000000000");
@@ -55,7 +74,11 @@ let TickMath = exports.TickMath = /*#__PURE__*/function () {
55
74
  if (tick > 0) ratio = _internalConstants.MaxUint256 / ratio;
56
75
 
57
76
  // back to Q64
58
- return ratio % _internalConstants.Q64 > _internalConstants.ZERO ? ratio / _internalConstants.Q64 + _internalConstants.ONE : ratio / _internalConstants.Q64;
77
+ const result = ratio % _internalConstants.Q64 > _internalConstants.ZERO ? ratio / _internalConstants.Q64 + _internalConstants.ONE : ratio / _internalConstants.Q64;
78
+
79
+ // Store in cache
80
+ sqrtRatioCache.set(tick, result);
81
+ return result;
59
82
  }
60
83
 
61
84
  /**
@@ -66,6 +89,12 @@ let TickMath = exports.TickMath = /*#__PURE__*/function () {
66
89
  }, {
67
90
  key: "getTickAtSqrtRatio",
68
91
  value: function getTickAtSqrtRatio(sqrtRatioX64) {
92
+ // Check cache first
93
+ const cacheKey = sqrtRatioX64.toString();
94
+ const cached = tickAtSqrtRatioCache.get(cacheKey);
95
+ if (cached !== undefined) {
96
+ return cached;
97
+ }
69
98
  (0, _tinyInvariant.default)(sqrtRatioX64 >= TickMath.MIN_SQRT_RATIO && sqrtRatioX64 < TickMath.MAX_SQRT_RATIO, "SQRT_RATIO");
70
99
  const sqrtRatioX128 = sqrtRatioX64 << 64n;
71
100
  const msb = (0, _mostSignificantBit.mostSignificantBit)(sqrtRatioX128);
@@ -85,7 +114,11 @@ let TickMath = exports.TickMath = /*#__PURE__*/function () {
85
114
  const log_sqrt10001 = log_2 * 255738958999603826347141n;
86
115
  const tickLow = Number(log_sqrt10001 - 3402992956809132418596140100660247210n >> 128n);
87
116
  const tickHigh = Number(log_sqrt10001 + 291339464771989622907027621153398088495n >> 128n);
88
- return tickLow === tickHigh ? tickLow : TickMath.getSqrtRatioAtTick(tickHigh) <= sqrtRatioX64 ? tickHigh : tickLow;
117
+ const result = tickLow === tickHigh ? tickLow : TickMath.getSqrtRatioAtTick(tickHigh) <= sqrtRatioX64 ? tickHigh : tickLow;
118
+
119
+ // Store in cache
120
+ tickAtSqrtRatioCache.set(cacheKey, result);
121
+ return result;
89
122
  }
90
123
  }]);
91
124
  }();
@@ -0,0 +1,45 @@
1
+ import { Token, Pool, Route, Trade } from '../entities';
2
+ import { CurrencyAmount } from '../entities/fractions';
3
+ import { TradeType } from '../internalConstants';
4
+ /**
5
+ * WASM-accelerated trade calculator
6
+ */
7
+ export declare class WASMTradeCalculator {
8
+ private initialized;
9
+ private poolsMap;
10
+ /**
11
+ * Initialize with pools including full swap data
12
+ */
13
+ initializeWithPools(pools: Pool[]): Promise<void>;
14
+ /**
15
+ * Calculate trade output for a single route (WASM-accelerated)
16
+ */
17
+ calculateTradeOutput(route: Route<Token, Token>, amountIn: CurrencyAmount<Token>): {
18
+ amountOut: CurrencyAmount<Token>;
19
+ priceImpact: number;
20
+ };
21
+ /**
22
+ * Batch calculate trades for multiple routes and amounts
23
+ */
24
+ calculateTradesBatch(routes: Route<Token, Token>[], amounts: CurrencyAmount<Token>[]): Array<{
25
+ route: Route<Token, Token>;
26
+ amountIn: CurrencyAmount<Token>;
27
+ amountOut: CurrencyAmount<Token>;
28
+ priceImpact: number;
29
+ }>;
30
+ /**
31
+ * Clear all pools from memory
32
+ */
33
+ clear(): void;
34
+ }
35
+ /**
36
+ * Create a WASM-accelerated Trade from a route
37
+ */
38
+ export declare function createTradeFromRouteWASM(route: Route<Token, Token>, amount: CurrencyAmount<Token>, tradeType: TradeType, pools: Pool[]): Promise<Trade<Token, Token, TradeType>>;
39
+ /**
40
+ * Find best trade with split using WASM acceleration
41
+ */
42
+ export declare function bestTradeWithSplitWASM(routes: Route<Token, Token>[], amount: CurrencyAmount<Token>, percents: number[], tradeType: TradeType, pools: Pool[], swapConfig?: {
43
+ minSplits: number;
44
+ maxSplits: number;
45
+ }): Promise<Trade<Token, Token, TradeType> | null>;
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.WASMTradeCalculator = void 0;
7
+ exports.bestTradeWithSplitWASM = bestTradeWithSplitWASM;
8
+ exports.createTradeFromRouteWASM = createTradeFromRouteWASM;
9
+ var _entities = require("../entities");
10
+ var _fractions = require("../entities/fractions");
11
+ var _internalConstants = require("../internalConstants");
12
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
13
+ 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
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), 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
+ // WASM module - lazy loaded
19
+ let wasmModule = null;
20
+ function loadWasmModule() {
21
+ if (!wasmModule) {
22
+ try {
23
+ wasmModule = require('./wasm_route_finder.js');
24
+ } catch (error) {
25
+ console.error('Failed to load WASM module:', error);
26
+ throw new Error('WASM module not available');
27
+ }
28
+ }
29
+ return wasmModule;
30
+ }
31
+
32
+ /**
33
+ * WASM-accelerated trade calculator
34
+ */
35
+ let WASMTradeCalculator = exports.WASMTradeCalculator = /*#__PURE__*/function () {
36
+ function WASMTradeCalculator() {
37
+ _classCallCheck(this, WASMTradeCalculator);
38
+ _defineProperty(this, "initialized", false);
39
+ _defineProperty(this, "poolsMap", new Map());
40
+ }
41
+ return _createClass(WASMTradeCalculator, [{
42
+ key: "initializeWithPools",
43
+ value:
44
+ /**
45
+ * Initialize with pools including full swap data
46
+ */
47
+ async function initializeWithPools(pools) {
48
+ loadWasmModule();
49
+
50
+ // Build pool map for fast lookup
51
+ this.poolsMap.clear();
52
+ for (const pool of pools) {
53
+ this.poolsMap.set(pool.id, pool);
54
+ }
55
+
56
+ // Convert pools to format needed by WASM (including swap data)
57
+ const wasmPools = pools.map(pool => {
58
+ // Extract ticks from tickDataProvider if available
59
+ let ticks = [];
60
+ const tickProvider = pool.tickDataProvider;
61
+ if (tickProvider && tickProvider.ticks) {
62
+ ticks = tickProvider.ticks.map(tick => ({
63
+ id: tick.id || tick.index,
64
+ index: tick.id || tick.index,
65
+ liquidityNet: (tick.liquidityNet || '0').toString(),
66
+ liquidityGross: (tick.liquidityGross || '0').toString()
67
+ }));
68
+ }
69
+ return {
70
+ id: String(pool.id),
71
+ token_a: {
72
+ id: pool.tokenA.id
73
+ },
74
+ token_b: {
75
+ id: pool.tokenB.id
76
+ },
77
+ fee: pool.fee,
78
+ sqrtPriceX64: pool.sqrtPriceX64.toString(),
79
+ liquidity: pool.liquidity.toString(),
80
+ tickCurrent: pool.tickCurrent,
81
+ ticks
82
+ };
83
+ });
84
+
85
+ // Initialize pools with full data in WASM
86
+ wasmModule.init_pools_with_data(wasmPools);
87
+ this.initialized = true;
88
+ }
89
+
90
+ /**
91
+ * Calculate trade output for a single route (WASM-accelerated)
92
+ */
93
+ }, {
94
+ key: "calculateTradeOutput",
95
+ value: function calculateTradeOutput(route, amountIn) {
96
+ if (!this.initialized) {
97
+ throw new Error('WASMTradeCalculator not initialized');
98
+ }
99
+ const poolIds = new Uint32Array(route.pools.map(p => p.id));
100
+ const result = wasmModule.calculate_trade_output(poolIds, amountIn.quotient.toString(), route.input.id);
101
+
102
+ // Check if result is valid
103
+ // The result might be a Map or an object depending on wasm-bindgen
104
+ let amountOutValue;
105
+ let priceImpactValue = 0;
106
+ if (result instanceof Map) {
107
+ amountOutValue = result.get('amountOut');
108
+ priceImpactValue = result.get('priceImpact') || 0;
109
+ } else if (result && typeof result === 'object') {
110
+ amountOutValue = result.amountOut;
111
+ priceImpactValue = result.priceImpact || 0;
112
+ }
113
+ if (!amountOutValue) {
114
+ console.error('WASM returned invalid result:', result);
115
+ throw new Error('WASM trade calculation failed - no amountOut');
116
+ }
117
+ const amountOut = _fractions.CurrencyAmount.fromRawAmount(route.output, BigInt(amountOutValue));
118
+ return {
119
+ amountOut,
120
+ priceImpact: priceImpactValue
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Batch calculate trades for multiple routes and amounts
126
+ */
127
+ }, {
128
+ key: "calculateTradesBatch",
129
+ value: function calculateTradesBatch(routes, amounts) {
130
+ var _routes$;
131
+ if (!this.initialized) {
132
+ throw new Error('WASMTradeCalculator not initialized');
133
+ }
134
+
135
+ // Convert routes to pool ID arrays
136
+ const routePoolIds = routes.map(route => route.pools.map(p => p.id));
137
+
138
+ // Convert amounts to strings
139
+ const amountStrings = amounts.map(a => a.quotient.toString());
140
+
141
+ // Assume all routes have same input token
142
+ const tokenInId = ((_routes$ = routes[0]) === null || _routes$ === void 0 ? void 0 : _routes$.input.id) || '';
143
+ const results = wasmModule.calculate_trades_for_routes(routePoolIds, amountStrings, tokenInId);
144
+ const trades = [];
145
+ let resultIdx = 0;
146
+ for (const route of routes) {
147
+ for (const amount of amounts) {
148
+ const result = results[resultIdx++];
149
+ if (result.success) {
150
+ trades.push({
151
+ route,
152
+ amountIn: amount,
153
+ amountOut: _fractions.CurrencyAmount.fromRawAmount(route.output, BigInt(result.amountOut)),
154
+ priceImpact: result.priceImpact
155
+ });
156
+ }
157
+ }
158
+ }
159
+ return trades;
160
+ }
161
+
162
+ /**
163
+ * Clear all pools from memory
164
+ */
165
+ }, {
166
+ key: "clear",
167
+ value: function clear() {
168
+ if (this.initialized) {
169
+ wasmModule.clear_pools();
170
+ this.poolsMap.clear();
171
+ this.initialized = false;
172
+ }
173
+ }
174
+ }]);
175
+ }();
176
+ /**
177
+ * Create a WASM-accelerated Trade from a route
178
+ */
179
+ async function createTradeFromRouteWASM(route, amount, tradeType, pools) {
180
+ const calculator = new WASMTradeCalculator();
181
+ await calculator.initializeWithPools(pools);
182
+ try {
183
+ if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
184
+ const {
185
+ amountOut
186
+ } = calculator.calculateTradeOutput(route, amount);
187
+ return _entities.Trade.createUncheckedTrade({
188
+ route,
189
+ inputAmount: amount,
190
+ outputAmount: amountOut,
191
+ tradeType: _internalConstants.TradeType.EXACT_INPUT,
192
+ percent: 100
193
+ });
194
+ } else {
195
+ // For EXACT_OUTPUT, we'd need to implement reverse calculation
196
+ // For now, fall back to JS implementation
197
+ return _entities.Trade.fromRoute(route, amount, tradeType);
198
+ }
199
+ } finally {
200
+ calculator.clear();
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Find best trade with split using WASM acceleration
206
+ */
207
+ async function bestTradeWithSplitWASM(routes, amount, percents, tradeType, pools) {
208
+ let swapConfig = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {
209
+ minSplits: 1,
210
+ maxSplits: 10
211
+ };
212
+ const calculator = new WASMTradeCalculator();
213
+ await calculator.initializeWithPools(pools);
214
+ try {
215
+ // Calculate all split amounts
216
+ const splitAmounts = percents.map(percent => _fractions.CurrencyAmount.fromRawAmount(amount.currency, amount.quotient * BigInt(percent) / 100n));
217
+
218
+ // Batch calculate all trades
219
+ const allTrades = calculator.calculateTradesBatch(routes, splitAmounts);
220
+
221
+ // Group trades by percent
222
+ const tradesByPercent = {};
223
+ let tradeIdx = 0;
224
+ for (let i = 0; i < percents.length; i++) {
225
+ const percent = percents[i];
226
+ tradesByPercent[percent] = [];
227
+ for (const route of routes) {
228
+ const tradeDat = allTrades[tradeIdx++];
229
+ if (tradeDat) {
230
+ const trade = _entities.Trade.createUncheckedTrade({
231
+ route: tradeDat.route,
232
+ inputAmount: tradeDat.amountIn,
233
+ outputAmount: tradeDat.amountOut,
234
+ tradeType,
235
+ percent
236
+ });
237
+ tradesByPercent[percent].push(trade);
238
+ }
239
+ }
240
+ }
241
+
242
+ // Use existing getBestSwapRoute logic
243
+ const {
244
+ getBestSwapRoute
245
+ } = require('./getBestSwapRoute');
246
+ const bestTrades = getBestSwapRoute(tradeType, tradesByPercent, percents, swapConfig);
247
+ if (!bestTrades) return null;
248
+ const routeData = bestTrades.map(trade => ({
249
+ inputAmount: trade.inputAmount,
250
+ outputAmount: trade.outputAmount,
251
+ route: trade.route,
252
+ percent: trade.swaps[0].percent
253
+ }));
254
+ return _entities.Trade.createUncheckedTradeWithMultipleRoutes({
255
+ routes: routeData,
256
+ tradeType
257
+ });
258
+ } finally {
259
+ calculator.clear();
260
+ }
261
+ }
@@ -226,6 +226,67 @@ module.exports.get_pool_count = function() {
226
226
  return ret >>> 0;
227
227
  };
228
228
 
229
+ /**
230
+ * @param {any} pools_js
231
+ */
232
+ module.exports.init_pools_with_data = function(pools_js) {
233
+ const ret = wasm.init_pools_with_data(pools_js);
234
+ if (ret[1]) {
235
+ throw takeFromExternrefTable0(ret[0]);
236
+ }
237
+ };
238
+
239
+ let cachedUint32ArrayMemory0 = null;
240
+
241
+ function getUint32ArrayMemory0() {
242
+ if (cachedUint32ArrayMemory0 === null || cachedUint32ArrayMemory0.byteLength === 0) {
243
+ cachedUint32ArrayMemory0 = new Uint32Array(wasm.memory.buffer);
244
+ }
245
+ return cachedUint32ArrayMemory0;
246
+ }
247
+
248
+ function passArray32ToWasm0(arg, malloc) {
249
+ const ptr = malloc(arg.length * 4, 4) >>> 0;
250
+ getUint32ArrayMemory0().set(arg, ptr / 4);
251
+ WASM_VECTOR_LEN = arg.length;
252
+ return ptr;
253
+ }
254
+ /**
255
+ * @param {Uint32Array} route_pool_ids
256
+ * @param {string} amount_in
257
+ * @param {string} token_in_id
258
+ * @returns {any}
259
+ */
260
+ module.exports.calculate_trade_output = function(route_pool_ids, amount_in, token_in_id) {
261
+ const ptr0 = passArray32ToWasm0(route_pool_ids, wasm.__wbindgen_malloc);
262
+ const len0 = WASM_VECTOR_LEN;
263
+ const ptr1 = passStringToWasm0(amount_in, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
264
+ const len1 = WASM_VECTOR_LEN;
265
+ const ptr2 = passStringToWasm0(token_in_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
266
+ const len2 = WASM_VECTOR_LEN;
267
+ const ret = wasm.calculate_trade_output(ptr0, len0, ptr1, len1, ptr2, len2);
268
+ if (ret[2]) {
269
+ throw takeFromExternrefTable0(ret[1]);
270
+ }
271
+ return takeFromExternrefTable0(ret[0]);
272
+ };
273
+
274
+ /**
275
+ * @param {any} routes_js
276
+ * @param {any} amounts_js
277
+ * @param {string} token_in_id
278
+ * @returns {any}
279
+ */
280
+ module.exports.calculate_trades_for_routes = function(routes_js, amounts_js, token_in_id) {
281
+ const ptr0 = passStringToWasm0(token_in_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
282
+ const len0 = WASM_VECTOR_LEN;
283
+ const ret = wasm.calculate_trades_for_routes(routes_js, amounts_js, ptr0, len0);
284
+ if (ret[2]) {
285
+ throw takeFromExternrefTable0(ret[1]);
286
+ }
287
+ return takeFromExternrefTable0(ret[0]);
288
+ };
289
+
229
290
  module.exports.__wbg_buffer_609cc3eee51ed158 = function(arg0) {
230
291
  const ret = arg0.buffer;
231
292
  return ret;
@@ -314,6 +375,20 @@ module.exports.__wbg_length_e2d2a49132c1b256 = function(arg0) {
314
375
  return ret;
315
376
  };
316
377
 
378
+ module.exports.__wbg_log_c222819a41e063d3 = function(arg0) {
379
+ console.log(arg0);
380
+ };
381
+
382
+ module.exports.__wbg_new_405e22f390576ce2 = function() {
383
+ const ret = new Object();
384
+ return ret;
385
+ };
386
+
387
+ module.exports.__wbg_new_5e0be73521bc8c17 = function() {
388
+ const ret = new Map();
389
+ return ret;
390
+ };
391
+
317
392
  module.exports.__wbg_new_78feb108b6472713 = function() {
318
393
  const ret = new Array();
319
394
  return ret;
@@ -338,15 +413,29 @@ module.exports.__wbg_set_37837023f3d740e8 = function(arg0, arg1, arg2) {
338
413
  arg0[arg1 >>> 0] = arg2;
339
414
  };
340
415
 
416
+ module.exports.__wbg_set_3f1d0b984ed272ed = function(arg0, arg1, arg2) {
417
+ arg0[arg1] = arg2;
418
+ };
419
+
341
420
  module.exports.__wbg_set_65595bdd868b3009 = function(arg0, arg1, arg2) {
342
421
  arg0.set(arg1, arg2 >>> 0);
343
422
  };
344
423
 
424
+ module.exports.__wbg_set_8fc6bf8a5b1071d1 = function(arg0, arg1, arg2) {
425
+ const ret = arg0.set(arg1, arg2);
426
+ return ret;
427
+ };
428
+
345
429
  module.exports.__wbg_value_cd1ffa7b1ab794f1 = function(arg0) {
346
430
  const ret = arg0.value;
347
431
  return ret;
348
432
  };
349
433
 
434
+ module.exports.__wbindgen_as_number = function(arg0) {
435
+ const ret = +arg0;
436
+ return ret;
437
+ };
438
+
350
439
  module.exports.__wbindgen_bigint_from_i64 = function(arg0) {
351
440
  const ret = arg0;
352
441
  return ret;
@@ -415,6 +504,11 @@ module.exports.__wbindgen_is_object = function(arg0) {
415
504
  return ret;
416
505
  };
417
506
 
507
+ module.exports.__wbindgen_is_string = function(arg0) {
508
+ const ret = typeof(arg0) === 'string';
509
+ return ret;
510
+ };
511
+
418
512
  module.exports.__wbindgen_jsval_eq = function(arg0, arg1) {
419
513
  const ret = arg0 === arg1;
420
514
  return ret;
@@ -437,6 +531,11 @@ module.exports.__wbindgen_number_get = function(arg0, arg1) {
437
531
  getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
438
532
  };
439
533
 
534
+ module.exports.__wbindgen_number_new = function(arg0) {
535
+ const ret = arg0;
536
+ return ret;
537
+ };
538
+
440
539
  module.exports.__wbindgen_string_get = function(arg0, arg1) {
441
540
  const obj = arg1;
442
541
  const ret = typeof(obj) === 'string' ? obj : undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alcorexchange/alcor-swap-sdk",
3
- "version": "1.0.402",
3
+ "version": "1.0.403",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "files": [