ERC2771Forwarder.sol 12 KB

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