UNPKG

viem

Version:

TypeScript Interface for Ethereum

194 lines • 8.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.multicall = multicall; const abis_js_1 = require("../../constants/abis.js"); const contracts_js_1 = require("../../constants/contracts.js"); const abi_js_1 = require("../../errors/abi.js"); const base_js_1 = require("../../errors/base.js"); const contract_js_1 = require("../../errors/contract.js"); const decodeFunctionResult_js_1 = require("../../utils/abi/decodeFunctionResult.js"); const encodeFunctionData_js_1 = require("../../utils/abi/encodeFunctionData.js"); const getChainContractAddress_js_1 = require("../../utils/chain/getChainContractAddress.js"); const getContractError_js_1 = require("../../utils/errors/getContractError.js"); const getAction_js_1 = require("../../utils/getAction.js"); const createBatchScheduler_js_1 = require("../../utils/promise/createBatchScheduler.js"); const stringify_js_1 = require("../../utils/stringify.js"); const readContract_js_1 = require("./readContract.js"); async function multicall(client, parameters) { const { account, authorizationList, allowFailure = true, blockHash, blockNumber, blockOverrides, blockTag, requireCanonical, stateOverride, } = parameters; const contracts = parameters.contracts; const batch = typeof client.batch?.multicall === 'object' ? client.batch.multicall : {}; const batchSize = parameters.batchSize ?? batch.batchSize ?? 1024; const deployless = parameters.deployless ?? batch.deployless ?? false; const multicallAddress = (() => { if (parameters.multicallAddress) return parameters.multicallAddress; if (deployless) return null; if (client.chain) { return (0, getChainContractAddress_js_1.getChainContractAddress)({ blockNumber, chain: client.chain, contract: 'multicall3', }); } throw new Error('client chain not configured. multicallAddress is required.'); })(); const chunkedCalls = [[]]; let currentChunk = 0; let currentChunkSize = 0; for (let i = 0; i < contracts.length; i++) { const { abi, address, args, functionName } = contracts[i]; try { const callData = (0, encodeFunctionData_js_1.encodeFunctionData)({ abi, args, functionName }); currentChunkSize += (callData.length - 2) / 2; if (batchSize > 0 && currentChunkSize > batchSize && chunkedCalls[currentChunk].length > 0) { currentChunk++; currentChunkSize = (callData.length - 2) / 2; chunkedCalls[currentChunk] = []; } chunkedCalls[currentChunk] = [ ...chunkedCalls[currentChunk], { allowFailure: true, callData, target: address, }, ]; } catch (err) { const error = (0, getContractError_js_1.getContractError)(err, { abi, address, args, docsPath: '/docs/contract/multicall', functionName, sender: account, }); if (!allowFailure) throw error; chunkedCalls[currentChunk] = [ ...chunkedCalls[currentChunk], { allowFailure: true, callData: '0x', target: address, }, ]; } } const batching = Boolean(client.batch?.multicall); const batches = batching ? chunkedCalls.flatMap((calls) => calls.map((call) => [call])) : chunkedCalls; const aggregate3Results = await Promise.allSettled(batches.map((calls) => { if (batching) return scheduleMulticall(client, { account, authorizationList, batchSize, blockHash, blockNumber, blockOverrides, blockTag, call: calls[0], multicallAddress, requireCanonical, stateOverride, }).then((result) => [result]); return (0, getAction_js_1.getAction)(client, readContract_js_1.readContract, 'readContract')({ ...(multicallAddress === null ? { code: contracts_js_1.multicall3Bytecode } : { address: multicallAddress }), abi: abis_js_1.multicall3Abi, account, args: [calls], authorizationList, blockHash, blockNumber, blockOverrides, blockTag, functionName: 'aggregate3', requireCanonical, stateOverride, }); })); const results = []; for (let i = 0; i < aggregate3Results.length; i++) { const result = aggregate3Results[i]; if (result.status === 'rejected') { if (!allowFailure) throw result.reason; for (let j = 0; j < batches[i].length; j++) { results.push({ status: 'failure', error: result.reason, result: undefined, }); } continue; } const aggregate3Result = result.value; for (let j = 0; j < aggregate3Result.length; j++) { const { returnData, success } = aggregate3Result[j]; const { callData } = batches[i][j]; const { abi, address, functionName, args } = contracts[results.length]; try { if (callData === '0x') throw new abi_js_1.AbiDecodingZeroDataError(); if (!success) throw new contract_js_1.RawContractError({ data: returnData }); const result = (0, decodeFunctionResult_js_1.decodeFunctionResult)({ abi, args, data: returnData, functionName, }); results.push(allowFailure ? { result, status: 'success' } : result); } catch (err) { const error = (0, getContractError_js_1.getContractError)(err, { abi, address, args, docsPath: '/docs/contract/multicall', functionName, }); if (!allowFailure) throw error; results.push({ error, result: undefined, status: 'failure' }); } } } if (results.length !== contracts.length) throw new base_js_1.BaseError('multicall results mismatch'); return results; } async function scheduleMulticall(client, parameters) { const { batchSize, call, multicallAddress, ...rest } = parameters; const { wait = 0 } = typeof client.batch?.multicall === 'object' ? client.batch.multicall : {}; const { schedule } = (0, createBatchScheduler_js_1.createBatchScheduler)({ id: (0, stringify_js_1.stringify)(['multicall', client.uid, batchSize, multicallAddress, rest]), wait, shouldSplitBatch(calls) { if (batchSize === 0) return false; const size = calls.reduce((size, { callData }) => size + (callData.length - 2) / 2, 0); return size > batchSize; }, fn: (calls) => (0, getAction_js_1.getAction)(client, readContract_js_1.readContract, 'readContract')({ ...(multicallAddress === null ? { code: contracts_js_1.multicall3Bytecode } : { address: multicallAddress }), ...rest, abi: abis_js_1.multicall3Abi, args: [calls], functionName: 'aggregate3', }), }); const [result] = await schedule(call); return result; } //# sourceMappingURL=multicall.js.map