Skip to content

Multicall

Multicall has two primary use cases: enabling the ability to read off of multiple contracts with a single request and execute multiple state-changing transactions in a single request.

Deployment

The multicall contract was deployed on SKALE Network on several chains and can be found under the address: 0xcA11bde05977b3631167028862bE2a173976CA11

The deployment is currently available on the following SKALE Chains.

Chains with Multicall

ChainMainnetTestnet
Europa
Calypso
Nebula
Titan

Implementation Example

const [ signer ] = await hre.ethers.getSigners();
const tokens = [
"0x4f2040FEaAD8b19E254006153E7BBB0674F4DaE3", // token0
"0x7464f84F2c6686b6365a373E8FB7c15741e07275",
"0x677b7FfE9435735F2b1CEE325527e9268CD011F2",
"0xa0aC0f7A8726351beF090B6966aA77E07cF0Fb15",
];
const multicallABI = [{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256"},{"type":"bytes[]","name":"returnData","internalType":"bytes[]"}],"name":"aggregate","inputs":[{"type":"tuple[]","name":"calls","internalType":"struct Multicall3.Call[]","components":[{"type":"address","name":"target","internalType":"address"},{"type":"bytes","name":"callData","internalType":"bytes"}]}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"tuple[]","name":"returnData","internalType":"struct Multicall3.Result[]","components":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes","name":"returnData","internalType":"bytes"}]}],"name":"aggregate3","inputs":[{"type":"tuple[]","name":"calls","internalType":"struct Multicall3.Call3[]","components":[{"type":"address","name":"target","internalType":"address"},{"type":"bool","name":"allowFailure","internalType":"bool"},{"type":"bytes","name":"callData","internalType":"bytes"}]}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"tuple[]","name":"returnData","internalType":"struct Multicall3.Result[]","components":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes","name":"returnData","internalType":"bytes"}]}],"name":"aggregate3Value","inputs":[{"type":"tuple[]","name":"calls","internalType":"struct Multicall3.Call3Value[]","components":[{"type":"address","name":"target","internalType":"address"},{"type":"bool","name":"allowFailure","internalType":"bool"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"bytes","name":"callData","internalType":"bytes"}]}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256"},{"type":"bytes32","name":"blockHash","internalType":"bytes32"},{"type":"tuple[]","name":"returnData","internalType":"struct Multicall3.Result[]","components":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes","name":"returnData","internalType":"bytes"}]}],"name":"blockAndAggregate","inputs":[{"type":"tuple[]","name":"calls","internalType":"struct Multicall3.Call[]","components":[{"type":"address","name":"target","internalType":"address"},{"type":"bytes","name":"callData","internalType":"bytes"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"basefee","internalType":"uint256"}],"name":"getBasefee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"blockHash","internalType":"bytes32"}],"name":"getBlockHash","inputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256"}],"name":"getBlockNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"chainid","internalType":"uint256"}],"name":"getChainId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"coinbase","internalType":"address"}],"name":"getCurrentBlockCoinbase","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"difficulty","internalType":"uint256"}],"name":"getCurrentBlockDifficulty","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"gaslimit","internalType":"uint256"}],"name":"getCurrentBlockGasLimit","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"timestamp","internalType":"uint256"}],"name":"getCurrentBlockTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"balance","internalType":"uint256"}],"name":"getEthBalance","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"blockHash","internalType":"bytes32"}],"name":"getLastBlockHash","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"tuple[]","name":"returnData","internalType":"struct Multicall3.Result[]","components":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes","name":"returnData","internalType":"bytes"}]}],"name":"tryAggregate","inputs":[{"type":"bool","name":"requireSuccess","internalType":"bool"},{"type":"tuple[]","name":"calls","internalType":"struct Multicall3.Call[]","components":[{"type":"address","name":"target","internalType":"address"},{"type":"bytes","name":"callData","internalType":"bytes"}]}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256"},{"type":"bytes32","name":"blockHash","internalType":"bytes32"},{"type":"tuple[]","name":"returnData","internalType":"struct Multicall3.Result[]","components":[{"type":"bool","name":"success","internalType":"bool"},{"type":"bytes","name":"returnData","internalType":"bytes"}]}],"name":"tryBlockAndAggregate","inputs":[{"type":"bool","name":"requireSuccess","internalType":"bool"},{"type":"tuple[]","name":"calls","internalType":"struct Multicall3.Call[]","components":[{"type":"address","name":"target","internalType":"address"},{"type":"bytes","name":"callData","internalType":"bytes"}]}]}];
const multicallAddress = "0xcA11bde05977b3631167028862bE2a173976CA11";
const multicall = new hre.ethers.Contract(multicallAddress, multicallABI, signer);
const contracts = tokens.map((addr: string) => new hre.ethers.Contract(addr, abi, signer));
const balances = await Promise.all(contracts.map((contract) => {
return contract.balanceOf(signer.address);
}));
const allowances = await Promise.all(contracts.map((contract) => {
return contract.allowance(signer.address, multicallAddress);
}));
for (let i = 0; i < contracts.length; i++) {
if (allowances[i].toString() !== balances[i].toString()) {
await contracts[i].approve(multicallAddress, balances[i]);
}
}
/** Creating Random Wallets for Distribution **/
const wallets = [
new hre.ethers.Wallet.createRandom(),
new hre.ethers.Wallet.createRandom(),
new hre.ethers.Wallet.createRandom(),
new hre.ethers.Wallet.createRandom(),
new hre.ethers.Wallet.createRandom(),
];
let distributionCalls = [];
contracts.forEach((contract, index) => {
wallets.forEach((wallet, index2) => {
distributionCalls.push([
contract.address,
false,
contract.interface.encodeFunctionData("transferFrom", [signer.address, wallet.address, 1])
]);
});
});
const results = await multicall.aggregate3(distributionCalls);

Additional Multicall Documentation

Click here for the official documentation.