什么是 EIP-7702
EIP-7701 是以太坊 Pectra 升级的内容之一。是一种新的交易类型( 0x4 )。提高了以太坊的账号抽象的能力,支持直接使用 EOA 账号去执行临时智能合约。可以实现批量交易、gas 代付 等能力,这将会在钱包开发中起到很大作用。
EIP-7702 的能力
-
批量交易:可以在一次
EOA账号发起交易时,发送多次交易。(非EIP-7702类型的EOA账号一次只能发送一笔交易) -
gas 代付:允许第三方(例如接收方或代理机构)为你支付本次交易的
gas费用。(原始EOA交易没有gas代付能力) -
安全访问:授予临时的对合约的访问,用完后
EOA账号恢复成为普通账号。调用时无须暴露私钥。 -
合约共享:逻辑合约可以共享,只需部署一次,便可提供给多个
EOA账号使用。逻辑合约可永久存在链上(临时合约用完即毁)。
EIP-7702 在钱包场景下的作用
可以用在交易所钱包的 归集 操作上。传统的以太坊 EOA 账号进行归集操作,从用户地址发送到归集地址,只能一笔一笔进行归集,并且需要交易所下发一笔小额资金作为归集 gas 费用。效率十分低下且存在粉尘资金增多、额外消耗 gas 的问题。而采用 EIP-7702 的新交易类型,可以在共用 EOA 账号的同时,平滑升级到支持批量归集、gas 代付的能力。
EIP-7702 实现的核心原理
-
签名授权
EOA账号在签名时候,签订一个授权信息,内容包括链 ID、nonce、委托地址、EIP-712 的签名。这个授权信息供验证者节点打包交易时,从委托地址中查出运行代码,然后附加到EOA账号中。
- 临时代码注入
节点收到 EOA账号发送的交易后,验证EOA账号的签名,从链上读取委托合约的bytecode然后临时注入到EOA账号的地址上。 -
自动代码卸载 在交易执行完后,该地址又会变回普通
EOA地址,EXTCODEHASH(address) == 0x0。代码区会变回0。且状态不会存储到链上,确保不存在污染。
EIP-7702 实操及流程解析
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/ethereum/eip-7702
- 安装依赖
安装
forge库和OZ代码库
forge install foundry-rs/forge-std
forge install OpenZeppelin/openzeppelin-contracts
- 启动 anvil
注意,在未完全升级之前,必须携带
--hardfork prague参数,否则无法执行。
anvil --hardfork prague
- 使用 script 部署合约
❯ forge script script/BatchCallAndSponsor.s.sol \
--rpc-url http://127.0.0.1:8545 \
--broadcast \
--tc BatchCallAndSponsorScript -vvvv
观察到以下结果,代码部署成功。
6. 观察日志
- performDirectExecution() 批量转账日志观察
- performSponsoredExecution() gas 代付日志观察
6. 部署流程解析
- 原始 EOA 转账
首先来分析一下没有 EIP-7702 的原始转账情况,用户 A 通过离线签名,将交易发送到以太坊 RPC 节点,在通过对用户 A 的签名认证之后,可以将交易广播到以太坊网络。此时,交易只能一笔一笔的发送,且无 gas 代付能力。

- performDirectExecution() 批量转账
而在使用了
EIP-7702后,交易流程发生了变化。首先,我们来看一个总的执行图。后面再回去根据代码分析。
1. 在这个执行图中(A 代指 Alice,B 代指 Bob),EOA 账户 Alice 离线发起委托签名,签名时,将委托的逻辑合约包含在其中,发送给以太坊 RPC 节点。

-
然后,
RPC节点经过验证签名有效后,读取这个逻辑合约的字节码,注入给EOA用户Alice。此时,用户Alice从一个EOA账户升级成为一个临时合约账户(合约的地址和用户Alice的地址一致),可以执行合约的逻辑(批量转账等)
-
在此处
Alice的临时合约中,因为合约的调用者是Alice,无须对Alice进行验签处理,使用require(msg.sender == address(this))即可完成签名的验证。
-
此外,由于发送给
RPC节点时from地址为Alice,to地址也为Alice。在此过程中,Alice为合约账户,属于合约内部交易,所以这条记录不会被记录在以太坊的交易记录中。 -
最后,这个账户就拥有了合约的调用能力,其中的交易给
Bob的两笔交易(ETH和ERC20代币)都通过B.call()的方式进行发送到以太坊网络中。(from地址为Alice,to地址为Bob,from地址属于合约地址,to地址属于EOA地址,属合约内部交易,故也不存在交易记录中)
- performSponsoredExecution() gas 代付

`gas` 代付的情况相对于普通批量转账的场景而言,稍微复杂与一点。可以分为以下几步:
1. **Alice 构造委托合约,并签名**:

2. **Bob 持有 Alice 的数据,并签名**:

3. **Bob 发送交易到 RPC 节点**:

4. `RPC` 节点对 `Bob` 进行验签,通过后读取 `Alice` 委托的逻辑合约代码,注入到 `Alice` 的地址中。然后广播交易。此时,用户 `Alice` 从一个 `EOA` 账户升级成为一个临时合约账户(合约的地址和用户 `Alice` 的地址一致),可以执行合约的逻辑(批量转账等)
5. 并且,由于在这种 `gas` 代付的方案中,交易的发起者是 `Bob`,所以 `RPC` 节点在进行验签的时候,只会验证 `Bob` 的签名,`Alice` 的签名并不会去处理。在合约中,没办法像上面批量交易那样使用 `require(msg.sender == address(this))` 故我们临时合约中还需要去验证 `Alice` 的签名,以防止伪造交易的情况出现。

6. 此外,由于发送给 `RPC` 节点时,发送者 `from` 为 `Bob`,接受者 `to` 地址为 `Alice`,`Bob` 是一个 `EOA` 账户,`Alice` 为一个合约账户,故这笔交易记录会被记录上。

7. 但是,由于在 `Alice` 合约内部,发生了从 `Alice` 转账给 `Tom`(也可以是 `Bob`) 的情况,`Alice` 属于合约地址,`Tom`(`Bob`) 属EOA 地址,属合约内部交易,故这笔记录也不会存在以太坊的交易记录中。
