Alert Source Discuss
⚠️ Review Standards Track: ERC

ERC-7144: 具有交易验证步骤的 ERC-20

为转账和授权调用添加新的验证步骤,以在钱包被盗时实现安全措施。

Authors Eduard López i Fina (@eduardfina)
Created 2023-05-07
Requires EIP-20

摘要

本标准是 ERC-20 的扩展。它定义了新的验证功能以避免钱包被盗:每个 transferapprove 都将被锁定,等待验证。

动机

区块链的力量同时也是它的弱点:让用户对其数据负全部责任。

目前存在许多 Token 被盗的情况,而当前的 Token 防盗方案(例如将 Token 转移到冷钱包)使得 Token 使用起来不方便。

在每次 transferapprove 之前都有一个验证步骤,这将使智能合约开发者有机会创建安全的 Token 防盗方案。

一个实现示例是一个系统,其中验证器地址负责验证所有智能合约交易。

该地址将连接到一个 dApp,用户可以在其中查看其 Token 的验证请求并接受正确的请求。

仅授予该地址验证交易的权限将使系统更加安全,窃取 Token 的小偷必须同时拥有用户的地址和验证器地址。

规范

本文档中的关键词“必须”,“禁止”,“必需”,“应”,“不应”,“应该”,“不应该”,“推荐”,“可以”和“可选”应按照 RFC 2119 中的描述进行解释。

符合 ERC-20 规范的合约可以实现此 EIP。

所有更改 Token 所有权的操作,如 transfer / transferFrom,应创建一个等待验证的 TransferValidation 并发出 ValidateTransfer,并且不应转移 Token。

所有启用批准以管理 Token 的操作,如 approve,应创建一个等待验证的 ApprovalValidation 并发出 ValidateApproval,并且不应启用批准。

当转账由已批准的帐户而不是所有者调用时,必须直接执行,无需验证。这是为了适应所有需要批准才能直接移动你的 Token 的当前项目。

验证 TransferValidationApprovalValidation 时,有效字段必须设置为 true,并且不得再次验证。

验证 TransferValidation 的操作应更改 Token 的所有权。

验证 ApprovalValidation 的操作应启用批准。

合约接口

interface IERC7144 {

    struct TransferValidation {
        // 所有者的地址。
        address from;
        // 接收者的地址。
        address to;
        // Token 数量。
        uint256 amount;
        // 是否是有效的转移。
        bool valid;
    }

    struct ApprovalValidation {
        // 所有者的地址。
        address owner;
        // 消费者的地址。
        address spender;
        // 批准的 Token 数量。
        uint256 amount;
        // 是否是有效的批准。
        bool valid;
    }

    /**
     * @dev 当请求新的转移验证时发出。
     */
    event ValidateTransfer(address indexed from, address indexed to, uint256 amount, uint256 indexed transferValidationId);

    /**
    * @dev 当请求新的批准验证时发出。
    */
    event ValidateApproval(address indexed owner, address indexed spender, uint256 amount, uint256 indexed approvalValidationId);

    /**
     * @dev 如果此合约是验证器 ERC20,则返回 true。
     */
    function isValidatorContract() external view returns (bool);

    /**
     * @dev 使用转移 ID 返回转移验证结构体。
     *
     */
    function transferValidation(uint256 transferId) external view returns (TransferValidation memory);

    /**
    * @dev 使用批准 ID 返回批准验证结构体。
    *
    */
    function approvalValidation(uint256 approvalId) external view returns (ApprovalValidation memory);

    /**
     * @dev 返回创建的转移验证的总量。
     *
     */
    function totalTransferValidations() external view returns (uint256);

    /**
     * @dev 返回创建的转移验证的总量。
     *
     */
    function totalApprovalValidations() external view returns (uint256);
}

isValidatorContract() 函数必须实现为 public

transferValidation(uint256 transferId) 函数可以实现为 publicexternal

approvalValidation(uint256 approveId) 函数可以实现为 publicexternal

totalTransferValidations() 函数可以实现为 pureview

totalApprovalValidations() 函数可以实现为 pureview

原理

普遍性

该标准仅定义了验证函数,但未定义应如何使用它们。它将验证定义为内部验证,并让用户决定如何管理它们。

一个例子是可以有一个连接到 dApp 的地址验证器,以便用户可以管理他们的验证。

该验证器可以用于所有 Token,也可以仅用于某些用户。

它也可以用作现有 ERC-20 的包装智能合约,允许与现有 Token 进行 1/1 转换。

扩展性

该标准仅定义了验证函数,但未定义必须使用哪种系统对其进行验证。第三方协议可以根据需要定义如何调用这些函数。

向后兼容性

本标准是 ERC-20 的扩展,与除 transfer / transferFrom / approve 之外的所有操作兼容。

这些操作将被覆盖以创建验证请求,而不是转移 Token 或启用批准。

参考实现

// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./IERC7144.sol";

/**
 * @dev ERC7144 的实现
 */
