摘要

引入一种新的EIP-2718交易类型,允许外部拥有账户(EOA)为其账户设置代码。这是通过在交易中附加授权元组列表来实现的,每个元组的格式为[chain_id, address, nonce, y_parity, r, s]。对于每个元组,授权账户的代码将被写入委托指示符0xef0100 || address。所有代码执行操作都必须加载并执行委托指向的代码

动机

尽管智能合约钱包生态系统取得了巨大进步,但EOA阻碍了用户体验改进在整个应用栈中的广泛采用。因此,本EIP重点为EOA添加短期功能改进,使UX改进能够渗透到整个应用栈。本EIP设计的三个特定功能是:

  • 批处理:允许同一用户在单个原子交易中执行多个操作
  • 赞助:账户X代表账户Y支付交易费用
  • 权限降级:用户可以签署子密钥并赋予其特定权限

规范

参数

参数
SET_CODE_TX_TYPE0x04
MAGIC0x05
PER_AUTH_BASE_COST12500
PER_EMPTY_ACCOUNT_COST25000

设置代码交易

引入新的EIP-2718交易类型"设置代码交易",其中:

  • TransactionType为SET_CODE_TX_TYPE

  • TransactionPayload是以下字段的RLP序列化:

    rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
    destination, value, data, access_list, authorization_list, signature_y_parity,
    signature_r, signature_s])
    
  • authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]

  • 外部交易的字段遵循EIP-4844语义

  • 签名基于keccak256(SET_CODE_TX_TYPE || TransactionPayload)

  • 授权列表长度不能为零

  • 授权元组中的字段必须符合特定边界

行为

在交易执行开始前(发送方nonce递增后)处理授权列表:

对于每个[chain_id, address, nonce, y_parity, r, s]元组:

  1. 验证chain ID为0或当前链的ID
  2. 验证nonce小于2⁶⁴ - 1
  3. 使用ecrecover恢复授权地址
  4. 验证签名符合EIP-2标准
  5. 将授权地址添加到accessed_addresses
  6. 验证授权账户的代码为空或已委托
  7. 验证授权账户的nonce匹配
  8. 如果授权账户非空,添加gas退款
  9. 将授权账户的代码设置为0xef0100 || address(委托指示符)
  10. 如果address为全零地址,则清除账户代码
  11. 将授权账户的nonce加1

如果任何步骤失败,停止处理当前元组并继续下一个

委托指示符

  • 使用EIP-3541定义的禁止操作码0xef表示代码需要特殊处理
  • 委托强制所有代码执行操作跟随地址指针获取要执行的代码
  • 受影响的执行操作:CALLCALLCODEDELEGATECALLSTATICCALL以及目标地址有委托指示符的任何交易
  • 对于代码读取,只有CODESIZECODECOPY指令受影响

预编译

当预编译地址是委托目标时,检索到的代码被视为空,针对此账户的调用指令将执行空代码

循环

如果委托指示符指向另一个委托,创建潜在的委托链或循环,客户端必须只检索第一个代码,然后停止跟随委托链

燃气成本

  • 内在成本继承自EIP-2930
  • 额外成本:PER_EMPTY_ACCOUNT_COST * 授权列表长度
  • 交易发送方为所有授权元组付费,无论有效性或重复性
  • 如果代码执行指令在解析委托代码期间访问冷账户,则添加额外的COLD_ACCOUNT_READ_COST

交易发起

修改EIP-3607的限制,允许代码为有效委托指示符的EOA发起交易。具有任何其他代码值的账户不得发起交易

原理阐述

总体设计理念

代码委托的持久性

早期版本中,代码在交易完成后会被清除,但这可能导致UX改进工作流的分裂。通过引入持久委托,增加了足够的部署摩擦,有望统一工作流。

无初始化代码

运行初始化代码不可取,因为:

  • 需要广泛测试的新执行模式
  • 可能用于标准智能合约钱包不可能的目的
  • 缺乏原子性会推动用户选择完整的智能合约钱包解决方案

通过模板创建

用户应通过指定链上已部署代码的地址来指定要运行的代码,而不是直接在交易中指定字节码。这种方式更高效,避免了数十亿次的不必要复制

与应用程序和钱包的交互

应用程序不能期望用户可以签署授权,钱包有责任不提供此类接口。没有安全的方式提供此接口,因为授权指定的代码对账户具有无限制访问权限

