UNPKG

@sky-mavis/smart-order-router

Version:
164 lines 17.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UniswapMulticallProvider = void 0; const katana_core_1 = require("@sky-mavis/katana-core"); const lodash_1 = __importDefault(require("lodash")); const stats_lite_1 = __importDefault(require("stats-lite")); const UniswapInterfaceMulticall__factory_1 = require("../types/v3/factories/UniswapInterfaceMulticall__factory"); const log_1 = require("../util/log"); const multicall_provider_1 = require("./multicall-provider"); /** * The UniswapMulticall contract has added functionality for limiting the amount of gas * that each call within the multicall can consume. This is useful for operations where * a call could consume such a large amount of gas that it causes the node to error out * with an out of gas error. * * @export * @class UniswapMulticallProvider */ class UniswapMulticallProvider extends multicall_provider_1.IMulticallProvider { constructor(chainId, provider, gasLimitPerCall = 1000000) { super(); this.chainId = chainId; this.provider = provider; this.gasLimitPerCall = gasLimitPerCall; const multicallAddress = katana_core_1.MULTICALL_ADDRESSES[this.chainId]; if (!multicallAddress) { throw new Error(`No address for Uniswap Multicall Contract on chain id: ${chainId}`); } this.multicallContract = UniswapInterfaceMulticall__factory_1.UniswapInterfaceMulticall__factory.connect(multicallAddress, this.provider); } async callSameFunctionOnMultipleContracts(params) { var _a; const { addresses, contractInterface, functionName, functionParams, providerConfig } = params; const blockNumberOverride = (_a = providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber) !== null && _a !== void 0 ? _a : undefined; const fragment = contractInterface.getFunction(functionName); const callData = contractInterface.encodeFunctionData(fragment, functionParams); const calls = lodash_1.default.map(addresses, address => { return { target: address, callData, gasLimit: this.gasLimitPerCall, }; }); log_1.log.debug({ calls }, `About to multicall for ${functionName} across ${addresses.length} addresses`); const { blockNumber, returnData: aggregateResults } = await this.multicallContract.callStatic.multicall(calls, { blockTag: blockNumberOverride, }); const results = []; for (let i = 0; i < aggregateResults.length; i++) { const { success, returnData } = aggregateResults[i]; // Return data "0x" is sometimes returned for invalid calls. if (!success || returnData.length <= 2) { log_1.log.debug({ result: aggregateResults[i] }, `Invalid result calling ${functionName} on address ${addresses[i]}`); results.push({ success: false, returnData, }); continue; } results.push({ success: true, result: contractInterface.decodeFunctionResult(fragment, returnData), }); } log_1.log.debug({ results }, `Results for multicall on ${functionName} across ${addresses.length} addresses as of block ${blockNumber}`); return { blockNumber, results }; } async callSameFunctionOnContractWithMultipleParams(params) { var _a, _b; const { address, contractInterface, functionName, functionParams, additionalConfig, providerConfig } = params; const fragment = contractInterface.getFunction(functionName); const gasLimitPerCall = (_a = additionalConfig === null || additionalConfig === void 0 ? void 0 : additionalConfig.gasLimitPerCallOverride) !== null && _a !== void 0 ? _a : this.gasLimitPerCall; const blockNumberOverride = (_b = providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber) !== null && _b !== void 0 ? _b : undefined; const calls = lodash_1.default.map(functionParams, functionParam => { const callData = contractInterface.encodeFunctionData(fragment, functionParam); return { target: address, callData, gasLimit: gasLimitPerCall, }; }); log_1.log.debug({ calls }, `About to multicall for ${functionName} at address ${address} with ${functionParams.length} different sets of params`); const { blockNumber, returnData: aggregateResults } = await this.multicallContract.callStatic.multicall(calls, { blockTag: blockNumberOverride, }); const results = []; const gasUsedForSuccess = []; for (let i = 0; i < aggregateResults.length; i++) { const { success, returnData, gasUsed } = aggregateResults[i]; // Return data "0x" is sometimes returned for invalid pools. if (!success || returnData.length <= 2) { log_1.log.debug({ result: aggregateResults[i] }, `Invalid result calling ${functionName} address ${address} with params ${functionParams[i]}`); results.push({ success: false, returnData, gasUsed, }); continue; } gasUsedForSuccess.push(gasUsed.toNumber()); results.push({ success: true, result: contractInterface.decodeFunctionResult(fragment, returnData), }); } log_1.log.debug({ results, functionName, address }, `Results for multicall for ${functionName} at address ${address} with ${functionParams.length} different sets of params. Results as of block ${blockNumber}`); return { blockNumber, results, approxGasUsedPerSuccessCall: stats_lite_1.default.percentile(gasUsedForSuccess, 99), }; } async callMultipleFunctionsOnSameContract(params) { var _a, _b; const { address, contractInterface, functionNames, functionParams, additionalConfig, providerConfig } = params; const gasLimitPerCall = (_a = additionalConfig === null || additionalConfig === void 0 ? void 0 : additionalConfig.gasLimitPerCallOverride) !== null && _a !== void 0 ? _a : this.gasLimitPerCall; const blockNumberOverride = (_b = providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.blockNumber) !== null && _b !== void 0 ? _b : undefined; const calls = lodash_1.default.map(functionNames, (functionName, i) => { const fragment = contractInterface.getFunction(functionName); const param = functionParams ? functionParams[i] : []; const callData = contractInterface.encodeFunctionData(fragment, param); return { target: address, callData, gasLimit: gasLimitPerCall, }; }); log_1.log.debug({ calls }, `About to multicall for ${functionNames.length} functions at address ${address} with ${functionParams === null || functionParams === void 0 ? void 0 : functionParams.length} different sets of params`); const { blockNumber, returnData: aggregateResults } = await this.multicallContract.callStatic.multicall(calls, { blockTag: blockNumberOverride, }); const results = []; const gasUsedForSuccess = []; for (let i = 0; i < aggregateResults.length; i++) { const fragment = contractInterface.getFunction(functionNames[i]); const { success, returnData, gasUsed } = aggregateResults[i]; // Return data "0x" is sometimes returned for invalid pools. if (!success || returnData.length <= 2) { log_1.log.debug({ result: aggregateResults[i] }, `Invalid result calling ${functionNames[i]} with ${functionParams ? functionParams[i] : '0'} params`); results.push({ success: false, returnData, }); continue; } gasUsedForSuccess.push(gasUsed.toNumber()); results.push({ success: true, result: contractInterface.decodeFunctionResult(fragment, returnData), }); } log_1.log.debug({ results, functionNames, address }, `Results for multicall for ${functionNames.length} functions at address ${address} with ${functionParams ? functionParams.length : ' 0'} different sets of params. Results as of block ${blockNumber}`); return { blockNumber, results, approxGasUsedPerSuccessCall: stats_lite_1.default.percentile(gasUsedForSuccess, 99), }; } } exports.UniswapMulticallProvider = UniswapMulticallProvider; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGljYWxsLXVuaXN3YXAtcHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL211bHRpY2FsbC11bmlzd2FwLXByb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUVBLHdEQUFzRTtBQUN0RSxvREFBdUI7QUFDdkIsNERBQStCO0FBRS9CLGlIQUE4RztBQUU5RyxxQ0FBa0M7QUFDbEMsNkRBTThCO0FBTTlCOzs7Ozs7OztHQVFHO0FBQ0gsTUFBYSx3QkFBeUIsU0FBUSx1Q0FBMEM7SUFHdEYsWUFDWSxPQUFnQixFQUNoQixRQUFzQixFQUN0QixrQkFBa0IsT0FBUztRQUVyQyxLQUFLLEVBQUUsQ0FBQztRQUpFLFlBQU8sR0FBUCxPQUFPLENBQVM7UUFDaEIsYUFBUSxHQUFSLFFBQVEsQ0FBYztRQUN0QixvQkFBZSxHQUFmLGVBQWUsQ0FBWTtRQUdyQyxNQUFNLGdCQUFnQixHQUFHLGlDQUFtQixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUUzRCxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7WUFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQywwREFBMEQsT0FBTyxFQUFFLENBQUMsQ0FBQztTQUN0RjtRQUVELElBQUksQ0FBQyxpQkFBaUIsR0FBRyx1RUFBa0MsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3ZHLENBQUM7SUFFTSxLQUFLLENBQUMsbUNBQW1DLENBQzlDLE1BQWtFOztRQUtsRSxNQUFNLEVBQUUsU0FBUyxFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLEdBQUcsTUFBTSxDQUFDO1FBRTlGLE1BQU0sbUJBQW1CLEdBQUcsTUFBQSxjQUFjLGFBQWQsY0FBYyx1QkFBZCxjQUFjLENBQUUsV0FBVyxtQ0FBSSxTQUFTLENBQUM7UUFFckUsTUFBTSxRQUFRLEdBQUcsaUJB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