@xspswap/smart-order-router
Version:
XSwap Protocol V3 Smart Order Router
299 lines • 27 kB
JavaScript
;
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 router_sdk_1 = require("@x-swap-protocol/router-sdk");
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 static_subgraph_provider_1 = require("../../../providers/v2-fathom/static-subgraph-provider");
const static_subgraph_provider_v3_uni_1 = require("../../../providers/v3/static-subgraph-provider-v3-uni");
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");
const hasUniV3Pool = (route) => route.pools.some((pool) => pool instanceof v3_sdk_1.Pool && pool.factory);
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 additionalV3Uni = static_subgraph_provider_v3_uni_1.Uni_V3_XDC_ADDITIONAL_BASES[lastToken.address];
const additionalV2 = providers_1.V2_XDC_ADDITIONAL_BASES[lastToken.address];
const additionalV2Fathom = static_subgraph_provider_1.FATHOM_V2_XDC_ADDITIONAL_BASES[lastToken.address];
if (additionalV3) {
for (const ad of additionalV3) {
const adToken = ad;
const newPair = [
lastToken,
...adToken,
router_sdk_1.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, Object.assign(Object.assign({}, used), { [id]: true }), adToken[0], dest, hops);
}
}
}
if (additionalV3Uni) {
for (const ad of additionalV3Uni) {
const adToken = ad;
const newPair = [
lastToken,
...adToken,
router_sdk_1.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, 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);
}
}
}
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, Object.assign(Object.assign({}, used), { [id]: true }), adToken, dest, hops);
}
}
}
}
};
class MixedQuoter extends base_quoter_1.BaseQuoter {
constructor(v3SubgraphProvider, v3PoolProvider, v3UniPoolProvider, v2SubgraphProvider, v2PoolProvider, onChainQuoteProvider, tokenProvider, chainId, blockedTokenListProvider, tokenValidatorProvider) {
super(tokenProvider, chainId, router_sdk_1.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 != 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 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, v3_sdk_1.FeeAmount.LOWEST, router_sdk_1.Protocol.V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.LOW, router_sdk_1.Protocol.V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.MEDIUM, router_sdk_1.Protocol.V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.HIGH, router_sdk_1.Protocol.V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.LOWEST, router_sdk_1.Protocol.UNI_V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.LOW, router_sdk_1.Protocol.UNI_V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.MEDIUM, router_sdk_1.Protocol.UNI_V3],
[tokenIn, tokenOut, v3_sdk_1.FeeAmount.HIGH, router_sdk_1.Protocol.UNI_V3],
]
: [];
const poolsPaths = [...Object.values(uniquePools), ...directV3];
const xswapV3PoolsPaths = poolsPaths
.filter((pool) => pool[3] === router_sdk_1.Protocol.V3)
.map((pool) => [pool[0], pool[1], pool[2]]);
const uniV3PoolsPaths = poolsPaths
.filter((pool) => pool[3] === router_sdk_1.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 = (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 routeGroups = lodash_1.default.partition(routes, hasUniV3Pool);
const quoteResults = await Promise.all(routeGroups
.filter((routeGroup) => routeGroup.length > 0)
.map((routeGroup) => quoteFn(amounts, routeGroup, {
blockNumber: routingConfig.blockNumber,
})));
const routesWithQuotes = lodash_1.default.flatMap(quoteResults, (quoteResult) => quoteResult.routesWithQuotes);
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWl4ZWQtcXVvdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3JvdXRlcnMvYWxwaGEtcm91dGVyL3F1b3RlcnMvbWl4ZWQtcXVvdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDREQUF1RDtBQUN2RCx3REFBc0U7QUFFdEUsb0RBQTBEO0FBQzFELG9EQUF1QjtBQUV2QixrREFXNEI7QUFDNUIsb0dBQXVHO0FBQ3ZHLDJHQUFvRztBQUNwRyx3Q0FNdUI7QUFHdkIsMENBQXVEO0FBQ3ZELHdFQUF3RTtBQUl4RSwrQ0FBMkM7QUFLM0MsTUFBTSxZQUFZLEdBQUcsQ0FBQyxLQUFpQixFQUFXLEVBQUUsQ0FDbEQsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksWUFBWSxhQUFJLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBRW5FLElBQUksS0FBSyxHQUFzQixFQUFFLENBQUM7QUFFbEMsTUFBTSxTQUFTLEdBQUcsQ0FDaEIsSUFBcUIsRUFDckIsSUFBb0MsRUFDcEMsU0FBZ0IsRUFDaEIsSUFBVyxFQUNYLElBQVksRUFDWixFQUFFO0lBQ0YsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUV4QixJQUFJLEdBQUcsR0FBRyxJQUFJLEVBQUU7UUFDZCxJQUFJLENBQUMsU0FBUyxFQUFFO1lBQ2QsT0FBTztTQUNSO1FBQ0QsTUFBTSxZQUFZLEdBQUcsbUNBQXVCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hFLE1BQU0sZUFBZSxHQUFHLDZEQUEyQixDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN2RSxNQUFNLFlBQVksR0FBRyxtQ0FBdUIsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDaEUsTUFBTSxrQkFBa0IsR0FDdEIseURBQThCLENBQUMsU0FBUyxDQUFD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