UNPKG

@hikaru-fi/swap-router

Version:

Package for calculating optimal path for executing swaps

181 lines (171 loc) 6.07 kB
const BN = require("bn.js"); const { toBN } = require("web3-utils"); const { WeightedPool } = require("../poolMath/weightedPool/weightedPool"); const { PoolTypes, SwapTypes } = require("../utils/types"); /** * @param {import("./poolCache").PoolDictionary} pools * @param {String} requiredPoolType * @returns {import("./poolCache").PoolDictionary} */ function filterPools( pools, requiredPoolType ) { requiredPoolType = requiredPoolType ? requiredPoolType : PoolTypes.WeightedPool; const filteredPools = {}; for (const poolAddress in pools) { if ( pools[poolAddress].type == PoolTypes.All || pools[poolAddress].type == requiredPoolType ) { filteredPools[poolAddress] = pools[poolAddress]; } } return filteredPools; } /** * * @param {import("./poolCache").PoolDictionary} pools * @param {String} tokenIn * @param {String} tokenOut * @returns {[Record<String, WeightedPool>, Record<String, WeightedPool[]>, Record<String, WeightedPool[]>]} */ function getPoolsOfInterest( pools, tokenIn, tokenOut ) { /** * @type {Record<String,WeightedPool>} */ const directPools = {}; /** * @type {Record<String,WeightedPool[]>} */ const hopsIn = {}; /** * @type {Record<String,WeightedPool[]>} */ const hopsOut = {}; for (const poolAddress in pools) { const pool = pools[poolAddress]; const tokenInExists = pool.containsToken(tokenIn); const tokenOutExists = pool.containsToken(tokenOut); if (tokenInExists && tokenOutExists) { directPools[pool.address] = [pool]; continue; } if (tokenInExists) { for (const token of pool.tokens) { if (token == tokenIn) continue; if (!hopsIn[token]) hopsIn[token] = []; hopsIn[token].push(pool); } } if (tokenOutExists) { for (const token of pool.tokens) { if (token == tokenOut) continue; if (!hopsOut[token]) hopsOut[token] = []; hopsOut[token].push(pool); } } } return [directPools, hopsIn, hopsOut] } /** * * @param {Record<String,WeightedPool>} pools * @param {String} poolAddress * @param {String} token * @param {BN} amount * @returns {Boolean} */ function checkSwapLimit(pools, poolAddress, token, amount) { const pool = pools[poolAddress]; const swapLimit = pool.getSwapLimit(token); return swapLimit.gt(amount); } /** * * @param {import("./pathCalculation").SwapRoute} swapPath * @param {Record<String,WeightedPool>} pools * @param {BN} swapAmount * @param {String} swapType */ function checkSwapPath(swapPath, pools, swapAmount, swapType) { let tokensInPools = true; let liquidityAvailable = true; switch(swapType) { case SwapTypes.Sell: { let amountIn = swapAmount.clone(); let amountOut = toBN(0); for (const swap of swapPath.path) { const pool = pools[swap.poolAddress]; tokensInPools = tokensInPools && pool.containsToken(swap.tokenIn) && pool.containsToken(swap.tokenOut); liquidityAvailable = liquidityAvailable && checkSwapLimit(pools, swap.poolAddress, swap.tokenIn, amountIn); if (!tokensInPools || !liquidityAvailable) break; amountOut = pool.sellTokens(swap.tokenIn, swap.tokenOut, amountIn); amountIn = amountOut; } break; } case SwapTypes.Buy: { let amountIn = toBN(0); let amountOut = swapAmount.clone(); for (let swapId = swapPath.path.length - 1; swapId >= 0; swapId--) { const swap = swapPath.path[swapId]; const pool = pools[swap.poolAddress]; tokensInPools = tokensInPools && pool.containsToken(swap.tokenIn) && pool.containsToken(swap.tokenOut); liquidityAvailable = liquidityAvailable && checkSwapLimit(pools, swap.poolAddress, swap.tokenOut, amountOut); if (!tokensInPools || !liquidityAvailable) break; amountIn = pool.buyTokens(swap.tokenIn, swap.tokenOut, amountOut); amountOut = amountIn; } break; } } return tokensInPools && liquidityAvailable; } /** * * @param {Record<String,WeightedPool>} pools * @param {import("./pathCalculation").SwapRoute[]} directSwaps * @param {import("./pathCalculation").SwapRoute[]} hopSwaps * @param {BN} swapAmount * @param {String} swapType * @returns {import("./pathCalculation").SwapRoute[]} */ function filterPathsForSwapType(pools, directSwaps, hopSwaps, swapAmount, swapType) { let suitableSwaps = []; switch(swapType) { case SwapTypes.Sell: { suitableSwaps = directSwaps.filter( (val) => checkSwapLimit(pools, val.path[0].poolAddress, val.path[0].tokenIn, swapAmount) ) suitableSwaps = suitableSwaps.concat( hopSwaps.filter( (swapPath) => checkSwapPath(swapPath, pools, swapAmount, swapType) ) ) break; } case SwapTypes.Buy: { suitableSwaps = directSwaps.filter( (val) => checkSwapLimit(pools, val.path[0].poolAddress, val.path[0].tokenOut, swapAmount) ); suitableSwaps = suitableSwaps.concat( hopSwaps.filter( (swapPath) => checkSwapPath(swapPath, pools, swapAmount, swapType) ) ) break; } } return suitableSwaps; } module.exports = { filterPools, getPoolsOfInterest, checkSwapLimit, filterPathsForSwapType }