账户模块 - OpenZeppelin 文档

本文介绍了基于 ERC-7579 标准的模块化智能账户,该标准允许通过模块扩展账户功能。文章详细阐述了 ERC-7579 的账户和模块类型(包括验证器、执行器、回退和Hook模块),并提供了构建自定义模块的示例,同时还探讨了不同的执行模式。最后,通过社交恢复的例子,展示了如何利用这些模块构建更安全的账户恢复机制。

账户模块

使用 ERC-7579 构建的智能账户提供了一种标准化的方式,通过模块(即智能合约实例)来扩展账户功能。这种架构允许账户支持各种与各种账户实现兼容的功能。请参阅 兼容模块

ERC-7579

ERC-7579 定义了模块化智能账户的标准化接口。此标准使账户能够以与不同账户实现可组合的方式安装、卸载模块并与模块交互,从而扩展其功能。

账户

OpenZeppelin 提供了一个 AccountERC7579 合约的实现,该合约允许安装符合此标准的模块。还有一个 AccountERC7579Hooked 变体,它支持安装 hooks。与 大多数账户 一样,实例应定义一个初始化函数,在该函数中将设置控制该账户的第一个模块:

// contracts/MyAccountERC7579.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {MODULE_TYPE_VALIDATOR} from "@openzeppelin/contracts/interfaces/draft-IERC7579.sol";
import {AccountERC7579} from "@openzeppelin/community-contracts/account/extensions/AccountERC7579.sol";

contract MyAccountERC7579 is Initializable, AccountERC7579 {
    function initializeAccount(address validator, bytes calldata validatorData) public initializer {
        // Install a validator module to handle signature verification
        // 安装一个验证器模块来处理签名验证
        _installModule(MODULE_TYPE_VALIDATOR, validator, validatorData);
    }
}
为了简单起见,AccountERC7579Hooked 仅支持单个 hook。 一种常见的解决方法是安装一个 带有复用器模式的单个 hook,以将功能扩展到多个 hook。

模块

通过部署为智能合约(称为 模块)的封装功能将功能添加到账户。 该标准定义了四种主要模块类型:

  • 验证器模块(类型 1):处理签名验证和用户操作验证

  • 执行器模块(类型 2):代表账户执行操作

  • 回退模块(类型 3):处理特定函数选择器的回退调用

  • Hook 模块(类型 4):在操作之前和之后执行逻辑

模块可以同时实现多种类型,这意味着你可以将执行器模块与 hook 结合起来,以在账户上强制执行行为,例如维护 ERC-20 批准或阻止删除某些权限。

请参阅 流行的模块实现

构建自定义模块

该库提供了 标准可组合模块 作为构建块,并为开发人员提供了一个内部 API。 通过组合这些组件,你可以创建丰富的变体集,而无需包含不必要的功能。

一个好的起点是 ERC7579ExecutorERC7579Validator,其中包括一个固执己见的基础层,可以轻松地与其他抽象模块组合。 Hooks 和 fallback handlers 更容易直接从接口实现:

// contracts/MyERC7579Modules.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {IERC7579Module, IERC7579Hook} from "@openzeppelin/contracts/interfaces/draft-IERC7579.sol";
import {ERC7579Executor} from "@openzeppelin/community-contracts/account/modules/ERC7579Executor.sol";
import {ERC7579Validator} from "@openzeppelin/community-contracts/account/modules/ERC7579Validator.sol";

// Basic validator module
// 基本验证器模块
abstract contract MyERC7579RecoveryValidator is ERC7579Validator {}

// Basic executor module
// 基本执行器模块
abstract contract MyERC7579RecoveryExecutor is ERC7579Executor {}

// Basic fallback handler
// 基本回退 handler
abstract contract MyERC7579RecoveryFallback is IERC7579Module {}

// Basic hook
// 基本 hook
abstract contract MyERC7579RecoveryHook is IERC7579Hook {}
API 参考 中探索这些抽象的 ERC-7579 模块。
执行模式

ERC-7579 支持多种执行模式,这些模式被编码为 bytes32 值。 ERC7579Utils 库提供了使用这些模式的实用函数:

// Parts of an execution mode
// 执行模式的组成部分
type Mode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ModeSelector is bytes4;
type ModePayload is bytes22;
调用类型

调用类型确定执行的种类:

类型 描述
CALLTYPE_SINGLE 0x00 单个 call 执行
CALLTYPE_BATCH 0x01 一批 call 执行
CALLTYPE_DELEGATECALL 0xFF 一个 delegatecall 执行
执行类型

执行类型确定如何处理失败:

类型 描述
EXECTYPE_DEFAULT 0x00 失败时恢复
EXECTYPE_TRY 0x01 失败时不会恢复,而是发出一个事件
执行数据格式

执行数据格式因调用类型而异:

  • 对于单次调用:abi.encodePacked(target, value, callData)

  • 对于批量调用:abi.encode(Execution[]),其中 Execution 是一个包含 targetvaluecallData 的结构体

  • 对于委托调用:abi.encodePacked(target, callData)

示例

社交恢复

社交恢复允许在丢失访问权限时通过依赖受信任方(“监护人”)来恢复账户,这些受信任方验证用户的身份并帮助恢复访问权限。

社交恢复不是单一的解决方案,而是一个具有多种配置选项的设计空间:

  • 延迟配置

  • 过期设置

  • 不同的监护人类型

  • 取消窗口

  • 确认要求

为了支持 不同的监护人类型,我们可以利用 ERC-7913,如 多重签名 部分中所讨论的。对于 ERC-7579 模块,这通过 ERC7579Multisig 验证器来实现。

ERC7579Executor 结合使用,它提供了一个基本的基础,可以通过更复杂的功能进行扩展:

// contracts/MyERC7579SocialRecovery.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ERC7579Executor} from "@openzeppelin/community-contracts/account/modules/ERC7579Executor.sol";
import {ERC7579Validator} from "@openzeppelin/community-contracts/account/modules/ERC7579Validator.sol";
import {ERC7579Multisig} from "@openzeppelin/community-contracts/account/modules/ERC7579Multisig.sol";

abstract contract MyERC7579SocialRecovery is EIP712, ERC7579Executor, ERC7579Multisig, Nonces {
    bytes32 private constant RECOVER_TYPEHASH =
        keccak256("Recover(address account,bytes32 salt,uint256 nonce,bytes32 mode,bytes executionCalldata)");

    function isModuleType(uint256 moduleTypeId) public pure override(ERC7579Executor, ERC7579Validator) returns (bool) {
        return ERC7579Executor.isModuleType(moduleTypeId) || ERC7579Executor.isModuleType(moduleTypeId);
    }

    // Data encoding: [uint16(executionCalldataLength), executionCalldata, signature]
    // 数据编码:[uint16(executionCalldataLength), executionCalldata, signature]
    function _validateExecution(
        address account,
        bytes32 salt,
        bytes32 mode,
        bytes calldata data
    ) internal override returns (bytes calldata) {
        uint16 executionCalldataLength = uint16(bytes2(data[0:2])); // First 2 bytes are the length
        // 前 2 个字节是长度
        bytes calldata executionCalldata = data[2:2 + executionCalldataLength]; // Next bytes are the calldata
        // 下一个字节是 calldata
        bytes calldata signature = data[2 + executionCalldataLength:]; // Remaining bytes are the signature
        // 剩余的字节是签名
        require(_rawERC7579Validation(account, _getExecuteTypeHash(account, salt, mode, executionCalldata), signature));
        return executionCalldata;
    }

    function _getExecuteTypeHash(
        address account,
        bytes32 salt,
        bytes32 mode,
        bytes calldata executionCalldata
    ) internal returns (bytes32) {
        return
            _hashTypedDataV4(
                keccak256(abi.encode(RECOVER_TYPEHASH, account, salt, _useNonce(account), mode, executionCalldata))
            );
    }
}

为了增强安全性,你可以使用 ERC7579DelayedExecutor 通过调度、延迟和取消来扩展此基础。这允许监护人安排具有时间延迟的恢复操作,从而提供一个安全窗口来检测和取消可疑的恢复尝试,然后再执行它们:

// contracts/MyERC7579DelayedSocialRecovery.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ERC7579Executor} from "@openzeppelin/community-contracts/account/modules/ERC7579Executor.sol";
import {ERC7579Validator} from "@openzeppelin/community-contracts/account/modules/ERC7579Validator.sol";
import {Calldata} from "@openzeppelin/contracts/utils/Calldata.sol";
import {ERC7579DelayedExecutor} from "@openzeppelin/community-contracts/account/modules/ERC7579DelayedExecutor.sol";
import {ERC7579Multisig} from "@openzeppelin/community-contracts/account/modules/ERC7579Multisig.sol";

abstract contract MyERC7579DelayedSocialRecovery is EIP712, ERC7579DelayedExecutor, ERC7579Multisig {
    bytes32 private constant RECOVER_TYPEHASH =
        keccak256("Recover(address account,bytes32 salt,bytes32 mode,bytes executionCalldata)");

    function isModuleType(uint256 moduleTypeId) public pure override(ERC7579Executor, ERC7579Validator) returns (bool) {
        return ERC7579Executor.isModuleType(moduleTypeId) || ERC7579Executor.isModuleType(moduleTypeId);
    }

    // Data encoding: [uint16(executorArgsLength), executorArgs, uint16(multisigArgsLength), multisigArgs]
    // 数据编码:[uint16(executorArgsLength), executorArgs, uint16(multisigArgsLength), multisigArgs]
    function onInstall(bytes calldata data) public override(ERC7579DelayedExecutor, ERC7579Multisig) {
        uint16 executorArgsLength = uint16(bytes2(data[0:2])); // First 2 bytes are the length
        // 前 2 个字节是长度
        bytes calldata executorArgs = data[2:2 + executorArgsLength]; // Next bytes are the args
        // 下一个字节是参数
        uint16 multisigArgsLength = uint16(bytes2(data[2 + executorArgsLength:4 + executorArgsLength])); // Next 2 bytes are the length
        // 下面 2 个字节是长度
        bytes calldata multisigArgs = data[4 + executorArgsLength:4 + executorArgsLength + multisigArgsLength]; // Next bytes are the args
        // 下一个字节是参数

        ERC7579DelayedExecutor.onInstall(executorArgs);
        ERC7579Multisig.onInstall(multisigArgs);
    }

    function onUninstall(bytes calldata) public override(ERC7579DelayedExecutor, ERC7579Multisig) {
        ERC7579DelayedExecutor.onUninstall(Calldata.emptyBytes());
        ERC7579Multisig.onUninstall(Calldata.emptyBytes());
    }

    // Data encoding: [uint16(executionCalldataLength), executionCalldata, signature]
    // 数据编码:[uint16(executionCalldataLength), executionCalldata, signature]
    function _validateSchedule(
        address account,
        bytes32 salt,
        bytes32 mode,
        bytes calldata data
    ) internal view override {
        uint16 executionCalldataLength = uint16(bytes2(data[0:2])); // First 2 bytes are the length
        // 前 2 个字节是长度
        bytes calldata executionCalldata = data[2:2 + executionCalldataLength]; // Next bytes are the calldata
        // 下一个字节是 calldata
        bytes calldata signature = data[2 + executionCalldataLength:]; // Remaining bytes are the signature
        // 剩余的字节是签名
        require(_rawERC7579Validation(account, _getExecuteTypeHash(account, salt, mode, executionCalldata), signature));
    }

    function _getExecuteTypeHash(
        address account,
        bytes32 salt,
        bytes32 mode,
        bytes calldata executionCalldata
    ) internal view returns (bytes32) {
        return _hashTypedDataV4(keccak256(abi.encode(RECOVER_TYPEHASH, account, salt, mode, executionCalldata)));
    }
}
延迟执行器的签名验证不需要 nonce,因为操作通过其 操作 ID 唯一标识,并且不能被安排两次。

这些实现演示了如何构建逐渐更安全的社交恢复机制,从基本的多重签名恢复到具有取消功能的时间延迟恢复。

对于其他功能,开发人员可以使用:

← 多重签名

Paymasters →

  • 原文链接: docs.openzeppelin.com/co...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。