UNPKG

@xspswap/smart-order-router

Version:
292 lines 26.6 kB
import { Protocol } from '@x-swap-protocol/router-sdk'; import { TradeType } from '@x-swap-protocol/sdk-core'; import { FeeAmount, Pool } from '@x-swap-protocol/v3-sdk'; import _ from 'lodash'; import { V2_XDC_ADDITIONAL_BASES, V3_XDC_ADDITIONAL_BASES, } from '../../../providers'; import { FATHOM_V2_XDC_ADDITIONAL_BASES } from '../../../providers/v2-fathom/static-subgraph-provider'; import { Uni_V3_XDC_ADDITIONAL_BASES } from '../../../providers/v3/static-subgraph-provider-v3-uni'; import { log, metric, MetricLoggerUnit, routeToString, } from '../../../util'; import { MixedRouteWithValidQuote } from '../entities'; import { computeAllMixedRoutes } from '../functions/compute-all-routes'; import { BaseQuoter } from './base-quoter'; const hasUniV3Pool = (route) => route.pools.some((pool) => pool instanceof Pool && pool.factory); let paths = []; const recFinder = (path, used, lastToken, dest, hops) => { const hop = path.length; if (hop < hops) { if (!lastToken) { return; } const additionalV3 = V3_XDC_ADDITIONAL_BASES[lastToken.address]; const additionalV3Uni = Uni_V3_XDC_ADDITIONAL_BASES[lastToken.address]; const additionalV2 = V2_XDC_ADDITIONAL_BASES[lastToken.address]; const additionalV2Fathom = FATHOM_V2_XDC_ADDITIONAL_BASES[lastToken.address]; if (additionalV3) { for (const ad of additionalV3) { const adToken = ad; const newPair = [ lastToken, ...adToken, Protocol.V3, ]; const newPath = [...path, newPair]; if (adToken[0].address === dest.address) { paths.push(newPath); continue; } // const id = `${adToken[0].address}-${dest.address}-${adToken[1]}` const id = adToken[0].address; if (!used[id]) { recFinder(newPath, { ...used, [id]: true }, adToken[0], dest, hops); } } } if (additionalV3Uni) { for (const ad of additionalV3Uni) { const adToken = ad; const newPair = [ lastToken, ...adToken, Protocol.UNI_V3, ]; const newPath = [...path, newPair]; if (adToken[0].address === dest.address) { paths.push(newPath); continue; } // const id = `${adToken[0].address}-${dest.address}-${adToken[1]}` const id = adToken[0].address; if (!used[id]) { recFinder(newPath, { ...used, [id]: true }, adToken[0], dest, hops); } } } if (additionalV2) { for (const ad of additionalV2) { const adToken = ad; const newPair = [lastToken, adToken]; const newPath = [...path, newPair]; if (adToken.address === dest.address) { paths.push(newPath); continue; } // const id = `${adToken.address}-${dest.address}` const id = adToken.address; if (!used[id]) { recFinder(newPath, { ...used, [id]: true }, adToken, dest, hops); } } } if (additionalV2Fathom) { for (const ad of additionalV2Fathom) { const adToken = ad; const newPair = [lastToken, adToken]; const newPath = [...path, newPair]; if (adToken.address === dest.address) { paths.push(newPath); continue; } // const id = `${adToken.address}-${dest.address}` const id = adToken.address; if (!used[id]) { recFinder(newPath, { ...used, [id]: true }, adToken, dest, hops); } } } } }; export class MixedQuoter extends BaseQuoter { constructor(v3SubgraphProvider, v3PoolProvider, v3UniPoolProvider, v2SubgraphProvider, v2PoolProvider, onChainQuoteProvider, tokenProvider, chainId, blockedTokenListProvider, tokenValidatorProvider) { super(tokenProvider, chainId, Protocol.MIXED, blockedTokenListProvider, tokenValidatorProvider); this.v3SubgraphProvider = v3SubgraphProvider; this.v3PoolProvider = v3PoolProvider; this.v3UniPoolProvider = v3UniPoolProvider; this.v2SubgraphProvider = v2SubgraphProvider; this.v2PoolProvider = v2PoolProvider; this.onChainQuoteProvider = onChainQuoteProvider; } async getRoutes(tokenIn, tokenOut, tradeType, routingConfig) { paths = []; if (tradeType != TradeType.EXACT_INPUT) { throw new Error('Mixed route quotes are not supported for EXACT_OUTPUT'); } // const { // V2poolAccessor, // V3poolAccessor, // candidatePools: mixedRouteCandidatePools, // } = await getMixedRouteCandidatePools({ // tokenIn, // tokenOut, // tokenProvider: this.tokenProvider, // blockedTokenListProvider: this.blockedTokenListProvider, // v3poolProvider: this.v3PoolProvider, // v2poolProvider: this.v2PoolProvider, // routeType: tradeType, // v3subgraphProvider: this.v3SubgraphProvider, // v2subgraphProvider: this.v2SubgraphProvider, // routingConfig, // chainId: this.chainId, // }); // const V3poolsRaw = V3poolAccessor.getAllPools(); // const V2poolsRaw = V2poolAccessor.getAllPools(); // // const poolsRaw = [...V3poolsRaw, ...V2poolsRaw]; // const candidatePools = mixedRouteCandidatePools; // Drop any pools that contain fee on transfer tokens (not supported by v3) or have issues with being transferred. // const pools = await this.applyTokenValidatorToPools( // poolsRaw, // ( // token: Currency, // tokenValidation: TokenValidationResult | undefined // ): boolean => { // // If there is no available validation result we assume the token is fine. // if (!tokenValidation) { // return false; // } // // // Only filters out *intermediate* pools that involve tokens that we detect // // cant be transferred. This prevents us trying to route through tokens that may // // not be transferrable, but allows users to still swap those tokens if they // // specify. // // // if ( // tokenValidation == TokenValidationResult.STF && // (token.equals(tokenIn) || token.equals(tokenOut)) // ) { // return false; // } // // return ( // tokenValidation == TokenValidationResult.FOT || // tokenValidation == TokenValidationResult.STF // ); // } // ); // vot tut nado v pools insert all needed pools and pairs recFinder([], { [tokenIn.address]: true }, tokenIn, tokenOut, 5); // console.log( // paths.map((path) => // path // .map( // (p) => `(${p[2]?'V3':'V2'} ${p[0].symbol} - ${p[1].symbol} : ${p[2] ? p[2] / 10000 : '0.3'}% )` // ) // .join(' --> ') // ) // ); const uniquePools = {}; const uniquePairs = {}; paths.forEach((path) => { path.forEach((pool) => { if (pool[2]) { const protocol = pool[3]; const id = `${protocol}-${pool[0].address}-${pool[1].address}-${pool[2]}`; uniquePools[id] = pool; } else { const id = `${pool[0].address}-${pool[1].address}`; uniquePairs[id] = pool; } }); }); const directV3 = tokenIn && tokenOut ? [ [tokenIn, tokenOut, FeeAmount.LOWEST, Protocol.V3], [tokenIn, tokenOut, FeeAmount.LOW, Protocol.V3], [tokenIn, tokenOut, FeeAmount.MEDIUM, Protocol.V3], [tokenIn, tokenOut, FeeAmount.HIGH, Protocol.V3], [tokenIn, tokenOut, FeeAmount.LOWEST, Protocol.UNI_V3], [tokenIn, tokenOut, FeeAmount.LOW, Protocol.UNI_V3], [tokenIn, tokenOut, FeeAmount.MEDIUM, Protocol.UNI_V3], [tokenIn, tokenOut, FeeAmount.HIGH, Protocol.UNI_V3], ] : []; const poolsPaths = [...Object.values(uniquePools), ...directV3]; const xswapV3PoolsPaths = poolsPaths .filter((pool) => pool[3] === Protocol.V3) .map((pool) => [pool[0], pool[1], pool[2]]); const uniV3PoolsPaths = poolsPaths .filter((pool) => pool[3] === Protocol.UNI_V3) .map((pool) => [pool[0], pool[1], pool[2]]); const directV2 = tokenIn && tokenOut ? [[tokenIn, tokenOut]] : []; const pairsPaths = [...Object.values(uniquePairs), ...directV2]; const [xswapPoolAccessor, uniPoolAccessor, pairAccessor] = await Promise.all([ this.v3PoolProvider.getPools(xswapV3PoolsPaths), this.v3UniPoolProvider.getPools(uniV3PoolsPaths), this.v2PoolProvider.getPools(pairsPaths), ]); const pools = [ ...xswapPoolAccessor.getAllPools(), ...uniPoolAccessor.getAllPools(), ]; const pairs = pairAccessor.getAllPools(); const poolsAndPairs = [...pairs, ...pools]; const { maxSwapsPerPath } = routingConfig; const routes = computeAllMixedRoutes(tokenIn, tokenOut, poolsAndPairs, maxSwapsPerPath); return { routes, }; } async getQuotes(routes, amounts, percents, quoteToken, tradeType, routingConfig, candidatePools, gasModel) { log.info('Starting to get mixed quotes'); if (gasModel === undefined) { throw new Error('GasModel for MixedRouteWithValidQuote is required to getQuotes'); } if (routes.length == 0) { return { routesWithValidQuotes: [], candidatePools }; } // For all our routes, and all the fractional amounts, fetch quotes on-chain. const quoteFn = this.onChainQuoteProvider.getQuotesManyExactIn.bind(this.onChainQuoteProvider); const beforeQuotes = Date.now(); log.info(`Getting quotes for mixed for ${routes.length} routes with ${amounts.length} amounts per route.`); const routeGroups = _.partition(routes, hasUniV3Pool); const quoteResults = await Promise.all(routeGroups .filter((routeGroup) => routeGroup.length > 0) .map((routeGroup) => quoteFn(amounts, routeGroup, { blockNumber: routingConfig.blockNumber, }))); const routesWithQuotes = _.flatMap(quoteResults, (quoteResult) => quoteResult.routesWithQuotes); metric.putMetric('MixedQuotesLoad', Date.now() - beforeQuotes, MetricLoggerUnit.Milliseconds); metric.putMetric('MixedQuotesFetched', _(routesWithQuotes) .map(([, quotes]) => quotes.length) .sum(), MetricLoggerUnit.Count); const routesWithValidQuotes = []; for (const routeWithQuote of routesWithQuotes) { const [route, quotes] = routeWithQuote; for (let i = 0; i < quotes.length; i++) { const percent = percents[i]; const amountQuote = quotes[i]; const { quote, amount, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate, } = amountQuote; if (!quote || !sqrtPriceX96AfterList || !initializedTicksCrossedList || !gasEstimate) { log.debug({ route: routeToString(route), amountQuote, }, 'Dropping a null mixed quote for route.'); continue; } const routeWithValidQuote = new MixedRouteWithValidQuote({ route, rawQuote: quote, amount, percent, sqrtPriceX96AfterList, initializedTicksCrossedList, quoterGasEstimate: gasEstimate, mixedRouteGasModel: gasModel, quoteToken, tradeType, v3PoolProvider: this.v3PoolProvider, v2PoolProvider: this.v2PoolProvider, }); routesWithValidQuotes.push(routeWithValidQuote); } } return { routesWithValidQuotes, candidatePools, }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWl4ZWQtcXVvdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3JvdXRlcnMvYWxwaGEtcm91dGVyL3F1b3RlcnMvbWl4ZWQtcXVvdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUN2RCxPQUFPLEVBQWtCLFNBQVMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRXRFLE9BQU8sRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUQsT0FBTyxDQUFDLE1BQU0sUUFBUSxDQUFDO0FBRXZCLE9BQU8sRUFTTCx1QkFBdUIsRUFDdkIsdUJBQXVCLEdBQ3hCLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUFFLDhCQUE4QixFQUFFLE1BQU0sdURBQXVELENBQUM7QUFDdkcsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sdURBQXVELENBQUM7QUFDcEcsT0FBTyxFQUVMLEdBQUcsRUFDSCxNQUFNLEVBQ04sZ0JBQWdCLEVBQ2hCLGFBQWEsR0FDZCxNQUFNLGVBQWUsQ0FBQztBQUd2QixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdkQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFJeEUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUszQyxNQUFNLFlBQVksR0FBRyxDQUFDLEtBQWlCLEVBQVcsRUFBRSxDQUNsRCxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxZQUFZLElBQUksSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7QUFFbkUsSUFBSSxLQUFLLEdBQXNCLEVBQUUsQ0FBQztBQUVsQyxNQUFNLFNBQVMsR0FBRyxDQUNoQixJQUFxQixFQUNyQixJQUFv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