UNPKG

@hikaru-fi/swap-router

Version:

Package for calculating optimal path for executing swaps

229 lines (213 loc) 5.93 kB
const BN = require('bn.js'); const { toBN } = require('web3-utils'); const { PoolTypes, SwapTypes } = require('../../utils/types'); const { calcInGivenOut, calcOutGivenIn, swapLimit } = require("./weightedPoolMath"); /** * @typedef DBNumber * @property {String} exact * @property {Number} approximate */ /** * @typedef DBToken * @property {String} address * @property {DBNumber} multiplier * @property {DBNumber} weight * @property {DBNumber} balance */ /** * @typedef DBPool * @property {String} address * @property {String} factoryAddress * @property {String} poolManagerAddress * @property {DBNumber} swapFee * @property {String} name * @property {String} symbol * @property {DBToken[]} tokens */ class WeightedPool { swapLimitCoefficient = toBN(0.3e18); /** * @param {DBPool} dbObj * @returns {WeightedPool} */ static fromDBObj(dbObj) { const vaultAddress = dbObj.vaultAddress; const poolAddress = dbObj.address; const tokens = dbObj.tokens.map((val) => val.address); const balances = dbObj.tokens.map((val) => toBN(val.balance.exact)); const weights = dbObj.tokens.map((val) => toBN(val.weight.exact)); const multipliers = dbObj.tokens.map((val) => toBN(val.multiplier.exact)); const swapFee = toBN(dbObj.swapFee.exact); return new WeightedPool( vaultAddress, poolAddress, tokens, balances, weights, multipliers, swapFee ); } /** * @param {String} vaultAddress * @param {String} address * @param {String[]} tokens * @param {BN[]} balances * @param {BN[]} weights * @param {BN[]} multipliers * @param {BN} swapFee */ constructor( vaultAddress, address, tokens, balances, weights, multipliers, swapFee ) { this.vaultAddress = vaultAddress; this.address = address; this.tokens = tokens.copyWithin(); this.balances = balances.copyWithin(); this.normalizedBalances = balances.map((val, index) => val.mul(multipliers[index])); this.weights = weights.copyWithin(); this.multipliers = multipliers.copyWithin(); this.swapFee = swapFee; this.type = PoolTypes.WeightedPool; } /** * @param {BN[]} balances */ setBalances( balances ) { this.balances = balances; this.normalizedBalances = balances.map((val, index) => val.mul(this.multipliers[index])); } /** * * @param {String} tokenIn * @param {String} tokenOut * @param {BN} swapAmount * @param {String} swapType * @returns {BN} */ getSwapResult( tokenIn, tokenOut, swapAmount, swapType ) { let res; switch(swapType) { case SwapTypes.Buy: { res = this.buyTokens(tokenIn, tokenOut, swapAmount); break; } case SwapTypes.Sell: { res = this.sellTokens(tokenIn, tokenOut, swapAmount); break } } return res; } /** * @param {String} tokenIn * @param {String} tokenOut * @param {BN} amountIn * @returns {BN} */ sellTokens( tokenIn, tokenOut, amountIn ) { const [indexIn, indexOut] = this.getTokenIndexes(tokenIn, tokenOut); return calcOutGivenIn( this.normalizedBalances[indexIn], this.weights[indexIn], this.normalizedBalances[indexOut], this.weights[indexOut], amountIn.mul(this.multipliers[indexIn]), this.swapFee ).div(this.multipliers[indexOut]); } /** * @param {String} tokenIn * @param {String} tokenOut * @param {BN} amountOut * @returns {BN} */ buyTokens( tokenIn, tokenOut, amountOut ) { const [indexIn, indexOut] = this.getTokenIndexes(tokenIn, tokenOut); return calcInGivenOut( this.normalizedBalances[indexIn], this.weights[indexIn], this.normalizedBalances[indexOut], this.weights[indexOut], amountOut.mul(this.multipliers[indexOut]), this.swapFee ).div(this.multipliers[indexIn]); } /** * @param {String} tokenIn * @param {String} tokenOut * @returns {Number[]} */ getTokenIndexes( tokenIn, tokenOut ) { return [ this.tokens.indexOf(tokenIn), this.tokens.indexOf(tokenOut) ] } /** * * @param {String} tokenIn * @param {String} tokenOut * @returns {BN} */ getNormalizedLiquidity( tokenIn, tokenOut ) { const [indexIn, indexOut] = this.getTokenIndexes(tokenIn, tokenOut); return ( this.normalizedBalances[indexOut].mul( this.weights[indexIn] ).div( this.weights[indexIn].add(this.weights[indexOut]) ) ).div( this.multipliers[indexOut] ); } /** * @param {String} token */ getSwapLimit( token ) { const index = this.tokens.indexOf(token); return swapLimit(this.balances[index], this.swapLimitCoefficient) } /** * @param {String} token * @returns {Boolean} */ containsToken( token ) { return this.tokens.indexOf(token) > -1; } } module.exports = { WeightedPool }