以太坊一次打包、千笔到账:批量转账合约实战指南

·

高效、省 gas 的「一对多」解决方案,一次交易发送 ETH 或 ERC20 代币。
核心关键词:以太坊批量转账、gas 优化、多地址发送、智能合约教程、ERC20、批量空投、省钱技巧

为什么需要「一对多」转账?

对空投社群、结算工资、归还质押奖励等场景来说,一笔一笔人工转账既费时也费钱。以太坊原本只支持「一对一」交易,当你循环调用 transfer() 时,gas 费用迅速飙升,还可能触碰单区块 gasLimit。
解决思路:写一个「多地址并发」的智能合约,把所有收款地址和金额作为参数一次性发送,合约内部帮你逐一转账,外部只消耗一次交易费


技术原理

  1. 用户把资金 + 数据发送到批量转账合约的某个函数。
  2. 合约在内部循环执行 transfer()token.transferFrom()
  3. 链上依旧生成多条内部交易,但只需消耗一次外层 gas。gas 便宜时,百元级空投能一次省下可观的 ETH。

可视化流程:

DApp ───────────────────→  BulkTransfer  ───┬──→ 地址1
                 (一次交易)                    ├──→ 地址2
                                             └──→ ……N

👉 三分钟部署自己的「批量空投」合约为空投季省钱


ETH 批量转账合约(示例代码)

核心逻辑:循环调用 Solidity 原生 transfer(),剩余 ETH自动退回发送方。

pragma solidity ^0.8.0;

contract ETHBulkSender {
    event MultiTransfer(
        address indexed from,
        address indexed to,
        uint amount
    );

    function multiSend(
        address[] calldata recipients,
        uint[] calldata amounts
    ) external payable {
        require(recipients.length == amounts.length, "Length mismatch");

        uint total;
        for (uint i = 0; i < recipients.length; i++) {
            total += amounts[i];
            require(payable(recipients[i]).send(amounts[i]), "Send failed");
            emit MultiTransfer(msg.sender, recipients[i], amounts[i]);
        }
        require(msg.value >= total, "Insufficient ETH");
        // 退回多余 ETH
        if (msg.value > total) {
            payable(msg.sender).transfer(msg.value - total);
        }
    }
}

⚠️ 实验性质,请先部署到测试网检验 gas 与回退逻辑。


ERC20 批量转账合约(示例代码)

当空投的是 USDT、DAI 等 ERC20 时,需先 approve 合约额度,再调用 transferFrom 多次。

pragma solidity ^0.8.0;

interface IERC20 {
    function transferFrom(address,address,uint) external returns (bool);
}

contract ERC20BulkSender {
    event MultiERC20Transfer(
        address indexed token,
        address indexed from,
        address indexed to,
        uint amount
    );

    function multiSendERC20(
        IERC20 token,
        address[] calldata recipients,
        uint[] calldata amounts
    ) external {
        require(recipients.length == amounts.length, "Length mismatch");
        
        for (uint i = 0; i < recipients.length; i++) {
            require(
                token.transferFrom(msg.sender, recipients[i], amounts[i]),
                "Transfer failed"
            );
            emit MultiERC20Transfer(
                address(token),
                msg.sender,
                recipients[i],
                amounts[i]
            );
        }
    }
}

别忘了先执行 approve(contractAddress, totalAmount)

👉 查看完整合约源码并一键部署到 Goerli 测试网


节省 gas 的 4 个实战技巧

  1. 合并数组长度检查:一次 require 省一次跳转。
  2. 使用 calldata:大数组走 calldata 可省复制 gas。
  3. 事件压缩:只留 toamount,减少日志大小。
  4. 批量检查 approve:链下脚本预先算好 totalAmountIRY。

示例场景:向 1,000 名社群成员各发 10 USDC。
传统方式需 1000 笔交易,gas ~ 10×1000×21000 = 210,000,000
采用合约批量操作,单次 gas ~ 1,300,000,节省 90%+


构建属于你的批量转账 DApp

步骤 1:编写并测试合约

步骤 2:前端集成

步骤 3:异常处理


常见问题 FAQ

|Q: 直接循环 transferFrom 会不会超限?
A: 是,数组过长会过区块 gasLimit(当前约 30M)。实测 400 笔左右较稳妥,再长可切分批次。

|Q: 退回多的 ETH 不是又烧一次 gas 吗?
A: 最后 transfer 回退金额在合约内部完成,属于子调用,不会触发新的外部交易费。

|Q: 能否同时发送 ETH 与 ERC20?
A: 可以把两部分逻辑写在单个函数,或让用户分两次调用,后者更灵活可并行。

|Q: 是否支持 EIP-1155 NFT 批量空投?
A: ERC1155 自带 safeBatchTransferFrom,同样可封装到统一入口,思路相同。

|Q: 如何防止同一地址重复领代币?
A: 可在 MerkleTree 白名单或链下签名参数上做校验,合约内解析后再转账。

|Q: 有哪些开源库可直接复用?
A: 可参考 OpenZeppelin Multicall 或一些空投脚本,但务必切换最新编译器,避免 pragma 0.4.x 老旧安全问题。


部署前的两大必做检查

  1. 安全审计:切勿把 PoC 代码直接用于生产,检查重放、整数溢出(Solidity 0.8+ 已内置 SafeMath)。
  2. 真实成本测算:用 Hardhat gasReporter 跑 50/200/1000 笔数据,估算主网费用后再决定是否上线。

做好以上步骤,就能在下一个空投活动里“一次打包,千笔到账”!