@xspswap/smart-order-router
Version:
XSwap Protocol V3 Smart Order Router
226 lines • 19.3 kB
JavaScript
import { TradeType } from '@x-swap-protocol/sdk-core';
import { FeeAmount } from '@x-swap-protocol/v3-sdk';
import _ from 'lodash';
import { V2_XDC_ADDITIONAL_BASES, V3_XDC_ADDITIONAL_BASES, } from '../../../providers';
import { log, metric, MetricLoggerUnit, routeToString, } from '../../../util';
import { MixedRouteWithValidQuote } from '../entities';
import { computeAllMixedRoutes } from '../functions/compute-all-routes';
import { BaseQuoter } from './base-quoter';
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 additionalV2 = 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, { ...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);
}
}
}
}
};
export class MixedQuoter extends 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 != 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, FeeAmount.LOWEST],
[tokenIn, tokenOut, FeeAmount.LOW],
[tokenIn, tokenOut, FeeAmount.MEDIUM],
[tokenIn, tokenOut, 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 = 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 { routesWithQuotes } = await quoteFn(amounts, routes, {
blockNumber: routingConfig.blockNumber,
});
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWl4ZWQtcXVvdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3JvdXRlcnMvYWxwaGEtcm91dGVyL3F1b3RlcnMvbWl4ZWQtcXVvdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBUyxTQUFTLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsU0FBUyxFQUFRLE1BQU0seUJBQXlCLENBQUM7QUFFMUQsT0FBTyxDQUFDLE1BQU0sUUFBUSxDQUFDO0FBRXZCLE9BQU8sRUFTTCx1QkFBdUIsRUFDdkIsdUJBQXVCLEdBQ3hCLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUdMLEdBQUcsRUFDSCxNQUFNLEVBQ04sZ0JBQWdCLEVBQ2hCLGFBQWEsR0FDZCxNQUFNLGVBQWUsQ0FBQztBQUd2QixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdkQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFJeEUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUczQyxJQUFJLEtBQUssR0FBcUQsRUFBRSxDQUFDO0FBRWpFLE1BQU0sU0FBUyxHQUFHLENBQ2hCLElBQW9ELEVBQ3BELElBQW9DLEVBQ3BDLFNBQWdCLEVBQ2hCLElBQVcsRUFDWCxJQUFZLEVBQ1osRUFBRTtJQUNGLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUM7SUFFeEIsSUFBSSxHQUFHLEdBQUcsSUFBSSxFQUFFO1FBQ2QsSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUNkLE9BQU87U0FDUjtRQUNELE1BQU0sWUFBWSxHQUFHLHVCQUF1QixDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNoRSxNQUFNLFlBQVksR0FBRyx1QkFBdUIsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDaEUsSUFBSSxZQUFZLEVBQUU7WUFDaEIsS0FBSyxNQUFNLEVBQUUsSUFBSSxZQUFZLEVBQUU7Z0JBQzdCLE1BQU0sT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDbkIsTUFBTSxPQUFPLEdBQThCLENBQUMsU0FBUyxFQUFFLEdBQUcsT0FBTyxDQUFDLENBQUM7Z0JBQ25FLE1BQU0sT0FBTyxHQUFHLENBQUMsR0FBRyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ25DLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxFQUFFO29CQUN2QyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO29CQUNwQixTQUFTO2lCQUNWO2dCQUNELG1FQUFtRTtnQkFDbkUsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztnQkFDOUIsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRTtvQkFDYixTQUFTLENBQUMsT0FBTyxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO2lCQUNyRTthQUNGO1NBQ0Y7UUFDRCxJQUFJLFlBQVksRUFBRTtZQUNoQixLQUFLLE1BQU0sRUFBRSxJQUFJLFlBQVksRUFBRTtnQkFDN0IsTUFBTSxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNuQixNQUFNLE9BQU8sR0FBbUIsQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3JELE1BQU0sT0FBTyxHQUFHLENBQUMsR0FBRyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ25DLElBQUksT0FBTyxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxFQUFFO29CQUNwQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO29CQUNwQixTQUFTO2lCQUNWO2dCQUNELGtEQUFrRDtnQkFDbEQsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQztnQkFDM0IsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRTtvQkFDYixTQUFTLENBQUMsT0FBTyxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO2lCQUNsRTthQUNGO1NBQ0Y7S0FDRjtBQUNILENBQUMsQ0FBQztBQUVGLE1BQU0sT0FBTyxXQUFZLFNBQVEsVUFBc0I7SUFPckQsWUFDRSxrQkFBdUMsRUFDdkMsY0FBK0IsRUFDL0Isa0JBQXVDLEVBQ3ZDLGNBQStCLEVBQy9CLG9CQUEyQyxFQUMzQyxhQUE2QixFQUM3QixPQUFnQixFQUNoQix3QkFBNkMsRUFDN0Msc0JBQWdEO1FBRWhELEtBQUssQ0FDSCxhQUFhLEVBQ2IsT0FBTyxFQUNQLHdCQUF3QixFQUN4QixzQkFBc0IsQ0FDdkIsQ0FBQztRQUNGLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztRQUM3QyxJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztRQUNyQyxJQUFJLENBQUMsa0JBQWtCLEdBQUcsa0JBQWtCLENBQUM7UUFDN0MsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLENBQUM7UUFDckMsSUFBSSxDQUFDLG9CQUFvQixHQUFHLG9CQUFvQixDQUFDO0lBQ25ELENBQUM7SUFFUyxLQUFLLENBQUMsU0FBUyxDQUN2QixPQUFjLEVBQ2QsUUFBZSxFQUNmLFNBQW9CLEVBQ3BCLGFBQWdDO1FBRWhDLEtBQUssR0FBRyxFQUFFLENBQUM7UUFDWCxJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsV0FBVyxFQUFFO1lBQ3RDLE1BQU0sSUFBSSxLQUFLLENBQUMsdURBQXVELENBQUMsQ0FBQztTQUMxRTtRQUVELFVBQVU7UUFDVixvQkFBb0I7UUFDcEIsb0JBQW9CO1FBQ3BCLDhDQUE4QztRQUM5QywwQ0FBMEM7UUFDMUMsYUFBYTtRQUNiLGNBQWM7UUFDZCx1Q0FBdUM7UUFDdkMsNkRBQTZEO1FBQzdELHlDQUF5QztRQUN6Qyx5Q0FBeUM7UUFDekMsMEJBQTBCO1FBQzFCLGlEQUFpRDtRQUNqRCxpREFBaUQ7UUFDakQsbUJBQW1CO1FBQ25CLDJCQUEyQjtRQUMzQixNQUFNO1FBRU4sbURBQW1EO1FBQ25ELG1EQUFtRDtRQUNuRCxFQUFFO1FBQ0YsbURBQW1EO1FBRW5ELG1EQUFtRDtRQUVuRCxrSEFBa0g7UUFDbEgsdURBQXVEO1FBQ3ZELGNBQWM7UUFDZCxNQUFNO1FBQ04sdUJBQXVCO1FBQ3ZCLHlEQUF5RDtRQUN6RCxvQkFBb0I7UUFDcEIsaUZBQWlGO1FBQ2pGLDhCQUE4QjtRQUM5QixzQkFBc0I7UUFDdEIsUUFBUTtRQUNSLEVBQUU7UUFDRixrRkFBa0Y7UUFDbEYsdUZBQXVGO1FBQ3ZGLG1GQUFtRjtRQUNuRixrQkFBa0I7UUFDbEIsU0FBUztRQUNULFdBQVc7UUFDWCx3REFBd0Q7UUFDeEQsMERBQTBEO1FBQzFELFVBQVU7UUFDVixzQkFBc0I7UUFDdEIsUUFBUTtRQUNSLEVBQUU7UUFDRixlQUFlO1FBQ2Ysd0RBQXdEO1FBQ3hELHFEQUFxRDtRQUNyRCxTQUFTO1FBQ1QsTUFBTTtRQUNOLEtBQUs7UUFFTCx5REFBeUQ7UUFDekQsU0FBUyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLElBQUksRUFBRSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFFakUsZUFBZTtRQUNmLHdCQUF3QjtRQUN4QixXQUFXO1FBQ1gsY0FBYztRQUNkLDBHQUEwRztRQUMxRyxVQUFVO1FBQ1YsdUJBQXVCO1FBQ3ZCLE1BQU07UUFDTixLQUFLO1FBRUwsTUFBTSxXQUFXLEdBQWdELEVBQUUsQ0FBQztRQUNwRSxNQUFNLFdBQVcsR0FBcUMsRUFBRSxDQUFDO1FBQ3pELEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtZQUNyQixJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUU7Z0JBQ3BCLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFO29CQUNYLE1BQU0sRUFBRSxHQUFHLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO29CQUM5RCxXQUFXLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBaUMsQ0FBQztpQkFDckQ7cUJBQU07b0JBQ0wsTUFBTSxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztvQkFDbkQsV0FBVyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQXNCLENBQUM7aUJBQzFDO1lBQ0gsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztRQUNILE1BQU0sUUFBUSxHQUNaLE9BQU8sSUFBSSxRQUFRO1lBQ2pCLENBQUMsQ0FBQztnQkFDRSxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxDQUFDLE1BQU0sQ0FBQztnQkFDckMsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLFNBQVMsQ0FBQyxHQUFHLENBQUM7Z0JBQ2xDLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUMsTUFBTSxDQUFDO2dCQUNyQyxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQzthQUNwQztZQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDVCxNQUFNLFVBQVUsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxHQUFHLFFBQVEsQ0FBQyxDQUFDO1FBRWhFLE1BQU0sUUFBUSxHQUNaLE9BQU8sSUFBSSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ25ELE1BQU0sVUFBVSxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxFQUFFLEdBQUcsUUFBUSxDQUFDLENBQUM7UUFFaEUsTUFBTSxDQUFDLFlBQVksRUFBRSxZQUFZLENBQUMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDckQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDO1lBQ3hDLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQztTQUN6QyxDQUFDLENBQUM7UUFFSCxNQUFNLEtBQUssR0FBRyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDekMsTUFBTSxLQUFLLEdBQUcsWUFBWSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBRXpDLE1BQU0sYUFBYSxHQUFvQixDQUFDLEdBQUcsS0FBSyxFQUFFLEdBQUcsS0FBSyxDQUFDLENBQUM7UUFDNUQsTUFBTSxFQUFFLGVBQWUsRUFBRSxHQUFHLGFBQWEsQ0FBQztRQUUxQyxNQUFNLE1BQU0sR0FBaUIscUJBQXFCLENBQ2hELE9BQU8sRUFDUCxRQUFRLEVBQ1IsYUFBYSxFQUNiLGVBQWUsQ0FDaEIsQ0FBQztRQUVGLE9BQU87WUFDTCxNQUFNO1NBQ1AsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsU0FBUyxDQUNwQixNQUFvQixFQUNwQixPQUF5QixFQUN6QixRQUFrQixFQUNsQixVQUFpQixFQUNqQixTQUFvQixFQUNwQixhQUFnQyxFQUNoQyxjQUFrRCxFQUNsRCxRQUE4QztRQUU5QyxHQUFHLENBQUMsSUFBSSxDQUFDLDhCQUE4QixDQUFDLENBQUM7UUFDekMsSUFBSSxRQUFRLEtBQUssU0FBUyxFQUFFO1lBQzFCLE1BQU0sSUFBSSxLQUFLLENBQ2IsZ0VBQWdFLENBQ2pFLENBQUM7U0FDSDtRQUNELElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUU7WUFDdEIsT0FBTyxFQUFFLHFCQUFxQixFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsQ0FBQztTQUN0RDtRQUVELDZFQUE2RTtRQUM3RSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUNqRSxJQUFJLENBQUMsb0JBQW9CLENBQzFCLENBQUM7UUFFRixNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDaEMsR0FBRyxDQUFDLElBQUksQ0FDTixnQ0FBZ0MsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLE9BQU8sQ0FBQyxNQUFNLHFCQUFxQixDQUNqRyxDQUFDO1FBRUYsTUFBTSxFQUFFLGdCQUFnQixFQUFFLEdBQUcsTUFBTSxPQUFPLENBQWEsT0FBTyxFQUFFLE1BQU0sRUFBRTtZQUN0RSxXQUFXLEVBQUUsYUFBYSxDQUFDLFdBQVc7U0FDdkMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxDQUFDLFNBQVMsQ0FDZCxpQkFBaUIsRUFDakIsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFlBQVksRUFDekIsZ0JBQWdCLENBQUMsWUFBWSxDQUM5QixDQUFDO1FBRUYsTUFBTSxDQUFDLFNBQVMsQ0FDZCxvQkFBb0IsRUFDcEIsQ0FBQyxDQUFDLGdCQUFnQixDQUFDO2FBQ2hCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQzthQUNsQyxHQUFHLEVBQUUsRUFDUixnQkFBZ0IsQ0FBQyxLQUFLLENBQ3ZCLENBQUM7UUFFRixNQUFNLHFCQUFxQixHQUFHLEVBQUUsQ0FBQztRQUVqQyxLQUFLLE1BQU0sY0FBYyxJQUFJLGdCQUFnQixFQUFFO1lBQzdDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLEdBQUcsY0FBYyxDQUFDO1lBRXZDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUN0QyxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFFLENBQUM7Z0JBQzdCLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUUsQ0FBQztnQkFDL0IsTUFBTSxFQUNKLEtBQUssRUFDTCxNQUFNLEVBQ04scUJBQXFCLEVBQ3JCLDJCQUEyQixFQUMzQixXQUFXLEdBQ1osR0FBRyxXQUFXLENBQUM7Z0JBRWhCLElBQ0UsQ0FBQyxLQUFLO29CQUNOLENBQUMscUJBQXFCO29CQUN0QixDQUFDLDJCQUEyQjtvQkFDNUIsQ0FBQyxXQUFXLEVBQ1o7b0JBQ0EsR0FBRyxDQUFDLEtBQUssQ0FDUDt3QkFDRSxLQUFLLEVBQUUsYUFBYSxDQUFDLEtBQUssQ0FBQzt3QkFDM0IsV0FBVztxQkFDWixFQUNELHdDQUF3QyxDQUN6QyxDQUFDO29CQUNGLFNBQVM7aUJBQ1Y7Z0JBRUQsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLHdCQUF3QixDQUFDO29CQUN2RCxLQUFLO29CQUNMLFFBQVEsRUFBRSxLQUFLO29CQUNmLE1BQU07b0JBQ04sT0FBTztvQkFDUCxxQkFBcUI7b0JBQ3JCLDJCQUEyQjtvQkFDM0IsaUJBQWlCLEVBQUUsV0FBVztvQkFDOUIsa0JBQWtCLEVBQUUsUUFBUTtvQkFDNUIsVUFBVTtvQkFDVixTQUFTO29CQUNULGNBQWMsRUFBRSxJQUFJLENBQUMsY0FBYztvQkFDbkMsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjO2lCQUNwQyxDQUFDLENBQUM7Z0JBRUgscUJBQXFCLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7YUFDakQ7U0FDRjtRQUVELE9BQU87WUFDTCxxQkFBcUI7WUFDckIsY0FBYztTQUNmLENBQUM7SUFDSixDQUFDO0NBQ0YifQ==