ERC-7897: 面向智能账户的钱包链接服务
定义一个链接到 ERC-4337 钱包的模块化服务注册表。
Authors | Francesco Sullo (@sullof) |
---|---|
Created | 2024-04-15 |
Discussion Link | https://ethereum-magicians.org/t/generalized-wallet-linked-services-for-erc-4337-wallets/23028 |
Requires | EIP-165, EIP-1167, EIP-4337, EIP-6551, EIP-7656 |
摘要
本提案定义了一个链接到智能账户的通用服务注册表,特别关注 ERC-4337 钱包,其中服务是扩展钱包功能的合约,由钱包本身拥有。它利用 ERC-1167 最小代理和确定性寻址来实现无需许可的创新,同时保持与现有 ERC-4337 钱包的向后兼容性。为了实现其目标,它采用了 ERC-6551 和 ERC-7656 标准引入的概念,这些标准适用于 NFT,并将其应用于钱包。
注意:由于可以使用 ERC-7656 标准实现相同的功能,因此不再需要此提案。
动机
ERC-4337 (账户抽象) 引入了可编程的智能账户。现有扩展钱包功能的提案 (例如,ERC-6900) 侧重于内部模块。本提案推广了服务绑定的概念,允许任何 ERC-4337 钱包附加外部服务 (例如,恢复、自动化、合规性),而无需更改钱包的核心逻辑。
通过启用模块化、非侵入式扩展,此标准促进了钱包链接服务的开放生态系统,同时确保与现有 ERC-4337 钱包的向后兼容性。
规范
本文档中的关键词 “MUST”,“MUST NOT”,“REQUIRED”,“SHALL”,“SHALL NOT”,“SHOULD”,“SHOULD NOT”,“RECOMMENDED”,“NOT RECOMMENDED”,“MAY” 和 “OPTIONAL” 按照 RFC 2119 和 RFC 8174 中的描述进行解释。
注册表接口
接口 IERC7897Registry
定义如下:
interface IERC7897Registry {
/**
* @notice 当钱包链接服务成功部署时发出。
* @param deployedService 已部署合约的地址
* @param serviceImplementation 实现合约的地址
* @param salt 用于 CREATE2 操作的盐
* @param chainId 合约部署到的链 ID
* @param wallet ERC-4337 钱包的地址
*/
event ServiceDeployed(
address deployedService,
address indexed serviceImplementation,
bytes32 salt,
uint256 chainId,
address indexed wallet
);
/**
* @notice 当 CREATE2 操作未能部署合约时抛出。
*/
error DeployFailed();
/**
* @notice 为 ERC-4337 钱包部署钱包链接的服务。
* 如果服务已经存在,则返回其地址而不调用 CREATE2。
* @param serviceImplementation 实现合约的地址
* @param salt CREATE2 操作使用的盐
* @param wallet ERC-4337 钱包的地址
* 发出 {ServiceDeployed} 事件。
* @return service 钱包链接服务的地址
*/
function deployService(
address serviceImplementation,
bytes32 salt,
address wallet
) external returns (address service);
/**
* @notice 计算 ERC-4337 钱包的预期钱包链接服务地址,
* 而不部署它。
* @param serviceImplementation 实现合约的地址
* @param salt CREATE2 操作使用的盐
* @param chainId 服务将要部署到的链 ID
* @param wallet ERC-4337 钱包的地址
* @return service 钱包链接服务的计算地址
*/
function serviceAddress(
address serviceImplementation,
bytes32 salt,
uint256 chainId,
address wallet
) external view returns (address service);
}
部署要求
注册表必须将每个钱包链接的服务部署为 ERC-1167 最小代理,并在字节码附加不可变的常量数据。
每个钱包链接服务的已部署字节码必须具有以下结构:
ERC-1167 Header (10 bytes)
<serviceImplementation (address)> (20 bytes)
ERC-1167 Footer (15 bytes)
<salt (bytes32)> (32 bytes)
<chainId (uint256)> (32 bytes)
<wallet (address)> (20 bytes)
推荐的服务接口
使用 ERC7897Registry
创建的任何合约都应该实现 IERC7897Service
接口:
interface IERC7897Service {
/**
* @notice 返回链接到合约的钱包
* @return chainId 钱包的 chainId
* @return wallet [ERC-4337](/docs/eips/EIPS/eip-4337/) 钱包的地址
*/
function wallet() external view returns (uint256 chainId, address wallet);
}
访问控制
服务应该实现访问控制,以限制对钱包所有者的关键操作。例如:
function owner() public view returns (address) {
(, address wallet) = IERC7897Service(address(this)).wallet();
return wallet;
}
modifier onlyOwner() {
require(msg.sender == owner(), "Unauthorized");
_;
}
原理
ERC-7897 的技术基础围绕可与 ERC-4337 钱包关联的合约类型的扩展和概括。关键决策包括:
-
灵活性:使任何 ERC-4337 钱包都可以在不修改其核心逻辑的情况下附加外部服务。
-
无需许可的创新:开发人员可以为任何钱包部署服务,从而促进开放的生态系统。
-
向后兼容性:与现有的 ERC-4337 钱包(包括 Safe、Argent 和 Biconomy)一起使用。
-
确定性寻址:使用 CREATE2 + salt/chainId/wallet 进行可预测的服务部署。
参考实现
// 此实现是 Jayden Windle @jaydenwindle 和 Vectorized @vectorized 编写的 ERC6551Registry 合约的变体
contract ERC7897Registry is IERC7897Registry {
function deployService(
address serviceImplementation,
bytes32 salt,
address wallet
) external override returns (address) {
// solhint-disable-next-line no-inline-assembly
assembly {
// 内存布局:
// ----
// 0x00 0xff (1 byte)
// 0x01 registry (address) (20 bytes)
// 0x15 salt (bytes32) (32 bytes)
// 0x35 Bytecode Hash (bytes32) (32 bytes)
// ----
// 0x55 ERC-1167 Constructor + Header (20 bytes)
// 0x69 implementation (address) (20 bytes)
// 0x5D ERC-1167 Footer (15 bytes)
// 0x8C salt (uint256) (32 bytes)
// 0xAC chainId (uint256) (32 bytes)
// 0xCC wallet (address) (20 bytes)
// 将字节码 + 常量数据复制到内存
mstore(0x8c, salt) // salt
mstore(0xac, chainid()) // chainId
mstore(0xcc, wallet) // wallet address (20 bytes)
mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
mstore(0x5d, serviceImplementation) // implementation
mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header
// 将 create2 计算数据复制到内存
mstore8(0x00, 0xff) // 0xFF
mstore(0x35, keccak256(0x55, 0x8b)) // keccak256(bytecode) - 0x8b = 139 bytes
mstore(0x01, shl(96, address())) // registry address
mstore(0x15, salt) // salt
// 计算服务地址
let computed := keccak256(0x00, 0x55)
// 如果尚未部署该服务
if iszero(extcodesize(computed)) {
// 部署服务合约
let deployed := create2(0, 0x55, 0x8b, salt) // 0x8b = 139 bytes
// 如果部署失败,则恢复
if iszero(deployed) {
mstore(0x00, 0xd786d393) // `DeployFailed()`
revert(0x1c, 0x04)
}
// 发出 ServiceDeployed 事件
mstore(0x00, deployed) // deployedService
mstore(0x20, serviceImplementation) // serviceImplementation
mstore(0x40, salt) // salt
mstore(0x60, chainid()) // chainId
mstore(0x80, wallet) // wallet
log4(
0x00, // 数据开始
0xa0, // 数据长度(160 字节:已部署 + 实现 + 盐 + 链 ID + 钱包)
0x2f82bd0c129ea2d065cf394fb7760031982c6278372c89e1a059f2478ddf4763, // 事件签名哈希
deployed, // 索引的 deployedService
serviceImplementation, // 索引的 serviceImplementation
salt, // salt
chainid(), // chainId
wallet // 索引的 wallet
)
// 返回服务地址
return(0x00, 0x20)
}
// 否则,返回计算出的服务地址
mstore(0x00, computed)
return(0x00, 0x20)
}
}
function serviceAddress(
address serviceImplementation,
bytes32 salt,
uint256 chainId,
address wallet
) external view override returns (address) {
// solhint-disable-next-line no-inline-assembly
assembly {
// 将字节码 + 常量数据复制到内存
mstore(0x8c, salt) // salt
mstore(0xac, chainId) // chainId
mstore(0xcc, wallet) // wallet address (20 bytes)
mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
mstore(0x5d, serviceImplementation) // implementation
mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header
// 将 create2 计算数据复制到内存
mstore8(0x00, 0xff) // 0xFF
mstore(0x35, keccak256(0x55, 0x8b)) // keccak256(bytecode) - 0x8b = 139 bytes
mstore(0x01, shl(96, address())) // registry address
mstore(0x15, salt) // salt
// 计算并返回服务地址
mstore(0x00, keccak256(0x00, 0x55))
return(0x00, 0x20)
}
}
}
安全考虑
所有权和控制权
钱包链接的服务必须由 ERC-4337 钱包所有者控制,以防止未经授权的访问。实施者应包括针对恶意或未经验证的实施的保护措施。
可升级性风险
如果服务是可升级的,请确保安全升级机制以防止未经授权的更改。例如:
-
服务的所有者应该是钱包本身。
-
只有钱包才能升级服务的实现。
-
实施版本控制以确保升级之间的向后兼容性。
-
对关键升级使用时间锁或多重签名,以降低恶意更改的风险。
重入和跨合约交互
与外部协议交互的服务应遵循最佳实践以防止重入攻击。
用户教育
应提供清晰的用户界面和警告,以降低网络钓鱼和社会工程风险。
测试
实施者应在测试网上彻底测试注册表和服务,以确保在部署到主网之前的正确性和安全性。
版权
在 CC0 下放弃版权和相关权利。
Citation
Please cite this document as:
Francesco Sullo (@sullof), "ERC-7897: 面向智能账户的钱包链接服务 [DRAFT]," Ethereum Improvement Proposals, no. 7897, April 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7897.