与未来账户抽象的向前兼容性

本EIP设计为与最终账户抽象向前兼容,用户签署的地址可以直接指向现有的ERC-4337钱包代码,避免创建两个独立的UX工作流

自我赞助:允许tx.origin设置代码

允许tx.origin设置和执行自己的委托代码,但打破了msg.sender == tx.origin仅在交易最顶层执行帧中成立的约定。这会影响包含require(msg.sender == tx.origin)检查的智能合约

技术细节原理

委托成本

PER_AUTH_BASE_COST设置为12500 gas,基于对系统影响的评估:

  • 101字节calldata:1616 gas
  • 恢复授权地址:3000 gas
  • 读取nonce和代码:2600 gas
  • 在已预热账户存储值:200 gas
  • 部署代码:4600 gas
  • 总计:12016 gas,向上取整为12500 gas

清除委托指示符

包含特殊情况允许用户清除委托指示符,恢复EOA的原始状态,避免第一次接触时的额外COLD_ACCOUNT_READ_COST

无指令禁止

虽然考虑了在EOA上下文中禁止几个指令族,但作者认为没有令人信服的理由这样做,因为这会导致智能合约钱包和EOA智能合约钱包沿着不同的UX工作流发展

跨链可塑性保护

签名时包含chain ID可以减少授权范围。当需要通用部署时,只需将chain ID设置为0

仅代码执行委托

其他代码检索操作(如EXTCODEHASH)不会自动跟随委托,它们操作委托指示符本身。如果跟随委托,账户将能够暂时伪装具有特定代码哈希,破坏依赖代码哈希作为账户可能行为定义的合约

预先收取最高费用

计算内在gas成本时,为每个委托收取最坏情况成本。稍后在处理授权列表时,如果账户已存在于状态中,则发放退款

无blob,无合约创建

交易应被视为专用工具,不一定是一种类型解决所有问题。EIP-7702对交易传播有不同的影响,没有必要通过使其成为所有可能功能的超集来使这些规则复杂化

禁止委托给预编译

考虑到预编译本身是边缘情况,且技术上没有与其账户关联的代码,作者决定在用户委托给预编译时不执行预编译逻辑,这样实现起来稍微简单一些

非空授权列表要求

设置代码交易必须至少有一个授权才被视为有效,这是为了防止发送方将类型4交易用作通用交易格式

向后兼容性

本EIP打破了一些约定:

  1. 账户余额只能因源自该账户的交易而减少 → 委托后,对账户的任何调用也可能导致余额减少
  2. EOA nonce在交易执行开始后可能不会增加 → 委托后,账户可能在执行期间调用创建操作,导致nonce增加
  3. tx.origin == msg.sender只能在执行的最顶层帧中为真 → 委托后,账户可以在每个交易中调用多个调用

安全考量

安全委托合约的实现

委托合约应注意以下陷阱并需要账户授权进行签名:

  • 重放保护(如nonce)
  • value值 - 否则恶意赞助者可能在受调用者中引起意外效果
  • gas - 否则恶意赞助者可能导致受调用者耗尽gas并失败
  • 目标/calldata - 否则恶意行为者可能调用任意合约中的任意函数

抢先初始化

智能合约钱包开发者必须考虑在账户中设置代码而不执行的影响。为确保账户只能使用所需值初始化,智能合约钱包开发者必须验证用于设置目的的账户初始calldata已使用ecrecover由EOA密钥签名

存储管理

更改账户的委托是安全关键操作,特别是如果新委托的代码不是有意识计并测试为旧代码的升级。为确保从一委托合约安全迁移到另一委托合约,这些合约应使用避免意外冲突的方式使用存储

作为tx.origin设置代码

允许EIP-7702的发送方也设置代码可能会:

  • 破坏依赖tx.origin的原子三明治保护
  • 破坏require(tx.origin == msg.sender)风格的重入保护 作者认为允许这样做的风险是可接受的

赞助交易中继器

授权账户可能通过使授权无效或清除相关资产,导致赞助交易中继器花费gas而得不到补偿。中继器应设计时考虑这些情况

交易传播

允许EOA通过委托指示符表现为智能合约对交易传播提出了一些挑战。传统上,EOA只能通过交易发送价值。使用本EIP,可能导致来自其他账户的交易过时。作者建议客户端不要为任何具有非零委托指示符的EOA接受多于一个待处理交易