UNPKG

@xspswap/smart-order-router

Version:
233 lines 19.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MixedQuoter = void 0; const sdk_core_1 = require("@x-swap-protocol/sdk-core"); const v3_sdk_1 = require("@x-swap-protocol/v3-sdk"); const lodash_1 = __importDefault(require("lodash")); const providers_1 = require("../../../providers"); const util_1 = require("../../../util"); const entities_1 = require("../entities"); const compute_all_routes_1 = require("../functions/compute-all-routes"); const base_quoter_1 = require("./base-quoter"); let paths = []; const recFinder = (path, used, lastToken, dest, hops) => { const hop = path.length; if (hop < hops) { if (!lastToken) { return; } const additionalV3 = providers_1.V3_XDC_ADDITIONAL_BASES[lastToken.address]; const additionalV2 = providers_1.V2_XDC_ADDITIONAL_BASES[lastToken.address]; if (additionalV3) { for (const ad of additionalV3) { const adToken = ad; const newPair = [lastToken, ...adToken]; 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, Object.assign(Object.assign({}, 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, Object.assign(Object.assign({}, used), { [id]: true }), adToken, dest, hops); } } } } }; class MixedQuoter extends base_quoter_1.BaseQuoter { constructor(v3SubgraphProvider, v3PoolProvider, v2SubgraphProvider, v2PoolProvider, onChainQuoteProvider, tokenProvider, chainId, blockedTokenListProvider, tokenValidatorProvider) { super(tokenProvider, chainId, blockedTokenListProvider, tokenValidatorProvider); this.v3SubgraphProvider = v3SubgraphProvider; this.v3PoolProvider = v3PoolProvider; this.v2SubgraphProvider = v2SubgraphProvider; this.v2PoolProvider = v2PoolProvider; this.onChainQuoteProvider = onChainQuoteProvider; } async getRoutes(tokenIn, tokenOut, tradeType, routingConfig) { paths = []; if (tradeType != sdk_core_1.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 id = `${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, v3_sdk_1.FeeAmount.LOWEST], [tokenIn, tokenOut, v3_sdk_1.FeeAmount.LOW], [tokenIn, tokenOut, v3_sdk_1.FeeAmount.MEDIUM], [tokenIn, tokenOut, v3_sdk_1.FeeAmount.HIGH], ] : []; const poolsPaths = [...Object.values(uniquePools), ...directV3]; const directV2 = tokenIn && tokenOut ? [[tokenIn, tokenOut]] : []; const pairsPaths = [...Object.values(uniquePairs), ...directV2]; const [poolAccessor, pairAccessor] = await Promise.all([ this.v3PoolProvider.getPools(poolsPaths), this.v2PoolProvider.getPools(pairsPaths), ]); const pools = poolAccessor.getAllPools(); const pairs = pairAccessor.getAllPools(); const poolsAndPairs = [...pairs, ...pools]; const { maxSwapsPerPath } = routingConfig; const routes = (0, compute_all_routes_1.computeAllMixedRoutes)(tokenIn, tokenOut, poolsAndPairs, maxSwapsPerPath); return { routes, }; } async getQuotes(routes, amounts, percents, quoteToken, tradeType, routingConfig, candidatePools, gasModel) { util_1.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(); util_1.log.info(`Getting quotes for mixed for ${routes.length} routes with ${amounts.length} amounts per route.`); const { routesWithQuotes } = await quoteFn(amounts, routes, { blockNumber: routingConfig.blockNumber, }); util_1.metric.putMetric('MixedQuotesLoad', Date.now() - beforeQuotes, util_1.MetricLoggerUnit.Milliseconds); util_1.metric.putMetric('MixedQuotesFetched', (0, lodash_1.default)(routesWithQuotes) .map(([, quotes]) => quotes.length) .sum(), util_1.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) { util_1.log.debug({ route: (0, util_1.routeToString)(route), amountQuote, }, 'Dropping a null mixed quote for route.'); continue; } const routeWithValidQuote = new entities_1.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, }; } } exports.MixedQuoter = MixedQuoter; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWl4ZWQtcXVvdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3JvdXRlcnMvYWxwaGEtcm91dGVyL3F1b3RlcnMvbWl4ZWQtcXVvdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLHdEQUE2RDtBQUM3RCxvREFBMEQ7QUFFMUQsb0RBQXVCO0FBRXZCLGtEQVc0QjtBQUM1Qix3Q0FPdUI7QUFHdkIsMENBQXVEO0FBQ3ZELHdFQUF3RTtBQUl4RSwrQ0FBMkM7QUFHM0MsSUFBSSxLQUFLLEdBQXFELEVBQUUsQ0FBQztBQUVqRSxNQUFNLFNBQVMsR0FBRyxDQUNoQixJQUFvRCxFQUNwRCxJQUFvQyxFQUNwQyxTQUFnQixFQUNoQixJQUFXLEVBQ1gsSUFBWSxFQUNaLEVBQUU7SUFDRixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBRXhCLElBQUksR0FBRyxHQUFHLElBQUksRUFBRTtRQUNkLElBQUksQ0FBQyxTQUFTLEVBQUU7WUFDZCxPQUFPO1NBQ1I7UUFDRCxNQUFNLFlBQVksR0FBRyxtQ0FBdUIsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDaEUsTUFBTSxZQUFZLEdBQUcsbUNBQXVCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hFLElBQUksWUFBWSxFQUFFO1lBQ2hCLEtBQUssTUFBTSxFQUFFLElBQUksWUFBWSxFQUFFO2dCQUM3QixNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUM7Z0JBQ25CLE1BQU0sT0FBTyxHQUE4QixDQUFDLFNBQVMsRUFBRSxHQUFHLE9BQU8sQ0FBQyxDQUFDO2dCQUNuRSxNQUFNLE9BQU8sR0FBRyxDQUFDLEdBQUcsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNuQyxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEtBQUssSUFBSSxDQUFDLE9BQU8sRUFBRTtvQkFDdkMsS0FBSyxDQUFDLElB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