ERC-6357 single-contract multicall (Single-contract Multi-delegatecall)

Last Call · Other · created 2023-01-18

Standardizes a multicall function on smart contracts so that an externally owned account (EOA) can call multiple functions on the same contract in a single transaction, with all of them reverting if any one fails.

Why it is needed

On Ethereum, a single transaction sent by an externally owned account (EOA) carries by default only one function call. For example, to transfer multiple ERC-721 non-fungible tokens (NFTs), a separate transaction must be sent for each one. Since every transaction carries a base fee of 21000 gas, this cost is paid repeatedly for each NFT transferred, wasting the user's money. Also, when a call is split across multiple transactions, some can succeed while others fail, leaving the system in an inconsistent in-between state. This standard defines a common interface with a single multicall function that takes an array of abi-encoded call data and has the contract call itself for each entry in turn. If any call fails, the entire batch is reverted, guaranteeing that bundling multiple operations into one transaction results in either all succeeding or all being cancelled.

What to implement

In depth

Required function: multicall

Contracts implementing this standard MUST implement the IMulticall interface. The core function, multicall, takes an array of ABI-encoded call data (bytes[] calldata data) and delegatecalls itself with each item. If all calls succeed, it returns an array of each call's ABI-encoded return value (bytes[] memory results). If any single delegatecall fails, the entire function reverts, so the calls bundled into one transaction either all execute or all revert. Because it is a delegatecall to itself, the calls target functions on the same contract.

The optional function multicallPayable and the reason for it

multicallPayable is an OPTIONAL function. It behaves the same way as multicall but is declared payable so it can receive ether alongside the call, and takes an additional argument, values (uint256[]), specifying the effective msg.value to apply to each call. The sum of values must not exceed the msg.value actually sent with the transaction. The original text gives as the reason for not making this function required that splitting msg.value per call is not always implementable. So the required requirements are kept to the minimum of a single function, multicall.

Compatibility with existing multicall implementations and usage example

The original text states that this standard is compatible with most existing multicall functions. As a usage example it includes a test case using the Ethers library. Encoding two transfer calls in the manner token.interface.encodeFunctionData('transfer', [addressA, amt]) and passing them as an array to token.multicall allows amt units of an ERC-20 token to be sent to addressA and addressB atomically, that is, together within a single transaction.

Reference implementation

The original text includes, as a reference implementation, an abstract contract Multicall derived from the OpenZeppelin implementation. multicall creates a results array the same length as the data array, then in a loop executes address(this).delegatecall(data[i]) for each item. It checks the success of each call result with require(success), reverting the entire transaction if any call fails. The return data (returndata) of successful calls is stored in results[i] and the array is returned at the end. The optional function multicallPayable is not included in the reference implementation.

Proposal status and metadata

This proposal was authored by Gavin John (@Pandapip1) and created on 2023-01-18, and belongs to the ERC category of the Standards Track. Discussion took place on the Ethereum Magicians forum. As of the original text, the status is Last Call with a last-call deadline of 2023-11-10. Last Call is the final round of feedback before Final status, so before implementing it is safer to check the proposal's latest status and whether its content has changed.

Things to watch out for

Original ERC · ← ERCs