摘要
引入一种新的EIP-2718交易类型,允许外部拥有账户(EOA)为其账户设置代码。这是通过在交易中附加授权元组列表来实现的,每个元组的格式为[chain_id, address, nonce, y_parity, r, s]。对于每个元组,授权账户的代码将被写入委托指示符0xef0100 || address。所有代码执行操作都必须加载并执行委托指向的代码
动机
尽管智能合约钱包生态系统取得了巨大进步,但EOA阻碍了用户体验改进在整个应用栈中的广泛采用。因此,本EIP重点为EOA添加短期功能改进,使UX改进能够渗透到整个应用栈。本EIP设计的三个特定功能是:
- 批处理:允许同一用户在单个原子交易中执行多个操作
- 赞助:账户X代表账户Y支付交易费用
- 权限降级:用户可以签署子密钥并赋予其特定权限
规范
参数
| 参数 | 值 |
|---|---|
| SET_CODE_TX_TYPE | 0x04 |
| MAGIC | 0x05 |
| PER_AUTH_BASE_COST | 12500 |
| PER_EMPTY_ACCOUNT_COST | 25000 |
设置代码交易
引入新的EIP-2718交易类型"设置代码交易",其中:
TransactionType为
SET_CODE_TX_TYPETransactionPayload是以下字段的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]元组:
- 验证chain ID为0或当前链的ID
- 验证nonce小于2⁶⁴ - 1
- 使用
ecrecover恢复授权地址 - 验证签名符合EIP-2标准
- 将授权地址添加到
accessed_addresses - 验证授权账户的代码为空或已委托
- 验证授权账户的nonce匹配
- 如果授权账户非空,添加gas退款
- 将授权账户的代码设置为
0xef0100 || address(委托指示符) - 如果address为全零地址,则清除账户代码
- 将授权账户的nonce加1
如果任何步骤失败,停止处理当前元组并继续下一个
委托指示符
- 使用EIP-3541定义的禁止操作码
0xef表示代码需要特殊处理 - 委托强制所有代码执行操作跟随地址指针获取要执行的代码
- 受影响的执行操作:
CALL、CALLCODE、DELEGATECALL、STATICCALL以及目标地址有委托指示符的任何交易 - 对于代码读取,只有
CODESIZE和CODECOPY指令受影响
预编译
当预编译地址是委托目标时,检索到的代码被视为空,针对此账户的调用指令将执行空代码
循环
如果委托指示符指向另一个委托,创建潜在的委托链或循环,客户端必须只检索第一个代码,然后停止跟随委托链
燃气成本
- 内在成本继承自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打破了一些约定:
- 账户余额只能因源自该账户的交易而减少 → 委托后,对账户的任何调用也可能导致余额减少
- EOA nonce在交易执行开始后可能不会增加 → 委托后,账户可能在执行期间调用创建操作,导致nonce增加
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接受多于一个待处理交易