ERC2771Forwarder.sol 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v4.9.0) (metatx/ERC2771Forwarder.sol)
  3. pragma solidity ^0.8.20;
  4. import {ECDSA} from "../utils/cryptography/ECDSA.sol";
  5. import {EIP712} from "../utils/cryptography/EIP712.sol";
  6. import {Nonces} from "../utils/Nonces.sol";
  7. import {Address} from "../utils/Address.sol";
  8. /**
  9. * @dev A forwarder compatible with ERC2771 contracts. See {ERC2771Context}.
  10. *
  11. * This forwarder operates on forward requests that include:
  12. *
  13. * * `from`: An address to operate on behalf of. It is required to be equal to the request signer.
  14. * * `to`: The address that should be called.
  15. * * `value`: The amount of native token to attach with the requested call.
  16. * * `gas`: The amount of gas limit that will be forwarded with the requested call.
  17. * * `nonce`: A unique transaction ordering identifier to avoid replayability and request invalidation.
  18. * * `deadline`: A timestamp after which the request is not executable anymore.
  19. * * `data`: Encoded `msg.data` to send with the requested call.
  20. *
  21. * Relayers are able to submit batches if they are processing a high volume of requests. With high
  22. * throughput, relayers may run into limitations of the chain such as limits on the number of
  23. * transactions in the mempool. In these cases the recommendation is to distribute the load among
  24. * multiple accounts.
  25. *
  26. * ==== Security Considerations
  27. *
  28. * If a relayer submits a forward request, it should be willing to pay up to 100% of the gas amount
  29. * specified in the request. This contract does not implement any kind of retribution for this gas,
  30. * and it is assumed that there is an out of band incentive for relayers to pay for execution on
  31. * behalf of signers. Often, the relayer is operated by a project that will consider it a user
  32. * acquisition cost.
  33. *
  34. * By offering to pay for gas, relayers are at risk of having that gas used by an attacker toward
  35. * some other purpose that is not aligned with the expected out of band incentives. If you operate a
  36. * relayer, consider whitelisting target contracts and function selectors. When relaying ERC-721 or
  37. * ERC-1155 transfers specifically, consider rejecting the use of the `data` field, since it can be
  38. * used to execute arbitrary code.
  39. */
  40. contract ERC2771Forwarder is EIP712, Nonces {
  41. using ECDSA for bytes32;
  42. struct ForwardRequestData {
  43. address from;
  44. address to;
  45. uint256 value;
  46. uint256 gas;
  47. uint48 deadline;
  48. bytes data;
  49. bytes signature;
  50. }
  51. bytes32 internal constant _FORWARD_REQUEST_TYPEHASH =
  52. keccak256(
  53. "ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,uint48 deadline,bytes data)"
  54. );
  55. /**
  56. * @dev Emitted when a `ForwardRequest` is executed.
  57. *
  58. * NOTE: An unsuccessful forward request could be due to an invalid signature, an expired deadline,
  59. * or simply a revert in the requested call. The contract guarantees that the relayer is not able to force
  60. * the requested call to run out of gas.
  61. */
  62. event ExecutedForwardRequest(address indexed signer, uint256 nonce, bool success);
  63. /**
  64. * @dev The request `from` doesn't match with the recovered `signer`.
  65. */
  66. error ERC2771ForwarderInvalidSigner(address signer, address from);
  67. /**
  68. * @dev The `requestedValue` doesn't match with the available `msgValue`.
  69. */
  70. error ERC2771ForwarderMismatchedValue(uint256 requestedValue, uint256 msgValue);
  71. /**
  72. * @dev The request `deadline` has expired.
  73. */
  74. error ERC2771ForwarderExpiredRequest(uint48 deadline);
  75. /**
  76. * @dev See {EIP712-constructor}.
  77. */
  78. constructor(string memory name) EIP712(name, "1") {}
  79. /**
  80. * @dev Returns `true` if a request is valid for a provided `signature` at the current block timestamp.
  81. *
  82. * A transaction is considered valid when it hasn't expired (deadline is not met), and the signer
  83. * matches the `from` parameter of the signed request.
  84. *
  85. * NOTE: A request may return false here but it won't cause {executeBatch} to revert if a refund
  86. * receiver is provided.
  87. */
  88. function verify(ForwardRequestData calldata request) public view virtual returns (bool) {
  89. (bool alive, bool signerMatch, ) = _validate(request);
  90. return alive && signerMatch;
  91. }
  92. /**
  93. * @dev Executes a `request` on behalf of `signature`'s signer using the ERC-2771 protocol. The gas
  94. * provided to the requested call may not be exactly the amount requested, but the call will not run
  95. * out of gas. Will revert if the request is invalid or the call reverts, in this case the nonce is not consumed.
  96. *
  97. * Requirements:
  98. *
  99. * - The request value should be equal to the provided `msg.value`.
  100. * - The request should be valid according to {verify}.
  101. */
  102. function execute(ForwardRequestData calldata request) public payable virtual {
  103. // We make sure that msg.value and request.value match exactly.
  104. // If the request is invalid or the call reverts, this whole function
  105. // will revert, ensuring value isn't stuck.
  106. if (msg.value != request.value) {
  107. revert ERC2771ForwarderMismatchedValue(request.value, msg.value);
  108. }
  109. if (!_execute(request, true)) {
  110. revert Address.FailedInnerCall();
  111. }
  112. }
  113. /**
  114. * @dev Batch version of {execute} with optional refunding and atomic execution.
  115. *
  116. * In case a batch contains at least one invalid request (see {verify}), the
  117. * request will be skipped and the `refundReceiver` parameter will receive back the
  118. * unused requested value at the end of the execution. This is done to prevent reverting
  119. * the entire batch when a request is invalid or has already been submitted.
  120. *
  121. * If the `refundReceiver` is the `address(0)`, this function will revert when at least
  122. * one of the requests was not valid instead of skipping it. This could be useful if
  123. * a batch is required to get executed atomically (at least at the top-level). For example,
  124. * refunding (and thus atomicity) can be opt-out if the relayer is using a service that avoids
  125. * including reverted transactions.
  126. *
  127. * Requirements:
  128. *
  129. * - The sum of the requests' values should be equal to the provided `msg.value`.
  130. * - All of the requests should be valid (see {verify}) when `refundReceiver` is the zero address.
  131. *
  132. * NOTE: Setting a zero `refundReceiver` guarantees an all-or-nothing requests execution only for
  133. * the first-level forwarded calls. In case a forwarded request calls to a contract with another
  134. * subcall, the second-level call may revert without the top-level call reverting.
  135. */
  136. function executeBatch(
  137. ForwardRequestData[] calldata requests,
  138. address payable refundReceiver
  139. ) public payable virtual {
  140. bool atomic = refundReceiver == address(0);
  141. uint256 requestsValue;
  142. uint256 refundValue;
  143. for (uint256 i; i < requests.length; ++i) {
  144. requestsValue += requests[i].value;
  145. bool success = _execute(requests[i], atomic);
  146. if (!success) {
  147. refundValue += requests[i].value;
  148. }
  149. }
  150. // The batch should revert if there's a mismatched msg.value provided
  151. // to avoid request value tampering
  152. if (requestsValue != msg.value) {
  153. revert ERC2771ForwarderMismatchedValue(requestsValue, msg.value);
  154. }
  155. // Some requests with value were invalid (possibly due to frontrunning).
  156. // To avoid leaving ETH in the contract this value is refunded.
  157. if (refundValue != 0) {
  158. // We know refundReceiver != address(0) && requestsValue == msg.value
  159. // meaning we can ensure refundValue is not taken from the original contract's balance
  160. // and refundReceiver is a known account.
  161. Address.sendValue(refundReceiver, refundValue);
  162. }
  163. }
  164. /**
  165. * @dev Validates if the provided request can be executed at current block timestamp with
  166. * the given `request.signature` on behalf of `request.signer`.
  167. */
  168. function _validate(
  169. ForwardRequestData calldata request
  170. ) internal view virtual returns (bool alive, bool signerMatch, address signer) {
  171. signer = _recoverForwardRequestSigner(request);
  172. return (request.deadline >= block.timestamp, signer == request.from, signer);
  173. }
  174. /**
  175. * @dev Recovers the signer of an EIP712 message hash for a forward `request` and its corresponding `signature`.
  176. * See {ECDSA-recover}.
  177. */
  178. function _recoverForwardRequestSigner(ForwardRequestData calldata request) internal view virtual returns (address) {
  179. return
  180. _hashTypedDataV4(
  181. keccak256(
  182. abi.encode(
  183. _FORWARD_REQUEST_TYPEHASH,
  184. request.from,
  185. request.to,
  186. request.value,
  187. request.gas,
  188. nonces(request.from),
  189. request.deadline,
  190. keccak256(request.data)
  191. )
  192. )
  193. ).recover(request.signature);
  194. }
  195. /**
  196. * @dev Validates and executes a signed request returning the request call `success` value.
  197. *
  198. * Internal function without msg.value validation.
  199. *
  200. * Requirements:
  201. *
  202. * - The caller must have provided enough gas to forward with the call.
  203. * - The request must be valid (see {verify}) if the `requireValidRequest` is true.
  204. *
  205. * Emits an {ExecutedForwardRequest} event.
  206. *
  207. * IMPORTANT: Using this function doesn't check that all the `msg.value` was sent, potentially
  208. * leaving value stuck in the contract.
  209. */
  210. function _execute(
  211. ForwardRequestData calldata request,
  212. bool requireValidRequest
  213. ) internal virtual returns (bool success) {
  214. (bool alive, bool signerMatch, address signer) = _validate(request);
  215. // Need to explicitly specify if a revert is required since non-reverting is default for
  216. // batches and reversion is opt-in since it could be useful in some scenarios
  217. if (requireValidRequest) {
  218. if (!alive) {
  219. revert ERC2771ForwarderExpiredRequest(request.deadline);
  220. }
  221. if (!signerMatch) {
  222. revert ERC2771ForwarderInvalidSigner(signer, request.from);
  223. }
  224. }
  225. // Ignore an invalid request because requireValidRequest = false
  226. if (signerMatch && alive) {
  227. // Nonce should be used before the call to prevent reusing by reentrancy
  228. uint256 currentNonce = _useNonce(signer);
  229. uint256 reqGas = request.gas;
  230. address to = request.to;
  231. uint256 value = request.value;
  232. bytes memory data = abi.encodePacked(request.data, request.from);
  233. uint256 gasLeft;
  234. assembly {
  235. success := call(reqGas, to, value, add(data, 0x20), mload(data), 0, 0)
  236. gasLeft := gas()
  237. }
  238. _checkForwardedGas(gasLeft, request);
  239. emit ExecutedForwardRequest(signer, currentNonce, success);
  240. }
  241. }
  242. /**
  243. * @dev Checks if the requested gas was correctly forwarded to the callee.
  244. *
  245. * As a consequence of https://eips.ethereum.org/EIPS/eip-150[EIP-150]:
  246. * - At most `gasleft() - floor(gasleft() / 64)` is forwarded to the callee.
  247. * - At least `floor(gasleft() / 64)` is kept in the caller.
  248. *
  249. * It reverts consuming all the available gas if the forwarded gas is not the requested gas.
  250. *
  251. * IMPORTANT: The `gasLeft` parameter should be measured exactly at the end of the forwarded call.
  252. * Any gas consumed in between will make room for bypassing this check.
  253. */
  254. function _checkForwardedGas(uint256 gasLeft, ForwardRequestData calldata request) private pure {
  255. // To avoid insufficient gas griefing attacks, as referenced in https://ronan.eth.limo/blog/ethereum-gas-dangers/
  256. //
  257. // A malicious relayer can attempt to shrink the gas forwarded so that the underlying call reverts out-of-gas
  258. // but the forwarding itself still succeeds. In order to make sure that the subcall received sufficient gas,
  259. // we will inspect gasleft() after the forwarding.
  260. //
  261. // Let X be the gas available before the subcall, such that the subcall gets at most X * 63 / 64.
  262. // We can't know X after CALL dynamic costs, but we want it to be such that X * 63 / 64 >= req.gas.
  263. // Let Y be the gas used in the subcall. gasleft() measured immediately after the subcall will be gasleft() = X - Y.
  264. // If the subcall ran out of gas, then Y = X * 63 / 64 and gasleft() = X - Y = X / 64.
  265. // Under this assumption req.gas / 63 > gasleft() is true is true if and only if
  266. // req.gas / 63 > X / 64, or equivalently req.gas > X * 63 / 64.
  267. // This means that if the subcall runs out of gas we are able to detect that insufficient gas was passed.
  268. //
  269. // We will now also see that req.gas / 63 > gasleft() implies that req.gas >= X * 63 / 64.
  270. // The contract guarantees Y <= req.gas, thus gasleft() = X - Y >= X - req.gas.
  271. // - req.gas / 63 > gasleft()
  272. // - req.gas / 63 >= X - req.gas
  273. // - req.gas >= X * 63 / 64
  274. // In other words if req.gas < X * 63 / 64 then req.gas / 63 <= gasleft(), thus if the relayer behaves honestly
  275. // the forwarding does not revert.
  276. if (gasLeft < request.gas / 63) {
  277. // We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since
  278. // neither revert or assert consume all gas since Solidity 0.8.20
  279. // https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require
  280. /// @solidity memory-safe-assembly
  281. assembly {
  282. invalid()
  283. }
  284. }
  285. }
  286. }