Skip to content

Instantly share code, notes, and snippets.

@bergeron
Last active January 4, 2024 04:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bergeron/938c8a317e2a3df21ed63587c1d0a7bc to your computer and use it in GitHub Desktop.
Save bergeron/938c8a317e2a3df21ed63587c1d0a7bc to your computer and use it in GitHub Desktop.
Multicall with fallback
// Given an array of contract calls, executes them via a single multicall RPC
// request if the chain supports it. And multiple parallel RPC calls otherwise.
const { Contract } = require('@ethersproject/contracts');
const { StaticJsonRpcProvider } = require('@ethersproject/providers');
const provider = new StaticJsonRpcProvider('https://sepolia.infura.io/v3/<OMITTED>')
const multicallContract = new Contract('0xcA11bde05977b3631167028862bE2a173976CA11', [
{
"name": "aggregate3", "type": "function", "stateMutability": "payable",
"inputs": [
{
"internalType": "struct Multicall3.Call3[]", "name": "calls", "type": "tuple[]",
"components": [
{ "internalType": "address", "name": "target", "type": "address" },
{ "internalType": "bool", "name": "allowFailure", "type": "bool" },
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
]
}
],
"outputs": [
{
"internalType": "struct Multicall3.Result[]", "name": "returnData", "type": "tuple[]",
"components": [
{ "internalType": "bool", "name": "success", "type": "bool" },
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
]
}
]
}
], provider);
const erc721 = new Contract('0xef2a38eb017563a3eafe5bb6d37cf41e4b9c091d', [
{
"name": "name", "type": "function", "stateMutability": "view", "inputs": [],
"outputs": [{ "internalType": "string", "name": "", "type": "string" }]
},
{
"name": "symbol", "type": "function", "stateMutability": "view", "inputs": [],
"outputs": [{ "internalType": "string", "name": "", "type": "string" }]
},
{
"name": "tokenURI", "type": "function", "stateMutability": "view",
"inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }],
"outputs": [{ "internalType": "string", "name": "", "type": "string" }]
}
], provider);
multicallSupportedChains = [1]; // 100+ supported
const multi = (calls, chainId) =>
multicallSupportedChains.includes(chainId)
? multicall(calls)
: fallback(calls);
async function multicall(calls) {
const calldata = calls.map(call => ({
target: call.contract.address,
allowFailure: true,
callData: call.contract.interface.encodeFunctionData(
call.contract.interface.functions[call.functionSignature],
call.arguments)
}));
return multicallContract.callStatic.aggregate3(calldata).then(
result => result.map((r, i) => ({
success: r.success,
value: (r.success ?
calls[i].contract.interface.decodeFunctionResult(
calls[i].functionSignature, r.returnData)[0] : undefined)
})));
}
const fallback = calls =>
Promise.allSettled(calls.map(call =>
call.contract[call.functionSignature](...call.arguments))
).then(r => r.map(p => ({ success: p.status === 'fulfilled', value: p.value })));
(async function main() {
const calls = [
{
contract: erc721,
functionSignature: 'name()',
arguments: []
},
{
contract: erc721,
functionSignature: 'symbol()',
arguments: []
},
{
contract: erc721,
functionSignature: 'tokenURI(uint256)',
arguments: [1]
}
];
console.log(await multi(calls, 1));
console.log(await multi(calls, 2));
})().catch(e => { console.error(e) })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment