本文介绍了基于 ERC-7579 标准的模块化智能账户,该标准允许通过模块扩展账户功能。文章详细阐述了 ERC-7579 的账户和模块类型(包括验证器、执行器、回退和Hook模块),并提供了构建自定义模块的示例,同时还探讨了不同的执行模式。最后,通过社交恢复的例子,展示了如何利用这些模块构建更安全的账户恢复机制。
使用 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。 通过组合这些组件,你可以创建丰富的变体集,而无需包含不必要的功能。
一个好的起点是 ERC7579Executor
或 ERC7579Validator
,其中包括一个固执己见的基础层,可以轻松地与其他抽象模块组合。 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
是一个包含 target
、value
和 callData
的结构体
对于委托调用: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 唯一标识,并且不能被安排两次。 |
这些实现演示了如何构建逐渐更安全的社交恢复机制,从基本的多重签名恢复到具有取消功能的时间延迟恢复。
对于其他功能,开发人员可以使用:
ERC7579MultisigWeighted
为签名者分配不同的权重
ERC7579MultisigConfirmation
实现一个确认系统,该系统在添加签名者时验证签名
ERC7579MultisigStorage
允许监护人预先签署恢复操作,以实现更灵活的协调
- 原文链接: docs.openzeppelin.com/co...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!