UNPKG

@alcorexchange/alcor-swap-sdk

Version:

​​ **npm** ``` npm i @alcorexchange/alcor-swap-sdk ``` **yarn** ``` yarn add @alcorexchange/alcor-swap-sdk ``` ## Usage ### Import:

260 lines (245 loc) 9.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WASMTradeCalculator = void 0; exports.bestTradeWithSplitWASM = bestTradeWithSplitWASM; exports.createTradeFromRouteWASM = createTradeFromRouteWASM; var _entities = require("../entities"); var _fractions = require("../entities/fractions"); var _internalConstants = require("../internalConstants"); var _getBestSwapRoute = require("./getBestSwapRoute"); function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 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); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 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; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } 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); } // WASM module - lazy loaded let wasmModule = null; function loadWasmModule() { if (!wasmModule) { try { wasmModule = require('./wasm_route_finder.js'); } catch (error) { console.error('Failed to load WASM module:', error); throw new Error('WASM module not available'); } } return wasmModule; } /** * WASM-accelerated trade calculator */ let WASMTradeCalculator = exports.WASMTradeCalculator = /*#__PURE__*/function () { function WASMTradeCalculator() { _classCallCheck(this, WASMTradeCalculator); _defineProperty(this, "initialized", false); _defineProperty(this, "poolsMap", new Map()); } return _createClass(WASMTradeCalculator, [{ key: "initializeWithPools", value: /** * Initialize with pools including full swap data */ async function initializeWithPools(pools) { loadWasmModule(); // Build pool map for fast lookup this.poolsMap.clear(); for (const pool of pools) { this.poolsMap.set(pool.id, pool); } // Convert pools to format needed by WASM (including swap data) const wasmPools = pools.map(pool => { // Extract ticks from tickDataProvider if available let ticks = []; const tickProvider = pool.tickDataProvider; if (tickProvider && tickProvider.ticks) { ticks = tickProvider.ticks.map(tick => ({ id: tick.id || tick.index, index: tick.id || tick.index, liquidityNet: (tick.liquidityNet || '0').toString(), liquidityGross: (tick.liquidityGross || '0').toString() })); } return { id: String(pool.id), token_a: { id: pool.tokenA.id }, token_b: { id: pool.tokenB.id }, fee: pool.fee, sqrtPriceX64: pool.sqrtPriceX64.toString(), liquidity: pool.liquidity.toString(), tickCurrent: pool.tickCurrent, ticks }; }); // Initialize pools with full data in WASM wasmModule.init_pools_with_data(wasmPools); this.initialized = true; } /** * Calculate trade output for a single route (WASM-accelerated) */ }, { key: "calculateTradeOutput", value: function calculateTradeOutput(route, amountIn) { if (!this.initialized) { throw new Error('WASMTradeCalculator not initialized'); } const poolIds = new Uint32Array(route.pools.map(p => p.id)); const result = wasmModule.calculate_trade_output(poolIds, amountIn.quotient.toString(), route.input.id); // Check if result is valid // The result might be a Map or an object depending on wasm-bindgen let amountOutValue; let priceImpactValue = 0; if (result instanceof Map) { amountOutValue = result.get('amountOut'); priceImpactValue = result.get('priceImpact') || 0; } else if (result && typeof result === 'object') { amountOutValue = result.amountOut; priceImpactValue = result.priceImpact || 0; } if (!amountOutValue) { console.error('WASM returned invalid result:', result); throw new Error('WASM trade calculation failed - no amountOut'); } const amountOut = _fractions.CurrencyAmount.fromRawAmount(route.output, BigInt(amountOutValue)); return { amountOut, priceImpact: priceImpactValue }; } /** * Batch calculate trades for multiple routes and amounts */ }, { key: "calculateTradesBatch", value: function calculateTradesBatch(routes, amounts) { var _routes$; if (!this.initialized) { throw new Error('WASMTradeCalculator not initialized'); } // Convert routes to pool ID arrays const routePoolIds = routes.map(route => route.pools.map(p => p.id)); // Convert amounts to strings const amountStrings = amounts.map(a => a.quotient.toString()); // Assume all routes have same input token const tokenInId = ((_routes$ = routes[0]) === null || _routes$ === void 0 ? void 0 : _routes$.input.id) || ''; const results = wasmModule.calculate_trades_for_routes(routePoolIds, amountStrings, tokenInId); const trades = []; let resultIdx = 0; for (const route of routes) { for (const amount of amounts) { const result = results[resultIdx++]; if (result.success) { trades.push({ route, amountIn: amount, amountOut: _fractions.CurrencyAmount.fromRawAmount(route.output, BigInt(result.amountOut)), priceImpact: result.priceImpact }); } else { trades.push(null); } } } return trades; } /** * Clear all pools from memory */ }, { key: "clear", value: function clear() { if (this.initialized) { wasmModule.clear_pools(); this.poolsMap.clear(); this.initialized = false; } } }]); }(); /** * Create a WASM-accelerated Trade from a route */ async function createTradeFromRouteWASM(route, amount, tradeType, pools) { const calculator = new WASMTradeCalculator(); await calculator.initializeWithPools(pools); try { if (tradeType === _internalConstants.TradeType.EXACT_INPUT) { const { amountOut } = calculator.calculateTradeOutput(route, amount); return _entities.Trade.createUncheckedTrade({ route, inputAmount: amount, outputAmount: amountOut, tradeType: _internalConstants.TradeType.EXACT_INPUT, percent: 100 }); } else { // For EXACT_OUTPUT, we'd need to implement reverse calculation // For now, fall back to JS implementation return _entities.Trade.fromRoute(route, amount, tradeType); } } finally { calculator.clear(); } } /** * Find best trade with split using WASM acceleration */ async function bestTradeWithSplitWASM(routes, amount, percents, tradeType, pools, swapConfig = { minSplits: 1, maxSplits: 10 }) { const calculator = new WASMTradeCalculator(); await calculator.initializeWithPools(pools); try { // Calculate all split amounts const splitAmounts = percents.map(percent => _fractions.CurrencyAmount.fromRawAmount(amount.currency, amount.quotient * BigInt(percent) / BigInt(100))); // Batch calculate all trades const allTrades = calculator.calculateTradesBatch(routes, splitAmounts); // Group trades by percent const quotesByPercent = {}; for (let i = 0; i < percents.length; i++) { const percent = percents[i]; quotesByPercent[percent] = []; } const amountsPerRoute = splitAmounts.length; for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) { for (let percentIndex = 0; percentIndex < percents.length; percentIndex++) { const flatIndex = routeIndex * amountsPerRoute + percentIndex; const tradeDat = allTrades[flatIndex]; if (tradeDat) { const percent = percents[percentIndex]; quotesByPercent[percent].push({ route: tradeDat.route, inputAmount: tradeDat.amountIn, outputAmount: tradeDat.amountOut, percent }); } } } const bestTrades = (0, _getBestSwapRoute.getBestSwapRoute)(tradeType, quotesByPercent, percents, swapConfig); if (!bestTrades) return null; const routeData = bestTrades.map(trade => ({ inputAmount: trade.inputAmount, outputAmount: trade.outputAmount, route: trade.route, percent: trade.percent })); return _entities.Trade.createUncheckedTradeWithMultipleRoutes({ routes: routeData, tradeType }); } finally { calculator.clear(); } }