UNPKG

@maxosllc/smart-order-router

Version:
455 lines (454 loc) 50.6 kB
import http from 'http'; import https from 'https'; import { MaxUint256 } from '@ethersproject/constants'; import { permit2Address } from '@uniswap/permit2-sdk'; import { ChainId } from '../../src/util/chains'; import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk'; import axios from 'axios'; import { BigNumber } from 'ethers/lib/ethers'; import { metric, MetricLoggerUnit, SwapType, } from '../routers'; import { Erc20__factory } from '../types/other/factories/Erc20__factory'; import { Permit2__factory } from '../types/other/factories/Permit2__factory'; import { BEACON_CHAIN_DEPOSIT_ADDRESS, log, MAX_UINT160, SWAP_ROUTER_02_ADDRESSES, } from '../util'; import { APPROVE_TOKEN_FOR_TRANSFER } from '../util/callData'; import { calculateGasUsed, initSwapRouteFromExisting, logGasEstimationVsSimulationMetrics, } from '../util/gas-factory-helpers'; import { breakDownTenderlySimulationError } from '../util/tenderlySimulationErrorBreakDown'; import { SimulationStatus, Simulator, } from './simulation-provider'; var TenderlySimulationType; (function (TenderlySimulationType) { TenderlySimulationType["QUICK"] = "quick"; TenderlySimulationType["FULL"] = "full"; TenderlySimulationType["ABI"] = "abi"; })(TenderlySimulationType || (TenderlySimulationType = {})); const TENDERLY_BATCH_SIMULATE_API = (tenderlyBaseUrl, tenderlyUser, tenderlyProject) => `${tenderlyBaseUrl}/api/v1/account/${tenderlyUser}/project/${tenderlyProject}/simulate-batch`; const TENDERLY_NODE_API = (chainId, tenderlyNodeApiKey) => { switch (chainId) { case ChainId.MAINNET: return `https://mainnet.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.BASE: return `https://base.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.ARBITRUM_ONE: return `https://arbitrum.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.OPTIMISM: return `https://optimism.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.POLYGON: return `https://polygon.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.AVALANCHE: return `https://avalanche.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.BLAST: return `https://blast.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.WORLDCHAIN: return `https://worldchain-mainnet.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.UNICHAIN: return `https://unichain.gateway.tenderly.co/${tenderlyNodeApiKey}`; case ChainId.SONEIUM: return `https://soneium.gateway.tenderly.co/${tenderlyNodeApiKey}`; default: throw new Error(`ChainId ${chainId} does not correspond to a tenderly node endpoint`); } }; export const TENDERLY_NOT_SUPPORTED_CHAINS = [ ChainId.CELO, ChainId.CELO_ALFAJORES, ChainId.ZKSYNC, // tenderly node RPC supports BNB and ZORA upon request, we will make them available ChainId.BNB, ChainId.ZORA, ChainId.MONAD_TESTNET, ChainId.BLOCKDAG_TESTNET, ]; // We multiply tenderly gas limit by this to overestimate gas limit const DEFAULT_ESTIMATE_MULTIPLIER = 1.3; export class FallbackTenderlySimulator extends Simulator { constructor(chainId, provider, portionProvider, tenderlySimulator, ethEstimateGasSimulator) { super(provider, portionProvider, chainId); this.tenderlySimulator = tenderlySimulator; this.ethEstimateGasSimulator = ethEstimateGasSimulator; } async simulateTransaction(fromAddress, swapOptions, swapRoute, providerConfig) { // Make call to eth estimate gas if possible // For erc20s, we must check if the token allowance is sufficient const inputAmount = swapRoute.trade.inputAmount; if (inputAmount.currency.isNative || (await this.checkTokenApproved(fromAddress, inputAmount, swapOptions, this.provider))) { log.info('Simulating with eth_estimateGas since token is native or approved.'); try { const swapRouteWithGasEstimate = await this.ethEstimateGasSimulator.ethEstimateGas(fromAddress, swapOptions, swapRoute, providerConfig); return swapRouteWithGasEstimate; } catch (err) { log.info({ err: err }, 'Error simulating using eth_estimateGas'); // If it fails, we should still try to simulate using Tenderly // return { ...swapRoute, simulationStatus: SimulationStatus.Failed }; } } try { return await this.tenderlySimulator.simulateTransaction(fromAddress, swapOptions, swapRoute, providerConfig); } catch (err) { log.error({ err: err }, 'Failed to simulate via Tenderly'); if (err instanceof Error && err.message.includes('timeout')) { metric.putMetric('TenderlySimulationTimeouts', 1, MetricLoggerUnit.Count); } return { ...swapRoute, simulationStatus: SimulationStatus.SystemDown }; } } } export class TenderlySimulator extends Simulator { constructor(chainId, tenderlyBaseUrl, tenderlyUser, tenderlyProject, tenderlyAccessKey, tenderlyNodeApiKey, v2PoolProvider, v3PoolProvider, v4PoolProvider, provider, portionProvider, overrideEstimateMultiplier, tenderlyRequestTimeout, tenderlyNodeApiMigrationPercent, tenderlyNodeApiEnabledChains) { super(provider, portionProvider, chainId); this.tenderlyNodeApiEnabledChains = []; this.tenderlyServiceInstance = axios.create({ // keep connections alive, // maxSockets default is Infinity, so Infinity is read as 50 sockets httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), }); this.tenderlyBaseUrl = tenderlyBaseUrl; this.tenderlyUser = tenderlyUser; this.tenderlyProject = tenderlyProject; this.tenderlyAccessKey = tenderlyAccessKey; this.tenderlyNodeApiKey = tenderlyNodeApiKey; this.v2PoolProvider = v2PoolProvider; this.v3PoolProvider = v3PoolProvider; this.v4PoolProvider = v4PoolProvider; this.overrideEstimateMultiplier = overrideEstimateMultiplier !== null && overrideEstimateMultiplier !== void 0 ? overrideEstimateMultiplier : {}; this.tenderlyRequestTimeout = tenderlyRequestTimeout; this.tenderlyNodeApiMigrationPercent = tenderlyNodeApiMigrationPercent; this.tenderlyNodeApiEnabledChains = tenderlyNodeApiEnabledChains; } async simulateTransaction(fromAddress, swapOptions, swapRoute, providerConfig) { var _a, _b, _c; const currencyIn = swapRoute.trade.inputAmount.currency; const tokenIn = currencyIn.wrapped; const currencyOut = swapRoute.trade.outputAmount.currency; const tokenOut = currencyOut.wrapped; const chainId = this.chainId; if (TENDERLY_NOT_SUPPORTED_CHAINS.includes(chainId)) { const msg = `${TENDERLY_NOT_SUPPORTED_CHAINS.toString()} not supported by Tenderly!`; log.info(msg); return { ...swapRoute, simulationStatus: SimulationStatus.NotSupported }; } if (!swapRoute.methodParameters) { const msg = 'No calldata provided to simulate transaction'; log.info(msg); throw new Error(msg); } const { calldata } = swapRoute.methodParameters; log.info({ calldata: swapRoute.methodParameters.calldata, fromAddress: fromAddress, chainId: chainId, tokenInAddress: tokenIn.address, router: swapOptions.type, }, 'Simulating transaction on Tenderly'); const blockNumber = await (providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber); let estimatedGasUsed; const estimateMultiplier = (_a = this.overrideEstimateMultiplier[chainId]) !== null && _a !== void 0 ? _a : DEFAULT_ESTIMATE_MULTIPLIER; if (swapOptions.type == SwapType.UNIVERSAL_ROUTER) { // simulating from beacon chain deposit address that should always hold **enough balance** if (currencyIn.isNative && this.chainId == ChainId.MAINNET) { fromAddress = BEACON_CHAIN_DEPOSIT_ADDRESS; } // Do initial onboarding approval of Permit2. const erc20Interface = Erc20__factory.createInterface(); const approvePermit2Calldata = erc20Interface.encodeFunctionData('approve', [permit2Address(this.chainId), MaxUint256]); // We are unsure if the users calldata contains a permit or not. We just // max approve the Universal Router from Permit2 instead, which will cover both cases. const permit2Interface = Permit2__factory.createInterface(); const approveUniversalRouterCallData = permit2Interface.encodeFunctionData('approve', [ tokenIn.address, UNIVERSAL_ROUTER_ADDRESS(swapOptions.version, this.chainId), MAX_UINT160, Math.floor(new Date().getTime() / 1000) + 10000000, ]); const approvePermit2 = { network_id: chainId, estimate_gas: true, input: approvePermit2Calldata, to: tokenIn.address, value: '0', from: fromAddress, block_number: blockNumber, simulation_type: TenderlySimulationType.QUICK, save_if_fails: providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.saveTenderlySimulationIfFailed, }; const approveUniversalRouter = { network_id: chainId, estimate_gas: true, input: approveUniversalRouterCallData, to: permit2Address(this.chainId), value: '0', from: fromAddress, block_number: blockNumber, simulation_type: TenderlySimulationType.QUICK, save_if_fails: providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.saveTenderlySimulationIfFailed, }; const swap = { network_id: chainId, input: calldata, estimate_gas: true, to: UNIVERSAL_ROUTER_ADDRESS(swapOptions.version, this.chainId), value: currencyIn.isNative ? swapRoute.methodParameters.value : '0', from: fromAddress, block_number: blockNumber, simulation_type: TenderlySimulationType.QUICK, save_if_fails: providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.saveTenderlySimulationIfFailed, }; const body = { simulations: [approvePermit2, approveUniversalRouter, swap], estimate_gas: true, }; const opts = { headers: { 'X-Access-Key': this.tenderlyAccessKey, }, timeout: this.tenderlyRequestTimeout, }; const url = TENDERLY_BATCH_SIMULATE_API(this.tenderlyBaseUrl, this.tenderlyUser, this.tenderlyProject); metric.putMetric('TenderlySimulationUniversalRouterRequests', 1, MetricLoggerUnit.Count); const before = Date.now(); if (Math.random() * 100 < ((_b = this.tenderlyNodeApiMigrationPercent) !== null && _b !== void 0 ? _b : 0) && ((_c = this.tenderlyNodeApiEnabledChains) !== null && _c !== void 0 ? _c : []).find((chainId) => chainId === this.chainId)) { const { data: resp, status: httpStatus } = await this.requestNodeSimulation(approvePermit2, approveUniversalRouter, swap); // We will maintain the original metrics TenderlySimulationUniversalRouterLatencies and TenderlySimulationUniversalRouterResponseStatus // so that they don't provide the existing tenderly dashboard as well as simulation alerts // In the meanwhile, we also add tenderly node metrics to distinguish from the tenderly api metrics // Once we migrate to node endpoint 100%, original metrics TenderlySimulationUniversalRouterLatencies and TenderlySimulationUniversalRouterResponseStatus // will work as is metric.putMetric('TenderlySimulationUniversalRouterLatencies', Date.now() - before, MetricLoggerUnit.Milliseconds); metric.putMetric('TenderlyNodeSimulationUniversalRouterLatencies', Date.now() - before, MetricLoggerUnit.Milliseconds); metric.putMetric(`TenderlySimulationUniversalRouterResponseStatus${httpStatus}`, 1, MetricLoggerUnit.Count); metric.putMetric(`TenderlyNodeSimulationUniversalRouterResponseStatus${httpStatus}`, 1, MetricLoggerUnit.Count); // technically, we can also early return SimulationStatus.SystemDown when http status is not 200. // in reality, when tenderly is down for whatever reason, i see it always throw during axios http request // so that it hits the catch block in https://github.com/Uniswap/smart-order-router/blob/8bfec299001d3204483f761f57a38be04512a948/src/providers/tenderly-simulation-provider.ts#L226 // which is where we want to actually return SimulationStatus.SystemDown // in other words, I've never see a TenderlySimulationUniversalRouterResponseStatus metric with a non-200 status // if there's downtime, it won't log metric at https://github.com/Uniswap/smart-order-router/blob/8bfec299001d3204483f761f57a38be04512a948/src/providers/tenderly-simulation-provider.ts#L434 // Validate tenderly response body if (!resp || !resp.result || resp.result.length < 3 || resp.result[2].error) { log.error({ resp }, `Failed to invoke Tenderly Node Endpoint for gas estimation bundle ${JSON.stringify(body, null, 2)}.`); if (resp && resp.result && resp.result.length >= 3 && resp.result[2].error && resp.result[2].error.data) { return { ...swapRoute, simulationStatus: breakDownTenderlySimulationError(tokenIn, tokenOut, resp.result[2].error.data), }; } return { ...swapRoute, simulationStatus: SimulationStatus.Failed }; } // Parse the gas used in the simulation response object, and then pad it so that we overestimate. estimatedGasUsed = BigNumber.from((Number(resp.result[2].gas) * estimateMultiplier).toFixed(0)); log.info({ body, approvePermit2GasUsed: resp.result[0].gasUsed, approveUniversalRouterGasUsed: resp.result[1].gasUsed, swapGasUsed: resp.result[2].gasUsed, approvePermit2Gas: resp.result[0].gas, approveUniversalRouterGas: resp.result[1].gas, swapGas: resp.result[2].gas, swapWithMultiplier: estimatedGasUsed.toString(), }, 'Successfully Simulated Approvals + Swap via Tenderly node endpoint for Universal Router. Gas used.'); } else { const { data: resp, status: httpStatus } = await this.tenderlyServiceInstance .post(url, body, opts) .finally(() => { metric.putMetric('TenderlySimulationLatencies', Date.now() - before, MetricLoggerUnit.Milliseconds); }); metric.putMetric('TenderlySimulationUniversalRouterLatencies', Date.now() - before, MetricLoggerUnit.Milliseconds); metric.putMetric('TenderlyApiSimulationUniversalRouterLatencies', Date.now() - before, MetricLoggerUnit.Milliseconds); metric.putMetric(`TenderlySimulationUniversalRouterResponseStatus${httpStatus}`, 1, MetricLoggerUnit.Count); metric.putMetric(`TenderlyApiSimulationUniversalRouterResponseStatus${httpStatus}`, 1, MetricLoggerUnit.Count); // Validate tenderly response body if (!resp || resp.simulation_results.length < 3 || !resp.simulation_results[2].transaction || resp.simulation_results[2].transaction.error_message) { this.logTenderlyErrorResponse(resp); return { ...swapRoute, simulationStatus: SimulationStatus.Failed }; } // Parse the gas used in the simulation response object, and then pad it so that we overestimate. estimatedGasUsed = BigNumber.from((resp.simulation_results[2].transaction.gas * estimateMultiplier).toFixed(0)); log.info({ body, approvePermit2GasUsed: resp.simulation_results[0].transaction.gas_used, approveUniversalRouterGasUsed: resp.simulation_results[1].transaction.gas_used, swapGasUsed: resp.simulation_results[2].transaction.gas_used, approvePermit2Gas: resp.simulation_results[0].transaction.gas, approveUniversalRouterGas: resp.simulation_results[1].transaction.gas, swapGas: resp.simulation_results[2].transaction.gas, swapWithMultiplier: estimatedGasUsed.toString(), }, 'Successfully Simulated Approvals + Swap via Tenderly Api endpoint for Universal Router. Gas used.'); log.info({ body, swapSimulation: resp.simulation_results[2].simulation, swapTransaction: resp.simulation_results[2].transaction, }, 'Successful Tenderly Api endpoint Swap Simulation for Universal Router'); } } else if (swapOptions.type == SwapType.SWAP_ROUTER_02) { const approve = { network_id: chainId, input: APPROVE_TOKEN_FOR_TRANSFER, estimate_gas: true, to: tokenIn.address, value: '0', from: fromAddress, simulation_type: TenderlySimulationType.QUICK, }; const swap = { network_id: chainId, input: calldata, to: SWAP_ROUTER_02_ADDRESSES(chainId), estimate_gas: true, value: currencyIn.isNative ? swapRoute.methodParameters.value : '0', from: fromAddress, block_number: blockNumber, simulation_type: TenderlySimulationType.QUICK, }; const body = { simulations: [approve, swap] }; const opts = { headers: { 'X-Access-Key': this.tenderlyAccessKey, }, timeout: this.tenderlyRequestTimeout, }; const url = TENDERLY_BATCH_SIMULATE_API(this.tenderlyBaseUrl, this.tenderlyUser, this.tenderlyProject); metric.putMetric('TenderlySimulationSwapRouter02Requests', 1, MetricLoggerUnit.Count); const before = Date.now(); const { data: resp, status: httpStatus } = await this.tenderlyServiceInstance.post(url, body, opts); metric.putMetric(`TenderlySimulationSwapRouter02ResponseStatus${httpStatus}`, 1, MetricLoggerUnit.Count); const latencies = Date.now() - before; log.info(`Tenderly simulation swap router02 request body: ${body}, having latencies ${latencies} in milliseconds.`); metric.putMetric('TenderlySimulationSwapRouter02Latencies', latencies, MetricLoggerUnit.Milliseconds); // Validate tenderly response body if (!resp || resp.simulation_results.length < 2 || !resp.simulation_results[1].transaction || resp.simulation_results[1].transaction.error_message) { const msg = `Failed to Simulate Via Tenderly!: ${resp.simulation_results[1].transaction.error_message}`; log.info({ err: resp.simulation_results[1].transaction.error_message }, msg); return { ...swapRoute, simulationStatus: SimulationStatus.Failed }; } // Parse the gas used in the simulation response object, and then pad it so that we overestimate. estimatedGasUsed = BigNumber.from((resp.simulation_results[1].transaction.gas * estimateMultiplier).toFixed(0)); log.info({ body, approveGasUsed: resp.simulation_results[0].transaction.gas_used, swapGasUsed: resp.simulation_results[1].transaction.gas_used, approveGas: resp.simulation_results[0].transaction.gas, swapGas: resp.simulation_results[1].transaction.gas, swapWithMultiplier: estimatedGasUsed.toString(), }, 'Successfully Simulated Approval + Swap via Tenderly for SwapRouter02. Gas used.'); log.info({ body, swapTransaction: resp.simulation_results[1].transaction, swapSimulation: resp.simulation_results[1].simulation, }, 'Successful Tenderly Swap Simulation for SwapRouter02'); } else { throw new Error(`Unsupported swap type: ${swapOptions}`); } const { estimatedGasUsedUSD, estimatedGasUsedQuoteToken, estimatedGasUsedGasToken, quoteGasAdjusted, } = await calculateGasUsed(chainId, swapRoute, estimatedGasUsed, this.v2PoolProvider, this.v3PoolProvider, this.provider, providerConfig); logGasEstimationVsSimulationMetrics(swapRoute, estimatedGasUsed, chainId); return { ...initSwapRouteFromExisting(swapRoute, this.v2PoolProvider, this.v3PoolProvider, this.v4PoolProvider, this.portionProvider, quoteGasAdjusted, estimatedGasUsed, estimatedGasUsedQuoteToken, estimatedGasUsedUSD, swapOptions, estimatedGasUsedGasToken, providerConfig), simulationStatus: SimulationStatus.Succeeded, }; } logTenderlyErrorResponse(resp) { log.info({ resp, }, 'Failed to Simulate on Tenderly'); log.info({ err: resp.simulation_results.length >= 1 ? resp.simulation_results[0].transaction : {}, }, 'Failed to Simulate on Tenderly #1 Transaction'); log.info({ err: resp.simulation_results.length >= 1 ? resp.simulation_results[0].simulation : {}, }, 'Failed to Simulate on Tenderly #1 Simulation'); log.info({ err: resp.simulation_results.length >= 2 ? resp.simulation_results[1].transaction : {}, }, 'Failed to Simulate on Tenderly #2 Transaction'); log.info({ err: resp.simulation_results.length >= 2 ? resp.simulation_results[1].simulation : {}, }, 'Failed to Simulate on Tenderly #2 Simulation'); log.info({ err: resp.simulation_results.length >= 3 ? resp.simulation_results[2].transaction : {}, }, 'Failed to Simulate on Tenderly #3 Transaction'); log.info({ err: resp.simulation_results.length >= 3 ? resp.simulation_results[2].simulation : {}, }, 'Failed to Simulate on Tenderly #3 Simulation'); } async requestNodeSimulation(approvePermit2, approveUniversalRouter, swap) { const nodeEndpoint = TENDERLY_NODE_API(this.chainId, this.tenderlyNodeApiKey); // TODO: ROUTE-362 - Revisit tenderly node simulation hardcode latest block number // https://linear.app/uniswap/issue/ROUTE-362/revisit-tenderly-node-simulation-hardcode-latest-block-number const blockNumber = // swap.block_number // ? BigNumber.from(swap.block_number).toHexString().replace('0x0', '0x') 'latest'; const body = { id: 1, jsonrpc: '2.0', method: 'tenderly_estimateGasBundle', params: [ [ { from: approvePermit2.from, to: approvePermit2.to, data: approvePermit2.input, }, { from: approveUniversalRouter.from, to: approveUniversalRouter.to, data: approveUniversalRouter.input, }, { from: swap.from, to: swap.to, data: swap.input }, ], blockNumber, ], }; const opts = { timeout: this.tenderlyRequestTimeout, }; const before = Date.now(); try { // For now, we don't timeout tenderly node endpoint, but we should before we live switch to node endpoint const { data: resp, status: httpStatus } = await this.tenderlyServiceInstance.post(nodeEndpoint, body, opts); const latencies = Date.now() - before; metric.putMetric('TenderlyNodeGasEstimateBundleLatencies', latencies, MetricLoggerUnit.Milliseconds); metric.putMetric('TenderlyNodeGasEstimateBundleSuccess', 1, MetricLoggerUnit.Count); if (httpStatus !== 200) { log.error(`Failed to invoke Tenderly Node Endpoint for gas estimation bundle ${JSON.stringify(body, null, 2)}. HTTP Status: ${httpStatus}`, { resp }); return { data: resp, status: httpStatus }; } return { data: resp, status: httpStatus }; } catch (err) { log.error({ err }, `Failed to invoke Tenderly Node Endpoint for gas estimation bundle ${JSON.stringify(body, null, 2)}. Error: ${err}`); metric.putMetric('TenderlyNodeGasEstimateBundleFailure', 1, MetricLoggerUnit.Count); // we will have to re-throw the error, so that simulation-provider can catch the error, and return simulation status = failed throw err; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wcm92aWRlcnMvdGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFDeEIsT0FBTyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBRTFCLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUV0RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ2hELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3pFLE9BQU8sS0FBNkIsTUFBTSxPQUFPLENBQUM7QUFDbEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRTlDLE9BQU8sRUFFTCxNQUFNLEVBQ04sZ0JBQWdCLEVBR2hCLFFBQVEsR0FDVCxNQUFNLFlBQVksQ0FBQztBQUNwQixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDekUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDN0UsT0FBTyxFQUNMLDRCQUE0QixFQUM1QixHQUFHLEVBQ0gsV0FBVyxFQUNYLHdCQUF3QixHQUN6QixNQUFNLFNBQVMsQ0FBQztBQUNqQixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5RCxPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLHlCQUF5QixFQUN6QixtQ0FBbUMsR0FDcEMsTUFBTSw2QkFBNkIsQ0FBQztBQUVyQyxPQUFPLEVBQUUsZ0NBQWdDLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUc1RixPQUFPLEVBRUwsZ0JBQWdCLEVBQ2hCLFNBQVMsR0FD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O1lBQ0UsR0FBRyxFQUNELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVO2dCQUN2QyxDQUFDLENBQUMsRUFBRTtTQUNULEVBQ0QsOENBQThDLENBQy9DLENBQUM7UUFDRixHQUFHLENBQUMsSUFBSSxDQUNOO1lBQ0UsR0FBRyxFQUNELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXO2dCQUN4QyxDQUFDLENBQUMsRUFBRTtTQUNULEVBQ0QsK0NBQStDLENBQ2hELENBQUM7UUFDRixHQUFHLENBQUMsSUFBSSxDQUNOO1lBQ0UsR0FBRyxFQUNELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVO2dCQUN2QyxDQUFDLENBQUMsRUFBRTtTQUNULEVBQ0QsOENBQThDLENBQy9DLENBQUM7UUFDRixHQUFHLENBQUMsSUFBSSxDQUNOO1lBQ0UsR0FBRyxFQUNELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXO2dCQUN4QyxDQUFDLENBQUMsRUFBRTtTQUNULEVBQ0QsK0NBQStDLENBQ2hELENBQUM7UUFDRixHQUFHLENBQUMsSUFBSSxDQUNOO1lBQ0UsR0FBRyxFQUNELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVO2dCQUN2QyxDQUFDLENBQUMsRUFBRTtTQUNULEVBQ0QsOENBQThDLENBQy9DLENBQUM7SUFDSixDQUFDO0lBRU8sS0FBSyxDQUFDLHFCQUFxQixDQUNqQyxjQUF5QyxFQUN6QyxzQkFBaUQsRUFDakQsSUFBK0I7UUFFL0IsTUFBTSxZQUFZLEdBQUcsaUJBQWlCLENBQ3BDLElBQUksQ0FBQyxPQUFPLEVBQ1osSUFBSSxDQUFDLGtCQUFrQixDQUN4QixDQUFDO1FBQ0Ysa0ZBQWtGO1FBQ2xGLDJHQUEyRztRQUMzRyxNQUFNLFdBQVcsR0FBRyxvQkFBb0I7O1FBQ3RDLHlFQUF5RTtRQUN6RSxRQUFRLENBQUM7UUFDWCxNQUFNLElBQUksR0FBc0M7WUFDOUMsRUFBRSxFQUFFLENBQUM7WUFDTCxPQUFPLEVBQUUsS0FBSztZQUNkLE1BQU0sRUFBRSw0QkFBNEI7WUFDcEMsTUFBTSxFQUFFO2dCQUNOO29CQUNFO3dCQUNFLElBQUksRUFBRSxjQUFjLENBQUMsSUFBSTt3QkFDekIsRUFBRSxFQUFFLGNBQWMsQ0FBQyxFQUFFO3dCQUNyQixJQUFJLEVBQUUsY0FBYyxDQUFDLEtBQUs7cUJBQzNCO29CQUNEO3dCQUNFLElBQUksRUFBRSxzQkFBc0IsQ0FBQyxJQUFJO3dCQUNqQyxFQUFFLEVBQUUsc0JBQXNCLENBQUMsRUFBRTt3QkFDN0IsSUFBSSxFQUFFLHNCQUFzQixDQUFDLEtBQUs7cUJBQ25DO29CQUNELEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUU7aUJBQ25EO2dCQUNELFdBQVc7YUFDWjtTQUNGLENBQUM7UUFFRixNQUFNLElBQUksR0FBdUI7WUFDL0IsT0FBTyxFQUFFLElBQUksQ0FBQyxzQkFBc0I7U0FDckMsQ0FBQztRQUVGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUUxQixJQUFJO1lBQ0YseUdBQXlHO1lBQ3pHLE1BQU0sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsR0FDdEMsTUFBTSxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUNyQyxZQUFZLEVBQ1osSUFBSSxFQUNKLElBQUksQ0FDTCxDQUFDO1lBRUosTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLE1BQU0sQ0FBQztZQUN0QyxNQUFNLENBQUMsU0FBUyxDQUNkLHdDQUF3QyxFQUN4QyxTQUFTLEVBQ1QsZ0JBQWdCLENBQUMsWUFBWSxDQUM5QixDQUFDO1lBQ0YsTUFBTSxDQUFDLFNBQVMsQ0FDZCxzQ0FBc0MsRUFDdEMsQ0FBQyxFQUNELGdCQUFnQixDQUFDLEtBQUssQ0FDdkIsQ0FBQztZQUVGLElBQUksVUFBVSxLQUFLLEdBQUcsRUFBRTtnQkFDdEIsR0FBRyxDQUFDLEtBQUssQ0FDUCxxRUFBcUUsSUFBSSxDQUFDLFNBQVMsQ0FDakYsSUFBSSxFQUNKLElBQUksRUFDSixDQUFDLENBQ0Ysa0JBQ