使用 EIP-7702 实现账户抽象
本指南将 EIP-7702 应用于 EOA,并使用委托将多个 USDT0 传输批量处理为一笔原子交易。EOA 在整个过程中保持其地址和私钥不变。对于会话密钥和消费限额,请参阅使用 EIP-7702 的会话密钥,该密钥使用经过审计的智能账户而不是自定义委托。
先决条件
概览
EIP-7702 引入了一种新的交易类型(0x04),它包含一个 authorizationList。每个授权都指定一个智能合约,EOA 执行该智能合约的代码。流程如下:
- 选择或部署委托合约:一个标准的 Solidity 合约,实现您希望 EOA 使用的逻辑。尽可能使用经过审计的合约。
- 签署授权:EOA 所有者签署一条消息,授权委托合约。
- 提交 EIP-7702 交易:交易包含授权,EOA 在执行期间运行委托的代码。
用例:批量交易
批量处理将多个调用(代币传输、批准或任何组合)在一个原子交易中运行。批量支付就是一个例子:EOA 不会为工资发放发送十笔单独的交易,而是同时执行所有交易。
步骤 1:部署批量执行器
BatchExecutor 按顺序运行一系列调用,如果任何调用失败,则回滚整个批次。它只接受来自 EOA 本身的调用,因此只有所有者的密钥才能触发批次。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title BatchExecutor (example, unaudited)
/// @notice EIP-7702 delegate that executes a batch of calls from the EOA.
contract BatchExecutor {
struct Call {
address target;
uint256 value;
bytes data;
}
function execute(Call[] calldata calls) external payable {
// SAFE: only the EOA itself, signing with its own key, can run a batch.
require(msg.sender == address(this), "only self");
for (uint256 i = 0; i < calls.length; i++) {
(bool success,) = calls[i].target.call{value: calls[i].value}(calls[i].data);
require(success, "call failed");
}
}
}部署一次。任意数量的 EOA 都可以委托给同一个部署。
forge create src/BatchExecutor.sol:BatchExecutor \
--rpc-url https://rpc.testnet.stable.xyz \
--private-key $PRIVATE_KEY \
--broadcastDeployer: 0xYourAddress...
Deployed to: 0xBatchExecutor...
Transaction hash: 0x...步骤 2:签署授权
EOA 所有者签署一份指定委托合约的授权。此授权包含在 EIP-7702 交易中。
// config.ts
import { ethers } from "ethers";
export const STABLE_TESTNET_RPC = "https://rpc.testnet.stable.xyz";
export const STABLE_TESTNET_CHAIN_ID = 2201;
export const USDT0_ADDRESS = "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9";
export const DELEGATE_ADDRESS = "0xBatchExecutor..."; // from Step 1
export const provider = new ethers.JsonRpcProvider(STABLE_TESTNET_RPC);
export const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);// signAuthorization.ts
import { DELEGATE_ADDRESS, STABLE_TESTNET_CHAIN_ID, provider, wallet } from "./config";
export async function signAuthorization() {
const txNonce = await provider.getTransactionCount(wallet.address);
return wallet.authorize({
chainId: STABLE_TESTNET_CHAIN_ID,
address: DELEGATE_ADDRESS,
// The EOA sends this transaction itself. Its nonce increments before the
// authorization is checked, so the authorization must use the next nonce.
nonce: txNonce + 1,
});
}步骤 3:提交 EIP-7702 交易
将授权与对 execute 的调用结合起来。此示例在一个交易中批量处理三个 USDT0 传输。
import { ethers } from "ethers";
import { wallet, USDT0_ADDRESS } from "./config";
import { signAuthorization } from "./signAuthorization";
const usdt0Interface = new ethers.Interface([
"function transfer(address to, uint256 amount)",
]);
const batchInterface = new ethers.Interface([
"function execute((address target, uint256 value, bytes data)[] calls)",
]);
async function main() {
const recipients = [
{ to: "0xAlice...", amount: ethers.parseUnits("100", 6) },
{ to: "0xBob...", amount: ethers.parseUnits("200", 6) },
{ to: "0xCarol...", amount: ethers.parseUnits("150", 6) },
];
const batchData = batchInterface.encodeFunctionData("execute", [
recipients.map(({ to, amount }) => ({
target: USDT0_ADDRESS,
value: 0n,
data: usdt0Interface.encodeFunctionData("transfer", [to, amount]),
})),
]);
const signedAuth = await signAuthorization();
const tx = await wallet.sendTransaction({
type: 4, // EIP-7702 transaction type
to: wallet.address, // call is directed at the EOA itself
data: batchData, // execute() call to run
authorizationList: [signedAuth],
maxPriorityFeePerGas: 0n,
});
const receipt = await tx.wait(1);
console.log("Batch transactions executed in tx:", receipt.hash);
}
main();Batch transactions executed in tx: 0x...EOA 在一个原子交易中执行所有三个传输。委托会持续存在,因此后续批次只需向 wallet.address 发送常规交易,其中包含 execute calldata 且没有 authorizationList。
消费限额和会话密钥
消费限额和会话密钥允许第二个密钥在所有者设定的规则内为 EOA 操作。使用经过审计的智能账户而不是自定义委托:
- 访问控制是整个功能。 自定义委托中遗漏的调用者检查会将 EOA 暴露给任何人,而不仅仅是会话密钥。
- 存储比委托更持久。 委托状态存在于 EOA 的存储中。重新委托给具有不同存储布局的合约可能会误读或覆盖它。
使用 EIP-7702 的会话密钥 将 EOA 委托给 Modular Account v2 并授予密钥过期的 USDT0 限额。该限额就是消费限额。
重要注意事项
- 持久委托:委托会持续存在,直到 EOA 明确更改或清除它。它不限于单个交易。
- Gas 成本:由于授权处理,EIP-7702 交易的基本 Gas 略高,当委托批量处理多个调用时,这会得到抵消。
- 使用经过审计的委托:恶意或不受保护的委托合约可能会耗尽 EOA 的资产。上面的
BatchExecutor是一个未经审计的示例。在生产中只委托给经过审计的合约。
主要收获
- EIP-7702 允许 EOA 执行智能合约逻辑,而无需迁移到新的账户类型。
- 在 Stable 上,EIP-7702 允许在现有 EOA 上进行批量支付、消费限额和范围会话密钥。
- 委托上的每个函数都可以被任何人通过 EOA 调用,因此委托必须检查调用者。
- 委托会持续存在,直到明确更改。始终使用经过审计的委托合约。
接下来推荐
- 使用 EIP-7702 的会话密钥:授予一个有范围的、过期的密钥,可以在没有所有者密钥的情况下发送交易。
- 订阅和收集:将 EIP-7702 应用于使用 SubscriptionManager 的定期订阅支付。
- EIP-7702 概念:在发布之前了解委托模型。
- EIP-7702 参考:查找
0x04交易格式和授权字段。