contract ERC7144 is IERC7144, ERC20 {

    // 从转移 ID 到转移验证的映射
    mapping(uint256 => TransferValidation) private _transferValidations;

    // 从批准 ID 到批准验证的映射
    mapping(uint256 => ApprovalValidation) private _approvalValidations;

    // 转移验证的总数
    uint256 private _totalTransferValidations;

    // 批准验证的总数
    uint256 private _totalApprovalValidations;

    /**
     * @dev 通过设置 Token 集合的 `name` 和 `symbol` 来初始化合约。
     */
    constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_){
    }

    /**
    * @dev 如果此合约是验证器 ERC721,则返回 true。
    */
    function isValidatorContract() public pure returns (bool) {
        return true;
    }

    /**
     * @dev 使用转移 ID 返回转移验证结构体。
     *
     */
    function transferValidation(uint256 transferId) public view override returns (TransferValidation memory) {
        require(transferId < _totalTransferValidations, "ERC7144: 无效的转移 ID");
        TransferValidation memory v = _transferValidation(transferId);

        return v;
    }

    /**
     * @dev 使用批准 ID 返回批准验证结构体。
     *
     */
    function approvalValidation(uint256 approvalId) public view override returns (ApprovalValidation memory) {
        require(approvalId < _totalApprovalValidations, "ERC7144: 无效的批准 ID");
        ApprovalValidation memory v = _approvalValidation(approvalId);

        return v;
    }

    /**
     * @dev 返回创建的转移验证的总量。
     *
     */
    function totalTransferValidations() public view override returns (uint256) {
        return _totalTransferValidations;
    }

    /**
     * @dev 返回创建的批准验证的总量。
     *
     */
    function totalApprovalValidations() public view override returns (uint256) {
        return _totalApprovalValidations;
    }

    /**
     * @dev 返回 `transferId` 的转移验证。如果转移不存在,则不会恢复
     */
    function _transferValidation(uint256 transferId) internal view virtual returns (TransferValidation memory) {
        return _transferValidations[transferId];
    }

    /**
     * @dev 返回 `approvalId` 的批准验证。如果转移不存在,则不会恢复
     */
    function _approvalValidation(uint256 approvalId) internal view virtual returns (ApprovalValidation memory) {
        return _approvalValidations[approvalId];
    }

    /**
     * @dev 使用转移 ID 验证转移。
     *
     */
    function _validateTransfer(uint256 transferId) internal virtual {
        TransferValidation memory v = transferValidation(transferId);
        require(!v.valid, "ERC721V: 转移已验证");

        super._transfer(v.from, v.to, v.amount);

        _transferValidations[transferId].valid = true;
    }

    /**
     * @dev 使用批准 ID 验证批准。
     *
     */
    function _validateApproval(uint256 approvalId) internal virtual {
        ApprovalValidation memory v = approvalValidation(approvalId);
        require(!v.valid, "ERC7144: 批准已验证");

        super._approve(v.owner, v.spender, v.amount);

        _approvalValidations[approvalId].valid = true;
    }

    /**
     * @dev 创建从 `from` 到 `to` 的 `tokenId` 的转移请求。
     *
     * 要求:
     *
     * - `from` 不能为零地址。
     * - `to` 不能为零地址。
     *
     * 发出 {ValidateTransfer} 事件。
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        require(from != address(0), "ERC7144: 从零地址转移");
        require(to != address(0), "ERC7144: 转移到零地址");

        if(_msgSender() == from) {
            TransferValidation memory v;

            v.from = from;
            v.to = to;
            v.amount = amount;

            _transferValidations[_totalTransferValidations] = v;

            emit ValidateTransfer(from, to, amount, _totalTransferValidations);

            _totalTransferValidations++;
        } else {
            super._transfer(from, to, amount);
        }
    }

    /**
     * @dev 创建从 `owner` 操作 `amount` 的批准请求
     *
     * 发出 {ValidateApproval} 事件。
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual override {
        require(owner != address(0), "ERC7144: 从零地址批准");
        require(spender != address(0), "ERC7144: 批准到零地址");

        ApprovalValidation memory v;

        v.owner = owner;
        v.spender = spender;
        v.amount = amount;

        _approvalValidations[_totalApprovalValidations] = v;

        emit ValidateApproval(v.owner, spender, amount, _totalApprovalValidations);

        _totalApprovalValidations++;
    }
}

安全注意事项

正如规范中定义的那样,更改 Token 所有权或启用批准以管理 Token 的操作应创建一个等待验证的 TransferValidationApprovalValidation,并且不应转移 Token 或启用批准。

考虑到这一前提,负责验证 TransferValidationApprovalValidation 的操作必须受到应用系统所需的最大安全性的保护。

例如,一个有效的系统是存在一个负责验证交易的验证器地址。

再举一个例子,每个用户都可以选择他的验证器地址的系统也是正确的。

在任何情况下,安全性的重要性在于,未经选定系统的许可,任何地址都无法验证 TransferValidationApprovalValidation

版权

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

Citation

Please cite this document as:

Eduard López i Fina (@eduardfina), "ERC-7144: 具有交易验证步骤的 ERC-20 [DRAFT]," Ethereum Improvement Proposals, no. 7144, May 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7144.