@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
JavaScript
;
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();
}
}