@hikaru-fi/swap-router
Version:
Package for calculating optimal path for executing swaps
229 lines (213 loc) • 5.93 kB
JavaScript
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
}