UNPKG

@xspswap/smart-order-router

Version:
244 lines 25.6 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 = void 0; const constants_1 = require("@ethersproject/constants"); const addresses_1 = require("../util/addresses"); 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 simulation_provider_1 = require("./simulation-provider"); const TENDERLY_BATCH_SIMULATE_API = (tenderlyBaseUrl, tenderlyUser, tenderlyProject) => `${tenderlyBaseUrl}/api/v1/account/${tenderlyUser}/project/${tenderlyProject}/simulate-batch`; // We multiply tenderly gas limit by this to overestimate gas limit const DEFAULT_ESTIMATE_MULTIPLIER = 1.25; class FallbackTenderlySimulator extends simulation_provider_1.Simulator { constructor(chainId, provider, tenderlySimulator, ethEstimateGasSimulator) { super(provider, chainId); this.tenderlySimulator = tenderlySimulator; this.ethEstimateGasSimulator = ethEstimateGasSimulator; } async simulateTransaction(fromAddress, swapOptions, swapRoute) { // 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); return swapRouteWithGasEstimate; } catch (err) { util_1.log.info({ err: err }, 'Error simulating using eth_estimateGas'); return Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.Failed }); } } try { return await this.tenderlySimulator.simulateTransaction(fromAddress, swapOptions, swapRoute); } catch (err) { util_1.log.info({ err: err }, 'Failed to simulate via Tenderly'); return Object.assign(Object.assign({}, swapRoute), { simulationStatus: simulation_provider_1.SimulationStatus.Failed }); } } } exports.FallbackTenderlySimulator = FallbackTenderlySimulator; class TenderlySimulator extends simulation_provider_1.Simulator { constructor(chainId, tenderlyBaseUrl, tenderlyUser, tenderlyProject, tenderlyAccessKey, v2PoolProvider, v3PoolProvider, provider, overrideEstimateMultiplier) { super(provider, chainId); this.tenderlyBaseUrl = tenderlyBaseUrl; this.tenderlyUser = tenderlyUser; this.tenderlyProject = tenderlyProject; this.tenderlyAccessKey = tenderlyAccessKey; this.v2PoolProvider = v2PoolProvider; this.v3PoolProvider = v3PoolProvider; this.overrideEstimateMultiplier = overrideEstimateMultiplier !== null && overrideEstimateMultiplier !== void 0 ? overrideEstimateMultiplier : {}; } async simulateTransaction(fromAddress, swapOptions, swapRoute) { var _a; const currencyIn = swapRoute.trade.inputAmount.currency; const tokenIn = currencyIn.wrapped; const chainId = this.chainId; 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'); let estimatedGasUsed; const estimateMultiplier = (_a = this.overrideEstimateMultiplier[chainId]) !== null && _a !== void 0 ? _a : DEFAULT_ESTIMATE_MULTIPLIER; if (swapOptions.type == routers_1.SwapType.UNIVERSAL_ROUTER) { // Do initial onboarding approval of Permit2. const erc20Interface = Erc20__factory_1.Erc20__factory.createInterface(); const approvePermit2Calldata = erc20Interface.encodeFunctionData('approve', [(0, addresses_1.PERMIT2_ADDRESS)(chainId), constants_1.MaxUint256]); // We are unsure if the users calldata contains a permit or not. We just // max approve the Univeral 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, util_1.UNIVERSAL_ROUTER_ADDRESS)(this.chainId), util_1.MAX_UINT160, Math.floor(new Date().getTime() / 1000) + 10000000, ]); const approvePermit2 = { network_id: chainId, gas_estimate: true, input: approvePermit2Calldata, to: tokenIn.address, value: '0', from: fromAddress, }; const approveUniversalRouter = { network_id: chainId, gas_estimate: true, input: approveUniversalRouterCallData, to: addresses_1.PERMIT2_ADDRESS, value: '0', from: fromAddress, }; const swap = { network_id: chainId, input: calldata, gas_estimate: true, to: (0, util_1.UNIVERSAL_ROUTER_ADDRESS)(this.chainId), value: currencyIn.isNative ? swapRoute.methodParameters.value : '0', from: fromAddress, // TODO: This is a Temporary fix given by Tenderly team, remove once resolved on their end. block_number: undefined, }; const body = { simulations: [approvePermit2, approveUniversalRouter, swap], gas_estimate: true, }; const opts = { headers: { 'X-Access-Key': this.tenderlyAccessKey, }, }; const url = TENDERLY_BATCH_SIMULATE_API(this.tenderlyBaseUrl, this.tenderlyUser, this.tenderlyProject); const resp = (await axios_1.default.post(url, body, opts)).data; // 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_used * 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, swapWithMultiplier: estimatedGasUsed.toString(), }, 'Successfully Simulated Approvals + Swap via Tenderly for Universal Router. Gas used.'); util_1.log.info({ swapTransaction: resp.simulation_results[2].transaction }, 'Successful Tenderly Swap Transaction for Universal Router'); util_1.log.info({ swapSimulation: resp.simulation_results[2].simulation }, 'Successful Tenderly 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, gas_estimate: true, to: tokenIn.address, value: '0', from: fromAddress, }; const swap = { network_id: chainId, input: calldata, to: (0, util_1.SWAP_ROUTER_02_ADDRESSES)(chainId), gas_estimate: true, value: currencyIn.isNative ? swapRoute.methodParameters.value : '0', from: fromAddress, // TODO: This is a Temporary fix given by Tenderly team, remove once resolved on their end. block_number: undefined, }; const body = { simulations: [approve, swap] }; const opts = { headers: { 'X-Access-Key': this.tenderlyAccessKey, }, }; const url = TENDERLY_BATCH_SIMULATE_API(this.tenderlyBaseUrl, this.tenderlyUser, this.tenderlyProject); const resp = (await axios_1.default.post(url, body, opts)).data; // 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_used * estimateMultiplier).toFixed(0)); util_1.log.info({ body, approveGasUsed: resp.simulation_results[0].transaction.gas_used, swapGasUsed: resp.simulation_results[1].transaction.gas_used, swapWithMultiplier: estimatedGasUsed.toString(), }, 'Successfully Simulated Approval + Swap via Tenderly for SwapRouter02. Gas used.'); util_1.log.info({ swapTransaction: resp.simulation_results[1].transaction }, 'Successful Tenderly Swap Transaction for SwapRouter02'); util_1.log.info({ swapSimulation: resp.simulation_results[1].simulation }, 'Successful Tenderly Swap Simulation for SwapRouter02'); } else { throw new Error(`Unsupported swap type: ${swapOptions}`); } const { estimatedGasUsedUSD, estimatedGasUsedQuoteToken, quoteGasAdjusted, } = await (0, gas_factory_helpers_1.calculateGasUsed)(chainId, swapRoute, estimatedGasUsed, this.v2PoolProvider, this.v3PoolProvider); return Object.assign(Object.assign({}, (0, gas_factory_helpers_1.initSwapRouteFromExisting)(swapRoute, this.v2PoolProvider, this.v3PoolProvider, quoteGasAdjusted, estimatedGasUsed, estimatedGasUsedQuoteToken, estimatedGasUsedUSD)), { 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'); } } exports.TenderlySimulator = TenderlySimulator; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wcm92aWRlcnMvdGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSx3REFBc0Q7QUFFdEQsaURBQW9EO0FBQ3BELGtEQUEwQjtBQUMxQiw4Q0FBOEM7QUFFOUMsd0NBQThEO0FBQzlELDRFQUF5RTtBQUN6RSxnRkFBNkU7QUFDN0Usa0NBTWlCO0FBQ2pCLCtDQUE4RDtBQUM5RCxxRUFHcUM7QUFHckMsK0RBSStCO0FBc0IvQixNQUFNLDJCQUEyQixHQUFHLENBQ2xDLGVBQXVCLEVBQ3ZCLFlBQW9CLEVBQ3BCLGVBQXVCLEVBQ3ZCLEVBQUUsQ0FDRixHQUFHLGVBQWUsbUJBQW1CLFlBQVksWUFBWSxlQUFlLGlCQUFpQixDQUFDO0FBRWhHLG1FQUFtRTtBQUNuRSxNQUFNLDJCQUEyQixHQUFHLElBQUksQ0FBQztBQUV6QyxNQUFhLHlCQUEwQixTQUFRLCtCQUFTO0lBR3RELFlBQ0UsT0FBZ0IsRUFDaEIsUUFBeUIsRUFDekIsaUJBQW9DLEVBQ3BDLHVCQUFnRDtRQUVoRCxLQUFLLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3pCLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQztRQUMzQyxJQUFJLENBQUMsdUJBQXVCLEdBQUcsdUJBQXVCLENBQUM7SUFDekQsQ0FBQztJQUVTLEtBQUssQ0FBQyxtQkFBbUIsQ0FDakMsV0FBbUIsRUFDbkIsV0FBd0IsRUFDeEIsU0FBb0I7UUFFcEIsNENBQTRDO1FBQzVDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQztRQUVoRCxJQUNFLFdBQVcsQ0FBQyxRQUFRLENBQUMsUUFBUTtZQUM3QixDQUFDLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUM1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