EOA 委托
EIP-7702 引入了一种新的交易类型 (0x4
),它授予 外部拥有账户 (EOAs) 将执行委托给智能合约的能力。这对于使传统的 EVM 账户能够做到以下几点特别有用:
本节将引导您完成遵循 ERC-7702 将 EOA 委托给合约的过程。这允许您使用 EOA 的私钥来签名并执行具有自定义执行逻辑的操作。结合 ERC-4337 基础设施,用户可以通过 paymasters 实现 gas 赞助。
委托执行
EIP-7702 使 EOA 能够将其执行能力委托给智能合约,从而有效地弥合了传统账户和 智能账户 之间的差距。SignerERC7702
实用程序通过验证针对 EOA 地址 (address(this)
) 的签名来促进这种委托,从而更容易在智能合约账户中实现 EIP-7702。
Unresolved include directive in modules/ROOT/pages/eoa-delegation.adoc - include::api:example$account/MyAccountERC7702.sol[]
用户可以委托给 ERC-7821 的实例,以获得一个不使用 ERC-4337 相关代码的最小批量执行器。
|
签名授权
要授权委托,EOA 所有者需要对包含链 ID、nonce、委托地址和签名组件的消息进行签名 (即 [chain_id, address, nonce, y_parity, r, s]
)。此签名授权有两个目的:它将执行限制为仅委托合约,并防止重放攻击。
EOA 为每个链上的每个授权地址维护一个委托指示符,该指示符指向合约,该合约的代码将在 EOA 的上下文中执行以处理委托的操作。
以下是如何使用 viem 构建授权:
// 记住不要硬编码你的私钥!
const eoa = privateKeyToAccount('<YOUR_PRIVATE_KEY>');
const eoaClient = createWalletClient({
account: eoa,
chain: publicClient.chain,
transport: http(),
});
const walletClient = createWalletClient({
account, // 参考 Viem 的 `privateKeyToAccount`
chain, // import { ... } from 'viem/chains';
transport: http(),
})
const authorization = await eoaClient.signAuthorization({
account: walletClient.account.address,
contractAddress: '0x<YOUR_DELEGATE_CONTRACT_ADDRESS>',
// 如果你的 walletClient 的 account 是发送交易的 account,
// 则使用此字段代替 `account`
// executor: "self",
});
在实施委托合约时,请确保它们需要的签名可以避免重放性(例如,域分隔符,nonce)。 实施不佳的委托可能会让恶意行为者几乎完全控制签名者的 EOA。 |
发送设置代码交易
签署授权后,您需要发送一个 SET_CODE_TX_TYPE
(0x04) 交易,将委托指示符 (即 0xef0100 || address
) 写入您的 EOA 代码,这将告诉 EVM 加载和执行来自指定地址的代码,当在您的 EOA 上执行操作时。
// 发送包含 `authorization` 的 `data`
const receipt = await walletClient
.sendTransaction({
authorizationList: [authorization],
data: '0x<CALLDATA_TO_EXECUTE_IN_THE_ACCOUNT>',
to: eoa.address,
})
.then((txHash) =>
publicClient.waitForTransactionReceipt({
hash: txHash,
})
);
// 打印收据
console.log(userOpReceipt);
要删除委托并将您的 EOA 恢复到原始状态,您可以发送一个授权元组指向零地址 (0x0000000000000000000000000000000000000000
) 的 SET_CODE_TX_TYPE
交易。这将清除帐户的代码并将其代码哈希重置为空哈希,但是,请注意,它不会自动清理 EOA 存储。
在更改帐户的委托时,请确保新委托的代码经过专门设计和测试,可以作为对旧代码的升级。为了确保委托合约之间安全迁移,请遵循 ERC-7201 使用避免意外冲突的命名空间存储。
由于潜在的存储冲突,更新委托指示符可能会导致您的 EOA 无法使用。我们建议遵循与 编写可升级智能合约 相似的做法。 |
与 ERC-4337 一起使用
在 EOA 上设置代码以执行逻辑的能力允许用户利用 ERC-4337 基础设施来处理用户操作。开发人员只需要将 Account
合约与 SignerERC7702
结合起来,即可实现开箱即用的 ERC-4337 兼容性。
发送 UserOp
一旦您的 EOA 委托给 ERC-4337 兼容帐户,您就可以通过入口点合约发送用户操作。用户操作包括所有必要的执行字段,包括 gas 限制、费用以及要执行的实际调用数据。入口点将验证操作并在您委托帐户的上下文中执行它。
const userOp = {
sender: eoa.address,
nonce: await entrypoint.read.getNonce([eoa.address, 0n]),
initCode: "0x" as Hex,
callData: '0x<CALLDATA_TO_EXECUTE_IN_THE_ACCOUNT>',
accountGasLimits: encodePacked(
["uint128", "uint128"],
[
100_000n, // verificationGas
300_000n, // callGas
]
),
preVerificationGas: 50_000n,
gasFees: encodePacked(
["uint128", "uint128"],
[
0n, // maxPriorityFeePerGas
0n, // maxFeePerGas
]
),
paymasterAndData: "0x" as Hex,
signature: "0x" as Hex,
};
const userOpHash = await entrypoint.read.getUserOpHash([userOp]);
userOp.signature = await eoa.sign({ hash: userOpHash });
const userOpReceipt = await eoaClient
.writeContract({
abi: EntrypointV08Abi,
address: entrypoint.address,
authorizationList: [authorization],
functionName: "handleOps",
args: [[userOp], eoa.address],
})
.then((txHash) =>
publicClient.waitForTransactionReceipt({
hash: txHash,
})
);
console.log(userOpReceipt);
当使用赞助交易中继器时,请注意,授权帐户可能会导致中继器花费 gas 而未获得报销,方法是使授权无效(增加帐户的 nonce)或将相关资产从帐户中扫出。中继器可以实施保护措施,例如要求保证金或使用声誉系统。 |