Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable connecting tokens #348

Merged
merged 15 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@balancer-labs/sor",
"version": "4.0.1-beta.18",
"version": "4.1.0-beta.0",
"license": "GPL-3.0-only",
"main": "dist/index.js",
"module": "dist/index.esm.js",
Expand Down
179 changes: 104 additions & 75 deletions src/routeProposal/filtering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ import { Zero } from '@ethersproject/constants';

const BOOSTED_PATHS_MAX_LENGTH = 7;

interface edgeDict {
[node: string]: [string, string, string][];
}

interface treeEdge {
edge: [string, string, string];
parentIndices: [number, number];
visitedNodes: string[];
}

export const filterPoolsByType = (
pools: SubgraphPoolBase[],
poolTypeFilter: PoolFilter
Expand Down Expand Up @@ -166,17 +176,20 @@ export function producePaths(
// We build this tree by adding at each step all the possible continuations for
// each branch. When a branch reaches tokenOut, we write down the corresponding path.
// We only allow paths up to length BOOSTED_PATHS_MAX_LENGTH = 7

export function getBoostedGraph(
tokenIn: string,
tokenOut: string,
poolsAllDict: PoolDictionary,
config: SorConfig
): edgeDict {
const wethAddress: string = config.weth.toLowerCase();
const graphPoolsSet: Set<PoolBase> = new Set();
const linearPools: PoolBase[] = [];
const phantomPools: PoolBase[] = [];
const connectingTokens = config.connectingTokens
? config.connectingTokens.map(
(connectingToken) => connectingToken.address
)
: [];
// Here we add all linear pools, take note of phantom pools,
// add LBP pools with tokenIn or tokenOut and their corresponding
// highest liquidity WETH connections
Expand All @@ -194,73 +207,34 @@ export function getBoostedGraph(
if (tokensList.includes(pool.address)) {
phantomPools.push(pool);
}
if (config.lbpRaisingTokens) {
const raisingTokens = config.lbpRaisingTokens.map((address) =>
address.toLowerCase()
if (config.lbpRaisingTokens && pool.isLBP) {
handleLBPCase(
graphPoolsSet,
config.lbpRaisingTokens,
pool,
tokenIn,
tokenOut,
connectingTokens,
poolsAllDict
);
if (pool.isLBP) {
const raisingTokenIn: string | undefined = getRaisingToken(
pool,
raisingTokens,
tokenIn
);
if (raisingTokenIn) {
graphPoolsSet.add(pool);
if (raisingTokenIn !== wethAddress) {
const bestRaisingTokenInToWeth =
getHighestLiquidityPool(
raisingTokenIn,
wethAddress,
poolsAllDict
);
if (bestRaisingTokenInToWeth) {
graphPoolsSet.add(
poolsAllDict[bestRaisingTokenInToWeth]
);
}
}
}
const raisingTokenOut: string | undefined = getRaisingToken(
pool,
raisingTokens,
tokenOut
);
if (raisingTokenOut) {
graphPoolsSet.add(pool);
if (raisingTokenOut !== wethAddress) {
const bestWethToRaisingTokenOut =
getHighestLiquidityPool(
wethAddress,
raisingTokenOut,
poolsAllDict
);
if (bestWethToRaisingTokenOut) {
graphPoolsSet.add(
poolsAllDict[bestWethToRaisingTokenOut]
);
}
}
}
}
}
}
}
// add highest liquidity pools with tokenIn and weth or tokenOut and weth
const bestTokenInToWeth = getHighestLiquidityPool(
tokenIn,
wethAddress,
poolsAllDict
);
if (bestTokenInToWeth) {
graphPoolsSet.add(poolsAllDict[bestTokenInToWeth]);
}
const bestWethToTokenOut = getHighestLiquidityPool(
wethAddress,
tokenOut,
poolsAllDict
);
if (bestWethToTokenOut) {
graphPoolsSet.add(poolsAllDict[bestWethToTokenOut]);
// add best pools tokenIn -> connectingToken and connectingToken -> tokenOut
// these can be part of a longer path so do not rely on being directly connected
for (const connectingToken of connectingTokens) {
addMostLiquidPoolToSet(
tokenIn,
connectingToken,
poolsAllDict,
graphPoolsSet
);
addMostLiquidPoolToSet(
connectingToken,
tokenOut,
poolsAllDict,
graphPoolsSet
);
}
if (linearPools.length == 0) return {};
const linearPoolsAddresses = linearPools.map((pool) => pool.address);
Expand Down Expand Up @@ -294,10 +268,6 @@ export function getBoostedGraph(
return edgeDict;
}

interface edgeDict {
[node: string]: [string, string, string][];
}

function getNodesAndEdges(pools: PoolBase[]): edgeDict {
const edgesFromNode: edgeDict = {};
for (const pool of pools) {
Expand All @@ -319,12 +289,6 @@ function getNodesAndEdges(pools: PoolBase[]): edgeDict {
return edgesFromNode;
}

interface treeEdge {
edge: [string, string, string];
parentIndices: [number, number];
visitedNodes: string[];
}

export function getBoostedPaths(
tokenIn: string,
tokenOut: string,
Expand Down Expand Up @@ -645,3 +609,68 @@ function getRaisingToken(
}
return theOtherToken;
}

function handleLBPCase(
graphPoolsSet: Set<PoolBase>,
lbpRaisingTokens: string[],
poolLbp: PoolBase,
tokenIn: string,
tokenOut: string,
connectingTokens: string[],
poolsAllDict: PoolDictionary
) {
// Tokens that will be used as LBP Base Token, e.g. USDC/DAI/WETH
const raisingTokens = lbpRaisingTokens.map((address) =>
address.toLowerCase()
);

if (raisingTokens.length === 0) return;

// Assuming tokenIn is the lbpToken find the corresponding base token if it exists
const raisingTokenIn: string | undefined = getRaisingToken(
poolLbp,
raisingTokens,
tokenIn
);
const raisingTokenOut: string | undefined = getRaisingToken(
poolLbp,
raisingTokens,
tokenOut
);
if (!raisingTokenIn && !raisingTokenOut) return;

// Add the LBP pool to the graph
graphPoolsSet.add(poolLbp);

// For each connecting token add most liquid pools with raisingToken and raisingTokenOut
for (const connectingToken of connectingTokens) {
if (raisingTokenIn && raisingTokenIn !== connectingToken) {
// raisingToken>[Pool]>connectingToken
addMostLiquidPoolToSet(
raisingTokenIn,
connectingToken,
poolsAllDict,
graphPoolsSet
);
}
if (raisingTokenOut && raisingTokenOut !== connectingToken) {
// connectingToken>[Pool]>raisingToken
addMostLiquidPoolToSet(
connectingToken,
raisingTokenOut,
poolsAllDict,
graphPoolsSet
);
}
}
}

function addMostLiquidPoolToSet(
tokenIn: string,
tokenOut: string,
pools: PoolDictionary,
graphPools: Set<PoolBase>
): void {
const pool = getHighestLiquidityPool(tokenIn, tokenOut, pools);
if (pool) graphPools.add(pools[pool]);
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface SorConfig {
chainId: number;
vault: string;
weth: string;
connectingTokens?: { symbol: string; address: string }[];
staBal3Pool?: { id: string; address: string };
usdcConnectingPool?: { id: string; usdc: string };
wETHwstETH?: { id: string; address: string };
Expand Down
26 changes: 26 additions & 0 deletions test/boostedPaths.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,32 @@ describe('generic boosted pools, path creation test', () => {
assert.equal(paths.length, 4);
});
});
context('using an alternative connecting token', () => {
const USDC = '0xe22da380ee6b445bb8273c81944adeb6e8450422'; // USDC
const GOLD = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb';
it('USDC to GOLD', () => {
const [, , boostedPaths] = getPaths(
USDC,
GOLD,
SwapTypes.SwapExactIn,
genericBoostedPools.pools,
maxPools,
sorConfigTest
);
assert.equal(boostedPaths.length, 2);
});
it('GOLD to USDC', () => {
const [, , boostedPaths] = getPaths(
GOLD,
USDC,
SwapTypes.SwapExactIn,
genericBoostedPools.pools,
maxPools,
sorConfigTest
);
assert.equal(boostedPaths.length, 2);
});
});
});

describe('generic boosted pools with wstETH, path creation test', () => {
Expand Down
6 changes: 6 additions & 0 deletions test/candidatePaths.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ describe('Tests pools filtering and path processing', () => {
chainId: 1,
vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8',
weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
connectingTokens: [
{
symbol: 'weth',
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
],
bbausd: {
id: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe',
address: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2',
Expand Down
4 changes: 2 additions & 2 deletions test/composableStablePool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ describe('composable stable pool', () => {
// parsePoolPairData contains pool's allBalances and allBalancesScaled
// both already multiplied by the price rates.
const poolPairData = composableStablePool.parsePoolPairData(
ADDRESSES[Network.MAINNET].bbausdt2.address,
ADDRESSES[Network.MAINNET].bbausd2.address
ADDRESSES[Network.MAINNET].bbausdt.address,
ADDRESSES[Network.MAINNET].bbausd.address
);
it('token -> BPT spot price with no rate', () => {
// spot prices
Expand Down
38 changes: 38 additions & 0 deletions test/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ export const sorConfigTest: SorConfig = {
chainId: 99,
weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
vault: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
connectingTokens: [
{
symbol: 'weth',
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
{
symbol: 'conn',
address: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
},
],
wETHwstETH: {
id: 'wETH-wstETH',
address: '0x0000000000000000000000000000000000222222',
Expand All @@ -25,6 +35,12 @@ export const sorConfigTestStaBal = {
chainId: 99,
weth: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
vault: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
connectingTokens: [
{
symbol: 'weth',
address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
},
],
usdcConnectingPool: {
id: 'usdcConnecting',
usdc: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
Expand All @@ -38,18 +54,40 @@ export const sorConfigTestStaBal = {
export const sorConfigEth: SorConfig = {
chainId: 1,
weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
connectingTokens: [
{
symbol: 'wstEth',
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
{
symbol: 'wEth',
address: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0',
},
],
vault: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
};

export const sorConfigKovan: SorConfig = {
chainId: 42,
weth: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1',
connectingTokens: [
{
symbol: 'weth',
address: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1',
},
],
vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8',
};

export const sorConfigFullKovan: SorConfig = {
chainId: 42,
weth: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1',
connectingTokens: [
{
symbol: 'weth',
address: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1',
},
],
vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8',
lbpRaisingTokens: [
'0xdfcea9088c8a88a76ff74892c1457c17dfeef9c1',
Expand Down
6 changes: 6 additions & 0 deletions test/linear.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ describe('linear pool tests', () => {
const config = {
chainId: 99,
weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
connectingTokens: [
{
symbol: 'weth',
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
],
vault: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
};
it('getPathsUsingLinearPool return empty paths', () => {
Expand Down
Loading