Multicall - Address.functionDelegateCall

Hi,

I'm trying to implement a very basic multicall contract such as:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Address.sol";
import "hardhat/console.sol";

contract Multicall {
  struct Call {
    address target;
    bytes callData;
  }
  function multicall(Call[] memory calls) public returns (bytes[] memory results) {
    results = new bytes[](calls.length);
    for(uint256 i = 0; i < calls.length; i++) {
      results[i] = Address.functionDelegateCall(address(calls[i].target), calls[i].callData);
    }
    return results;
  }
}

I have a small test script that creates an ERC20 token and grants wallet "Alice" 1000 tokens. I'm then trying to test the multicall by simply passing in a single call to transfer 10 tokens from "Alice" to a second wallet "Bob". I've checked that doing a simple transfer by directly calling transfer on the ERC20 token does indeed work and that the balances of both wallets reflect the transferred amounts. However, when I try to do this through the Multicall contract, I'm getting odd errors:

  • The Call struct that I'm passing multicall is:

    • target: ERC20 token contract
  • callData: transfer(bob.address, "2") (encoded using ethers populateTransaction on the ERC20 contract)

  • The function call reverts, however, saying that the balance of the sender is insufficient. When I check the msg.sender's balance within the multicall function, the returned value is "0" and that the msg.sender is indeed Alice, even though Alice's balance should be however many tokens I granted her at the beginning (minus the minor transfer that I did as checks before the multicall).

I'm clearly missing something fundamentally about how to use multicall / Address.functionDelegateCall. Any suggestions?

Thanks

This type of Multicall contract is not one that you deploy standalone and use to interact with other contracts. This is a module that can be included inside a contract to offer users the ability to batch several of its own function calls.

What you want is more often called Multisend. We don't have such a contract but see for example Gnosis Safe Multisend.