@xspswap/smart-order-router
Version:
XSwap Protocol V3 Smart Order Router
245 lines • 25.8 kB
JavaScript
"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 sdk_core_1 = require("@x-swap-protocol/sdk-core");
const universal_router_sdk_1 = require("@x-swap-protocol/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 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, universal_router_sdk_1.PERMIT2_ADDRESSES)(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, universal_router_sdk_1.UNIVERSAL_ROUTER_ADDRESS)(this.chainId, true),
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: universal_router_sdk_1.PERMIT2_ADDRESSES,
value: '0',
from: fromAddress,
};
const swap = {
network_id: chainId,
input: calldata,
gas_estimate: true,
to: (0, universal_router_sdk_1.UNIVERSAL_ROUTER_ADDRESS)(this.chainId, 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: [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, sdk_core_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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wcm92aWRlcnMvdGVuZGVybHktc2ltdWxhdGlvbi1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSx3REFBc0Q7QUFFdEQsd0RBQThFO0FBQzlFLGdGQUcrQztBQUMvQyxrREFBMEI7QUFDMUIsOENBQThDO0FBRTlDLHdDQUE4RDtBQUM5RCw0RUFBeUU7QUFDekUsZ0ZBQTZFO0FBQzdFLGtDQUEyQztBQUMzQywrQ0FBOEQ7QUFDOUQscUVBR3FDO0FBR3JDLCtEQUkrQjtBQXNCL0IsTUFBTSwyQkFBMkIsR0FBRyxDQUNsQyxlQUF1QixFQUN2QixZQUFvQixFQUNwQixlQUF1QixFQUN2QixFQUFFLENBQ0YsR0FBRyxlQUFlLG1CQUFtQixZQUFZLFlBQVksZUFBZSxpQkFBaUIsQ0FBQztBQUVoRyxtRUFBbUU7QUFDbkUsTUFBTSwyQkFBMkIsR0FBRyxJQUFJLENBQUM7QUFFekMsTUFBYSx5QkFBMEIsU0FBUSwrQkFBUztJQUd0RCxZQUNFLE9BQWdCLEVBQ2hCLFFBQXlCLEVBQ3pCLGlCQUFvQyxFQUNwQyx1QkFBZ0Q7UUFFaEQsS0FBSyxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN6QixJQUFJLENBQUMsaUJBQWlCLEdBQUcsaUJBQWlCLENBQUM7UUFDM0MsSUFBSSxDQUFDLHVCQUF1QixHQUFHLHVCQUF1QixDQUFDO0lBQ3pELENBQUM7SUFFUyxLQUFLLENBQUMsbUJBQW1CLENBQ2pDLFdBQW1CLEVBQ25CLFdBQXdCLEVBQ3hCLFNBQW9CO1FBRXBCLDRDQUE0QztRQUM1QyxpRUFBaUU7UUFDakUsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUM7UUFFaEQsSUFDRSxXQUFXLENBQUMsUUFBUSxDQUFDLFFBQVE7WUFDN0IsQ0FBQyxNQUFNLElBQUksQ0FB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