viem
Version:
194 lines • 8.04 kB
JavaScript
;
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