跳到主要内容

智能账户

SimpleAccount 是最小单 Owner ECDSA 参考实现。开发者也可以实现自己的 ERC-4337 智能账户。

SimpleAccount implementation 是所有账户代理共享的实现地址,不是用户自己的账户地址。每个账户实例由 Owner 和 salt 确定。

1. SimpleAccountFactory API​

interface ISimpleAccountFactory {
function accountImplementation()
external view returns (address);

function getAddress(
address owner,
uint256 salt
) external view returns (address);

function createAccount(
address owner,
uint256 salt
) external returns (address);
}
方法说明
accountImplementation返回共享 SimpleAccount implementation
getAddress计算 Owner 和 salt 对应的反事实代理地址,不部署账户
createAccount地址无代码时创建代理;已经部署时返回现有账户

首次 UserOperation 的 Factory 数据由 Factory 地址和 createAccount(owner, salt) 的 ABI 编码组成。EntryPoint 要求 Factory 返回的地址与 UserOperation sender 一致,并要求部署后 sender 存在代码。

2. SimpleAccount API​

interface ISimpleAccount {
function owner() external view returns (address);

function entryPoint() external view returns (address);

function getNonce() external view returns (uint256);

function initialize(address owner) external;

function execute(
address dest,
uint256 value,
bytes calldata func
) external;

function executeBatch(
address[] calldata dest,
uint256[] calldata value,
bytes[] calldata func
) external;

function getDeposit() external view returns (uint256);

function addDeposit() external payable;

function withdrawDepositTo(
address payable withdrawAddress,
uint256 amount
) external;
}

2.1 账户状态​

方法说明
owner当前单一 ECDSA Owner
entryPoint账户绑定的 EntryPoint
getNonceEntryPoint 中 key 0 的下一个 nonce
initialize初始化代理 Owner;只允许初始化一次

Factory 在创建代理时完成初始化。外部开发者不应把未初始化代理暴露给其他调用者。

2.2 业务执行​

方法调用权限行为
executeOwner 或绑定的 EntryPoint执行单个目标调用,并透传回滚数据
executeBatchOwner 或绑定的 EntryPoint原子执行多个目标调用

executeBatch 要求目标地址数量与 calldata 数量相同;value 数组可以为空,表示全部调用不转 BOT,也可以与目标数组等长。

Owner 直接调用执行接口时使用普通链上交易并自行支付交易 Gas。EntryPoint 调用时使用 UserOperation,Gas 从账户或 Paymaster 的 deposit 结算。

2.3 EntryPoint deposit​

方法说明
getDeposit查询该账户在 EntryPoint 中的 Gas deposit
addDeposit为该账户增加 EntryPoint deposit
withdrawDepositTo由 Owner 提取账户 deposit

账户原生 BOT 余额用于转账和业务调用,EntryPoint deposit 用于支付 UserOperation Gas,两者相互独立。

2.4 Token 接收​

SimpleAccount 可以接收原生 BOT、ERC-721 和 ERC-1155,并实现对应的 receiver 接口检测。

3. SimpleAccount 验证行为​

SimpleAccount 继承 BaseAccount.validateUserOp:

function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
步骤SimpleAccount 行为
调用方检查只接受绑定的 EntryPoint
签名 hash对 userOpHash 应用 EIP-191
签名验证ECDSA recover 后必须等于 Owner
签名失败返回 SIG_VALIDATION_FAILED,即 1
nonce唯一性由 EntryPoint 管理,默认 key 为 0
prefund按 missingAccountFunds 向 EntryPoint 补足资金

SimpleAccount 不原生提供多签、社交恢复、Session Key、Passkey 或 ERC-7579 模块。需要这些能力时,应实现自定义账户,而不是假设 SimpleAccount 存在未公开接口。

4. 自定义智能账户​

开发者可以从零实现 IAccount,也可以继承 BaseAccount。无论采用哪种方式,都必须与当前 EntryPoint v0.7 和 Bundler 验证规则兼容。

4.1 必须实现的接口​

interface IAccount {
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}
参数说明
userOp即将验证和执行的完整 PackedUserOperation
userOpHash已绑定 EntryPoint 和 chainId 的 hash
missingAccountFunds当前账户 deposit 距离 required prefund 的差额

validationData 的布局为:

低 160 位:0 表示签名有效,1 表示签名失败,其他值表示聚合器地址
接续 48 位:validUntil,0 表示无限期
高 48 位:validAfter

4.2 必须满足的行为​

  • 验证 msg.sender 是账户信任的 EntryPoint。

  • 验证 signature、授权策略和必要的账户状态。

  • 签名不匹配时返回 1,其他格式或策略错误可以回滚。

  • 按账户策略检查 nonce;nonce 唯一性仍由 EntryPoint 更新。

  • 没有 Paymaster 且 deposit 不足时,补足 missingAccountFunds。

  • 避免违反 ERC-7562 的 opcode、存储访问和外部调用限制。

  • 为估算阶段提供可模拟、确定且不会产生不可控副作用的验证逻辑。

继承 BaseAccount 时,子类至少需要提供:

  • entryPoint():返回绑定的 EntryPoint。

  • _validateSignature(...):返回签名和时间范围验证结果。

  • 可选 _validateNonce(...):增加账户自己的 nonce 策略。

  • 可选 _payPrefund(...):自定义 prefund 资金管理。

4.3 执行接口设计​

账户可以定义自己的 callData 接口,例如单笔执行、批量执行、模块执行或权限分层执行。EntryPoint 默认将 userOp.callData 直接发送给账户。

账户还可以实现可选接口:

interface IAccountExecute {
function executeUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external;
}

当 callData 使用 executeUserOp selector 时,EntryPoint 会把完整 UserOperation 和 hash 交给账户执行。该接口不是实现 ERC-4337 账户的必选项。

4.4 Factory 与部署​

自定义 Factory 应满足:

  • 根据确定参数稳定计算账户地址。

  • 创建函数返回实际部署的账户地址。

  • 首次 UserOperation 的 initCode 前 20 字节是 Factory 地址。

  • Factory 调用数据位于 initCode 剩余部分。

  • 部署结果必须等于 UserOperation sender。

  • 已部署账户不应继续携带 Factory 字段。

Bundler 会模拟 Factory 和账户验证。Factory 或账户行为不符合规则时,UserOperation 会在进入 mempool 前被拒绝。