本文档介绍了Meta Transactions,即从交易发起者抽象执行上下文的能力,遵循ERC-2771规范。主要介绍了ERC2771Context和ERC2771Forwarder两个合约,分别用于重写执行上下文的发送者和calldata,以及实现一个生产级别的转发器,用于转发由EOA离线签名的操作请求。文档还详细说明了每个合约的函数、事件、错误以及安全考虑。
建议在 https://docs.openzeppelin.com/contracts/api/metatx 上查看此文档 |
此目录包含用于添加元交易功能(即,从交易发起方抽象执行上下文)的合约,遵循 ERC-2771 规范。
ERC2771Context
: 提供一种机制,通过受信任的转发器指定的自定义值来覆盖执行上下文的发送者和calldata(msg.sender
和 msg.data
)。
ERC2771Forwarder
: 一个生产就绪的转发器,它转发由 EOA 链下签名的操作请求。
ERC2771Context
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
具有 ERC-2771 支持的上下文变体。
避免在依赖于特定calldata长度的合约中使用此模式,因为它们会受到任何转发器的影响,转发器的 msg.data 根据 ERC-2771 规范以 from 地址作为后缀,将地址大小(以字节为单位,20)添加到calldata大小。 一个意想不到的行为的例子可能是尝试调用 receive 函数时,意外地调用了回退函数(或其他函数),只有当 msg.data.length == 0 时才能访问 receive 函数。 |
在此合约中使用 delegatecall 是危险的,可能会导致上下文损坏。<br>转发到此合约的任何请求触发对自身的 delegatecall ,都将导致无效的 _msgSender <br>恢复。 |
函数
constructor(address trustedForwarder_)
internaltrustedForwarder() → address
public返回受信任转发器的地址。
isTrustedForwarder(address forwarder) → bool
public指示任何特定地址是否为受信任的转发器。
_msgSender() → address
internalmsg.sender
的覆盖。 只要调用不是由受信任的转发器执行的,或者calldata长度小于 20 字节(地址长度),则默认为原始 msg.sender
。
_msgData() → bytes
internalmsg.data
的覆盖。 只要调用不是由受信任的转发器执行的,或者calldata长度小于 20 字节(地址长度),则默认为原始 msg.data
。
_contextSuffixLength() → uint256
internalERC-2771 指定上下文作为单个地址(20 字节)。
ERC2771Forwarder
import "@openzeppelin/contracts/metatx/ERC2771Forwarder.sol";
与 ERC-2771 合约兼容的转发器。 参见 ERC2771Context
。
此转发器对包含以下内容的转发请求进行操作:
from
: 代表其进行操作的地址。 它必须等于请求签名者。
to
: 应该调用的地址。
value
: 要附加到请求调用的原生代币的数量。
gas
: 将随请求的调用一起转发的 gas 限制量。
nonce
: 唯一的交易排序标识符,用于避免重放性和请求失效。
deadline
: 请求不再可执行的时间戳。
data
: 要随请求的调用一起发送的编码 msg.data
。
如果 Relayer 正在处理大量请求,则能够提交批处理。 通过高 吞吐量,Relayer 可能会遇到链的限制,例如mempool中交易数量的限制。 在这些情况下,建议在多个帐户之间分配负载。
批处理请求包括一个可选的未使用 msg.value 的退款,这是通过调用空calldata实现的。 虽然这在 ERC-2771 合规性的范围内,但如果退款接收者碰巧认为转发器是受信任的转发器,则它必须正确处理 msg.data.length == 0 。 OpenZeppelin Contracts 4.9.3 之前的版本中的 ERC2771Context 无法正确处理此问题。 |
如果 Relayer 提交转发请求,它应该愿意支付请求中指定的 gas 量的 100%。 此合约未对此 gas 实施任何形式的补偿,并且假定存在链外激励,鼓励 Relayer 代表签名者支付执行费用。 通常,Relayer 由一个项目运营,该项目将这视为用户获取成本。
通过提供 gas 费用支付,Relayer 有可能让攻击者将 gas 用于与预期链外激励不符的其他目的。 如果你运行 Relayer,请考虑将目标合约和函数选择器列入白名单。 在专门转发 ERC-721 或 ERC-1155 时,请考虑拒绝使用 data
字段,因为它可用于执行任意代码。
函数
Nonces (随机数)
EIP712
Events (事件)
IERC5267
Errors (错误)
Nonces (随机数)
Internal Variables (内部变量)
constructor(string name)
publicverify(struct ERC2771Forwarder.ForwardRequestData request) → bool
public如果在当前块时间戳下,请求对于提供的 signature
有效,则返回 true
。
当目标信任此转发器、请求未过期(未达到deadline),并且签名者与签名请求的 from
参数匹配时,交易被认为是有效的。
请求可能在此处返回 false,但如果提供退款接收者,则不会导致 executeBatch 还原。 |
execute(struct ERC2771Forwarder.ForwardRequestData request)
public使用 ERC-2771 协议代表 `signature’s signer 执行 request
。 提供给请求调用的 gas 可能不完全是请求的量,但调用不会耗尽 gas。 如果请求无效或调用还原,则将还原,在这种情况下,不会消耗nonce。
要求:
请求值应等于提供的 msg.value
。
根据 verify
,请求应有效。
executeBatch(struct ERC2771Forwarder.ForwardRequestData[] requests, address payable refundReceiver)
public具有可选退款和原子执行的 execute
的批量版本。
如果批处理包含至少一个无效请求(参见 verify
),
将跳过该请求,并且 refundReceiver
参数将在执行结束时收到未使用的请求值。 这样做是为了防止在请求无效或已提交时还原整个批处理。
如果 refundReceiver
为 address(0)
,则当至少一个请求无效时,此函数将还原,而不是跳过它。 如果需要以原子方式执行批处理(至少在顶层),这可能很有用。 例如,如果 Relayer 正在使用一种避免包含已还原交易的服务,则可以opt-out退款(因此原子性)。
要求:
请求的值的总和应等于提供的 msg.value
。
当 refundReceiver
为零地址时,所有请求都应有效(参见 verify
)。
仅对于第一级转发调用,设置零 refundReceiver 保证了全有或全无的请求执行。 如果转发的请求通过另一个子调用调用合约,则第二级调用可能会在没有顶层调用还原的情况下还原。 |
_validate(struct ERC2771Forwarder.ForwardRequestData request) → bool isTrustedForwarder, bool active, bool signerMatch, address signer
internal验证是否可以在当前块时间戳下,代表 request.signer
使用给定的 request.signature
执行所提供的请求。
_recoverForwardRequestSigner(struct ERC2771Forwarder.ForwardRequestData request) → bool isValid, address signer
internal返回一个元组,其中包含 EIP712 转发请求消息哈希的已恢复签名者和一个布尔值,指示签名是否有效。
如果 ECDSA.tryRecover 指示没有恢复错误,则认为签名有效。 |
_execute(struct ERC2771Forwarder.ForwardRequestData request, bool requireValidRequest) → bool success
internal验证并执行签名请求,返回请求调用的 success
值。
没有 msg.value 验证的内部函数。
要求:
调用者必须提供足够的 gas 才能通过调用进行转发。
如果 requireValidRequest
为 true,则请求必须有效(参见 verify
)。
发出 ExecutedForwardRequest
事件。
使用此函数不会检查是否已发送所有 msg.value ,可能会导致 value 卡在合约中。 |
_isTrustedByTarget(address target) → bool
internal返回目标是否信任此转发器。
此函数对目标合约执行静态调用,调用 ERC2771Context.isTrustedForwarder
函数。
请考虑此转发器的执行是免许可的。 如果没有此检查,任何人都可以转移由此转发器拥有或批准的资产。 |
ExecutedForwardRequest(address indexed signer, uint256 nonce, bool success)
event当执行 ForwardRequest
时发出。
转发请求不成功可能是由于签名无效、deadline 过期,或者只是请求的调用还原。 合约保证 Relayer 无法强制请求的调用耗尽 gas。 |
ERC2771ForwarderInvalidSigner(address signer, address from)
error请求的 from
与恢复的 signer
不匹配。
ERC2771ForwarderMismatchedValue(uint256 requestedValue, uint256 msgValue)
errorrequestedValue
与可用的 msgValue
不匹配。
ERC2771ForwarderExpiredRequest(uint48 deadline)
error请求的 deadline
已过期。
ERC2771UntrustfulTarget(address target, address forwarder)
error请求目标不信任 forwarder
。
bytes32 _FORWARD_REQUEST_TYPEHASH
internal constant
- 原文链接: docs.openzeppelin.com/co...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!