Skip to content

Commit

Permalink
Minimal Forwarder
Browse files Browse the repository at this point in the history
  • Loading branch information
ernestognw committed Jun 13, 2023
1 parent 87f3f88 commit 5bee13c
Showing 1 changed file with 19 additions and 6 deletions.
25 changes: 19 additions & 6 deletions contracts/metatx/MinimalForwarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ contract MinimalForwarder is EIP712, Nonces {
*/
error MinimalForwaderMismatchedValue(uint256 msgValue, uint256 value);

/**
* @dev The list of requests length doesn't match with the list of signatures length.
*/
error MinimalForwaderInvalidBatchLength(uint256 requestsLength, uint256 signaturesLength);

/**
* @dev The request `deadline` has expired.
*/
Expand Down Expand Up @@ -87,13 +92,21 @@ contract MinimalForwarder is EIP712, Nonces {

_useCheckedNonce(request.from, request.nonce);

// As a consequence of EIP-150, the maximum gas forwarded to a call is 63/64 of the remaining gas. So:
// - At most `gasleft() - floor(gasleft() / 64)` is passed.
// - At least `floor(gasleft() / 64)` is kept in the caller.
// The current gas available is saved for later checking.
uint256 gasBefore = gasleft();
(bool success, bytes memory returndata) = request.to.call{gas: request.gas, value: request.value}(
abi.encodePacked(request.data, request.from)
);

// Validate that the relayer has sent enough gas for the call.
// See https://ronan.eth.limo/blog/ethereum-gas-dangers/
if (gasleft() <= request.gas / 63) {
// To avoid gas griefing attacks, as referenced in https://ronan.eth.limo/blog/ethereum-gas-dangers/
// A malicious relayer can attempt to manipulate the gas forwarded so that the underlying call reverts and
// the top-level call still passes.
// Such manipulation can be prevented by checking if `gasleft() < floor(gasBefore / 64)`. If so, we
// can assume an out of gas error was forced in the subcall. There's no need to process such transactions.
if (gasleft() < gasBefore / 64) {
// 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.0
// https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require
Expand All @@ -110,9 +123,9 @@ contract MinimalForwarder is EIP712, Nonces {
* @dev Validates if the provided request can be executed at `blockNumber` with `signature`.
*/
function _validateAt(
uint256 blockNumber,
ForwardRequest calldata request,
bytes calldata signature,
uint256 blockNumber
bytes calldata signature
) internal view virtual returns (bool alive, bool signerMatch, bool nonceMatch) {
address signer = _recoverForwardRequestSigner(request, signature);
return (request.deadline >= blockNumber, signer == request.from, nonces(request.from) == request.nonce);
Expand All @@ -125,7 +138,7 @@ contract MinimalForwarder is EIP712, Nonces {
ForwardRequest calldata request,
bytes calldata signature
) internal view virtual returns (bool alive, bool signerMatch, bool nonceMatch) {
return _validateAt(request, signature, block.number);
return _validateAt(block.number, request, signature);
}

/**
Expand Down

0 comments on commit 5bee13c

Please sign in to comment.