@koyofinance/sor
Version:
539 lines (526 loc) • 18.5 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
INFINITY: () => INFINITY,
KoyoSor: () => KoyoSor,
ONE: () => ONE,
RouteProposer: () => RouteProposer,
calculatePathLimits: () => calculatePathLimits,
createPath: () => createPath,
filterPoolsOfInterest: () => filterPoolsOfInterest,
getBoostedGraph: () => getBoostedGraph,
getBoostedPaths: () => getBoostedPaths,
getLimitAmountSwapForPath: () => getLimitAmountSwapForPath,
getNodesAndEdges: () => getNodesAndEdges,
getOutputAmountSwap: () => getOutputAmountSwap,
getPathInfo: () => getPathInfo,
getRaisingToken: () => getRaisingToken,
pathsInfoToPaths: () => pathsInfoToPaths,
producePaths: () => producePaths,
scale: () => scale
});
module.exports = __toCommonJS(src_exports);
// src/pools/handle.ts
var import_sor2 = require("@balancer-labs/sor");
// src/utilities/oldBigNumber.ts
var import_sor = require("@balancer-labs/sor");
var ONE = (0, import_sor.bnum)(1);
var INFINITY = (0, import_sor.bnum)("Infinity");
function scale(input, decimalPlaces) {
const scalePow = new import_sor.OldBigNumber(decimalPlaces.toString());
const scaleMul = new import_sor.OldBigNumber(10).pow(scalePow);
return input.times(scaleMul);
}
__name(scale, "scale");
// src/pools/handle.ts
function getOutputAmountSwap(pool, poolPairData, swapType, amount) {
if (swapType === import_sor2.SwapTypes.SwapExactIn) {
if (poolPairData.poolType !== import_sor2.PoolTypes.Linear && poolPairData.balanceIn.isZero()) {
return import_sor2.ZERO;
}
return pool._exactTokenInForTokenOut(poolPairData, amount);
}
if (poolPairData.balanceOut.isZero()) {
return import_sor2.ZERO;
} else if (scale(amount, poolPairData.decimalsOut).gte(poolPairData.balanceOut.toString())) {
return INFINITY;
}
return pool._tokenInForExactTokenOut(poolPairData, amount);
}
__name(getOutputAmountSwap, "getOutputAmountSwap");
// src/routes/boosted.ts
var import_sor3 = require("@balancer-labs/sor");
// src/utilities/lbp.ts
function getRaisingToken(pool, lbpRaisingTokens, token) {
let theOtherToken;
const { tokensList } = pool;
if (tokensList.includes(token) && !lbpRaisingTokens.includes(token)) {
for (let i = 0; i < 2; i++) {
if (tokensList[i] === token) {
theOtherToken = tokensList[1 - i];
}
}
}
return theOtherToken;
}
__name(getRaisingToken, "getRaisingToken");
// src/utilities/routing.ts
var import_constants = require("@ethersproject/constants");
function getNodesAndEdges(pools) {
const edgesFromNode = {};
for (const pool of pools) {
const n = pool.tokensList.length;
for (let i = 0; i < n; i++) {
if (!edgesFromNode[pool.tokensList[i]])
edgesFromNode[pool.tokensList[i]] = [];
for (let j = 0; j < n; j++) {
if (i === j)
continue;
const edge = [
pool.id,
pool.tokensList[i],
pool.tokensList[j]
];
edgesFromNode[pool.tokensList[i]].push(edge);
}
}
}
return edgesFromNode;
}
__name(getNodesAndEdges, "getNodesAndEdges");
function getPathInfo(edge, treeEdge, treeEdges) {
const pathEdges = [
edge
];
pathEdges.unshift(treeEdge.edge);
let indices = treeEdge.parentIndices;
while (indices[0] !== -1) {
pathEdges.unshift(treeEdges[indices[0]][indices[1]].edge);
indices = treeEdges[indices[0]][indices[1]].parentIndices;
}
const pools = pathEdges.map((pathEdge) => pathEdge[0]);
pools.splice(0, 1);
const tokens = pathEdges.map((pathEdge) => pathEdge[2]);
return [
tokens,
pools
];
}
__name(getPathInfo, "getPathInfo");
function pathsInfoToPaths(flexBoostedPathsInfo, poolsAllDict) {
const paths = [];
for (const boostedPathInfo of flexBoostedPathsInfo) {
const pools = boostedPathInfo[1].map((id) => poolsAllDict[id]);
if (pools.length > 2) {
paths.push(createPath(boostedPathInfo[0], pools));
}
}
return paths;
}
__name(pathsInfoToPaths, "pathsInfoToPaths");
function createPath(tokens, pools) {
let tI;
let tO;
const swaps = [];
const poolPairData = [];
let id = "";
for (let i = 0; i < pools.length; i++) {
tI = tokens[i];
tO = tokens[i + 1];
const poolPair = pools[i].parsePoolPairData(tI, tO);
poolPairData.push(poolPair);
id += poolPair.id;
const swap = {
pool: pools[i].id,
tokenIn: tI,
tokenOut: tO,
tokenInDecimals: poolPair.decimalsIn,
tokenOutDecimals: poolPair.decimalsOut
};
swaps.push(swap);
}
const path = {
id,
swaps,
limitAmount: import_constants.Zero,
poolPairData,
pools
};
return path;
}
__name(createPath, "createPath");
// src/routes/boosted.ts
function getBoostedGraph(tokenIn, tokenOut, poolsAllDict, config) {
const wethAddress = config.weth.toLowerCase();
const pools = [
...Object.values(poolsAllDict)
];
const graphPoolsSet = /* @__PURE__ */ new Set();
const linearPools = [];
const phantomPools = [];
const relevantRaisingTokens = [];
for (const pool3 of pools) {
if (pool3.poolType === import_sor3.PoolTypes.Linear) {
linearPools.push(pool3);
graphPoolsSet.add(pool3);
} else {
const tokensList = pool3.tokensList.map((address) => address.toLowerCase());
if (tokensList.includes(pool3.address)) {
phantomPools.push(pool3);
}
if (tokenIn !== wethAddress && tokenOut !== wethAddress && tokensList.includes(wethAddress)) {
if (tokensList.length <= 3 && (tokensList.includes(tokenIn) || tokensList.includes(tokenOut))) {
graphPoolsSet.add(pool3);
}
}
if (config.lbpRaisingTokens) {
const raisingTokens = config.lbpRaisingTokens.map((address) => address.toLowerCase());
if (pool3.isLBP) {
const raisingTokenIn = getRaisingToken(pool3, raisingTokens, tokenIn);
if (raisingTokenIn) {
graphPoolsSet.add(pool3);
relevantRaisingTokens.push(raisingTokenIn);
}
const raisingTokenOut = getRaisingToken(pool3, raisingTokens, tokenOut);
if (raisingTokenOut) {
graphPoolsSet.add(pool3);
relevantRaisingTokens.push(raisingTokenOut);
}
}
}
}
}
if (linearPools.length === 0)
return {};
const linearPoolsAddresses = linearPools.map((pool) => pool.address);
const secondStepPoolsSet = /* @__PURE__ */ new Set();
for (const pool1 of phantomPools) {
for (const linearPoolAddress of linearPoolsAddresses) {
if (pool1.tokensList.includes(linearPoolAddress)) {
graphPoolsSet.add(pool1);
secondStepPoolsSet.add(pool1);
}
}
}
const secondStepPoolsAddresses = [
...secondStepPoolsSet
].map((pool) => pool.address);
for (const pool2 of pools) {
for (const secondStepPoolAddress of secondStepPoolsAddresses) {
if (pool2.tokensList.includes(secondStepPoolAddress)) {
graphPoolsSet.add(pool2);
}
}
const { tokensList } = pool2;
for (const raisingToken of relevantRaisingTokens) {
if (tokensList.includes(raisingToken) && tokensList.includes(wethAddress) && raisingToken !== wethAddress) {
graphPoolsSet.add(pool2);
}
}
}
const graphPools = [
...graphPoolsSet
];
const edgeDict = getNodesAndEdges(graphPools);
return edgeDict;
}
__name(getBoostedGraph, "getBoostedGraph");
function getBoostedPaths(tokenIn, tokenOut, poolsAllDict, config) {
const edgesFromNode = getBoostedGraph(tokenIn, tokenOut, poolsAllDict, config);
const pathsInfo = [];
const rootTreeEdge = {
edge: [
"",
"",
tokenIn
],
parentIndices: [
-1,
-1
],
visitedNodes: []
};
const treeEdges = [
[
rootTreeEdge
]
];
let iterate = true;
while (iterate) {
const n = treeEdges.length;
const newTreeEdges = [];
for (let i = 0; i < treeEdges[n - 1].length; i++) {
const treeEdge = treeEdges[n - 1][i];
const token = treeEdge.edge[2];
const edgesFromToken = edgesFromNode[token];
if (!edgesFromToken)
continue;
for (const edge of edgesFromToken) {
if (treeEdge.visitedNodes.includes(edge[2]) || treeEdge.edge[0] === edge[0]) {
continue;
}
if (edge[2] === tokenOut) {
pathsInfo.push(getPathInfo(edge, treeEdge, treeEdges));
}
const newTreeEdge = {
edge,
parentIndices: [
n - 1,
i
],
visitedNodes: treeEdge.visitedNodes.concat(edge[1])
};
newTreeEdges.push(newTreeEdge);
}
}
if (newTreeEdges.length === 0) {
iterate = false;
} else
treeEdges.push(newTreeEdges);
}
return pathsInfoToPaths(pathsInfo, poolsAllDict);
}
__name(getBoostedPaths, "getBoostedPaths");
// src/routes/filter.ts
function filterPoolsOfInterest(allPools, tokenIn, tokenOut, maxPools) {
const directPools = {};
const hopsIn = {};
const hopsOut = {};
Object.keys(allPools).forEach((id) => {
const pool = allPools[id];
const tokenListSet = new Set(pool.tokensList);
const containsTokenIn = tokenListSet.has(tokenIn.toLowerCase());
const containsTokenOut = tokenListSet.has(tokenOut.toLowerCase());
if (containsTokenIn && containsTokenOut) {
directPools[pool.id] = pool;
return;
}
if (maxPools > 1) {
if (containsTokenIn && !containsTokenOut) {
for (const hopToken of tokenListSet) {
if (!hopsIn[hopToken])
hopsIn[hopToken] = /* @__PURE__ */ new Set([]);
hopsIn[hopToken].add(pool.id);
}
} else if (!containsTokenIn && containsTokenOut) {
for (const hopToken of [
...tokenListSet
]) {
if (!hopsOut[hopToken])
hopsOut[hopToken] = /* @__PURE__ */ new Set([]);
hopsOut[hopToken].add(pool.id);
}
}
}
});
return [
directPools,
hopsIn,
hopsOut
];
}
__name(filterPoolsOfInterest, "filterPoolsOfInterest");
// src/routes/pathLimits.ts
var import_sor4 = require("@balancer-labs/sor");
var import_bignumber = require("@ethersproject/bignumber");
var import_constants2 = require("@ethersproject/constants");
function calculatePathLimits(paths, swapType) {
let maxLiquidityAvailable = import_constants2.Zero;
paths.forEach((path) => {
path.limitAmount = getLimitAmountSwapForPath(path, swapType);
maxLiquidityAvailable = maxLiquidityAvailable.add(path.limitAmount);
});
const sortedPaths = paths.sort((a, b) => {
return b.limitAmount.gt(a.limitAmount) ? 1 : -1;
});
return [
sortedPaths,
maxLiquidityAvailable
];
}
__name(calculatePathLimits, "calculatePathLimits");
function getLimitAmountSwapForPath(path, swapType) {
const { poolPairData } = path;
let limit;
if (swapType === import_sor4.SwapTypes.SwapExactIn) {
limit = path.pools[poolPairData.length - 1].getLimitAmountSwap(poolPairData[poolPairData.length - 1], import_sor4.SwapTypes.SwapExactIn);
for (let i = poolPairData.length - 2; i >= 0; i--) {
const poolLimitExactIn = path.pools[i].getLimitAmountSwap(poolPairData[i], import_sor4.SwapTypes.SwapExactIn);
const poolLimitExactOut = path.pools[i].getLimitAmountSwap(poolPairData[i], import_sor4.SwapTypes.SwapExactOut);
if (poolLimitExactOut.lte(limit)) {
limit = poolLimitExactIn;
} else {
const pulledLimit = getOutputAmountSwap(path.pools[i], path.poolPairData[i], import_sor4.SwapTypes.SwapExactOut, limit);
limit = import_sor4.OldBigNumber.min(pulledLimit, poolLimitExactIn);
}
}
if (limit.isZero())
return import_constants2.Zero;
const result = (0, import_bignumber.parseFixed)(limit.dp(poolPairData[0].decimalsIn).toString(), poolPairData[0].decimalsIn);
return result;
}
limit = path.pools[0].getLimitAmountSwap(poolPairData[0], import_sor4.SwapTypes.SwapExactOut);
for (let i = 1; i < poolPairData.length; i++) {
const poolLimitExactIn = path.pools[i].getLimitAmountSwap(poolPairData[i], import_sor4.SwapTypes.SwapExactIn);
const poolLimitExactOut = path.pools[i].getLimitAmountSwap(poolPairData[i], import_sor4.SwapTypes.SwapExactOut);
if (poolLimitExactIn.lte(limit)) {
limit = poolLimitExactOut;
} else {
const pushedLimit = getOutputAmountSwap(path.pools[i], path.poolPairData[i], import_sor4.SwapTypes.SwapExactIn, limit);
limit = import_sor4.OldBigNumber.min(pushedLimit, poolLimitExactOut);
}
}
if (limit.isZero())
return import_constants2.Zero;
return (0, import_bignumber.parseFixed)(limit.dp(poolPairData[poolPairData.length - 1].decimalsOut).toString(), poolPairData[poolPairData.length - 1].decimalsOut);
}
__name(getLimitAmountSwapForPath, "getLimitAmountSwapForPath");
// src/routes/produce.ts
var import_sor5 = require("@balancer-labs/sor");
function producePaths(tokenIn, tokenOut, directPools, hopsIn, hopsOut, pools) {
const paths = [];
for (const directPool of [
...Object.values(directPools)
]) {
const path = createPath([
tokenIn,
tokenOut
], [
pools[directPool.id]
]);
paths.push(path);
}
for (const hopToken in hopsIn) {
if (hopsOut[hopToken]) {
let highestNormalizedLiquidityFirst = import_sor5.ZERO;
let highestNormalizedLiquidityFirstPoolId;
let highestNormalizedLiquiditySecond = import_sor5.ZERO;
let highestNormalizedLiquiditySecondPoolId;
for (const poolInId of [
...hopsIn[hopToken]
]) {
const poolIn = pools[poolInId];
const poolPairData = poolIn.parsePoolPairData(tokenIn, hopToken);
const normalizedLiquidity = poolIn.getNormalizedLiquidity(poolPairData);
if (normalizedLiquidity.isGreaterThanOrEqualTo(highestNormalizedLiquidityFirst)) {
highestNormalizedLiquidityFirst = normalizedLiquidity;
highestNormalizedLiquidityFirstPoolId = poolIn.id;
}
}
for (const poolOutId of [
...hopsOut[hopToken]
]) {
const poolOut = pools[poolOutId];
const poolPairData = poolOut.parsePoolPairData(hopToken, tokenOut);
const normalizedLiquidity = poolOut.getNormalizedLiquidity(poolPairData);
if (normalizedLiquidity.isGreaterThanOrEqualTo(highestNormalizedLiquiditySecond)) {
highestNormalizedLiquiditySecond = normalizedLiquidity;
highestNormalizedLiquiditySecondPoolId = poolOut.id;
}
}
if (highestNormalizedLiquidityFirstPoolId && highestNormalizedLiquiditySecondPoolId) {
const path = createPath([
tokenIn,
hopToken,
tokenOut
], [
pools[highestNormalizedLiquidityFirstPoolId],
pools[highestNormalizedLiquiditySecondPoolId]
]);
paths.push(path);
}
}
}
return paths;
}
__name(producePaths, "producePaths");
// src/routes/proposer.ts
var import_sor6 = require("@balancer-labs/sor");
var RouteProposer = class {
constructor(config) {
this.config = config;
this.cache = {};
}
getCandidatePaths(tokenIn, tokenOut, swapType, pools, swapOptions) {
tokenIn = tokenIn.toLowerCase();
tokenOut = tokenOut.toLowerCase();
if (pools.length === 0)
return [];
const cache = this.cache[`${tokenIn}${tokenOut}${swapType}${swapOptions.timestamp}`];
if (!swapOptions.forceRefresh && Boolean(cache)) {
return cache.paths;
}
const poolsAllDict = (0, import_sor6.parseToPoolsDict)(pools, swapOptions.timestamp);
const [directPools, hopsIn, hopsOut] = filterPoolsOfInterest(poolsAllDict, tokenIn, tokenOut, swapOptions.maxPools);
const pathData = producePaths(tokenIn, tokenOut, directPools, hopsIn, hopsOut, poolsAllDict);
const boostedPaths = getBoostedPaths(tokenIn, tokenOut, poolsAllDict, this.config);
const combinedPathData = pathData.concat(...boostedPaths);
const [paths] = calculatePathLimits(combinedPathData, swapType);
this.cache[`${tokenIn}${tokenOut}${swapType}${swapOptions.timestamp}`] = {
paths
};
return paths;
}
getCandidatePathsFromDict(tokenIn, tokenOut, swapType, poolsAllDict, maxPools) {
tokenIn = tokenIn.toLowerCase();
tokenOut = tokenOut.toLowerCase();
if (Object.keys(poolsAllDict).length === 0)
return [];
const [directPools, hopsIn, hopsOut] = filterPoolsOfInterest(poolsAllDict, tokenIn, tokenOut, maxPools);
const pathData = producePaths(tokenIn, tokenOut, directPools, hopsIn, hopsOut, poolsAllDict);
const boostedPaths = getBoostedPaths(tokenIn, tokenOut, poolsAllDict, this.config);
const combinedPathData = pathData.concat(...boostedPaths);
const [paths] = calculatePathLimits(combinedPathData, swapType);
return paths;
}
};
__name(RouteProposer, "RouteProposer");
// src/sor.ts
var import_sor7 = require("@balancer-labs/sor");
var KoyoSor = class extends import_sor7.SOR {
constructor(provider, config, poolDataService, tokenPriceService) {
super(provider, config, poolDataService, tokenPriceService);
this.routeProposer = new RouteProposer(config);
}
};
__name(KoyoSor, "KoyoSor");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
INFINITY,
KoyoSor,
ONE,
RouteProposer,
calculatePathLimits,
createPath,
filterPoolsOfInterest,
getBoostedGraph,
getBoostedPaths,
getLimitAmountSwapForPath,
getNodesAndEdges,
getOutputAmountSwap,
getPathInfo,
getRaisingToken,
pathsInfoToPaths,
producePaths,
scale
});
//# sourceMappingURL=index.js.map