EIP-4337ERC账户抽象Final(2023-03-01)账户抽象(Account Abstraction)
不改协议层就实现智能合约钱包:用 UserOperation 替代交易,由 Bundler 代发,Paymaster 可代付 Gas。
适用链:以太坊主网 + Arbitrum / Optimism / Base / Polygon 等(需有 EntryPoint 与 Bundler)
为什么重要:它解决了 EOA 的三个硬伤:私钥丢失即资产全丢、必须持有原生币才能操作、多步操作要多次签名。社交恢复、无 Gas 交易、批量执行、会话密钥(session key)都建立在它之上。
函数签名与选择器
选择器是签名的 keccak256 前 4 字节;事件则是完整的 32 字节 topic0。 点一下即可复制。
| 签名 / 字段 | 选择器 / topic0 | 说明 |
|---|---|---|
| EntryPoint.handleOps(UserOperation[],address) | Bundler 调用的总入口,按顺序执行一批 userOp。 | |
| EntryPoint.getUserOpHash(UserOperation,bytes32) | 计算 userOp 哈希,签名必须签这个值(而不是 userOp 本身的编码)。 | |
| IAccount.validateUserOp(UserOperation,bytes32,uint256) | 账户自己验证签名与 Gas 预付款,返回 0 表示验证通过。 | |
| EntryPoint.simulateValidation(UserOperation) | — | 调试接口:本地模拟验证阶段,定位 AA 系列错误码。 |
| UserOperation.sender | — | 发起操作的智能账户地址。 |
| UserOperation.nonce | — | 非简单递增:高 192 位是 key(可用多个并行 nonce 通道),低 64 位是序号。 |
| UserOperation.callData | — | 账户要执行的实际调用,通常是 execute(dest, value, func) 的编码。 |
| UserOperation.paymasterAndData | — | v0.6:paymaster 地址 + 额外数据。v0.7 拆分成了四个独立字段。 |
| UserOperation.signature | — | 账户自定义的签名数据,EntryPoint 不解析,直接透传给 validateUserOp。 |
代码模板
最小智能账户(validateUserOp + execute)
最小智能账户(validateUserOp + execute)Solidity
真实项目用 OpenZeppelin 的 Account + ECDSA 库(或 SimpleAccount 参考实现),它们处理了 EIP-191 前缀、可延展性与批量执行。
客户端:构造并发送 UserOperation
客户端:构造并发送 UserOperationTypeScript
常见 AA 错误码(都出现在 error.message 前缀里):AA21 预付款不足、AA22 费率过低、AA23 验证超限、AA25 nonce 无效。
实现与使用时的常见陷阱
- ✕必须签 userOpHash,不能签 userOp 的编码结果——后者在不同链/不同 EntryPoint 上不可区分,会被重放。
- ✕v0.6 与 v0.7 的 UserOperation 结构不兼容(paymasterAndData 与 initCode 被拆成多个字段),接入前先确认 Bundler 与 EntryPoint 版本。
- ✕validateUserOp 里能调的存储范围受 ERC-7562 验证规则限制,不能读任意外部合约状态,否则 Bundler 会拒绝该操作。
- ✕Paymaster 有余额与抵押(stake)要求,欠费后所有依赖它的账户会集体无法发交易。
- ✕UserOp 的 nonce 是 256 位:高 192 位是 key,可以用不同 key 并行发多笔而不互相阻塞。