dexorder
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.1) (metatx/ERC2771Context.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Context} from "../utils/Context.sol";
|
||||
|
||||
/**
|
||||
* @dev Context variant with ERC-2771 support.
|
||||
*
|
||||
* WARNING: Avoid using this pattern in contracts that rely in a specific calldata length as they'll
|
||||
* be affected by any forwarder whose `msg.data` is suffixed with the `from` address according to the ERC-2771
|
||||
* specification adding the address size in bytes (20) to the calldata size. An example of an unexpected
|
||||
* behavior could be an unintended fallback (or another function) invocation while trying to invoke the `receive`
|
||||
* function only accessible if `msg.data.length == 0`.
|
||||
*
|
||||
* WARNING: The usage of `delegatecall` in this contract is dangerous and may result in context corruption.
|
||||
* Any forwarded request to this contract triggering a `delegatecall` to itself will result in an invalid {_msgSender}
|
||||
* recovery.
|
||||
*/
|
||||
abstract contract ERC2771Context is Context {
|
||||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
||||
address private immutable _trustedForwarder;
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract with a trusted forwarder, which will be able to
|
||||
* invoke functions on this contract on behalf of other accounts.
|
||||
*
|
||||
* NOTE: The trusted forwarder can be replaced by overriding {trustedForwarder}.
|
||||
*/
|
||||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||||
constructor(address trustedForwarder_) {
|
||||
_trustedForwarder = trustedForwarder_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the trusted forwarder.
|
||||
*/
|
||||
function trustedForwarder() public view virtual returns (address) {
|
||||
return _trustedForwarder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Indicates whether any particular address is the trusted forwarder.
|
||||
*/
|
||||
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
|
||||
return forwarder == trustedForwarder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Override for `msg.sender`. Defaults to the original `msg.sender` whenever
|
||||
* a call is not performed by the trusted forwarder or the calldata length is less than
|
||||
* 20 bytes (an address length).
|
||||
*/
|
||||
function _msgSender() internal view virtual override returns (address) {
|
||||
uint256 calldataLength = msg.data.length;
|
||||
uint256 contextSuffixLength = _contextSuffixLength();
|
||||
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
|
||||
return address(bytes20(msg.data[calldataLength - contextSuffixLength:]));
|
||||
} else {
|
||||
return super._msgSender();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Override for `msg.data`. Defaults to the original `msg.data` whenever
|
||||
* a call is not performed by the trusted forwarder or the calldata length is less than
|
||||
* 20 bytes (an address length).
|
||||
*/
|
||||
function _msgData() internal view virtual override returns (bytes calldata) {
|
||||
uint256 calldataLength = msg.data.length;
|
||||
uint256 contextSuffixLength = _contextSuffixLength();
|
||||
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
|
||||
return msg.data[:calldataLength - contextSuffixLength];
|
||||
} else {
|
||||
return super._msgData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev ERC-2771 specifies the context as being a single address (20 bytes).
|
||||
*/
|
||||
function _contextSuffixLength() internal view virtual override returns (uint256) {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
371
lib_openzeppelin_contracts/contracts/metatx/ERC2771Forwarder.sol
Normal file
371
lib_openzeppelin_contracts/contracts/metatx/ERC2771Forwarder.sol
Normal file
@@ -0,0 +1,371 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (metatx/ERC2771Forwarder.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {ERC2771Context} from "./ERC2771Context.sol";
|
||||
import {ECDSA} from "../utils/cryptography/ECDSA.sol";
|
||||
import {EIP712} from "../utils/cryptography/EIP712.sol";
|
||||
import {Nonces} from "../utils/Nonces.sol";
|
||||
import {Address} from "../utils/Address.sol";
|
||||
import {Errors} from "../utils/Errors.sol";
|
||||
|
||||
/**
|
||||
* @dev A forwarder compatible with ERC-2771 contracts. See {ERC2771Context}.
|
||||
*
|
||||
* This forwarder operates on forward requests that include:
|
||||
*
|
||||
* * `from`: An address to operate on behalf of. It is required to be equal to the request signer.
|
||||
* * `to`: The address that should be called.
|
||||
* * `value`: The amount of native token to attach with the requested call.
|
||||
* * `gas`: The amount of gas limit that will be forwarded with the requested call.
|
||||
* * `nonce`: A unique transaction ordering identifier to avoid replayability and request invalidation.
|
||||
* * `deadline`: A timestamp after which the request is not executable anymore.
|
||||
* * `data`: Encoded `msg.data` to send with the requested call.
|
||||
*
|
||||
* Relayers are able to submit batches if they are processing a high volume of requests. With high
|
||||
* throughput, relayers may run into limitations of the chain such as limits on the number of
|
||||
* transactions in the mempool. In these cases the recommendation is to distribute the load among
|
||||
* multiple accounts.
|
||||
*
|
||||
* NOTE: Batching requests includes an optional refund for unused `msg.value` that is achieved by
|
||||
* performing a call with empty calldata. While this is within the bounds of ERC-2771 compliance,
|
||||
* if the refund receiver happens to consider the forwarder a trusted forwarder, it MUST properly
|
||||
* handle `msg.data.length == 0`. `ERC2771Context` in OpenZeppelin Contracts versions prior to 4.9.3
|
||||
* do not handle this properly.
|
||||
*
|
||||
* ==== Security Considerations
|
||||
*
|
||||
* If a relayer submits a forward request, it should be willing to pay up to 100% of the gas amount
|
||||
* specified in the request. This contract does not implement any kind of retribution for this gas,
|
||||
* and it is assumed that there is an out of band incentive for relayers to pay for execution on
|
||||
* behalf of signers. Often, the relayer is operated by a project that will consider it a user
|
||||
* acquisition cost.
|
||||
*
|
||||
* By offering to pay for gas, relayers are at risk of having that gas used by an attacker toward
|
||||
* some other purpose that is not aligned with the expected out of band incentives. If you operate a
|
||||
* relayer, consider whitelisting target contracts and function selectors. When relaying ERC-721 or
|
||||
* ERC-1155 transfers specifically, consider rejecting the use of the `data` field, since it can be
|
||||
* used to execute arbitrary code.
|
||||
*/
|
||||
contract ERC2771Forwarder is EIP712, Nonces {
|
||||
using ECDSA for bytes32;
|
||||
|
||||
struct ForwardRequestData {
|
||||
address from;
|
||||
address to;
|
||||
uint256 value;
|
||||
uint256 gas;
|
||||
uint48 deadline;
|
||||
bytes data;
|
||||
bytes signature;
|
||||
}
|
||||
|
||||
bytes32 internal constant _FORWARD_REQUEST_TYPEHASH =
|
||||
keccak256(
|
||||
"ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,uint48 deadline,bytes data)"
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev Emitted when a `ForwardRequest` is executed.
|
||||
*
|
||||
* NOTE: An unsuccessful forward request could be due to an invalid signature, an expired deadline,
|
||||
* or simply a revert in the requested call. The contract guarantees that the relayer is not able to force
|
||||
* the requested call to run out of gas.
|
||||
*/
|
||||
event ExecutedForwardRequest(address indexed signer, uint256 nonce, bool success);
|
||||
|
||||
/**
|
||||
* @dev The request `from` doesn't match with the recovered `signer`.
|
||||
*/
|
||||
error ERC2771ForwarderInvalidSigner(address signer, address from);
|
||||
|
||||
/**
|
||||
* @dev The `requestedValue` doesn't match with the available `msgValue`.
|
||||
*/
|
||||
error ERC2771ForwarderMismatchedValue(uint256 requestedValue, uint256 msgValue);
|
||||
|
||||
/**
|
||||
* @dev The request `deadline` has expired.
|
||||
*/
|
||||
error ERC2771ForwarderExpiredRequest(uint48 deadline);
|
||||
|
||||
/**
|
||||
* @dev The request target doesn't trust the `forwarder`.
|
||||
*/
|
||||
error ERC2771UntrustfulTarget(address target, address forwarder);
|
||||
|
||||
/**
|
||||
* @dev See {EIP712-constructor}.
|
||||
*/
|
||||
constructor(string memory name) EIP712(name, "1") {}
|
||||
|
||||
/**
|
||||
* @dev Returns `true` if a request is valid for a provided `signature` at the current block timestamp.
|
||||
*
|
||||
* A transaction is considered valid when the target trusts this forwarder, the request hasn't expired
|
||||
* (deadline is not met), and the signer matches the `from` parameter of the signed request.
|
||||
*
|
||||
* NOTE: A request may return false here but it won't cause {executeBatch} to revert if a refund
|
||||
* receiver is provided.
|
||||
*/
|
||||
function verify(ForwardRequestData calldata request) public view virtual returns (bool) {
|
||||
(bool isTrustedForwarder, bool active, bool signerMatch, ) = _validate(request);
|
||||
return isTrustedForwarder && active && signerMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Executes a `request` on behalf of `signature`'s signer using the ERC-2771 protocol. The gas
|
||||
* provided to the requested call may not be exactly the amount requested, but the call will not run
|
||||
* out of gas. Will revert if the request is invalid or the call reverts, in this case the nonce is not consumed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - The request value should be equal to the provided `msg.value`.
|
||||
* - The request should be valid according to {verify}.
|
||||
*/
|
||||
function execute(ForwardRequestData calldata request) public payable virtual {
|
||||
// We make sure that msg.value and request.value match exactly.
|
||||
// If the request is invalid or the call reverts, this whole function
|
||||
// will revert, ensuring value isn't stuck.
|
||||
if (msg.value != request.value) {
|
||||
revert ERC2771ForwarderMismatchedValue(request.value, msg.value);
|
||||
}
|
||||
|
||||
if (!_execute(request, true)) {
|
||||
revert Errors.FailedCall();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Batch version of {execute} with optional refunding and atomic execution.
|
||||
*
|
||||
* In case a batch contains at least one invalid request (see {verify}), the
|
||||
* request will be skipped and the `refundReceiver` parameter will receive back the
|
||||
* unused requested value at the end of the execution. This is done to prevent reverting
|
||||
* the entire batch when a request is invalid or has already been submitted.
|
||||
*
|
||||
* If the `refundReceiver` is the `address(0)`, this function will revert when at least
|
||||
* one of the requests was not valid instead of skipping it. This could be useful if
|
||||
* a batch is required to get executed atomically (at least at the top-level). For example,
|
||||
* refunding (and thus atomicity) can be opt-out if the relayer is using a service that avoids
|
||||
* including reverted transactions.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - The sum of the requests' values should be equal to the provided `msg.value`.
|
||||
* - All of the requests should be valid (see {verify}) when `refundReceiver` is the zero address.
|
||||
*
|
||||
* NOTE: Setting a zero `refundReceiver` guarantees an all-or-nothing requests execution only for
|
||||
* the first-level forwarded calls. In case a forwarded request calls to a contract with another
|
||||
* subcall, the second-level call may revert without the top-level call reverting.
|
||||
*/
|
||||
function executeBatch(
|
||||
ForwardRequestData[] calldata requests,
|
||||
address payable refundReceiver
|
||||
) public payable virtual {
|
||||
bool atomic = refundReceiver == address(0);
|
||||
|
||||
uint256 requestsValue;
|
||||
uint256 refundValue;
|
||||
|
||||
for (uint256 i; i < requests.length; ++i) {
|
||||
requestsValue += requests[i].value;
|
||||
bool success = _execute(requests[i], atomic);
|
||||
if (!success) {
|
||||
refundValue += requests[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
// The batch should revert if there's a mismatched msg.value provided
|
||||
// to avoid request value tampering
|
||||
if (requestsValue != msg.value) {
|
||||
revert ERC2771ForwarderMismatchedValue(requestsValue, msg.value);
|
||||
}
|
||||
|
||||
// Some requests with value were invalid (possibly due to frontrunning).
|
||||
// To avoid leaving ETH in the contract this value is refunded.
|
||||
if (refundValue != 0) {
|
||||
// We know refundReceiver != address(0) && requestsValue == msg.value
|
||||
// meaning we can ensure refundValue is not taken from the original contract's balance
|
||||
// and refundReceiver is a known account.
|
||||
Address.sendValue(refundReceiver, refundValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates if the provided request can be executed at current block timestamp with
|
||||
* the given `request.signature` on behalf of `request.signer`.
|
||||
*/
|
||||
function _validate(
|
||||
ForwardRequestData calldata request
|
||||
) internal view virtual returns (bool isTrustedForwarder, bool active, bool signerMatch, address signer) {
|
||||
(bool isValid, address recovered) = _recoverForwardRequestSigner(request);
|
||||
|
||||
return (
|
||||
_isTrustedByTarget(request.to),
|
||||
request.deadline >= block.timestamp,
|
||||
isValid && recovered == request.from,
|
||||
recovered
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns a tuple with the recovered the signer of an EIP712 forward request message hash
|
||||
* and a boolean indicating if the signature is valid.
|
||||
*
|
||||
* NOTE: The signature is considered valid if {ECDSA-tryRecover} indicates no recover error for it.
|
||||
*/
|
||||
function _recoverForwardRequestSigner(
|
||||
ForwardRequestData calldata request
|
||||
) internal view virtual returns (bool, address) {
|
||||
(address recovered, ECDSA.RecoverError err, ) = _hashTypedDataV4(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
_FORWARD_REQUEST_TYPEHASH,
|
||||
request.from,
|
||||
request.to,
|
||||
request.value,
|
||||
request.gas,
|
||||
nonces(request.from),
|
||||
request.deadline,
|
||||
keccak256(request.data)
|
||||
)
|
||||
)
|
||||
).tryRecover(request.signature);
|
||||
|
||||
return (err == ECDSA.RecoverError.NoError, recovered);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates and executes a signed request returning the request call `success` value.
|
||||
*
|
||||
* Internal function without msg.value validation.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - The caller must have provided enough gas to forward with the call.
|
||||
* - The request must be valid (see {verify}) if the `requireValidRequest` is true.
|
||||
*
|
||||
* Emits an {ExecutedForwardRequest} event.
|
||||
*
|
||||
* IMPORTANT: Using this function doesn't check that all the `msg.value` was sent, potentially
|
||||
* leaving value stuck in the contract.
|
||||
*/
|
||||
function _execute(
|
||||
ForwardRequestData calldata request,
|
||||
bool requireValidRequest
|
||||
) internal virtual returns (bool success) {
|
||||
(bool isTrustedForwarder, bool active, bool signerMatch, address signer) = _validate(request);
|
||||
|
||||
// Need to explicitly specify if a revert is required since non-reverting is default for
|
||||
// batches and reversion is opt-in since it could be useful in some scenarios
|
||||
if (requireValidRequest) {
|
||||
if (!isTrustedForwarder) {
|
||||
revert ERC2771UntrustfulTarget(request.to, address(this));
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
revert ERC2771ForwarderExpiredRequest(request.deadline);
|
||||
}
|
||||
|
||||
if (!signerMatch) {
|
||||
revert ERC2771ForwarderInvalidSigner(signer, request.from);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore an invalid request because requireValidRequest = false
|
||||
if (isTrustedForwarder && signerMatch && active) {
|
||||
// Nonce should be used before the call to prevent reusing by reentrancy
|
||||
uint256 currentNonce = _useNonce(signer);
|
||||
|
||||
uint256 reqGas = request.gas;
|
||||
address to = request.to;
|
||||
uint256 value = request.value;
|
||||
bytes memory data = abi.encodePacked(request.data, request.from);
|
||||
|
||||
uint256 gasLeft;
|
||||
|
||||
assembly {
|
||||
success := call(reqGas, to, value, add(data, 0x20), mload(data), 0, 0)
|
||||
gasLeft := gas()
|
||||
}
|
||||
|
||||
_checkForwardedGas(gasLeft, request);
|
||||
|
||||
emit ExecutedForwardRequest(signer, currentNonce, success);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether the target trusts this forwarder.
|
||||
*
|
||||
* This function performs a static call to the target contract calling the
|
||||
* {ERC2771Context-isTrustedForwarder} function.
|
||||
*/
|
||||
function _isTrustedByTarget(address target) private view returns (bool) {
|
||||
bytes memory encodedParams = abi.encodeCall(ERC2771Context.isTrustedForwarder, (address(this)));
|
||||
|
||||
bool success;
|
||||
uint256 returnSize;
|
||||
uint256 returnValue;
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
// Perform the staticcall and save the result in the scratch space.
|
||||
// | Location | Content | Content (Hex) |
|
||||
// |-----------|----------|--------------------------------------------------------------------|
|
||||
// | | | result ↓ |
|
||||
// | 0x00:0x1F | selector | 0x0000000000000000000000000000000000000000000000000000000000000001 |
|
||||
success := staticcall(gas(), target, add(encodedParams, 0x20), mload(encodedParams), 0, 0x20)
|
||||
returnSize := returndatasize()
|
||||
returnValue := mload(0)
|
||||
}
|
||||
|
||||
return success && returnSize >= 0x20 && returnValue > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Checks if the requested gas was correctly forwarded to the callee.
|
||||
*
|
||||
* As a consequence of https://eips.ethereum.org/EIPS/eip-150[EIP-150]:
|
||||
* - At most `gasleft() - floor(gasleft() / 64)` is forwarded to the callee.
|
||||
* - At least `floor(gasleft() / 64)` is kept in the caller.
|
||||
*
|
||||
* It reverts consuming all the available gas if the forwarded gas is not the requested gas.
|
||||
*
|
||||
* IMPORTANT: The `gasLeft` parameter should be measured exactly at the end of the forwarded call.
|
||||
* Any gas consumed in between will make room for bypassing this check.
|
||||
*/
|
||||
function _checkForwardedGas(uint256 gasLeft, ForwardRequestData calldata request) private pure {
|
||||
// To avoid insufficient gas griefing attacks, as referenced in https://ronan.eth.limo/blog/ethereum-gas-dangers/
|
||||
//
|
||||
// A malicious relayer can attempt to shrink the gas forwarded so that the underlying call reverts out-of-gas
|
||||
// but the forwarding itself still succeeds. In order to make sure that the subcall received sufficient gas,
|
||||
// we will inspect gasleft() after the forwarding.
|
||||
//
|
||||
// Let X be the gas available before the subcall, such that the subcall gets at most X * 63 / 64.
|
||||
// We can't know X after CALL dynamic costs, but we want it to be such that X * 63 / 64 >= req.gas.
|
||||
// Let Y be the gas used in the subcall. gasleft() measured immediately after the subcall will be gasleft() = X - Y.
|
||||
// If the subcall ran out of gas, then Y = X * 63 / 64 and gasleft() = X - Y = X / 64.
|
||||
// Under this assumption req.gas / 63 > gasleft() is true is true if and only if
|
||||
// req.gas / 63 > X / 64, or equivalently req.gas > X * 63 / 64.
|
||||
// This means that if the subcall runs out of gas we are able to detect that insufficient gas was passed.
|
||||
//
|
||||
// We will now also see that req.gas / 63 > gasleft() implies that req.gas >= X * 63 / 64.
|
||||
// The contract guarantees Y <= req.gas, thus gasleft() = X - Y >= X - req.gas.
|
||||
// - req.gas / 63 > gasleft()
|
||||
// - req.gas / 63 >= X - req.gas
|
||||
// - req.gas >= X * 63 / 64
|
||||
// In other words if req.gas < X * 63 / 64 then req.gas / 63 <= gasleft(), thus if the relayer behaves honestly
|
||||
// the forwarding does not revert.
|
||||
if (gasLeft < request.gas / 63) {
|
||||
// We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since
|
||||
// neither revert or assert consume all gas since Solidity 0.8.20
|
||||
// https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
invalid()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
lib_openzeppelin_contracts/contracts/metatx/README.adoc
Normal file
17
lib_openzeppelin_contracts/contracts/metatx/README.adoc
Normal file
@@ -0,0 +1,17 @@
|
||||
= Meta Transactions
|
||||
|
||||
[.readme-notice]
|
||||
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/metatx
|
||||
|
||||
This directory includes contracts for adding meta-transaction capabilities (i.e. abstracting the execution context from the transaction origin) following the https://eips.ethereum.org/EIPS/eip-2771[ERC-2771 specification].
|
||||
|
||||
- {ERC2771Context}: Provides a mechanism to override the sender and calldata of the execution context (`msg.sender` and `msg.data`) with a custom value specified by a trusted forwarder.
|
||||
- {ERC2771Forwarder}: A production-ready forwarder that relays operation requests signed off-chain by an EOA.
|
||||
|
||||
== Core
|
||||
|
||||
{{ERC2771Context}}
|
||||
|
||||
== Utils
|
||||
|
||||
{{ERC2771Forwarder}}
|
||||
Reference in New Issue
Block a user