Multicaller
Efficiently call multiple contracts in a single transaction.
Enables "forwarding" of msg.sender
to the contracts called.
Deployments
Please open an issue if you need help to deploy to an EVM chain of your choice.
Contracts
src
├─ Multicaller.sol — "The multicaller contract"
├─ MulticallerWithSender.sol — "The multicaller with sender contract"
└─ LibMulticaller.sol — "Library to read the multicaller with sender contract"
Installation
You can use the src/LibMulticaller.sol
library in your contracts to query the multicaller with sender contract efficiently.
To install with Foundry:
forge install vectorized/multicaller
To install with Hardhat or Truffle:
npm install multicaller
API
Multicaller
aggregate
function aggregate(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
external
payable
returns (bytes[] memory)
Aggregates multiple calls in a single transaction.
MulticallerWithSender
aggregateWithSender
function aggregateWithSender(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
external
payable
returns (bytes[] memory)
Aggregates multiple calls in a single transaction.
This method will set the multicaller sender to the msg.sender
temporarily for the span of its execution.
This method does not support reentrancy.
receive
receive() external payable
Returns the address that called aggregateWithSender
on the contract.
The value is always the zero address outside a transaction.
LibMulticaller
Library to read the multicaller with sender contract.
multicallerSender
function multicallerSender() internal view returns (address)
Returns the address that called aggregateWithSender
on the multicaller with sender contract.
sender
function sender() internal view returns (address result)
Returns the address that called aggregateWithSender
on the multicaller with sender contract, if msg.sender
is the multicaller with sender contract.
Otherwise, returns msg.sender
.
Design
The contracts are designed with a priority on efficiency and minimalism.
-
Multiple input calldata arrays instead of an array of structs for more compact calldata encoding.
-
Omission of utility functions like getBlockNumber
for more efficient function dispatch. If you need those functions, just add those functions into your contract, or read them off a separate utility contract like MakerDao's Multicall.
Use Cases
For the following, the contracts called must read the msg.sender
from the multicaller contract.
The LibMulticaller
library can be used for efficient reading.
Example use cases:
-
Calling access role restricted functions across multiple contracts in a single transaction.
-
Approving a trusted operator contract to transfer tokens, and doing the transfer in a single transaction.
Warning This will skip the approval warning on wallets. To mitigate phishing risk, you should make a custom approval function that validates a time-limited EIP-712 signature signed by the msg.sender
.
Safety
We do not give any warranties and will not be liable for any loss incurred through any use of this codebase.
Acknowledgments
Multicaller is inspired by and directly modified from:
This project is a public good initiative of sound.xyz and Solady.
We would like to thank our reviewers and contributors for their invaluable help.