Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-7598: 使用合约签名进行签名转账

一个扩展 ERC-3009 授权转账以支持 ERC-1271 签名验证的 ERC。

Authors Yvonne Zhang (@yvonnezhangc), Aloysius Chan (@circle-aloychan)
Created 2024-01-15
Discussion Link https://ethereum-magicians.org/t/add-erc-contract-signature-validation-extension-for-erc-3009-transfer-with-authorization/18158
Requires EIP-1271, EIP-3009

EIP: ERC-3009 授权转账的合约签名验证扩展

摘要

本提案旨在扩展现有的 ERC-3009 标准“授权转账”的功能,以支持由智能合约钱包发起的转账操作。

动机

现有的 ERC-3009 标准支持使用 ECDSA 签名进行资产转移。然而,随着智能合约钱包在生态系统中变得越来越普遍,当前的标准已不再足够。

本提案旨在通过使用 ERC-1271 中定义的智能合约钱包签名验证来扩展 ERC-3009,从而增强标准可用性和可组合性。通过合并此扩展,用户将可以在管理其资产方面拥有更大的灵活性,同时确保安全的授权过程。

规范

鉴于 ERC-3009 中定义的初始规范,以下事件和接口必须仍然存在。

  • 事件 AuthorizationUsed
  • 常量 TRANSFER_WITH_AUTHORIZATION_TYPEHASHRECEIVE_WITH_AUTHORIZATION_TYPEHASH
  • 查看函数接口 authorizationState(address authorizer, bytes32 nonce)

此外,必须添加以下接口才能符合标准:

/**
 * @notice Execute a transfer with a signed authorization
 * @param from          Payer's address (Authorizer)
 * @param to            Payee's address
 * @param value         Amount to be transferred
 * @param validAfter    The time after which this is valid (unix time)
 * @param validBefore   The time before which this is valid (unix time)
 * @param nonce         Unique nonce
 * @param signature     Unstructured bytes signature signed by an EOA wallet or a contract wallet
 */
function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    bytes memory signature
) external;

/**
 * @notice Receive a transfer with a signed authorization from the payer
 * @dev This has an additional check to ensure that the payee's address matches
 * the caller of this function to prevent front-running attacks. (See security
 * considerations)
 * @param from          Payer's address (Authorizer)
 * @param to            Payee's address
 * @param value         Amount to be transferred
 * @param validAfter    The time after which this is valid (unix time)
 * @param validBefore   The time before which this is valid (unix time)
 * @param nonce         Unique nonce
 * @param signature     Unstructured bytes signature signed by an EOA wallet or a contract wallet
 */
function receiveWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    bytes memory signature
) external;

可选:

ERC-3009 规范中定义的 AuthorizationCanceled 事件和 CANCEL_AUTHORIZATION_TYPEHASH 常量。

/**
 * @notice Attempt to cancel an authorization
 * @param authorizer    Authorizer's address
 * @param nonce         Nonce of the authorization
 * @param signature     Unstructured bytes signature signed by an EOA wallet or a contract wallet
 */
function cancelAuthorization(
    address authorizer,
    bytes32 nonce,
    bytes memory signature
) external;

理由

通过替换现有的 V、R、S 签名验证方案并引入对非结构化字节输入的支持,合约开发者可以使用统一的接口来验证来自 EOA 和 SC 钱包的签名。这允许利用适合钱包类型的不同签名方案和算法,从而为智能合约钱包和高级钱包类型增强其签名验证过程铺平道路,从而提高灵活性和创新性。

向后兼容性

此提案与现有的 ERC-3009 标准完全向后兼容。当前依赖 V、R、S 签名验证方案的合约将继续正常运行,不会出现任何问题。

如果为了向后兼容性需要同时支持现有的 V、R、S 签名验证方案和新的非结构化字节签名验证,开发者可以通过改编以下代码块作为示例来减少重复:

function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    uint8 v,
    bytes32 r,
    bytes32 s
) external {
    transferWithAuthorization(owner, spender, value, deadline, abi.encodePacked(r, s, v));
}

参考实现

/**
  * @notice Execute a transfer with a signed authorization
  * @dev EOA wallet signatures should be packed in the order of r, s, v.
  * @param from          Payer's address (Authorizer)
  * @param to            Payee's address
  * @param value         Amount to be transferred
  * @param validAfter    The time after which this is valid (unix time)
  * @param validBefore   The time before which this is valid (unix time)
  * @param nonce         Unique nonce
  * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
  */
function _transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    bytes memory signature
) internal {
    require(now > validAfter, "Authorization is not yet valid"); // 授权尚未生效
    require(now < validBefore, "Authorization is expired"); // 授权已过期
    require(!_authorizationStates[authorizer][nonce], "Authorization is used or canceled"); // 授权已使用或已取消

    bytes32 digest = keccak256(abi.encodePacked(
        hex"1901",
        DOMAIN_SEPARATOR,
        keccak256(abi.encode(
            TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
            from,
            to,
            value,
            validAfter,
            validBefore,
            nonce
        ))
    ));
    require(
        // Check for both ECDSA signature and and ERC-1271 signature. A sample SignatureChecker is available at
        // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2a/contracts/utils/cryptography/SignatureChecker.sol
        // 同时检查 ECDSA 签名和 ERC-1271 签名。SignatureChecker 示例可在以下网址获取
        // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2a/contracts/utils/cryptography/SignatureChecker.sol
        SignatureChecker.isValidSignatureNow(
            owner,
            typedDataHash,
            signature
        ),
        "Invalid signature" // 无效签名
    );

    _authorizationStates[authorizer][nonce] = true;
    emit AuthorizationUsed(authorizer, nonce);
    
    _transfer(from, to, value);
}

安全注意事项

  • 对于合约钱包, transferWithAuthorizationreceiveWithAuthorizationcancelAuthorization 的安全性依赖于 ContractWallet.isValidSignature(),以确保签名字节表示合约钱包所有者所需的执行。合约钱包开发者在实现自定义签名验证逻辑时必须谨慎,以确保其合约的安全性。

版权

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

Citation

Please cite this document as:

Yvonne Zhang (@yvonnezhangc), Aloysius Chan (@circle-aloychan), "ERC-7598: 使用合约签名进行签名转账 [DRAFT]," Ethereum Improvement Proposals, no. 7598, January 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7598.