quickswap-smart-order-router
Version:
QuickSwap Smart Order Router
599 lines • 57.6 kB
JavaScript
import { Protocol } from '@uniswap/router-sdk';
import { TradeType } from '@uniswap/sdk-core';
import { FeeAmount } from '@uniswap/v3-sdk';
import _ from 'lodash';
import { DAI_ARBITRUM, DAI_ARBITRUM_RINKEBY, DAI_MAINNET, DAI_OPTIMISM, DAI_OPTIMISTIC_KOVAN, DAI_POLYGON, DAI_POLYGON_MUMBAI, DAI_RINKEBY_1, DAI_RINKEBY_2, FEI_MAINNET, USDC_ARBITRUM, USDC_MAINNET, USDC_OPTIMISM, USDC_OPTIMISTIC_KOVAN, USDC_POLYGON, USDT_ARBITRUM, USDT_ARBITRUM_RINKEBY, USDT_MAINNET, USDT_OPTIMISM, USDT_OPTIMISTIC_KOVAN, USDT_POLYGON, WBTC_ARBITRUM, WBTC_MAINNET, WBTC_OPTIMISM, WBTC_OPTIMISTIC_KOVAN, WETH_POLYGON, WMATIC_POLYGON, WMATIC_POLYGON_MUMBAI, } from '../../../providers/token-provider';
import { ChainId, WRAPPED_NATIVE_CURRENCY } from '../../../util';
import { parseFeeAmount, unparseFeeAmount } from '../../../util/amounts';
import { log } from '../../../util/log';
import { metric, MetricLoggerUnit } from '../../../util/metric';
const baseTokensByChain = {
[ChainId.MAINNET]: [
USDC_MAINNET,
USDT_MAINNET,
WBTC_MAINNET,
DAI_MAINNET,
WRAPPED_NATIVE_CURRENCY[1],
FEI_MAINNET,
],
[ChainId.RINKEBY]: [DAI_RINKEBY_1, DAI_RINKEBY_2],
[ChainId.OPTIMISM]: [
DAI_OPTIMISM,
USDC_OPTIMISM,
USDT_OPTIMISM,
WBTC_OPTIMISM,
],
[ChainId.OPTIMISTIC_KOVAN]: [
DAI_OPTIMISTIC_KOVAN,
USDC_OPTIMISTIC_KOVAN,
WBTC_OPTIMISTIC_KOVAN,
USDT_OPTIMISTIC_KOVAN,
],
[ChainId.ARBITRUM_ONE]: [
DAI_ARBITRUM,
USDC_ARBITRUM,
WBTC_ARBITRUM,
USDT_ARBITRUM,
],
[ChainId.ARBITRUM_RINKEBY]: [DAI_ARBITRUM_RINKEBY, USDT_ARBITRUM_RINKEBY],
[ChainId.POLYGON]: [USDC_POLYGON, WMATIC_POLYGON, WETH_POLYGON, USDT_POLYGON, DAI_POLYGON],
[ChainId.POLYGON_MUMBAI]: [DAI_POLYGON_MUMBAI, WMATIC_POLYGON_MUMBAI],
};
export async function getV3CandidatePools({ tokenIn, tokenOut, routeType, routingConfig, subgraphProvider, tokenProvider, poolProvider, blockedTokenListProvider, chainId, }) {
var _a, _b, _c, _d, _e;
const { blockNumber, v3PoolSelection: { topN, topNDirectSwaps, topNTokenInOut, topNSecondHop, topNWithEachBaseToken, topNWithBaseToken, }, } = routingConfig;
const tokenInAddress = tokenIn.address.toLowerCase();
const tokenOutAddress = tokenOut.address.toLowerCase();
const beforeSubgraphPools = Date.now();
const allPoolsRaw = await subgraphProvider.getPools(tokenIn, tokenOut, {
blockNumber,
});
log.info({ samplePools: allPoolsRaw.slice(0, 3) }, 'Got all pools from V3 subgraph provider');
const allPools = _.map(allPoolsRaw, (pool) => {
return {
...pool,
token0: {
...pool.token0,
id: pool.token0.id.toLowerCase(),
},
token1: {
...pool.token1,
id: pool.token1.id.toLowerCase(),
},
};
});
metric.putMetric('V3SubgraphPoolsLoad', Date.now() - beforeSubgraphPools, MetricLoggerUnit.Milliseconds);
// Only consider pools where neither tokens are in the blocked token list.
let filteredPools = allPools;
if (blockedTokenListProvider) {
filteredPools = [];
for (const pool of allPools) {
const token0InBlocklist = await blockedTokenListProvider.getTokenByAddress(pool.token0.id);
const token1InBlocklist = await blockedTokenListProvider.getTokenByAddress(pool.token1.id);
if (token0InBlocklist || token1InBlocklist) {
continue;
}
filteredPools.push(pool);
}
}
const subgraphPoolsSorted = _(filteredPools)
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.value();
log.info(`After filtering blocked tokens went from ${allPools.length} to ${subgraphPoolsSorted.length}.`);
const poolAddressesSoFar = new Set();
const addToAddressSet = (pools) => {
_(pools)
.map((pool) => pool.id)
.forEach((poolAddress) => poolAddressesSoFar.add(poolAddress));
};
const baseTokens = (_a = baseTokensByChain[chainId]) !== null && _a !== void 0 ? _a : [];
const topByBaseWithTokenIn = _(baseTokens)
.flatMap((token) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
const tokenAddress = token.address.toLowerCase();
return ((subgraphPool.token0.id == tokenAddress &&
subgraphPool.token1.id == tokenInAddress) ||
(subgraphPool.token1.id == tokenAddress &&
subgraphPool.token0.id == tokenInAddress));
})
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.slice(0, topNWithEachBaseToken)
.value();
})
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.slice(0, topNWithBaseToken)
.value();
const topByBaseWithTokenOut = _(baseTokens)
.flatMap((token) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
const tokenAddress = token.address.toLowerCase();
return ((subgraphPool.token0.id == tokenAddress &&
subgraphPool.token1.id == tokenOutAddress) ||
(subgraphPool.token1.id == tokenAddress &&
subgraphPool.token0.id == tokenOutAddress));
})
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.slice(0, topNWithEachBaseToken)
.value();
})
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.slice(0, topNWithBaseToken)
.value();
let top2DirectSwapPool = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
((subgraphPool.token0.id == tokenInAddress &&
subgraphPool.token1.id == tokenOutAddress) ||
(subgraphPool.token1.id == tokenInAddress &&
subgraphPool.token0.id == tokenOutAddress)));
})
.slice(0, topNDirectSwaps)
.value();
if (top2DirectSwapPool.length == 0 && topNDirectSwaps > 0) {
// If we requested direct swap pools but did not find any in the subgraph query.
// Optimistically add them into the query regardless. Invalid pools ones will be dropped anyway
// when we query the pool on-chain. Ensures that new pools for new pairs can be swapped on immediately.
top2DirectSwapPool = _.map([FeeAmount.HIGH, FeeAmount.MEDIUM, FeeAmount.LOW, FeeAmount.LOWEST], (feeAmount) => {
const { token0, token1, poolAddress } = poolProvider.getPoolAddress(tokenIn, tokenOut, feeAmount);
return {
id: poolAddress,
feeTier: unparseFeeAmount(feeAmount),
liquidity: '10000',
token0: {
id: token0.address,
},
token1: {
id: token1.address,
},
tvlETH: 10000,
tvlUSD: 10000,
};
});
}
addToAddressSet(top2DirectSwapPool);
const wrappedNativeAddress = (_b = WRAPPED_NATIVE_CURRENCY[chainId]) === null || _b === void 0 ? void 0 : _b.address;
// Main reason we need this is for gas estimates, only needed if token out is not native.
// We don't check the seen address set because if we've already added pools for getting native quotes
// theres no need to add more.
let top2EthQuoteTokenPool = [];
if ((((_c = WRAPPED_NATIVE_CURRENCY[chainId]) === null || _c === void 0 ? void 0 : _c.symbol) ==
((_d = WRAPPED_NATIVE_CURRENCY[ChainId.MAINNET]) === null || _d === void 0 ? void 0 : _d.symbol) &&
tokenOut.symbol != 'WETH' &&
tokenOut.symbol != 'WETH9' &&
tokenOut.symbol != 'ETH') ||
(((_e = WRAPPED_NATIVE_CURRENCY[chainId]) === null || _e === void 0 ? void 0 : _e.symbol) == WMATIC_POLYGON.symbol &&
tokenOut.symbol != 'MATIC' &&
tokenOut.symbol != 'WMATIC')) {
top2EthQuoteTokenPool = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
if (routeType == TradeType.EXACT_INPUT) {
return ((subgraphPool.token0.id == wrappedNativeAddress &&
subgraphPool.token1.id == tokenOutAddress) ||
(subgraphPool.token1.id == wrappedNativeAddress &&
subgraphPool.token0.id == tokenOutAddress));
}
else {
return ((subgraphPool.token0.id == wrappedNativeAddress &&
subgraphPool.token1.id == tokenInAddress) ||
(subgraphPool.token1.id == wrappedNativeAddress &&
subgraphPool.token0.id == tokenInAddress));
}
})
.slice(0, 1)
.value();
}
addToAddressSet(top2EthQuoteTokenPool);
const topByTVL = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return !poolAddressesSoFar.has(subgraphPool.id);
})
.slice(0, topN)
.value();
addToAddressSet(topByTVL);
const topByTVLUsingTokenIn = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == tokenInAddress ||
subgraphPool.token1.id == tokenInAddress));
})
.slice(0, topNTokenInOut)
.value();
addToAddressSet(topByTVLUsingTokenIn);
const topByTVLUsingTokenOut = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == tokenOutAddress ||
subgraphPool.token1.id == tokenOutAddress));
})
.slice(0, topNTokenInOut)
.value();
addToAddressSet(topByTVLUsingTokenOut);
const topByTVLUsingTokenInSecondHops = _(topByTVLUsingTokenIn)
.map((subgraphPool) => {
return tokenInAddress == subgraphPool.token0.id
? subgraphPool.token1.id
: subgraphPool.token0.id;
})
.flatMap((secondHopId) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == secondHopId ||
subgraphPool.token1.id == secondHopId));
})
.slice(0, topNSecondHop)
.value();
})
.uniqBy((pool) => pool.id)
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.slice(0, topNSecondHop)
.value();
addToAddressSet(topByTVLUsingTokenInSecondHops);
const topByTVLUsingTokenOutSecondHops = _(topByTVLUsingTokenOut)
.map((subgraphPool) => {
return tokenOutAddress == subgraphPool.token0.id
? subgraphPool.token1.id
: subgraphPool.token0.id;
})
.flatMap((secondHopId) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == secondHopId ||
subgraphPool.token1.id == secondHopId));
})
.slice(0, topNSecondHop)
.value();
})
.uniqBy((pool) => pool.id)
.sortBy((tokenListPool) => -tokenListPool.tvlUSD)
.slice(0, topNSecondHop)
.value();
addToAddressSet(topByTVLUsingTokenOutSecondHops);
const subgraphPools = _([
...topByBaseWithTokenIn,
...topByBaseWithTokenOut,
...top2DirectSwapPool,
...top2EthQuoteTokenPool,
...topByTVL,
...topByTVLUsingTokenIn,
...topByTVLUsingTokenOut,
...topByTVLUsingTokenInSecondHops,
...topByTVLUsingTokenOutSecondHops,
])
.compact()
.uniqBy((pool) => pool.id)
.value();
const tokenAddresses = _(subgraphPools)
.flatMap((subgraphPool) => [subgraphPool.token0.id, subgraphPool.token1.id])
.compact()
.uniq()
.value();
log.info(`Getting the ${tokenAddresses.length} tokens within the ${subgraphPools.length} V3 pools we are considering`);
const tokenAccessor = await tokenProvider.getTokens(tokenAddresses, {
blockNumber,
});
const printV3SubgraphPool = (s) => {
var _a, _b, _c, _d;
return `${(_b = (_a = tokenAccessor.getTokenByAddress(s.token0.id)) === null || _a === void 0 ? void 0 : _a.symbol) !== null && _b !== void 0 ? _b : s.token0.id}/${(_d = (_c = tokenAccessor.getTokenByAddress(s.token1.id)) === null || _c === void 0 ? void 0 : _c.symbol) !== null && _d !== void 0 ? _d : s.token1.id}/${s.feeTier}`;
};
log.info({
topByBaseWithTokenIn: topByBaseWithTokenIn.map(printV3SubgraphPool),
topByBaseWithTokenOut: topByBaseWithTokenOut.map(printV3SubgraphPool),
topByTVL: topByTVL.map(printV3SubgraphPool),
topByTVLUsingTokenIn: topByTVLUsingTokenIn.map(printV3SubgraphPool),
topByTVLUsingTokenOut: topByTVLUsingTokenOut.map(printV3SubgraphPool),
topByTVLUsingTokenInSecondHops: topByTVLUsingTokenInSecondHops.map(printV3SubgraphPool),
topByTVLUsingTokenOutSecondHops: topByTVLUsingTokenOutSecondHops.map(printV3SubgraphPool),
top2DirectSwap: top2DirectSwapPool.map(printV3SubgraphPool),
top2EthQuotePool: top2EthQuoteTokenPool.map(printV3SubgraphPool),
}, `V3 Candidate Pools`);
const tokenPairsRaw = _.map(subgraphPools, (subgraphPool) => {
const tokenA = tokenAccessor.getTokenByAddress(subgraphPool.token0.id);
const tokenB = tokenAccessor.getTokenByAddress(subgraphPool.token1.id);
let fee;
try {
fee = parseFeeAmount(subgraphPool.feeTier);
}
catch (err) {
log.info({ subgraphPool }, `Dropping candidate pool for ${subgraphPool.token0.id}/${subgraphPool.token1.id}/${subgraphPool.feeTier} because fee tier not supported`);
return undefined;
}
if (!tokenA || !tokenB) {
log.info(`Dropping candidate pool for ${subgraphPool.token0.id}/${subgraphPool.token1.id}/${fee} because ${tokenA ? subgraphPool.token1.id : subgraphPool.token0.id} not found by token provider`);
return undefined;
}
return [tokenA, tokenB, fee];
});
const tokenPairs = _.compact(tokenPairsRaw);
const beforePoolsLoad = Date.now();
const poolAccessor = await poolProvider.getPools(tokenPairs);
metric.putMetric('V3PoolsLoad', Date.now() - beforePoolsLoad, MetricLoggerUnit.Milliseconds);
const poolsBySelection = {
protocol: Protocol.V3,
selections: {
topByBaseWithTokenIn,
topByBaseWithTokenOut,
topByDirectSwapPool: top2DirectSwapPool,
topByEthQuoteTokenPool: top2EthQuoteTokenPool,
topByTVL,
topByTVLUsingTokenIn,
topByTVLUsingTokenOut,
topByTVLUsingTokenInSecondHops,
topByTVLUsingTokenOutSecondHops,
},
};
return { poolAccessor, candidatePools: poolsBySelection };
}
export async function getV2CandidatePools({ tokenIn, tokenOut, routeType, routingConfig, subgraphProvider, tokenProvider, poolProvider, blockedTokenListProvider, chainId, }) {
var _a, _b, _c, _d;
const { blockNumber, v2PoolSelection: { topN, topNDirectSwaps, topNTokenInOut, topNSecondHop, topNWithEachBaseToken, topNWithBaseToken, }, } = routingConfig;
const tokenInAddress = tokenIn.address.toLowerCase();
const tokenOutAddress = tokenOut.address.toLowerCase();
const beforeSubgraphPools = Date.now();
const allPoolsRaw = await subgraphProvider.getPools(tokenIn, tokenOut, {
blockNumber,
});
const allPools = _.map(allPoolsRaw, (pool) => {
return {
...pool,
token0: {
...pool.token0,
id: pool.token0.id.toLowerCase(),
},
token1: {
...pool.token1,
id: pool.token1.id.toLowerCase(),
},
};
});
metric.putMetric('V2SubgraphPoolsLoad', Date.now() - beforeSubgraphPools, MetricLoggerUnit.Milliseconds);
// Only consider pools where neither tokens are in the blocked token list.
let filteredPools = allPools;
if (blockedTokenListProvider) {
filteredPools = [];
for (const pool of allPools) {
const token0InBlocklist = await blockedTokenListProvider.getTokenByAddress(pool.token0.id);
const token1InBlocklist = await blockedTokenListProvider.getTokenByAddress(pool.token1.id);
if (token0InBlocklist || token1InBlocklist) {
continue;
}
filteredPools.push(pool);
}
}
const subgraphPoolsSorted = _(filteredPools)
.sortBy((tokenListPool) => -tokenListPool.reserve)
.value();
log.info(`After filtering blocked tokens went from ${allPools.length} to ${subgraphPoolsSorted.length}.`);
const poolAddressesSoFar = new Set();
const addToAddressSet = (pools) => {
_(pools)
.map((pool) => pool.id)
.forEach((poolAddress) => poolAddressesSoFar.add(poolAddress));
};
const baseTokens = (_a = baseTokensByChain[chainId]) !== null && _a !== void 0 ? _a : [];
const topByBaseWithTokenIn = _(baseTokens)
.flatMap((token) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
const tokenAddress = token.address.toLowerCase();
return ((subgraphPool.token0.id == tokenAddress &&
subgraphPool.token1.id == tokenInAddress) ||
(subgraphPool.token1.id == tokenAddress &&
subgraphPool.token0.id == tokenInAddress));
})
.sortBy((tokenListPool) => -tokenListPool.reserve)
.slice(0, topNWithEachBaseToken)
.value();
})
.sortBy((tokenListPool) => -tokenListPool.reserve)
.slice(0, topNWithBaseToken)
.value();
const topByBaseWithTokenOut = _(baseTokens)
.flatMap((token) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
const tokenAddress = token.address.toLowerCase();
return ((subgraphPool.token0.id == tokenAddress &&
subgraphPool.token1.id == tokenOutAddress) ||
(subgraphPool.token1.id == tokenAddress &&
subgraphPool.token0.id == tokenOutAddress));
})
.sortBy((tokenListPool) => -tokenListPool.reserve)
.slice(0, topNWithEachBaseToken)
.value();
})
.sortBy((tokenListPool) => -tokenListPool.reserve)
.slice(0, topNWithBaseToken)
.value();
// Always add the direct swap pool into the mix regardless of if it exists in the subgraph pool list.
// Ensures that new pools can be swapped on immediately, and that if a pool was filtered out of the
// subgraph query for some reason (e.g. trackedReserveETH was 0), then we still consider it.
let topByDirectSwapPool = [];
if (topNDirectSwaps != 0) {
const { token0, token1, poolAddress } = poolProvider.getPoolAddress(tokenIn, tokenOut);
topByDirectSwapPool = [
{
id: poolAddress,
token0: {
id: token0.address,
},
token1: {
id: token1.address,
},
supply: 10000,
reserve: 10000, // Not used. Set to arbitrary number.
},
];
}
addToAddressSet(topByDirectSwapPool);
const wrappedNativeAddress = WRAPPED_NATIVE_CURRENCY[chainId].address;
// Main reason we need this is for gas estimates, only needed if token out is not ETH.
// We don't check the seen address set because if we've already added pools for getting ETH quotes
// theres no need to add more.
// Note: we do not need to check other native currencies for the V2 Protocol
let topByEthQuoteTokenPool = [];
if ((((_b = WRAPPED_NATIVE_CURRENCY[chainId]) === null || _b === void 0 ? void 0 : _b.symbol) ==
((_c = WRAPPED_NATIVE_CURRENCY[ChainId.MAINNET]) === null || _c === void 0 ? void 0 : _c.symbol) &&
tokenOut.symbol != 'WETH' &&
tokenOut.symbol != 'WETH9' &&
tokenOut.symbol != 'ETH') ||
(((_d = WRAPPED_NATIVE_CURRENCY[chainId]) === null || _d === void 0 ? void 0 : _d.symbol) == WMATIC_POLYGON.symbol &&
tokenOut.symbol != 'MATIC' &&
tokenOut.symbol != 'WMATIC')) {
topByEthQuoteTokenPool = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
if (routeType == TradeType.EXACT_INPUT) {
return ((subgraphPool.token0.id == wrappedNativeAddress &&
subgraphPool.token1.id == tokenOutAddress) ||
(subgraphPool.token1.id == wrappedNativeAddress &&
subgraphPool.token0.id == tokenOutAddress));
}
else {
return ((subgraphPool.token0.id == wrappedNativeAddress &&
subgraphPool.token1.id == tokenInAddress) ||
(subgraphPool.token1.id == wrappedNativeAddress &&
subgraphPool.token0.id == tokenInAddress));
}
})
.slice(0, 1)
.value();
}
addToAddressSet(topByEthQuoteTokenPool);
const topByTVL = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return !poolAddressesSoFar.has(subgraphPool.id);
})
.slice(0, topN)
.value();
addToAddressSet(topByTVL);
const topByTVLUsingTokenIn = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == tokenInAddress ||
subgraphPool.token1.id == tokenInAddress));
})
.slice(0, topNTokenInOut)
.value();
addToAddressSet(topByTVLUsingTokenIn);
const topByTVLUsingTokenOut = _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == tokenOutAddress ||
subgraphPool.token1.id == tokenOutAddress));
})
.slice(0, topNTokenInOut)
.value();
addToAddressSet(topByTVLUsingTokenOut);
const topByTVLUsingTokenInSecondHops = _(topByTVLUsingTokenIn)
.map((subgraphPool) => {
return tokenInAddress == subgraphPool.token0.id
? subgraphPool.token1.id
: subgraphPool.token0.id;
})
.flatMap((secondHopId) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == secondHopId ||
subgraphPool.token1.id == secondHopId));
})
.slice(0, topNSecondHop)
.value();
})
.uniqBy((pool) => pool.id)
.sortBy((tokenListPool) => -tokenListPool.reserve)
.slice(0, topNSecondHop)
.value();
addToAddressSet(topByTVLUsingTokenInSecondHops);
const topByTVLUsingTokenOutSecondHops = _(topByTVLUsingTokenOut)
.map((subgraphPool) => {
return tokenOutAddress == subgraphPool.token0.id
? subgraphPool.token1.id
: subgraphPool.token0.id;
})
.flatMap((secondHopId) => {
return _(subgraphPoolsSorted)
.filter((subgraphPool) => {
return (!poolAddressesSoFar.has(subgraphPool.id) &&
(subgraphPool.token0.id == secondHopId ||
subgraphPool.token1.id == secondHopId));
})
.slice(0, topNSecondHop)
.value();
})
.uniqBy((pool) => pool.id)
.sortBy((tokenListPool) => -tokenListPool.reserve)
.slice(0, topNSecondHop)
.value();
addToAddressSet(topByTVLUsingTokenOutSecondHops);
const subgraphPools = _([
...topByBaseWithTokenIn,
...topByBaseWithTokenOut,
...topByDirectSwapPool,
...topByEthQuoteTokenPool,
...topByTVL,
...topByTVLUsingTokenIn,
...topByTVLUsingTokenOut,
...topByTVLUsingTokenInSecondHops,
...topByTVLUsingTokenOutSecondHops,
])
.compact()
.uniqBy((pool) => pool.id)
.value();
const tokenAddresses = _(subgraphPools)
.flatMap((subgraphPool) => [subgraphPool.token0.id, subgraphPool.token1.id])
.compact()
.uniq()
.value();
log.info(`Getting the ${tokenAddresses.length} tokens within the ${subgraphPools.length} V2 pools we are considering`);
const tokenAccessor = await tokenProvider.getTokens(tokenAddresses, {
blockNumber,
});
const printV2SubgraphPool = (s) => {
var _a, _b, _c, _d;
return `${(_b = (_a = tokenAccessor.getTokenByAddress(s.token0.id)) === null || _a === void 0 ? void 0 : _a.symbol) !== null && _b !== void 0 ? _b : s.token0.id}/${(_d = (_c = tokenAccessor.getTokenByAddress(s.token1.id)) === null || _c === void 0 ? void 0 : _c.symbol) !== null && _d !== void 0 ? _d : s.token1.id}`;
};
log.info({
topByBaseWithTokenIn: topByBaseWithTokenIn.map(printV2SubgraphPool),
topByBaseWithTokenOut: topByBaseWithTokenOut.map(printV2SubgraphPool),
topByTVL: topByTVL.map(printV2SubgraphPool),
topByTVLUsingTokenIn: topByTVLUsingTokenIn.map(printV2SubgraphPool),
topByTVLUsingTokenOut: topByTVLUsingTokenOut.map(printV2SubgraphPool),
topByTVLUsingTokenInSecondHops: topByTVLUsingTokenInSecondHops.map(printV2SubgraphPool),
topByTVLUsingTokenOutSecondHops: topByTVLUsingTokenOutSecondHops.map(printV2SubgraphPool),
top2DirectSwap: topByDirectSwapPool.map(printV2SubgraphPool),
top2EthQuotePool: topByEthQuoteTokenPool.map(printV2SubgraphPool),
}, `V2 Candidate pools`);
const tokenPairsRaw = _.map(subgraphPools, (subgraphPool) => {
const tokenA = tokenAccessor.getTokenByAddress(subgraphPool.token0.id);
const tokenB = tokenAccessor.getTokenByAddress(subgraphPool.token1.id);
if (!tokenA || !tokenB) {
log.info(`Dropping candidate pool for ${subgraphPool.token0.id}/${subgraphPool.token1.id}`);
return undefined;
}
return [tokenA, tokenB];
});
const tokenPairs = _.compact(tokenPairsRaw);
const beforePoolsLoad = Date.now();
const poolAccessor = await poolProvider.getPools(tokenPairs, { blockNumber });
metric.putMetric('V2PoolsLoad', Date.now() - beforePoolsLoad, MetricLoggerUnit.Milliseconds);
const poolsBySelection = {
protocol: Protocol.V2,
selections: {
topByBaseWithTokenIn,
topByBaseWithTokenOut,
topByDirectSwapPool,
topByEthQuoteTokenPool: topByEthQuoteTokenPool,
topByTVL,
topByTVLUsingTokenIn,
topByTVLUsingTokenOut,
topByTVLUsingTokenInSecondHops,
topByTVLUsingTokenOutSecondHops,
},
};
return { poolAccessor, candidatePools: poolsBySelection };
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LWNhbmRpZGF0ZS1wb29scy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9yb3V0ZXJzL2FscGhhLXJvdXRlci9mdW5jdGlvbnMvZ2V0LWNhbmRpZGF0ZS1wb29scy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxFQUFTLFNBQVMsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM1QyxPQUFPLENBQUMsTUFBTSxRQUFRLENBQUM7QUFNdkIsT0FBTyxFQUNMLFlBQVksRUFDWixvQkFBb0IsRUFDcEIsV0FBVyxFQUNYLFlBQVksRUFDWixvQkFBb0IsRUFDcEIsV0FBVyxFQUNYLGtCQUFrQixFQUNsQixhQUFhLEVBQ2IsYUFBYSxFQUNiLFdBQVcsRUFFWCxhQUFhLEVBQ2IsWUFBWSxFQUNaLGFBQWEsRUFDYixxQkFBcUIsRUFDckIsWUFBWSxFQUNaLGFBQWEsRUFDYixxQkFBcUIsRUFDckIsWUFBWSxFQUNaLGFBQWEsRUFDYixxQkFBcUIsRUFDckIsWUFBWSxFQUNaLGFBQWEsRUFDYixZQUFZLEVBQ1osYUFBYSxFQUNiLHFCQUFxQixFQUNyQixZQUFZLEVBQ1osY0FBYyxFQUNkLHFCQUFxQixHQUN0QixNQUFNLG1DQUFtQyxDQUFDO0FBYTNDLE9BQU8sRUFBRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDakUsT0FBTyxFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3pFLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN4QyxPQUFPLEVBQUUsTUFBTSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUEyQ2hFLE1BQU0saUJBQWlCLEdBQXVDO0lB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