@alcorexchange/alcor-swap-sdk
Version:
**npm** ``` npm i @alcorexchange/alcor-swap-sdk ``` **yarn** ``` yarn add @alcorexchange/alcor-swap-sdk ``` ## Usage ### Import:
688 lines (661 loc) • 28.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Trade = void 0;
exports.tradeComparator = tradeComparator;
var _lodash = _interopRequireDefault(require("lodash"));
var _tinyInvariant = _interopRequireDefault(require("tiny-invariant"));
var _fractions = require("./fractions");
var _sortedInsert = require("../utils/sortedInsert");
var _internalConstants = require("../internalConstants");
var _getBestSwapRoute = require("../utils/getBestSwapRoute");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
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; }
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; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
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); }
/**
* Trades comparator, an extension of the input output comparator that also considers other dimensions of the trade in ranking them
* @template TInput The input token, either Ether or an ERC-20
* @template TOutput The output token, either Ether or an ERC-20
* @template TTradeType The trade type, either exact input or exact output
* @param a The first trade to compare
* @param b The second trade to compare
* @returns A sorted ordering for two neighboring elements in a trade array
*/
function tradeComparator(a, b) {
// must have same input and output token for comparison
(0, _tinyInvariant.default)(a.inputAmount.currency.equals(b.inputAmount.currency), 'INPUT_CURRENCY');
(0, _tinyInvariant.default)(a.outputAmount.currency.equals(b.outputAmount.currency), 'OUTPUT_CURRENCY');
if (a.outputAmount.equalTo(b.outputAmount)) {
if (a.inputAmount.equalTo(b.inputAmount)) {
// consider the number of hops since each hop costs cpu
const aHops = a.swaps.reduce((total, cur) => total + cur.route.tokenPath.length, 0);
const bHops = b.swaps.reduce((total, cur) => total + cur.route.tokenPath.length, 0);
return aHops - bHops;
}
// trade A requires less input than trade B, so A should come first
if (a.inputAmount.lessThan(b.inputAmount)) {
return -1;
} else {
return 1;
}
} else {
// tradeA has less output than trade B, so should come second
if (a.outputAmount.lessThan(b.outputAmount)) {
return 1;
} else {
return -1;
}
}
}
/**
* Represents a trade executed against a set of routes where some percentage of the input is
* split across each route.
*
* Each route has its own set of pools. Pools can not be re-used across routes.
*
* Does not account for slippage, i.e., changes in price environment that can occur between
* the time the trade is submitted and when it is executed.
* @template TInput The input token, either Ether or an ERC-20
* @template TOutput The output token, either Ether or an ERC-20
* @template TTradeType The trade type, either exact input or exact output
*/
let Trade = exports.Trade = /*#__PURE__*/function () {
/**
* Construct a trade by passing in the pre-computed property values
* @param routes The routes through which the trade occurs
* @param tradeType The type of trade, exact input or exact output
*/
function Trade({
routes,
tradeType
}) {
_classCallCheck(this, Trade);
/**
* The swaps of the trade, i.e. which routes and how much is swapped in each that
* make up the trade.
*/
_defineProperty(this, "swaps", void 0);
/**
* The type of the trade, either exact in or exact out.
*/
_defineProperty(this, "tradeType", void 0);
/**
* The cached result of the input amount computation
* @private
*/
_defineProperty(this, "_inputAmount", void 0);
/**
* The cached result of the output amount computation
* @private
*/
_defineProperty(this, "_outputAmount", void 0);
/**
* The cached result of the computed execution price
* @private
*/
_defineProperty(this, "_executionPrice", void 0);
/**
* The cached result of the price impact computation
* @private
*/
_defineProperty(this, "_priceImpact", void 0);
const inputCurrency = routes[0].inputAmount.currency;
const outputCurrency = routes[0].outputAmount.currency;
(0, _tinyInvariant.default)(routes.every(({
route
}) => inputCurrency.equals(route.input)), 'INPUT_CURRENCY_MATCH');
(0, _tinyInvariant.default)(routes.every(({
route
}) => outputCurrency.equals(route.output)), 'OUTPUT_CURRENCY_MATCH');
const numPools = routes.map(({
route
}) => route.pools.length).reduce((total, cur) => total + cur, 0);
const poolAddressSet = new Set();
for (const {
route
} of routes) {
for (const pool of route.pools) {
poolAddressSet.add(pool.id);
}
}
(0, _tinyInvariant.default)(numPools == poolAddressSet.size, 'POOLS_DUPLICATED');
this.swaps = routes;
this.tradeType = tradeType;
}
/**
* Get the minimum amount that must be received from this trade for the given slippage tolerance
* @param slippageTolerance The tolerance of unfavorable slippage from the execution price of this trade
* @returns The amount out
*/
return _createClass(Trade, [{
key: "route",
get:
/**
* @deprecated Deprecated in favor of 'swaps' property. If the trade consists of multiple routes
* this will return an error.
*
* When the trade consists of just a single route, this returns the route of the trade,
* i.e. which pools the trade goes through.
*/
function () {
(0, _tinyInvariant.default)(this.swaps.length == 1, 'MULTIPLE_ROUTES');
return this.swaps[0].route;
}
}, {
key: "inputAmount",
get:
/**
* The input amount for the trade assuming no slippage.
*/
function () {
if (this._inputAmount) {
return this._inputAmount;
}
const inputCurrency = this.swaps[0].inputAmount.currency;
const totalInputFromRoutes = this.swaps.map(({
inputAmount
}) => inputAmount).reduce((total, cur) => total.add(cur), _fractions.CurrencyAmount.fromRawAmount(inputCurrency, 0));
this._inputAmount = totalInputFromRoutes;
return this._inputAmount;
}
}, {
key: "outputAmount",
get:
/**
* The output amount for the trade assuming no slippage.
*/
function () {
if (this._outputAmount) {
return this._outputAmount;
}
const outputCurrency = this.swaps[0].outputAmount.currency;
const totalOutputFromRoutes = this.swaps.map(({
outputAmount
}) => outputAmount).reduce((total, cur) => total.add(cur), _fractions.CurrencyAmount.fromRawAmount(outputCurrency, 0));
this._outputAmount = totalOutputFromRoutes;
return this._outputAmount;
}
}, {
key: "executionPrice",
get:
/**
* The price expressed in terms of output amount/input amount.
*/
function () {
var _this$_executionPrice;
return (_this$_executionPrice = this._executionPrice) !== null && _this$_executionPrice !== void 0 ? _this$_executionPrice : this._executionPrice = new _fractions.Price(this.inputAmount.currency, this.outputAmount.currency, this.inputAmount.quotient, this.outputAmount.quotient);
}
}, {
key: "priceImpact",
get:
/**
* Returns the percent difference between the route's mid price and the price impact
*/
function () {
if (this._priceImpact) {
return this._priceImpact;
}
let spotOutputAmount = _fractions.CurrencyAmount.fromRawAmount(this.outputAmount.currency, 0);
for (const {
route,
inputAmount
} of this.swaps) {
const midPrice = route.midPrice;
spotOutputAmount = spotOutputAmount.add(midPrice.quote(inputAmount));
}
const priceImpact = spotOutputAmount.subtract(this.outputAmount).divide(spotOutputAmount);
this._priceImpact = new _fractions.Percent(priceImpact.numerator, priceImpact.denominator);
return this._priceImpact;
}
/**
* Constructs an exact in trade with the given amount in and route
* @template TInput The input token, either Ether or an ERC-20
* @template TOutput The output token, either Ether or an ERC-20
* @param route The route of the exact in trade
* @param amountIn The amount being passed in
* @returns The exact in trade
*/
}, {
key: "minimumAmountOut",
value: function minimumAmountOut(slippageTolerance, amountOut = this.outputAmount) {
(0, _tinyInvariant.default)(!slippageTolerance.lessThan(_internalConstants.ZERO), 'SLIPPAGE_TOLERANCE');
if (this.tradeType === _internalConstants.TradeType.EXACT_OUTPUT) {
return amountOut;
} else {
const slippageAdjustedAmountOut = new _fractions.Fraction(_internalConstants.ONE).add(slippageTolerance).invert().multiply(amountOut.quotient).quotient;
return _fractions.CurrencyAmount.fromRawAmount(amountOut.currency, slippageAdjustedAmountOut);
}
}
/**
* Get the maximum amount in that can be spent via this trade for the given slippage tolerance
* @param slippageTolerance The tolerance of unfavorable slippage from the execution price of this trade
* @returns The amount in
*/
}, {
key: "maximumAmountIn",
value: function maximumAmountIn(slippageTolerance, amountIn = this.inputAmount) {
(0, _tinyInvariant.default)(!slippageTolerance.lessThan(_internalConstants.ZERO), 'SLIPPAGE_TOLERANCE');
if (this.tradeType === _internalConstants.TradeType.EXACT_INPUT) {
return amountIn;
} else {
const slippageAdjustedAmountIn = new _fractions.Fraction(_internalConstants.ONE).add(slippageTolerance).multiply(amountIn.quotient).quotient;
return _fractions.CurrencyAmount.fromRawAmount(amountIn.currency, slippageAdjustedAmountIn);
}
}
/**
* Return the execution price after accounting for slippage tolerance
* @param slippageTolerance the allowed tolerated slippage
* @returns The execution price
*/
}, {
key: "worstExecutionPrice",
value: function worstExecutionPrice(slippageTolerance) {
return new _fractions.Price(this.inputAmount.currency, this.outputAmount.currency, this.maximumAmountIn(slippageTolerance).quotient, this.minimumAmountOut(slippageTolerance).quotient);
}
}], [{
key: "exactIn",
value: function exactIn(route, amountIn) {
return Trade.fromRoute(route, amountIn, _internalConstants.TradeType.EXACT_INPUT);
}
/**
* Constructs an exact out trade with the given amount out and route
* @template TInput The input token, either Ether or an ERC-20
* @template TOutput The output token, either Ether or an ERC-20
* @param route The route of the exact out trade
* @param amountOut The amount returned by the trade
* @returns The exact out trade
*/
}, {
key: "exactOut",
value: function exactOut(route, amountOut) {
return Trade.fromRoute(route, amountOut, _internalConstants.TradeType.EXACT_OUTPUT);
}
/**
* WASM-accelerated version of fromRoute (for EXACT_INPUT only)
* @param route route to swap through
* @param amount the input amount
* @param pools All pools for calculation
* @returns Promise of the trade
*/
}, {
key: "fromRouteWASM",
value: async function fromRouteWASM(route, amount, pools) {
const {
createTradeFromRouteWASM
} = await Promise.resolve().then(() => _interopRequireWildcard(require('../utils/tradeCalculatorWASM')));
return createTradeFromRouteWASM(route, amount, _internalConstants.TradeType.EXACT_INPUT, pools);
}
/**
* Constructs a trade by simulating swaps through the given route
* @template TInput The input token, either Ether or an ERC-20.
* @template TOutput The output token, either Ether or an ERC-20.
* @template TTradeType The type of the trade, either exact in or exact out.
* @param route route to swap through
* @param amount the amount specified, either input or output, depending on tradeType
* @param tradeType whether the trade is an exact input or exact output swap
* @returns The route
*/
}, {
key: "fromRoute",
value: function fromRoute(route, amount, tradeType, percent = 100) {
const amounts = new Array(route.tokenPath.length);
let inputAmount;
let outputAmount;
if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
amounts[0] = amount; // Переиспользуем amount напрямую
for (let i = 0; i < route.tokenPath.length - 1; i++) {
amounts[i + 1] = route.pools[i].getOutputAmount(amounts[i]);
}
inputAmount = amount; // Без создания нового объекта
outputAmount = amounts[amounts.length - 1];
} else {
amounts[amounts.length - 1] = amount;
for (let i = route.tokenPath.length - 1; i > 0; i--) {
amounts[i - 1] = route.pools[i - 1].getInputAmount(amounts[i]);
}
inputAmount = amounts[0];
outputAmount = amount;
}
return new Trade({
routes: [{
inputAmount,
outputAmount,
route,
percent
}],
tradeType
});
}
/**
* Constructs a trade from routes by simulating swaps
*
* @template TInput The input token, either Ether or an ERC-20.
* @template TOutput The output token, either Ether or an ERC-20.
* @template TTradeType The type of the trade, either exact in or exact out.
* @param routes the routes to swap through and how much of the amount should be routed through each
* @param tradeType whether the trade is an exact input or exact output swap
* @returns The trade
*/
}, {
key: "fromRoutes",
value: function fromRoutes(routes, tradeType) {
const populatedRoutes = [];
for (const {
route,
amount,
percent
} of routes) {
const amounts = new Array(route.tokenPath.length);
let inputAmount;
let outputAmount;
if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
(0, _tinyInvariant.default)(amount.currency.equals(route.input), 'INPUT');
inputAmount = _fractions.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
amounts[0] = _fractions.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
for (let i = 0; i < route.tokenPath.length - 1; i++) {
const pool = route.pools[i];
const outputAmount = pool.getOutputAmount(amounts[i]);
amounts[i + 1] = outputAmount;
}
outputAmount = _fractions.CurrencyAmount.fromFractionalAmount(route.output, amounts[amounts.length - 1].numerator, amounts[amounts.length - 1].denominator);
} else {
(0, _tinyInvariant.default)(amount.currency.equals(route.output), 'OUTPUT');
outputAmount = _fractions.CurrencyAmount.fromFractionalAmount(route.output, amount.numerator, amount.denominator);
amounts[amounts.length - 1] = _fractions.CurrencyAmount.fromFractionalAmount(route.output, amount.numerator, amount.denominator);
for (let i = route.tokenPath.length - 1; i > 0; i--) {
const pool = route.pools[i - 1];
const inputAmount = pool.getInputAmount(amounts[i]);
amounts[i - 1] = inputAmount;
}
inputAmount = _fractions.CurrencyAmount.fromFractionalAmount(route.input, amounts[0].numerator, amounts[0].denominator);
}
populatedRoutes.push({
route,
inputAmount,
outputAmount,
percent
});
}
return new Trade({
routes: populatedRoutes,
tradeType
});
}
/**
* Creates a trade without computing the result of swapping through the route. Useful when you have simulated the trade
* elsewhere and do not have any tick data
* @template TInput The input token, either Ether or an ERC-20
* @template TOutput The output token, either Ether or an ERC-20
* @template TTradeType The type of the trade, either exact in or exact out
* @param constructorArguments The arguments passed to the trade constructor
* @returns The unchecked trade
*/
}, {
key: "createUncheckedTrade",
value: function createUncheckedTrade(constructorArguments) {
return new Trade(_objectSpread(_objectSpread({}, constructorArguments), {}, {
routes: [{
percent: constructorArguments.percent,
inputAmount: constructorArguments.inputAmount,
outputAmount: constructorArguments.outputAmount,
route: constructorArguments.route
}]
}));
}
/**
* Creates a trade without computing the result of swapping through the routes. Useful when you have simulated the trade
* elsewhere and do not have any tick data
* @template TInput The input token, either Ether or an ERC-20
* @template TOutput The output token, either Ether or an ERC-20
* @template TTradeType The type of the trade, either exact in or exact out
* @param constructorArguments The arguments passed to the trade constructor
* @returns The unchecked trade
*/
}, {
key: "createUncheckedTradeWithMultipleRoutes",
value: function createUncheckedTradeWithMultipleRoutes(constructorArguments) {
return new Trade(constructorArguments);
}
}, {
key: "bestTradeExactIn",
value: function bestTradeExactIn(routes, currencyAmountIn, maxNumResults = 1) {
(0, _tinyInvariant.default)(routes.length > 0, 'ROUTES');
// Pre-filter: remove routes with zero-liquidity pools
const validRoutes = routes.filter(route => route.pools.every(pool => pool.active && pool.liquidity > _internalConstants.ZERO));
// Helper: compute min liquidity (no overflow)
const getMinLiquidity = route => {
let min = route.pools[0].liquidity;
for (let i = 1; i < route.pools.length; i++) {
if (route.pools[i].liquidity < min) {
min = route.pools[i].liquidity;
}
}
return min;
};
// Precompute min liquidity for sorting
const routeMinLiq = new Map();
for (const route of validRoutes) {
routeMinLiq.set(route, getMinLiquidity(route));
}
// Sort routes: fewer hops first, then by min liquidity desc
validRoutes.sort((a, b) => {
if (a.pools.length !== b.pools.length) return a.pools.length - b.pools.length;
const minLiqA = routeMinLiq.get(a);
const minLiqB = routeMinLiq.get(b);
if (minLiqA > minLiqB) return -1;
if (minLiqA < minLiqB) return 1;
return 0;
});
const bestTrades = [];
for (const route of validRoutes) {
let trade;
try {
trade = Trade.fromRoute(route, currencyAmountIn, _internalConstants.TradeType.EXACT_INPUT);
} catch (error) {
// not enough liquidity in this pair
if (error.isInsufficientInputAmountError) {
continue;
}
throw error;
}
// Only check outputAmount > 0, skip expensive priceImpact calculation
if (!trade.outputAmount.greaterThan(0)) continue;
(0, _sortedInsert.sortedInsert)(bestTrades, trade, maxNumResults, tradeComparator);
}
return bestTrades;
}
}, {
key: "bestTradeExactOut",
value: function bestTradeExactOut(routes, currencyAmountOut, maxNumResults = 1) {
(0, _tinyInvariant.default)(routes.length > 0, 'ROUTES');
// Pre-filter: remove routes with zero-liquidity pools
const validRoutes = routes.filter(route => route.pools.every(pool => pool.active && pool.liquidity > _internalConstants.ZERO));
// Helper: compute min liquidity (no overflow)
const getMinLiquidity = route => {
let min = route.pools[0].liquidity;
for (let i = 1; i < route.pools.length; i++) {
if (route.pools[i].liquidity < min) {
min = route.pools[i].liquidity;
}
}
return min;
};
// Precompute min liquidity for sorting
const routeMinLiq = new Map();
for (const route of validRoutes) {
routeMinLiq.set(route, getMinLiquidity(route));
}
// Sort routes: fewer hops first, then by min liquidity desc
validRoutes.sort((a, b) => {
if (a.pools.length !== b.pools.length) return a.pools.length - b.pools.length;
const minLiqA = routeMinLiq.get(a);
const minLiqB = routeMinLiq.get(b);
if (minLiqA > minLiqB) return -1;
if (minLiqA < minLiqB) return 1;
return 0;
});
const bestTrades = [];
for (const route of validRoutes) {
let trade;
try {
trade = Trade.fromRoute(route, currencyAmountOut, _internalConstants.TradeType.EXACT_OUTPUT);
} catch (error) {
// not enough liquidity in this pair
if (error.isInsufficientReservesError) {
continue;
}
throw error;
}
if (!trade.inputAmount.greaterThan(0)) continue;
(0, _sortedInsert.sortedInsert)(bestTrades, trade, maxNumResults, tradeComparator);
}
return bestTrades;
}
/**
* WASM-accelerated version of bestTradeWithSplit
* @param _routes Routes to consider
* @param amount Amount to swap
* @param percents Percentages to split
* @param tradeType Type of trade
* @param pools All pools for trade calculation
* @param swapConfig Configuration for splits
* @returns Best trade or null
*/
}, {
key: "bestTradeWithSplitWASM",
value: async function bestTradeWithSplitWASM(_routes, amount, percents, tradeType, pools, swapConfig = {
minSplits: 1,
maxSplits: 10
}) {
const {
bestTradeWithSplitWASM
} = await Promise.resolve().then(() => _interopRequireWildcard(require('../utils/tradeCalculatorWASM')));
return bestTradeWithSplitWASM(_routes, amount, percents, tradeType, pools, swapConfig);
}
}, {
key: "bestTradeWithSplit",
value: function bestTradeWithSplit(_routes, amount, percents, tradeType, swapConfig = {
minSplits: 1,
maxSplits: 10
}) {
(0, _tinyInvariant.default)(_routes.length > 0, 'ROUTES');
(0, _tinyInvariant.default)(percents.length > 0, 'PERCENTS');
// Pre-filter: remove routes with zero-liquidity or inactive pools
const validRoutes = _routes.filter(route => route.pools.every(pool => pool.active && pool.liquidity > _internalConstants.ZERO));
// Helper: compute min liquidity for a route (no overflow)
const getMinLiquidity = route => {
let min = route.pools[0].liquidity;
for (let i = 1; i < route.pools.length; i++) {
if (route.pools[i].liquidity < min) {
min = route.pools[i].liquidity;
}
}
return min;
};
// Precompute min liquidity for sorting (avoid recalculating)
const routeMinLiq = new Map();
for (const route of validRoutes) {
routeMinLiq.set(route, getMinLiquidity(route));
}
// Sort routes by min liquidity (descending) - no hop preference
validRoutes.sort((a, b) => {
const minLiqA = routeMinLiq.get(a);
const minLiqB = routeMinLiq.get(b);
if (minLiqA > minLiqB) return -1;
if (minLiqA < minLiqB) return 1;
return 0;
});
// Предварительно вычисляем splitAmount для всех процентов
const percentToAmount = new Map();
for (const percent of percents) {
percentToAmount.set(percent, amount.multiply(percent).divide(100));
}
const quoteRoute = (route, splitAmount, percent) => {
const amounts = new Array(route.tokenPath.length);
let inputAmount;
let outputAmount;
if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
amounts[0] = splitAmount;
for (let i = 0; i < route.tokenPath.length - 1; i++) {
amounts[i + 1] = route.pools[i].getOutputAmount(amounts[i]);
}
inputAmount = splitAmount;
outputAmount = amounts[amounts.length - 1];
} else {
amounts[amounts.length - 1] = splitAmount;
for (let i = route.tokenPath.length - 1; i > 0; i--) {
amounts[i - 1] = route.pools[i - 1].getInputAmount(amounts[i]);
}
inputAmount = amounts[0];
outputAmount = splitAmount;
}
if (!outputAmount.greaterThan(0)) {
return null;
}
return {
percent,
route: route,
inputAmount,
outputAmount
};
};
const percentToQuotes = {};
for (const percent of percents) {
percentToQuotes[percent] = [];
}
for (const route of validRoutes) {
for (const percent of percents) {
const splitAmount = percentToAmount.get(percent);
try {
const quote = quoteRoute(route, splitAmount, percent);
if (quote) {
percentToQuotes[percent].push(quote);
}
} catch (error) {
if (error.isInsufficientReservesError || error.isInsufficientInputAmountError) {
continue;
}
throw error;
}
}
}
const bestQuotes = (0, _getBestSwapRoute.getBestSwapRoute)(tradeType, percentToQuotes, percents, swapConfig);
if (!bestQuotes) return null;
const routes = bestQuotes.map(({
inputAmount,
outputAmount,
route,
percent
}) => {
return {
inputAmount,
outputAmount,
route,
percent
};
});
// Check missing input after splitting
// TODO Do we need it for exact out?
if (tradeType === _internalConstants.TradeType.EXACT_INPUT) {
const totalAmount = _lodash.default.reduce(routes, (total, route) => total.add(route.inputAmount), _fractions.CurrencyAmount.fromRawAmount(routes[0].route.input, 0));
const missingAmount = amount.subtract(totalAmount);
if (missingAmount.greaterThan(0)) {
console.log("MISSING AMOUNT!!!", missingAmount.toFixed());
routes[0].inputAmount = routes[0].inputAmount.add(missingAmount);
}
}
return new Trade({
routes,
tradeType
});
}
}]);
}();