EVM交易终局:三层堆叠账户抽象方案
文章回顾了以太坊账户抽象十年的尝试(EIP-86、859、2938、ERC-4337、EIP-7702等),指出核心问题在于交易只能由外部账户发起,合约钱包依赖外部账户。提出了最终解决方案:三层堆叠——EIP-8141帧交易作为原生运行时,实现原生身份、气体隔离、原子性;ERC-8211智能批处理作为编程模型,实现多步意图的单签名原子执行;EIP-7906断言层通过TXTRACE操作码验证交易完整状态变更。三者结合使用户无需中间件即可实现复杂操作的原子化、安全性和完全验证,标志着账户抽象的终局。

以太坊关于账户抽象的争论已经持续了近十年。最近的变化是,一个连贯答案的各个组成部分终于同时出现了。本文追溯了这个问题、失败和部分尝试的历史,以及三个层——它们共同构成了终局:帧交易、智能批处理和断言。
问题的起点
用户对以太坊账户的几乎所有挫败感都可以追溯到网络启动前的一个单一决定:外部拥有账户(EOA)和合约之间的硬性分割。合约可以被编程来执行几乎任何操作。相比之下,EOA实际上在2015年就被冻结了。每个EOA都由一个单一的ECDSA密钥永久控制,具有全有或全无的访问权限,并由发送方用以太支付Gas。这就是整个模型。
局限性直接由此而来。没有密钥轮换,没有社交恢复,没有会话密钥,没有用USDC支付Gas,也没有赞助(除非借助中继器变通方法)。所有这一切之上笼罩着一个长期威胁:量子计算机最终将破解ECDSA,这意味着链上的每个EOA都自带一个无人能预知的到期日。
智能合约钱包原则上解决了这些问题,因为代码可以实现设计者想要的任何认证方案、恢复流程或签名算法。但问题是智能钱包有一个引导问题。在以太坊上,交易只能从EOA发起。一个合约钱包保持静止,直到有东西调用它,而这个东西不可避免地是一个用以太支付Gas的EOA。
因此,账户抽象归结为一个问题:如何在没有EOA参与循环的情况下从合约钱包启动操作?
第一个问题中还隐藏着第二个更微妙的问题。一旦账户的有效性逻辑被表达为代码,链上的任何东西都可能使待处理的交易失效。如果一个攻击者构造了一千笔交易,这些交易在任意一笔被确认之前都是有效的,那么网络最终会为了一笔交易的代价验证一千笔交易。这被称为多重失效问题,它笼罩着这个故事中的每一个提案。EOA对此免疫,因为唯一能使来自EOA的待处理交易失效的是来自同一账户的另一笔交易。
十年尝试简史
这段历史大致分为三个阶段。
第一阶段,从2016年到2020年,包括在协议内部解决问题的原生尝试。EIP-86提出了无签名交易,其中钱包合约可以验证它选择的任何内容并直接向block.coinbase支付。其致命弱点是区块构建者只能通过先执行整个交易来发现它是否会被支付,这使其成为拒绝服务机器。EIP-859引入了PAYGAS操作码,一个锁定构建者支付的EVM内检查点;它解决了验证成本问题,但没有解决多重失效问题。EIP-2938随后添加了关于验证阶段可运行操作码的限制性规则,以便交易的有效性只能依赖于钱包自身状态。这种方法在技术上是合理的,但由于核心开发者忙于合并而停滞。
第二阶段,从2021年到2023年,放弃了协议,并围绕它重建了整个机制。ERC-4337允许用户将“UserOperation”签名到单独的替代内存池中。称为捆绑者的专门参与者将这些UserOps批量处理成普通交易,并通过单个合约EntryPoint路由它们,该合约编排验证、执行和支付。支付人是一个相关组件,将执行与支付分离,从而使得可以用ERC-20代币支付Gas或赞助新用户的第一笔交易。对多重失效的防御作为ERC-7562重新出现,与EIP-2938提出的相同风格的操作码限制,但通过跟踪模拟在EVM外部强制执行。ERC-4337已发布并在生产环境中运行,但其上限即使是其作者也承认:捆绑仍然源自EOA,Gas开销是永久性的,并且UserOps仍然是二等对象,未来的协议功能不会自动适用于它们。
第三阶段,在2024年和2025年,采取了不同的策略,试图直接增强EOA。EIP-3074设想“调用者”合约可以代表EOA行动。Pectra升级最初接受了它,4337阵营反对,Vitalik Buterin通过起草EIP-7702打破了僵局——据他所说,大约用了22分钟。根据EIP-7702,EOA可以将其代码委托给一个合约(理想情况下是智能钱包实现),从而表现得几乎与智能账户相同。它在Pectra中已发布。即便如此,7702账户仍然需要有人调用它,而这正是原生账户抽象旨在解决的问题。
在所有三个阶段中,同样的两个教训不断浮现。验证必须低廉到易于检查,并且只有账户的所有者才能使该账户的待处理交易失效。争议从来不是关于是否执行这些规则,而是关于在哪里执行它们。
EIP-8141:帧交易
EIP-8141 试图从整个十年的教训中提炼出原生账户抽象。将账户抽象纳入协议的明显路径是简单地将ERC-4337复制到共识中,两个提案RIP-7560和EIP-7701大致做到了这一点,包括UserOp结构等。这种方法的问题在于4337的形状是由其中心约束决定的——即不允许对协议进行更改。将该形状复制到共识中将会固化变通方案的伤疤。
EIP-8141提出了一个更有用的问题:最小的原生原语是什么?分解任何账户抽象交互,会出现三种步骤。VERIFY步骤询问合约是否同意特定角色——是否授权执行,或同意为执行支付。SENDER步骤是一个调用,其中合约被原生视为tx.sender;这是实际动作发生的地方。DEFAULT步骤是一个调用,其中发送方不重要,用于部署、后操作工作和清理。
在这个设计中,帧交易只是一个帧的列表,每个帧以这三种模式之一运行,每个帧有自己的Gas预算,并在交易收据中有自己的状态行。多重失效问题得到了与其他提案相同的答案,即ERC-7562规则,但这里它们作为软内存池策略应用,由能够实际观察EVM的节点执行,并且可以在不进行硬分叉的情况下演变。
了解这如何与开发者已经使用的工具相比较是有帮助的。与ERC-4337相比,帧交易本质上是去掉了中间件的4337:捆绑者的工作转移到区块构建者,EntryPoint的逻辑变成协议规则,替代的内存池合并回真实的内存池。没有永久的Gas开销,没有隐藏在堆栈底部的EOA,操作变成了一等交易,未来的每个协议功能(包括包含列表)都会自动适用于它们。与EIP-7702相比,关系是互补而非竞争。EIP-7702使EOA变得可编程,但没有回答谁来调用它;EIP-8141提供了答案,即没有人必须调用,因为账户原生发起交易。一个7702委托的EOA是一个智能账户,而帧交易正是为服务智能账户而构建的。从这个意义上说,7702是迁移路径,8141是目的地。
这种集成比新的交易类型更深,值得一步步了解帧交易如何实际在协议中移动。
第一点是验证和执行是同一件事。当节点接收到帧交易时,它自己运行VERIFY帧。账户的代码执行,检查它实现的任何签名方案或多签逻辑,并通过APPROVE调用表示同意,该调用可以批准执行、支付或两者。没有链下模拟,没有跟踪检查,也没有捆绑者猜测EVM最终是否会同意其追踪器,因为执行验证的节点正是运行代码的节点。如果VERIFY帧未批准,交易无效且永远不会传播。ERC-7562操作码限制在此阶段仍然适用——验证期间不读取外部余额或区块上下文——这保持了多重失效防御完整。但由于这些是节点执行的内存池策略而不是共识规则,它们可以随着时间的推移在不进行硬分叉的情况下放宽。
第二点,发送方语义是真实的而非模拟的。当SENDER帧运行时,协议将tx.sender设置为该帧的智能账户。这不是一个合约代表用户调用另一个合约;链本身将调用归属于该账户,就像今天它将普通交易归属于EOA一样。没有代码的账户会回退到协议定义的默认行为,这就是普通签名检查账户如何在不部署任何东西的情况下工作的方式。
第三点,Gas核算在协议级别进行。每个帧声明自己的Gas限制,交易的总成本是帧预算之和加上固定的内在成本和每帧的小额成本。协议独立计量每个帧,无论谁在VERIFY阶段批准了支付(账户本身或支付人),都为实际使用的Gas付费。未使用和跳过的Gas的退款在交易结束时按规则结算。
最后,原子性是共识的属性,而不是任何合约的属性。标记为原子批次的连续帧组,如果其中任何一帧回滚,协议会撤销该组;剩余的帧在收据中标记为跳过,并退还其Gas。没有合约实现这种行为。它是区块验证方式的一部分,这意味着每个客户端都一致地执行它,并且账户所做的任何事情都不能破坏它。
累积的效果是整个4337管道——模拟、捆绑、EntryPoint编排、信誉系统——简化为普通的节点行为。帧交易与其他一切事务位于同一内存池中,由相同的构建者构建到区块中,并继承原生交易的每个属性,因为它就是原生交易。
终局,逐层构建
单靠EIP-8141是管道。终局是建立在其上的东西:一个运行时、一个编程模型和一个保证层。这三个部分中的每一个在没有其他部分的情况下用处有限;它们加在一起相当于完全替代了今天用户进行交易的方式。
第1层:EIP-8141作为运行时
将帧交易视为一个进程,将帧视为其系统调用是有用的。协议提供了四个没有合约能独自提供的原语。
第一个是原生身份。在SENDER帧内,智能账户确实是tx.sender——不是通过EntryPoint冒充它的合约,也不是为钱包做前台的EOA,而是账户本身。任何检查msg.sender或tx.origin的协议都会看到真实账户。
第二个是每帧Gas隔离。每个帧有自己的Gas预算,因此错误估计的互换不能消耗为后续借贷调用预留的空间。未使用的Gas在交易结束时退还。
第三个是每帧可观察性。每个帧在收据中产生自己的状态、Gas使用量和日志,因此区块浏览器可以报告“帧3失败:余额低于约束”,而不是无用的“交易回滚”。
第四个,也是基石,是协议强制执行的原子性。标记为原子批次的连续帧一起回滚。如果四个帧中的第二个回滚,协议会回滚该组,将剩余帧标记为跳过,并退还其Gas,因此用户只支付尝试过的工作。没有合约的代码做出那个承诺;链做出了承诺。所有更高层都依赖于原子性已从应用代码移出进入共识这一事实。
第2层:ERC-8211,编程模型
运行时需要一种语言,ERC-8211,即智能批处理,就是这种语言。
一个8211批次是一个签名的多步程序,其中每个参数在执行时从实时链状态解析。而不是指示链“正好交换1000 USDC”,一个批次可以说“交换我的全部USDC余额,无论它在此交易落地时是多少”。开发者使用辅助函数(如swap、supply、fullBalance和predicate)在TypeScript中组成批次,并签名一次。
该标准的第一阶段以与底层无关的形式发布,因此相同的批次编码可以作为4337 UserOperation通过智能账户上的解释器运行。第二阶段将编码绑定到帧交易,绑定正是设计得到回报的地方:每个批次条目编译为一个SENDER帧。一个三步批次变成一个VERIFY前缀,后跟三个标记为单个原子组的SENDER帧。
这种一对一的映射使得每个8141原语成为每个批次步骤的一个属性。每个步骤都有自己的Gas预算、自己的收据行和自己在协议强制执行的原子边界内的位置。将步骤打包到单个帧中会将所有内容折叠成一个不透明的块;一对一的映射让协议看到程序的结构。
随之而来几个后果。第一个是路由合约变得很大程度上不必要。很多合约存在只是因为没有其他东西可以以运行时解析的参数序列化调用——zaps、迁移器、交换后供应包装器,每个流程写一个,每次更改都要审计,并在每条链上重新部署。在帧原生批处理下,该逻辑存在于签名的批次中,新流程作为SDK代码发布,而不是作为部署周期。
第二个是组合变得无许可。新的借贷市场或去中心化交易所上线当天就可以与其他一切组合,无需合作伙伴、路由器或聚合器集成。账户本身是组合表面,新流程只是一个TypeScript表达式。
第三个是迁移实际上是免费的。构建在第一阶段上的应用可以通过将对.toCalldata()的调用替换为.toFrameTx()而不改变其他任何东西,因为编码是位一致的。它是相同批次具有不同的编译目标;捆绑者和EntryPoint只是从图中消失。
ERC-8211还引入了断言,即不执行任何调用的批次条目。断言语解析实时状态,根据一组约束评估它,如果失败则回滚。在第二阶段下,它编译为原子组内的自己的SENDER帧,因此失败的断言不仅仅是孤立地失败——它根据协议规则撤销之前的一切。这就是将条件转化为交易强制执行属性的方式。
最引人注目的应用是对公共内存池中最大可提取价值(MEV)的抵抗。用户可以向互换附加一个断言,该断言读取预言机,检查其新鲜度,并断言交易后余额超过参考值。试图夹击该交易的构建者会触发断言,原子组回滚,构建者一无所获;诚实包含该交易的构建者收取小费。保护不再是与私有订单流通道的关系,而是任何构建者都可以根据公共状态验证的标准链上对象。同样的模式泛化为条件执行作为原语:限价订单是由价格断言门控的互换,条件平仓仅在健康因子超过阈值时关闭头寸,余额绑定扫网转移执行时持有的任何余额,但须满足下限。它们都是相同的形状——工作加断言,签名一次。
第3层:EIP-7906,断言层
断言有一个盲点:它们只检查它们被告知要检查的状态。一个断言可以断言USDC余额、aUSDC头寸、预言机价格或用户在签名时枚举的任何其他值,但它对任何未枚举的内容不做任何说明。如果一笔交易也耗尽了遗忘的授权,或者写入了一个用户从未想过要约束的存储槽,断言通过,用户仍然会损失。不完整的断言相当于虚假的安全。EIP-7906,标题为“通过状态差异操作码进行交易断言”,通过添加两个操作码来弥补这一差距。TXTRACE允许合约从交易内部读取交易自身的状态差异:每个余额变化及其前后值,每个写入的存储槽及其地址、键和值,每个部署的合约及其代码哈希,以及每个发出的事件,全部以确定顺序排列,以便合约可以完整枚举差异。EVENTDATACOPY将事件负载复制到内存进行检查。
这颠倒了断言模型。断言询问某个特定事物是否为真,而TXTRACE断言询问交易实际做了什么,然后对照期望检查完整答案——一次观察而不是逐约束解决。关键是,它还可以断言负面空间:没有余额变化除了两个,没有触及给定合约集之外的存储,没有触发批准事件。这就是检查收据与审计整个交易之间的区别。
EIP中所述的动机是直白的。用户盲目签名。因为他们无法在签名前审查合约代码,他们今天真正的唯一保护是模拟(它显示的是基于当前状态会发生什么,而不是交易被包含时实际会发生什么)和信任。TXTRACE将检查移至链上并在执行时进行。钱包可以附加一个断言,声明一笔交易最多移动特定数量的WETH,至少进入特定数量的USDC,没有其他;如果现实不同意,交易回滚。
在终局堆栈中,自然形式是在第二阶段批次末尾的单个断言条目。原子组的关闭帧调用TXTRACE,在一次遍历中验证完整的执行后差异,要么批准整个组,要么回滚。8211编码不变;断言只是最后一帧,协议的原子批处理完成其余工作。
EIP-7906仍处于草案状态,并且没有被其上的任何东西要求——批处理和断言可以在没有它的情况下工作。但它是将“我约束了我碰巧想到的东西”升级为“我验证了发生的一切”的部分。
各层如何协同工作
通过一个单一示例从上到下运行堆栈使相互作用具体化。假设用户想要将WETH兑换为USDC,将所得的USDC供应给Aave,并保证最终头寸以及没有发生其他任何事情。
在第2层,用户将其编写为8211批次:一个交换条目和一个供应条目,供应量表示为运行时值,加上aUSDC头寸的下限断言。在第1层,SDK将批次一对一编译为帧——一个VERIFY帧,其中账户授权执行和支付,后跟分组为单个原子单元的SENDER帧。在第3层,关闭帧是断言:它调用TXTRACE,读取完整状态差异,并验证WETH最多减少了输入量,aUSDC至少增加了下限,并且没有其他余额、存储槽或授权在任何地方被触及。如果现实不同意,协议回滚交换和供应,并退还未触及的Gas。
值得注意的是所有缺失的东西。没有捆绑者决定是否包含交易。没有EntryPoint调解验证。没有路由合约在流程中持有资金。没有私有通道保护价格。没有盲签名,因为交易在提交前审计了自己。结果是一个签名,公共内存池中的一笔交易,以及一个从头到尾强制执行用户意图的协议。
每个层覆盖了其他层的空白。没有ERC-8211的EIP-8141是一个没有应用表面的强大运行时——一组基础性好处过于抽象,无法单独推动迁移。没有EIP-8141的ERC-8211可以运行,但它的原子性是由账户代码做出的承诺,其步骤是UserOp内不可见的块。没有协议强制原子性的EIP-7906是一个没有回滚对象的警报,而没有ERC-8211它就没有批次可以关闭。堆叠在一起,这三个提供了用户一直要求的东西:复杂、多步的意图,一次表达,原子执行,完整验证,签名和链之间没有障碍。
退出测试
Buterin有一个有用的方式来框定这个问题:如果以太坊在下一次分叉后永远无法再升级,它是否可以接受?今天诚实的答案是否定的。账户仍然是量子脆弱的,可用的最佳账户抽象依赖于协议外部的EOA动力脚手架,而最受欢迎的用户体验存在于中间件中。
终局堆栈本身也不能通过该测试;后量子预编译和成熟的内存池策略仍然必须跟进。但这是第一次答案的完整形状变得清晰。账户是代码。交易是三种帧类型的程序。用户的条件随签名一同旅行。十年的提案一直在重新发现规则属于哪里,现在结论很明确:在协议中,在账户下,在用户命令下。Hegota升级要么包含该答案,要么不包含。无论如何,终局终于有了附加的规范编号。
- 原文链接: x.com/biconomy/status/20...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~