Bundler3

0x6478e9393d4c5bb4d53ee881d1de78786a0344a6

Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
1,547 bytes
ABI entries
8
2 read · 2 write
License
none

Contract information

Address
0x6478e9393d4c5bb4d53ee881d1de78786a0344a6
Chain
Robinhood Chain (4663)
Compiler
v0.8.28+commit.7893614a
Optimization
Enabled
Creation tx
0xe1927e1ab3…5b64f74d20

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (2)

initiator()address
reenterHash()bytes32

ABI

[
  {
    "inputs": [],
    "name": "AlreadyInitiated",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EmptyBundle",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "IncorrectReenterHash",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MissingExpectedReenter",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "initiator",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "to",
            "type": "address"
          },
          {
            "internalType": "bytes",
            "name": "data",
            "type": "bytes"
          },
          {
            "internalType": "uint256",
            "name": "value",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "skipRevert",
            "type": "bool"
          },
          {
            "internalType": "bytes32",
            "name": "callbackHash",
            "type": "bytes32"
          }
        ],
        "internalType": "struct Call[]",
        "name": "bundle",
        "type": "tuple[]"
      }
    ],
    "name": "multicall",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "to",
            "type": "address"
          },
          {
            "internalType": "bytes",
            "name": "data",
            "type": "bytes"
          },
          {
            "internalType": "uint256",
            "name": "value",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "skipRevert",
            "type": "bool"
          },
          {
            "internalType": "bytes32",
            "name": "callbackHash",
            "type": "bytes32"
          }
        ],
        "internalType": "struct Call[]",
        "name": "bundle",
        "type": "tuple[]"
      }
    ],
    "name": "reenter",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "reenterHash",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import {IBundler3, Call} from "./interfaces/IBundler3.sol";

import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {UtilsLib} from "./libraries/UtilsLib.sol";

/// @custom:security-contact security@morpho.org
/// @notice Enables batching multiple calls in a single one.
/// @notice Transiently stores the initiator of the multicall.
/// @notice Can be reentered by the last unreturned callee with known data.
/// @dev Anybody can do arbitrary calls with this contract, so it should not be approved/authorized anywhere.
contract Bundler3 is IBundler3 {
    /* TRANSIENT STORAGE */

    /// @notice The initiator of the multicall transaction.
    address public transient initiator;

    /// @notice Hash of the concatenation of the sender and the hash of the calldata of the next call to `reenter`.
    bytes32 public transient reenterHash;

    /* EXTERNAL */

    /// @notice Executes a sequence of calls.
    /// @dev Locks the initiator so that the sender can be identified by other contracts.
    /// @param bundle The ordered array of calldata to execute.
    function multicall(Call[] calldata bundle) external payable {
        require(initiator == address(0), ErrorsLib.AlreadyInitiated());

        initiator = msg.sender;

        _multicall(bundle);

        initiator = address(0);
    }

    /// @notice Executes a sequence of calls.
    /// @dev Useful during callbacks.
    /// @dev Can only be called by the last unreturned callee with known data.
    /// @param bundle The ordered array of calldata to execute.
    function reenter(Call[] calldata bundle) external {
        require(
            reenterHash == keccak256(bytes.concat(bytes20(msg.sender), keccak256(msg.data[4:]))),
            ErrorsLib.IncorrectReenterHash()
        );
        _multicall(bundle);
        // After _multicall the value of reenterHash is bytes32(0).
    }

    /* INTERNAL */

    /// @notice Executes a sequence of calls.
    function _multicall(Call[] calldata bundle) internal {
        require(bundle.length > 0, ErrorsLib.EmptyBundle());

        for (uint256 i; i < bundle.length; ++i) {
            address to = bundle[i].to;
            bytes32 callbackHash = bundle[i].callbackHash;
            if (callbackHash == bytes32(0)) reenterHash = bytes32(0);
            else reenterHash = keccak256(bytes.concat(bytes20(to), callbackHash));

            (bool success, bytes memory returnData) = to.call{value: bundle[i].value}(bundle[i].data);
            if (!bundle[i].skipRevert && !success) UtilsLib.lowLevelRevert(returnData);

            require(reenterHash == bytes32(0), ErrorsLib.MissingExpectedReenter());
        }
    }
}
Chain explorer3440msChain node76ms