Multicall3 Documentation

repository·main·Indexed 22 days ago

https://github.com/mds1/multicall3

A smart contract designed to aggregate multiple contract reads or writes into a single transaction or JSON-RPC request. Deployed on over 250 chains, it provides methods like aggregate3 for batching reads and supports block property retrieval. The documentation includes implementation examples for Python (Ape), Rust (ethers-rs), TypeScript (viem, ethers.js), and Solidity, as well as deployment and security guidelines.

Tokens
3.5K
Snippets
10
Records
17
Agent score
79%

What's inside Multicall3

  1. Batch contract writes with Multicall3

    main

    Multicall3 can execute multiple state-changing calls in a single transaction.

    Security Warning: This usage is unaudited. While Multicall3 is a stateless contract and should not hold funds, you must follow best practices: never approve Multicall3 to spend your tokens.

    Critical Implementation Details

    1. msg.sender Behavior

    • Calling from an EOA: If you call Multicall3 directly from an Externally Owned Account (EOA), the msg.sender for all sub-calls will be the Multicall3 contract address. Use this only if the identity of the caller does not matter to the target contracts.
    • Calling from a Contract (via CALL): If a contract calls Multicall3 using the CALL opcode, the msg.sender for the sub-calls will be the calling contract.
    • Calling from a Contract (via DELEGATECALL): If a contract calls Multicall3 using DELEGATECALL, the context is preserved. The msg.sender and msg.value for the sub-calls will be the same as the original caller. This is how tools like Gnosis Safe's Transaction Builder batch calls.

    2. msg.value Risks

    Because msg.value does not change during a DELEGATECALL, you must be extremely careful when relying on msg.value within a multicall context.

  2. Gas optimization techniques in Multicall3

    main

    The aggregate3 and aggregate3Value methods utilize several Solidity and Yul optimizations to reduce gas costs. Note that some optimizations may vary in effectiveness depending on the Solidity version and compiler settings (e.g., via-ir).

    Key optimizations include:

    • Loop Optimizations: Caching array lengths, using unchecked blocks for increments, and using prefix increments (++i).
    • Memory Management: Using calldata for function parameters and caching calldata/memory pointers to avoid redundant reads.
    • Assembly/Yul: Using assembly's or() instruction to evaluate success conditions to avoid JUMPI and iszero() instructions.
    • Solidity Modifiers: Using payable modifiers to remove msg.value == 0 checks.
    • Data Minimization: Not returning block data (block number, hash, or timestamp) by default; these must be requested by the caller if needed.
    • Error Handling: Ensuring all revert strings fit within a single 32-byte slot.
  3. Deploy Multicall3 using a pre-signed transaction

    main

    You can deploy Multicall3 to a new chain using a pre-signed transaction.

    Prerequisites and Warnings

    • Gas Metering: You MUST ensure the chain's gas metering is equivalent to the EVM.
    • Gas Limit: The pre-signed transaction has a gas limit of 1,000,000. If the chain requires more than 1M gas to deploy, the transaction will revert.
      • EVM chains typically require exactly 872,776 gas.
      • Arbitrum chains cannot use this method as they require significantly more gas (e.g., ~14.3M gas).
    • Deployer Account: The transaction uses a deployer address 0x05f32b3cc3888453ff71b01135b34ff8e41263f2. You must send at least 0.1 ETH to this address to cover the gas price (100 gwei). It is recommended to send ETH as late as possible to avoid nonce issues.
    • Testing: It is highly recommended to test the transaction on a local network (like a forked anvil instance) before deploying to production.

    Deployment Steps

    1. Copy the signed transaction hex string provided in the documentation.
    2. Use cast to publish the transaction to your target RPC URL.
    # TX is the signed transaction hex string
    # RPC_URL is the RPC URL of the chain you want to deploy to
    cast publish $TX --rpc-url $RPC_URL
  4. Run Multicall3 Rust examples

    main
    To run the Multicall3 Rust examples, which demonstrate batching contract calls (such as DAI contract interactions) alongside ETH balance and coinbase address lookups using ethers-rs, use the standard cargo run command from the example directory.
    cargo run
  5. Set up the environment for Multicall3 examples

    main

    To run the provided Multicall3 examples in TypeScript or Rust, you must ensure that the MAINNET_RPC_URL environment variable is set to a valid Ethereum mainnet RPC URL. This allows the examples to interact with the blockchain.

    export MAINNET_RPC_URL="https://your-ethereum-mainnet-rpc-url"
  6. Develop and test Multicall3 locally

    main

    The repository uses Foundry for development and testing, and git submodules for dependency management.

    To run the test suite:

    1. Clone the repository.
    2. Run forge test.

    Forge will automatically install any missing dependencies during the process.

    forge test
  7. Run Multicall3 examples using ethers.js

    main

    Since ethers.js does not have native Multicall3 support, this example demonstrates how to interact with the Multicall3 contract directly. It uses the aggregate3 method to support reverting calls, which is useful when performing batch operations where some calls might fail.

    The example demonstrates reverse resolving ENS names for a list of addresses.

    To run this example:

    1. Install dependencies: pnpm install
    2. Execute the script: pnpm ts-node ethers.ts

    Note: You can replace pnpm with your preferred node package manager (e.g., npm or yarn). Detailed implementation logic is contained within ethers.ts.

    pnpm install
    pnpm ts-node ethers.ts
  8. Run the Multicall3 Python Example

    main

    The Multicall3 Python example leverages Ape for native Multicall3 support. It demonstrates fetching token balances for multiple users and tokens, including metadata like symbols and decimals, in a single batch.

    To run the example, follow these steps:

    1. Set your Infura project ID as an environment variable: export WEB3_INFURA_PROJECT_ID=<your_project_id>
    2. Create a virtual environment: python3 -m venv venv
    3. Activate the virtual environment: source venv/bin/activate
    4. Install dependencies: pip install -r requirements.txt
    5. Execute the main script: python3 main.py
    export WEB3_INFURA_PROJECT_ID=<your_project_id>
    python3 -m venv venv
    source venv/bin/activate
    pip install -r requirements.txt
    python3 main.py
  9. How to use Multicall3 in Solidity

    main

    Using Multicall3 in Solidity follows the standard pattern of making arbitrary external calls and encoding/decoding the results. For practical implementation patterns, refer to the following resources:

    • Decoding Responses: The testAggregate3 test in Multicall3.t.sol demonstrates how to correctly decode the responses returned by the aggregate3 method.
    • Helper Methods: For common tasks like retrieving token balances, you can use the getTokenBalances helper method provided by forge-std and examine its corresponding tests in StdUtils.t.sol for implementation details.
    // Refer to Multicall3.t.sol for aggregate3 decoding patterns
    // Refer to forge-std's getTokenBalances for balance retrieval patterns
  10. Run Multicall3 examples using viem

    main

    The viem example leverages viem's native Multicall3 support. It demonstrates how to compute Uniswap V3 pool addresses and look up their token balances in a single batch.

    To run this example:

    1. Install dependencies: pnpm install
    2. Execute the script: pnpm ts-node viem.ts

    Note: You can replace pnpm with your preferred node package manager (e.g., npm or yarn). Detailed implementation logic is contained within viem.ts.

    pnpm install
    pnpm ts-node viem.ts
  11. Use Multicall3 with ethers-rs

    main

    The Rust implementation leverages the native Multicall3 support provided by the ethers-rs library. This allows for batching various contract calls into a single request to optimize network usage. For detailed API documentation on how to implement this in your own Rust projects, refer to the ethers::contract::Multicall struct documentation.

    https://docs.rs/ethers/2.0.4/ethers/contract/struct.Multicall.html
  12. Verify Multicall3 contract on block explorers

    main

    To verify the Multicall3 contract source code on a block explorer, use the following settings:

    • Source Code: Use the code from src/Multicall3.sol.
    • Compiler Version: Solidity 0.8.12.
    • Optimization: Enabled with 10000000 runs.
    • Constructor Arguments: None.