// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Contract that enables a single call to call multiple methods on itself. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol) /// /// WARNING: /// This implementation is NOT to be used with ERC2771 out-of-the-box. /// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure /// This also applies to potentially other ERCs / patterns appending to the back of calldata. /// /// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context. /// Moreover, it is infeasible and inefficient for us to add checks and mitigations /// for all possible ERC / patterns appending to the back of calldata. /// /// We would highly recommend using an alternative pattern such as /// https://github.com/Vectorized/multicaller /// which is more flexible, futureproof, and safer by default. abstract contract Multicallable { /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`, /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`. /// If any of the `DELEGATECALL`s reverts, the entire context is reverted, /// and the error is bubbled up. /// /// This function is deliberately made non-payable to guard against double-spending. /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong) /// /// For efficiency, this function will directly return the results, terminating the context. /// If called internally, it must be called at the end of a function /// that returns `(bytes[] memory)`. function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) { assembly { mstore(0x00, 0x20) mstore(0x20, data.length) // Store `data.length` into `results`. // Early return if no data. if iszero(data.length) { return(0x00, 0x40) } let results := 0x40 // `shl` 5 is equivalent to multiplying by 0x20. let end := shl(5, data.length) // Copy the offsets from calldata into memory. calldatacopy(0x40, data.offset, end) // Offset into `results`. let resultsOffset := end // Pointer to the end of `results`. end := add(results, end) for {} 1 {} { // The offset of the current bytes in the calldata. let o := add(data.offset, mload(results)) let m := add(resultsOffset, 0x40) // Copy the current bytes from calldata to the memory. calldatacopy( m, add(o, 0x20), // The offset of the current bytes' bytes. calldataload(o) // The length of the current bytes. ) if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) { // Bubble up the revert if the delegatecall reverts. returndatacopy(0x00, 0x00, returndatasize()) revert(0x00, returndatasize()) } // Append the current `resultsOffset` into `results`. mstore(results, resultsOffset) results := add(results, 0x20) // Append the `returndatasize()`, and the return data. mstore(m, returndatasize()) returndatacopy(add(m, 0x20), 0x00, returndatasize()) // Advance the `resultsOffset` by `returndatasize() + 0x20`, // rounded up to the next multiple of 32. resultsOffset := and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0) if iszero(lt(results, end)) { break } } return(0x00, add(resultsOffset, 0x40)) } } }