UNPKG

@sky-mavis/smart-order-router

Version:
144 lines 13.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MixedRouteHeuristicGasModelFactory = void 0; const bignumber_1 = require("@ethersproject/bignumber"); const router_sdk_1 = require("@uniswap/router-sdk"); const v2_sdk_1 = require("@uniswap/v2-sdk"); const v3_sdk_1 = require("@uniswap/v3-sdk"); const jsbi_1 = __importDefault(require("jsbi")); const lodash_1 = __importDefault(require("lodash")); const __1 = require("../../../.."); const util_1 = require("../../../../util"); const amounts_1 = require("../../../../util/amounts"); const gas_factory_helpers_1 = require("../../../../util/gas-factory-helpers"); const gas_model_1 = require("../gas-model"); const v2_heuristic_gas_model_1 = require("../v2/v2-heuristic-gas-model"); const gas_costs_1 = require("../v3/gas-costs"); /** * Computes a gas estimate for a mixed route swap using heuristics. * Considers number of hops in the route, number of ticks crossed * and the typical base cost for a swap. * * We get the number of ticks crossed in a swap from the MixedRouteQuoterV1 * contract. * * We compute gas estimates off-chain because * 1/ Calling eth_estimateGas for a swaps requires the caller to have * the full balance token being swapped, and approvals. * 2/ Tracking gas used using a wrapper contract is not accurate with Multicall * due to EIP-2929. We would have to make a request for every swap we wanted to estimate. * 3/ For V2 we simulate all our swaps off-chain so have no way to track gas used. * * @export * @class MixedRouteHeuristicGasModelFactory */ class MixedRouteHeuristicGasModelFactory extends gas_model_1.IOnChainGasModelFactory { constructor() { super(); } async buildGasModel({ chainId, gasPriceWei, pools, quoteToken, v2poolProvider: V2poolProvider, providerConfig, }) { const nativeCurrency = __1.WRAPPED_NATIVE_CURRENCY[chainId]; const usdPool = pools.usdPool; const usdToken = usdPool.token0.equals(nativeCurrency) ? usdPool.token1 : usdPool.token0; let nativeV2Pool; // Avoid fetching for a (WETH,WETH) pool here, we handle the quoteToken = wrapped native case in estimateGasCost if (!quoteToken.equals(nativeCurrency) && V2poolProvider) { /// MixedRoutes nativeV2Pool = await (0, gas_factory_helpers_1.getV2NativePool)(quoteToken, V2poolProvider, providerConfig); } const estimateGasCost = (routeWithValidQuote) => { var _a; const { totalGasCostNativeCurrency, baseGasUse } = this.estimateGas(routeWithValidQuote, gasPriceWei, chainId, providerConfig); /** ------ MARK: USD Logic -------- */ const gasCostInTermsOfUSD = (0, gas_model_1.getQuoteThroughNativePool)(chainId, totalGasCostNativeCurrency, usdPool); /** ------ MARK: Conditional logic run if gasToken is specified -------- */ const nativeAndSpecifiedGasTokenPool = pools.nativeAndSpecifiedGasTokenV3Pool; let gasCostInTermsOfGasToken = undefined; if (nativeAndSpecifiedGasTokenPool) { gasCostInTermsOfGasToken = (0, gas_model_1.getQuoteThroughNativePool)(chainId, totalGasCostNativeCurrency, nativeAndSpecifiedGasTokenPool); } // if the gasToken is the native currency, we can just use the totalGasCostNativeCurrency else if ((_a = providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.gasToken) === null || _a === void 0 ? void 0 : _a.equals(nativeCurrency)) { gasCostInTermsOfGasToken = totalGasCostNativeCurrency; } /** ------ MARK: return early if quoteToken is wrapped native currency ------- */ if (quoteToken.equals(nativeCurrency)) { return { gasEstimate: baseGasUse, gasCostInToken: totalGasCostNativeCurrency, gasCostInUSD: gasCostInTermsOfUSD, gasCostInGasToken: gasCostInTermsOfGasToken, }; } /** ------ MARK: Main gas logic in terms of quote token -------- */ // If the quote token is not in the native currency, we convert the gas cost to be in terms of the quote token. // We do this by getting the highest liquidity <quoteToken>/<nativeCurrency> pool. eg. <quoteToken>/ETH pool. const nativeV3Pool = pools.nativeAndQuoteTokenV3Pool; if (!nativeV3Pool && !nativeV2Pool) { util_1.log.info(`Unable to find ${nativeCurrency.symbol} pool with the quote token, ${quoteToken.symbol} to produce gas adjusted costs. Route will not account for gas.`); return { gasEstimate: baseGasUse, gasCostInToken: amounts_1.CurrencyAmount.fromRawAmount(quoteToken, 0), gasCostInUSD: amounts_1.CurrencyAmount.fromRawAmount(usdToken, 0), }; } /// we will use nativeV2Pool for fallback if nativeV3 does not exist or has 0 liquidity /// can use ! here because we return above if v3Pool and v2Pool are null const nativePool = (!nativeV3Pool || jsbi_1.default.equal(nativeV3Pool.liquidity, jsbi_1.default.BigInt(0))) && nativeV2Pool ? nativeV2Pool : nativeV3Pool; const gasCostInTermsOfQuoteToken = (0, gas_model_1.getQuoteThroughNativePool)(chainId, totalGasCostNativeCurrency, nativePool); return { gasEstimate: baseGasUse, gasCostInToken: gasCostInTermsOfQuoteToken, gasCostInUSD: gasCostInTermsOfUSD, gasCostInGasToken: gasCostInTermsOfGasToken, }; }; return { estimateGasCost: estimateGasCost.bind(this), }; } estimateGas(routeWithValidQuote, gasPriceWei, chainId, providerConfig) { const totalInitializedTicksCrossed = bignumber_1.BigNumber.from(Math.max(1, lodash_1.default.sum(routeWithValidQuote.initializedTicksCrossedList))); /** * Since we must make a separate call to multicall for each v3 and v2 section, we will have to * add the BASE_SWAP_COST to each section. */ let baseGasUse = bignumber_1.BigNumber.from(0); const route = routeWithValidQuote.route; const res = (0, router_sdk_1.partitionMixedRouteByProtocol)(route); res.map((section) => { if (section.every(pool => pool instanceof v3_sdk_1.Pool)) { baseGasUse = baseGasUse.add(gas_costs_1.BASE_SWAP_COST); baseGasUse = baseGasUse.add(gas_costs_1.COST_PER_HOP.mul(section.length)); } else if (section.every(pool => pool instanceof v2_sdk_1.Pair)) { baseGasUse = baseGasUse.add(v2_heuristic_gas_model_1.BASE_SWAP_COST); baseGasUse = baseGasUse.add( /// same behavior in v2 heuristic gas model factory v2_heuristic_gas_model_1.COST_PER_EXTRA_HOP.mul(section.length - 1)); } }); const tickGasUse = gas_costs_1.COST_PER_INIT_TICK.mul(totalInitializedTicksCrossed); const uninitializedTickGasUse = gas_costs_1.COST_PER_UNINIT_TICK.mul(0); // base estimate gas used based on chainId estimates for hops and ticks gas useage baseGasUse = baseGasUse.add(tickGasUse).add(uninitializedTickGasUse); if (providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.additionalGasOverhead) { baseGasUse = baseGasUse.add(providerConfig.additionalGasOverhead); } const baseGasCostWei = gasPriceWei.mul(baseGasUse); const wrappedCurrency = __1.WRAPPED_NATIVE_CURRENCY[chainId]; const totalGasCostNativeCurrency = amounts_1.CurrencyAmount.fromRawAmount(wrappedCurrency, baseGasCostWei.toString()); return { totalGasCostNativeCurrency, totalInitializedTicksCrossed, baseGasUse, }; } } exports.MixedRouteHeuristicGasModelFactory = MixedRouteHeuristicGasModelFactory; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWl4ZWQtcm91dGUtaGV1cmlzdGljLWdhcy1tb2RlbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9yb3V0ZXJzL2FscGhhLXJvdXRlci9nYXMtbW9kZWxzL21peGVkUm91dGUvbWl4ZWQtcm91dGUtaGV1cmlzdGljLWdhcy1tb2RlbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSx3REFBcUQ7QUFFckQsb0RBQW9FO0FBQ3BFLDRDQUF1QztBQUN2Qyw0Q0FBdUM7QUFDdkMsZ0RBQXdCO0FBQ3hCLG9EQUF1QjtBQUV2QixtQ0FBc0Q7QUFDdEQsMkNBQXVDO0FBQ3ZDLHNEQUEwRDtBQUMxRCw4RUFBdUU7QUFFdkUsNENBTXNCO0FBQ3RCLHlFQUdzQztBQUN0QywrQ0FBeUc7QUFFekc7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFBYSxrQ0FBbUMsU0FBUSxtQ0FBdUI7SUFDN0U7UUFDRSxLQUFLLEVBQUUsQ0FBQztJQUNWLENBQUM7SUFFTSxLQUFLLENBQUMsYUFBYSxDQUFDLEVBQ3pCLE9BQU8sRUFDUCxXQUFXLEVBQ1gsS0FBSyxFQUNMLFVBQVUsRUFDVixjQUFjLEVBQUUsY0FBYyxFQUM5QixjQUFjLEdBQ2tCO1FBQ2hDLE1BQU0sY0FBYyxHQUFHLDJCQUF1QixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3hELE1BQU0sT0FBTyxHQUFTLEtBQUssQ0FBQyxPQUFPLENBQUM7UUFDcEMsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7UUFFekYsSUFBSSxZQUF5QixDQUFDO1FBQzlCLGdIQUFnSDtRQUNoSCxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxjQUFjLEVBQUU7WUFDeEQsZUFB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