UNPKG

@maxosllc/smart-order-router

Version:
457 lines (456 loc) 51.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.TenderlySimulator = exports.FallbackTenderlySimulator = exports.TENDERLY_NOT_SUPPORTED_CHAINS = void 0; const http_1 = __importDefault(require("http")); const https_1 = __importDefault(require("https")); const constants_1 = require("@ethersproject/constants"); const permit2_sdk_1 = require("@uniswap/permit2-sdk"); const chains_1 = require("../../src/util/chains"); const universal_router_sdk_1 = require("@uniswap/universal-router-sdk"); const axios_1 = __importDefault(require("axios")); const ethers_1 = require("ethers/lib/ethers"); const routers_1 = require("../routers"); const Erc20__factory_1 = require("../types/other/factories/Erc20__factory"); const Permit2__factory_1 = require("../types/other/factories/Permit2__factory"); const util_1 = require("../util"); const callData_1 = require("../util/callData"); const gas_factory_helpers_1 = require("../util/gas-factory-helpers"); const tenderlySimulationErrorBreakDown_1 = require("../util/tenderlySimulationErrorBreakDown"); const simulation_provider_1 = require("./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 chains_1.ChainId.MAINNET: return `https://mainnet.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.BASE: return `https://base.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.ARBITRUM_ONE: return `https://arbitrum.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.OPTIMISM: return `https://optimism.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.POLYGON: return `https://polygon.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.AVALANCHE: return `https://avalanche.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.BLAST: return `https://blast.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.WORLDCHAIN: return `https://worldchain-mainnet.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.UNICHAIN: return `https://unichain.gateway.tenderly.co/${tenderlyNodeApiKey}`; case chains_1.ChainId.SONEIUM: return `https://soneium.gateway.tenderly.co/${tenderlyNodeApiKey}`; default: throw new Error(`ChainId ${chainId} does not correspond to a tenderly node endpoint`); } }; exports.TENDERLY_NOT_SUPPORTED_CHAINS = [ chains_1.ChainId.CELO, chains_1.ChainId.CELO_ALFAJORES, chains_1.ChainId.ZKSYNC, // tenderly node RPC supports BNB and ZORA upon request, we will make them available chains_1.ChainId.BNB, chains_1.ChainId.ZORA, chains_1.ChainId.MONAD_TESTNET, chains_1.ChainId.BLOCKDAG_TESTNET, ]; // We multiply tenderly gas limit by this to overestimate gas limit const DEFAULT_ESTIMATE_MULTIPLIER = 1.3; class FallbackTenderlySimulator extends simulation_provider_1.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))) { util_1.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) { util_1.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) { util_1.log.error({ err: err }, 'Failed to simulate via Tenderly'); if (err instanceof Error && err.message.includes('timeout')) { routers_1.metric.putMetric('TenderlySimulationTimeouts', 1, routers_1.MetricLoggerUnit.Count); } return Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.SystemDown }); } } } exports.FallbackTenderlySimulator = FallbackTenderlySimulator; class TenderlySimulator extends simulation_provider_1.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_1.default.create({ // keep connections alive, // maxSockets default is Infinity, so Infinity is read as 50 sockets httpAgent: new http_1.default.Agent({ keepAlive: true }), httpsAgent: new https_1.default.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 (exports.TENDERLY_NOT_SUPPORTED_CHAINS.includes(chainId)) { const msg = `${exports.TENDERLY_NOT_SUPPORTED_CHAINS.toString()} not supported by Tenderly!`; util_1.log.info(msg); return Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.NotSupported }); } if (!swapRoute.methodParameters) { const msg = 'No calldata provided to simulate transaction'; util_1.log.info(msg); throw new Error(msg); } const { calldata } = swapRoute.methodParameters; util_1.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 == routers_1.SwapType.UNIVERSAL_ROUTER) { // simulating from beacon chain deposit address that should always hold **enough balance** if (currencyIn.isNative && this.chainId == chains_1.ChainId.MAINNET) { fromAddress = util_1.BEACON_CHAIN_DEPOSIT_ADDRESS; } // Do initial onboarding approval of Permit2. const erc20Interface = Erc20__factory_1.Erc20__factory.createInterface(); const approvePermit2Calldata = erc20Interface.encodeFunctionData('approve', [(0, permit2_sdk_1.permit2Address)(this.chainId), constants_1.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_1.Permit2__factory.createInterface(); const approveUniversalRouterCallData = permit2Interface.encodeFunctionData('approve', [ tokenIn.address, (0, universal_router_sdk_1.UNIVERSAL_ROUTER_ADDRESS)(swapOptions.version, this.chainId), util_1.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: (0, permit2_sdk_1.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: (0, universal_router_sdk_1.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); routers_1.metric.putMetric('TenderlySimulationUniversalRouterRequests', 1, routers_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 routers_1.metric.putMetric('TenderlySimulationUniversalRouterLatencies', Date.now() - before, routers_1.MetricLoggerUnit.Milliseconds); routers_1.metric.putMetric('TenderlyNodeSimulationUniversalRouterLatencies', Date.now() - before, routers_1.MetricLoggerUnit.Milliseconds); routers_1.metric.putMetric(`TenderlySimulationUniversalRouterResponseStatus${httpStatus}`, 1, routers_1.MetricLoggerUnit.Count); routers_1.metric.putMetric(`TenderlyNodeSimulationUniversalRouterResponseStatus${httpStatus}`, 1, routers_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) { util_1.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 Object.assign(Object.assign({}, swapRoute), { simulationStatus: (0, tenderlySimulationErrorBreakDown_1.breakDownTenderlySimulationError)(tokenIn, tokenOut, resp.result[2].error.data) }); } return Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.Failed }); } // Parse the gas used in the simulation response object, and then pad it so that we overestimate. estimatedGasUsed = ethers_1.BigNumber.from((Number(resp.result[2].gas) * estimateMultiplier).toFixed(0)); util_1.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(() => { routers_1.metric.putMetric('TenderlySimulationLatencies', Date.now() - before, routers_1.MetricLoggerUnit.Milliseconds); }); routers_1.metric.putMetric('TenderlySimulationUniversalRouterLatencies', Date.now() - before, routers_1.MetricLoggerUnit.Milliseconds); routers_1.metric.putMetric('TenderlyApiSimulationUniversalRouterLatencies', Date.now() - before, routers_1.MetricLoggerUnit.Milliseconds); routers_1.metric.putMetric(`TenderlySimulationUniversalRouterResponseStatus${httpStatus}`, 1, routers_1.MetricLoggerUnit.Count); routers_1.metric.putMetric(`TenderlyApiSimulationUniversalRouterResponseStatus${httpStatus}`, 1, routers_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 Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.Failed }); } // Parse the gas used in the simulation response object, and then pad it so that we overestimate. estimatedGasUsed = ethers_1.BigNumber.from((resp.simulation_results[2].transaction.gas * estimateMultiplier).toFixed(0)); util_1.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.'); util_1.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 == routers_1.SwapType.SWAP_ROUTER_02) { const approve = { network_id: chainId, input: callData_1.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: (0, util_1.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); routers_1.metric.putMetric('TenderlySimulationSwapRouter02Requests', 1, routers_1.MetricLoggerUnit.Count); const before = Date.now(); const { data: resp, status: httpStatus } = await this.tenderlyServiceInstance.post(url, body, opts); routers_1.metric.putMetric(`TenderlySimulationSwapRouter02ResponseStatus${httpStatus}`, 1, routers_1.MetricLoggerUnit.Count); const latencies = Date.now() - before; util_1.log.info(`Tenderly simulation swap router02 request body: ${body}, having latencies ${latencies} in milliseconds.`); routers_1.metric.putMetric('TenderlySimulationSwapRouter02Latencies', latencies, routers_1.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}`; util_1.log.info({ err: resp.simulation_results[1].transaction.error_message }, msg); return Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.Failed }); } // Parse the gas used in the simulation response object, and then pad it so that we overestimate. estimatedGasUsed = ethers_1.BigNumber.from((resp.simulation_results[1].transaction.gas * estimateMultiplier).toFixed(0)); util_1.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.'); util_1.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 (0, gas_factory_helpers_1.calculateGasUsed)(chainId, swapRoute, estimatedGasUsed, this.v2PoolProvider, this.v3PoolProvider, this.provider, providerConfig); (0, gas_factory_helpers_1.logGasEstimationVsSimulationMetrics)(swapRoute, estimatedGasUsed, chainId); return Object.assign(Object.assign({}, (0, gas_factory_helpers_1.initSwapRouteFromExisting)(swapRoute, this.v2PoolProvider, this.v3PoolProvider, this.v4PoolProvider, this.portionProvider, quoteGasAdjusted, estimatedGasUsed, estimatedGasUsedQuoteToken, estimatedGasUsedUSD, swapOptions, estimatedGasUsedGasToken, providerConfig)), { simulationStatus: simulation_provider_1.SimulationStatus.Succeeded }); } logTenderlyErrorResponse(resp) { util_1.log.info({ resp, }, 'Failed to Simulate on Tenderly'); util_1.log.info({ err: resp.simulation_results.length >= 1 ? resp.simulation_results[0].transaction : {}, }, 'Failed to Simulate on Tenderly #1 Transaction'); util_1.log.info({ err: resp.simulation_results.length >= 1 ? resp.simulation_results[0].simulation : {}, }, 'Failed to Simulate on Tenderly #1 Simulation'); util_1.log.info({ err: resp.simulation_results.length >= 2 ? resp.simulation_results[1].transaction : {}, }, 'Failed to Simulate on Tenderly #2 Transaction'); util_1.log.info({ err: resp.simulation_results.length >= 2 ? resp.simulation_results[1].simulation : {}, }, 'Failed to Simulate on Tenderly #2 Simulation'); util_1.log.info({ err: resp.simulation_results.length >= 3 ? resp.simulation_results[2].transaction : {}, }, 'Failed to Simulate on Tenderly #3 Transaction'); util_1.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; routers_1.metric.putMetric('TenderlyNodeGasEstimateBundleLatencies', latencies, routers_1.MetricLoggerUnit.Milliseconds); routers_1.metric.putMetric('TenderlyNodeGasEstimateBundleSuccess', 1, routers_1.MetricLoggerUnit.Count); if (httpStatus !== 200) { util_1.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) { util_1.log.error({ err }, `Failed to invoke Tenderly Node Endpoint for gas estimation bundle ${JSON.stringify(body, null, 2)}. Error: ${err}`); routers_1.metric.putMetric('TenderlyNodeGasEstimateBundleFailure', 1, routers_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; } } } exports.TenderlySimulator = TenderlySimulator; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wcm92aWRlcnMvdGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxnREFBd0I7QUFDeEIsa0RBQTBCO0FBRTFCLHdEQUFzRDtBQUV0RCxzREFBc0Q7QUFDdEQsa0RBQWdEO0FBQ2hELHdFQUF5RTtBQUN6RSxrREFBa0Q7QUFDbEQsOENBQThDO0FBRTlDLHdDQU9vQjtBQUNwQiw0RUFBeUU7QUFDekUsZ0ZBQTZFO0FBQzdFLGtDQUtpQjtBQUNqQiwrQ0FBOEQ7QUFDOUQscUVBSXFDO0FBRXJDLCtGQUE0RjtBQUc1RiwrREFJK0I7QUEyQy9CLElBQUssc0JBSUo7QUFKRCxXQUFLLHNCQUFzQjtJQUN6Qix5Q0FBZSxDQUFBO0lBQ2YsdUNBQWEsQ0FBQTtJQUNiLHFDQUFXLENBQUE7QUFDYixDQUFDLEVBSkksc0JBQXNCLEtBQXRCLHNCQUFzQixRQUkxQjtBQXlDRCxNQUFNLDJCQUEyQixHQUFHLENBQ2xDLGVBQXVCLEVBQ3ZCLFlBQW9CLEVBQ3BCLGVBQXVCLEVBQ3ZCLEVBQUUsQ0FDRixHQUFHLGVBQWUsbUJBQW1CLFlBQVksWUFBWSxlQUFlLGlCQUFpQixDQUFDO0FBRWhHLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxPQUFnQixFQUFFLGtCQUEwQixFQUFFLEVBQUU7SUFDekUsUUFBUSxPQUFPLEVBQUU7UUFDZixLQUFLLGdCQUFPLENBQUMsT0FBTztZQUNsQixPQUFPLHVDQUF1QyxrQkFBa0IsRUFBRSxDQUFDO1FBQ3JFLEtBQUssZ0JBQU8sQ0FBQyxJQUFJO1lBQ2YsT0FBTyxvQ0FBb0Msa0JBQWtCLEVBQUUsQ0FBQztRQUNsRSxLQUFLLGdCQUFPLENBQUMsWUFBWTtZQUN2QixPQUFPLHdDQUF3QyxrQkFBa0IsRUFBRSxDQUFDO1FBQ3RFLEtBQUssZ0JBQU8sQ0FB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