Alert Source Discuss
📢 Last Call Standards Track: ERC

ERC-6357: 单合约多重委托调用

允许 EOA 在单个交易中调用智能合约的多个函数

Authors Gavin John (@Pandapip1)
Created 2023-01-18
Last Call Deadline 2023-11-10

摘要

此 EIP 标准化了一个包含单个函数 multicall 的接口,允许 EOA 在单个交易中调用智能合约的多个函数,并且如果任何调用失败,则回滚所有调用。

动机

目前,为了转移若干 ERC-721 NFT,需要提交与正在转移的 NFT 数量相等的交易。这浪费了用户的资金,因为他们需要为每个转移的 NFT 支付 21000 gas 费。

规范

本文档中的关键词 “MUST”,“MUST NOT”,“REQUIRED”,“SHALL”,“SHALL NOT”,“SHOULD”,“SHOULD NOT”,“ RECOMMENDED”,“NOT RECOMMENDED”,“MAY” 和 “OPTIONAL” 应按照 RFC 2119 和 RFC 8174 中的描述进行解释。

实现此 EIP 的合约必须实现以下接口:

pragma solidity ^0.8.0;

interface IMulticall {
    /// @notice           接受 abi 编码的调用数据数组,使用每个调用数据委托调用自身,并返回 abi 编码的结果
    /// @dev              如果任何委托调用回滚,则回滚
    /// @param    data    abi 编码的数据
    /// @returns  results abi 编码的返回值
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results);

    /// @notice           可选。接受 abi 编码的调用数据数组,使用每个调用数据委托调用自身,并返回 abi 编码的结果
    /// @dev              如果任何委托调用回滚,则回滚
    /// @param    data    abi 编码的数据
    /// @param    values  有效的 msg.values。这些加起来必须最多为 msg.value
    /// @returns  results abi 编码的返回值
    function multicallPayable(bytes[] calldata data, uint256[] values) external payable virtual returns (bytes[] memory results);
}

Rationale

multicallPayable 是可选的,因为它并非总是可行,因为 msg.value 的拆分。

向后兼容性

这与大多数现有的 multicall 函数兼容。

测试用例

以下 JavaScript 代码,使用 Ethers 库,应原子地将 amt 单位的 ERC-20 token 转移到 addressAaddressB

await token.multicall(await Promise.all([
    token.interface.encodeFunctionData('transfer', [ addressA, amt ]),
    token.interface.encodeFunctionData('transfer', [ addressB, amt ]),
]));

参考实现

pragma solidity ^0.8.0;

/// 源自 OpenZeppelin 的实现
abstract contract Multicall is IMulticall {
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory returndata) = address(this).delegatecall(data);
            require(success);
            results[i] = returndata;
        }
        return results;
    }
}

安全考虑

只有当合约能够支持 multicallPayable 时才应该使用它。 简单地尝试实现它可能会允许攻击者使用相同的以太多次调用 payable 函数。

版权

版权和相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Gavin John (@Pandapip1), "ERC-6357: 单合约多重委托调用 [DRAFT]," Ethereum Improvement Proposals, no. 6357, January 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6357.