UNPKG

@uniswap/smart-order-router

Version:
147 lines 15.6 kB
import { Protocol } from '@uniswap/router-sdk'; import { TradeType } from '@uniswap/sdk-core'; import { UniversalRouterVersion } from '@uniswap/universal-router-sdk'; import _ from 'lodash'; import { TokenValidationResult, } from '../../../providers'; import { log, metric, MetricLoggerUnit, routeToString, } from '../../../util'; import { mixedRouteFilterOutV4Pools } from '../../../util/mixedRouteFilterOutV4Pools'; import { MixedRouteWithValidQuote } from '../entities'; import { computeAllMixedRoutes } from '../functions/compute-all-routes'; import { getMixedRouteCandidatePools, } from '../functions/get-candidate-pools'; import { BaseQuoter } from './base-quoter'; export class MixedQuoter extends BaseQuoter { constructor(v4SubgraphProvider, v4PoolProvider, v3SubgraphProvider, v3PoolProvider, v2SubgraphProvider, v2PoolProvider, onChainQuoteProvider, tokenProvider, chainId, blockedTokenListProvider, tokenValidatorProvider) { super(tokenProvider, chainId, Protocol.MIXED, blockedTokenListProvider, tokenValidatorProvider); this.v4SubgraphProvider = v4SubgraphProvider; this.v4PoolProvider = v4PoolProvider; this.v3SubgraphProvider = v3SubgraphProvider; this.v3PoolProvider = v3PoolProvider; this.v2SubgraphProvider = v2SubgraphProvider; this.v2PoolProvider = v2PoolProvider; this.onChainQuoteProvider = onChainQuoteProvider; } async getRoutes(currencyIn, currencyOut, v4v3v2candidatePools, tradeType, routingConfig) { var _a, _b, _c, _d; const beforeGetRoutes = Date.now(); if (tradeType != TradeType.EXACT_INPUT) { throw new Error('Mixed route quotes are not supported for EXACT_OUTPUT'); } const [v4CandidatePools, v3CandidatePools, v2CandidatePools, crossLiquidityPools,] = v4v3v2candidatePools; const { V2poolAccessor, V3poolAccessor, V4poolAccessor, candidatePools: mixedRouteCandidatePools, } = await getMixedRouteCandidatePools({ v4CandidatePools, v3CandidatePools, v2CandidatePools, crossLiquidityPools, tokenProvider: this.tokenProvider, v4PoolProvider: this.v4PoolProvider, v3poolProvider: this.v3PoolProvider, v2poolProvider: this.v2PoolProvider, routingConfig, chainId: this.chainId, }); const V4poolsRaw = V4poolAccessor.getAllPools(); const V3poolsRaw = V3poolAccessor.getAllPools(); const V2poolsRaw = V2poolAccessor.getAllPools(); const poolsRaw = []; if (!((_a = routingConfig.excludedProtocolsFromMixed) === null || _a === void 0 ? void 0 : _a.includes(Protocol.V4))) { poolsRaw.push(...V4poolsRaw); } if (!((_b = routingConfig.excludedProtocolsFromMixed) === null || _b === void 0 ? void 0 : _b.includes(Protocol.V3))) { poolsRaw.push(...V3poolsRaw); } if (!((_c = routingConfig.excludedProtocolsFromMixed) === null || _c === void 0 ? void 0 : _c.includes(Protocol.V2))) { poolsRaw.push(...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, tokenValidation) => { // 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(currencyIn) || token.equals(currencyOut))) { return false; } return (tokenValidation == TokenValidationResult.FOT || tokenValidation == TokenValidationResult.STF); }); const { maxSwapsPerPath } = routingConfig; let routes = computeAllMixedRoutes(currencyIn, currencyOut, pools, maxSwapsPerPath, routingConfig.shouldEnableMixedRouteEthWeth, routingConfig.hooksOptions); if (routingConfig.universalRouterVersion === UniversalRouterVersion.V1_2 || !((_d = routingConfig.protocols) === null || _d === void 0 ? void 0 : _d.includes(Protocol.V4))) { routes = mixedRouteFilterOutV4Pools(routes); } metric.putMetric('MixedGetRoutesLoad', Date.now() - beforeGetRoutes, MetricLoggerUnit.Milliseconds); metric.putMetric(`MixedGetRoutesLoad_Chain${this.chainId}`, Date.now() - beforeGetRoutes, MetricLoggerUnit.Milliseconds); return { routes: routes, candidatePools, }; } async getQuotes(routes, amounts, percents, quoteCurrency, tradeType, routingConfig, candidatePools, gasModel) { const beforeGetQuotes = Date.now(); 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 { routesWithQuotes } = await quoteFn(amounts, routes, routingConfig); 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: quoteCurrency.wrapped, tradeType, v4PoolProvider: this.v4PoolProvider, v3PoolProvider: this.v3PoolProvider, v2PoolProvider: this.v2PoolProvider, }); routesWithValidQuotes.push(routeWithValidQuote); } } metric.putMetric('MixedGetQuotesLoad', Date.now() - beforeGetQuotes, MetricLoggerUnit.Milliseconds); return { routesWithValidQuotes, candidatePools, }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWl4ZWQtcXVvdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3JvdXRlcnMvYWxwaGEtcm91dGVyL3F1b3RlcnMvbWl4ZWQtcXVvdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEVBQXFCLFNBQVMsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3ZFLE9BQU8sQ0FBQyxNQUFNLFFBQVEsQ0FBQztBQUV2QixPQUFPLEVBV0wscUJBQXFCLEdBQ3RCLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUVMLEdBQUcsRUFDSCxNQUFNLEVBQ04sZ0JBQWdCLEVBQ2hCLGFBQWEsR0FDZCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUd0RixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdkQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDeEUsT0FBTyxFQUdMLDJCQUEyQixHQUk1QixNQUFNLGtDQUFrQyxDQUFDO0FBRzFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFHM0MsTUFBTSxPQUFPLFdBQVksU0FBUSxVQVNoQztJQVNDLFlBQ0Usa0JBQXVDLEVBQ3ZDLGNBQStCLEVBQy9CLGtCQUF1QyxFQUN2QyxjQUErQixFQUMvQixrQkFBdUMsRUFDdkMsY0FBK0IsRUFDL0Isb0JBQTJDLEVBQzNDLGFBQTZCLEVBQzdCLE9BQWdCLEVBQ2hCLHdCQUE2QyxFQUM3QyxzQkFBZ0Q7UUFFaEQsS0FBSyxDQUNILGFBQWEsRUFDYixPQUFPLEVBQ1AsUUFBUSxDQUFDLEtBQUssRUFDZCx3QkFBd0IsRUFDeEIs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