BatchTransfer

Description:

Smart contract deployed on Ethereum with Factory features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/BatchTransfer.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title BatchTransfer
 * @notice Gas-efficient batch transfer contract for ERC721 tokens with access control
 * @dev Allows ONLY the authorized caller to transfer multiple ERC721 tokens in a single transaction
 *
 * Security Model:
 * 1. Contract is deployed with a single authorized caller address (immutable)
 * 2. Safe approves this contract: setApprovalForAll(BatchTransfer, true)
 * 3. ONLY the authorized caller can execute batchTransferFrom
 * 4. Any other address attempting to call will revert with "Unauthorized"
 *
 * Usage:
 * 1. Deploy with authorizedCaller = EOA operator address
 * 2. Safe approves this contract via setApprovalForAll
 * 3. EOA operator calls batchTransferFrom to transfer tokens
 *
 * Gas optimizations:
 * - Minimal storage usage (one immutable address)
 * - Unchecked iterator increments
 * - No events (rely on ERC721 Transfer events)
 * - Direct interface calls (no try/catch overhead)
 */
interface IERC721 {
    function transferFrom(address from, address to, uint256 tokenId) external;
}

contract BatchTransfer {
    /// @notice The only address authorized to call batch transfer functions
    /// @dev Immutable for gas efficiency and security - cannot be changed after deployment
    address public immutable authorizedCaller;

    /// @notice Emitted when contract is deployed
    /// @param caller The authorized caller address
    event BatchTransferDeployed(address indexed caller);

    /// @notice Reverts when msg.sender is not the authorized caller
    error Unauthorized();

    /**
     * @notice Deploy the BatchTransfer contract with a specific authorized caller
     * @param _authorizedCaller The address that will be allowed to call batch transfer functions
     * @dev The authorized caller cannot be changed after deployment
     */
    constructor(address _authorizedCaller) {
        require(_authorizedCaller != address(0), "Zero address not allowed");
        authorizedCaller = _authorizedCaller;
        emit BatchTransferDeployed(_authorizedCaller);
    }

    /**
     * @notice Modifier to restrict function access to authorized caller only
     */
    modifier onlyAuthorized() {
        if (msg.sender != authorizedCaller) revert Unauthorized();
        _;
    }
    /**
     * @notice Batch transfer ERC721 tokens from one address to another
     * @param token The ERC721 token contract address
     * @param from The address to transfer from (must have approved this contract)
     * @param to The address to transfer to
     * @param tokenIds Array of token IDs to transfer
     *
     * @dev Reverts if:
     * - Caller is not the authorized caller (Unauthorized)
     * - Any tokenId is not owned by `from`
     * - This contract is not approved by `from`
     * - Any individual transfer fails
     *
     * All transfers are atomic - if one fails, all revert.
     */
    function batchTransferFrom(
        address token,
        address from,
        address to,
        uint256[] calldata tokenIds
    ) external onlyAuthorized {
        uint256 length = tokenIds.length;

        for (uint256 i; i < length;) {
            IERC721(token).transferFrom(from, to, tokenIds[i]);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Batch transfer multiple ERC721 collections in one transaction
     * @param tokens Array of ERC721 token contract addresses
     * @param from The address to transfer from (must have approved this contract for all tokens)
     * @param to The address to transfer to
     * @param tokenIds Array of arrays - tokenIds[i] contains IDs for tokens[i]
     *
     * @dev Useful for transferring tokens from multiple collections at once
     * Requires tokens.length == tokenIds.length
     * Reverts if caller is not the authorized caller (Unauthorized)
     */
    function batchTransferFromMultiple(
        address[] calldata tokens,
        address from,
        address to,
        uint256[][] calldata tokenIds
    ) external onlyAuthorized {
        uint256 length = tokens.length;
        require(length == tokenIds.length, "Length mismatch");

        for (uint256 i; i < length;) {
            uint256 tokenIdsLength = tokenIds[i].length;

            for (uint256 j; j < tokenIdsLength;) {
                IERC721(tokens[i]).transferFrom(from, to, tokenIds[i][j]);

                unchecked {
                    ++j;
                }
            }

            unchecked {
                ++i;
            }
        }
    }
}
"
    }
  },
  "settings": {
    "remappings": [
      "forge-std/=lib/forge-std/src/",
      "solady/=lib/solady/src/"
    ],
    "optimizer": {
      "enabled": false,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": false
  }
}}

Tags:
Factory|addr:0xdc7687d7df0efdb4d20db733ddee649f5be08a2c|verified:true|block:23638546|tx:0x1828a773c360c29ba972a8934e2ba06ba1f67f7da800629143d0e65825dfd6c1|first_check:1761303999

Submitted on: 2025-10-24 13:06:42

Comments

Log in to comment.

No comments yet.