编写支持 GSN 的合约 - OpenZeppelin 文档

本文介绍了如何使用 OpenZeppelin Contracts 编写能够接收来自 Gas Station Network (GSN) 的交易的智能合约。

你当前阅读的不是此文档的最新版本。5.x 是最新版本。

编写支持 GSN 的合约

Gas Station Network 允许你构建由你支付用户交易费用的应用,因此他们无需持有以太币来支付 gas 费用,从而简化了他们的上手流程。在本指南中,我们将学习如何使用 OpenZeppelin Contracts 编写可以从 GSN 接收交易的智能合约。

如果你不熟悉 GSN,你可能需要先查看系统概述,以更清楚地了解如何实现无 gas 交易。否则,请系好安全带!

接收中继调用

编写 recipient 的第一步是继承我们的 GSNRecipient 合约。如果你还继承了其他合约,例如 ERC20,这也很好:添加 GSNRecipient 将使你的所有 token 函数都可被 GSN 调用。

import "@openzeppelin/contracts/GSN/GSNRecipient.sol";

contract MyContract is GSNRecipient, ... {

}

msg.sendermsg.data

在使用 GSN recipient 合约时,你只需要注意一个额外的细节:你绝对不能直接使用 msg.sendermsg.data。 在中继调用中,msg.sender 将是 RelayHub 而不是你的用户! 然而,这并不意味着你无法检索用户的地址:GSNRecipient 提供了两个函数 _msgSender()_msgData(),它们是 msg.sendermsg.data 的直接替代品,同时处理底层细节。 只要你使用这两个函数而不是原始的 getter,一切都很好!

你继承的第三方合约可能不使用这些替代函数,因此在与 GSNRecipient 混合使用时可能不安全。 如果有疑问,请访问我们的支持论坛

接受和收费

与常规合约函数调用不同,每个中继调用都必须经过额外的步骤,这些步骤是 IRelayRecipient 接口的函数,RelayHub 将在你的合约上调用这些函数。 GSNRecipient 包含此接口但没有实现:编写 recipient 的大部分工作都涉及处理这些函数调用。 它们旨在提供灵活性,但基本的 recipient 可以安全地忽略其中大多数,同时仍然保持安全可靠。

OpenZeppelin Contracts 提供了许多经过尝试和测试的方法供你直接使用,但你仍然应该对底层发生的事情有一个基本的了解。

acceptRelayedCall

首先,RelayHub 会询问你的 recipient 合约是否要接收中继调用。 回想一下,你将被 relayer 收取产生的 gas 费用,因此你只应接受你愿意支付的调用!

    function acceptRelayedCall(
        address relay,
        address from,
        bytes calldata encodedFunction,
        uint256 transactionFee,
        uint256 gasPrice,
        uint256 gasLimit,
        uint256 nonce,
        bytes calldata approvalData,
        uint256 maxPossibleCharge
    ) external view returns (uint256, bytes memory);

有多种方法可以实现这一点,包括:

  1. 拥有受信任用户的白名单

  2. 仅接受对 onboarding 函数的调用

  3. 以 token(可能由你发行)向用户收费

  4. 将接受逻辑委托给链下

所有中继调用请求都可以被拒绝,而 recipient 无需承担任何费用。

在此函数中,你返回一个数字,指示你是否接受调用 (0) 或不接受调用(任何其他数字)。 你还可以返回一些任意数据,这些数据将作为执行上下文传递给以下两个函数(pre 和 post)。

pre 和 postRelayedCall

接受中继调用后,RelayHub 将为你的合约提供两次向用户收取调用费用的机会,执行一些簿记等:在实际中继调用之前之后。 这些函数被恰当地命名为 preRelayedCallpostRelayedCall

function preRelayedCall(bytes calldata context) external returns (bytes32);

preRelayedCall 会通知你调用可能产生的最大成本,并且可以用于提前向用户收费。 如果用户可能会花费他们的 allowance 作为调用的一部分,这将非常有用,因此你可以在此处锁定一些资金。

    function postRelayedCall(
        bytes calldata context,
        bool success,
        uint256 actualCharge,
        bytes32 preRetVal
    ) external;

postRelayedCall 将为你提供交易成本的准确估算,使其成为向用户收费的自然场所。 它还会让你知道中继调用是否已还原。 例如,这允许你不向已还原的调用收费 - 但请记住,你仍然会被 relayer 收取费用。

这些函数允许你实现一个流程,例如,你以自定义 token 向用户收取中继交易的费用。 你可以在 pre 中锁定他们的一些 token,并在 post 中执行实际收费。 这类似于 gas 费用在以太坊中的工作方式:网络首先锁定足够的 ETH,以 gas 价格支付交易的 gas limit,然后支付实际花费的费用。

延伸阅读

阅读我们关于 OpenZeppelin Contracts 中预构建和提供的支付策略指南,或查看 GSN 基础合约的 API 参考

← ERC1155

策略 →

  • 原文链接: docs.openzeppelin.com/co...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。