github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/libraries/SafeCall.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  /// @title SafeCall
     5  /// @notice Perform low level safe calls
     6  library SafeCall {
     7      /// @notice Performs a low level call without copying any returndata.
     8      /// @dev Passes no calldata to the call context.
     9      /// @param _target   Address to call
    10      /// @param _gas      Amount of gas to pass to the call
    11      /// @param _value    Amount of value to pass to the call
    12      function send(address _target, uint256 _gas, uint256 _value) internal returns (bool) {
    13          bool _success;
    14          assembly {
    15              _success :=
    16                  call(
    17                      _gas, // gas
    18                      _target, // recipient
    19                      _value, // ether value
    20                      0, // inloc
    21                      0, // inlen
    22                      0, // outloc
    23                      0 // outlen
    24                  )
    25          }
    26          return _success;
    27      }
    28  
    29      /// @notice Perform a low level call without copying any returndata
    30      /// @param _target   Address to call
    31      /// @param _gas      Amount of gas to pass to the call
    32      /// @param _value    Amount of value to pass to the call
    33      /// @param _calldata Calldata to pass to the call
    34      function call(address _target, uint256 _gas, uint256 _value, bytes memory _calldata) internal returns (bool) {
    35          bool _success;
    36          assembly {
    37              _success :=
    38                  call(
    39                      _gas, // gas
    40                      _target, // recipient
    41                      _value, // ether value
    42                      add(_calldata, 32), // inloc
    43                      mload(_calldata), // inlen
    44                      0, // outloc
    45                      0 // outlen
    46                  )
    47          }
    48          return _success;
    49      }
    50  
    51      /// @notice Helper function to determine if there is sufficient gas remaining within the context
    52      ///         to guarantee that the minimum gas requirement for a call will be met as well as
    53      ///         optionally reserving a specified amount of gas for after the call has concluded.
    54      /// @param _minGas      The minimum amount of gas that may be passed to the target context.
    55      /// @param _reservedGas Optional amount of gas to reserve for the caller after the execution
    56      ///                     of the target context.
    57      /// @return `true` if there is enough gas remaining to safely supply `_minGas` to the target
    58      ///         context as well as reserve `_reservedGas` for the caller after the execution of
    59      ///         the target context.
    60      /// @dev !!!!! FOOTGUN ALERT !!!!!
    61      ///      1.) The 40_000 base buffer is to account for the worst case of the dynamic cost of the
    62      ///          `CALL` opcode's `address_access_cost`, `positive_value_cost`, and
    63      ///          `value_to_empty_account_cost` factors with an added buffer of 5,700 gas. It is
    64      ///          still possible to self-rekt by initiating a withdrawal with a minimum gas limit
    65      ///          that does not account for the `memory_expansion_cost` & `code_execution_cost`
    66      ///          factors of the dynamic cost of the `CALL` opcode.
    67      ///      2.) This function should *directly* precede the external call if possible. There is an
    68      ///          added buffer to account for gas consumed between this check and the call, but it
    69      ///          is only 5,700 gas.
    70      ///      3.) Because EIP-150 ensures that a maximum of 63/64ths of the remaining gas in the call
    71      ///          frame may be passed to a subcontext, we need to ensure that the gas will not be
    72      ///          truncated.
    73      ///      4.) Use wisely. This function is not a silver bullet.
    74      function hasMinGas(uint256 _minGas, uint256 _reservedGas) internal view returns (bool) {
    75          bool _hasMinGas;
    76          assembly {
    77              // Equation: gas × 63 ≥ minGas × 64 + 63(40_000 + reservedGas)
    78              _hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63))))
    79          }
    80          return _hasMinGas;
    81      }
    82  
    83      /// @notice Perform a low level call without copying any returndata. This function
    84      ///         will revert if the call cannot be performed with the specified minimum
    85      ///         gas.
    86      /// @param _target   Address to call
    87      /// @param _minGas   The minimum amount of gas that may be passed to the call
    88      /// @param _value    Amount of value to pass to the call
    89      /// @param _calldata Calldata to pass to the call
    90      function callWithMinGas(
    91          address _target,
    92          uint256 _minGas,
    93          uint256 _value,
    94          bytes memory _calldata
    95      )
    96          internal
    97          returns (bool)
    98      {
    99          bool _success;
   100          bool _hasMinGas = hasMinGas(_minGas, 0);
   101          assembly {
   102              // Assertion: gasleft() >= (_minGas * 64) / 63 + 40_000
   103              if iszero(_hasMinGas) {
   104                  // Store the "Error(string)" selector in scratch space.
   105                  mstore(0, 0x08c379a0)
   106                  // Store the pointer to the string length in scratch space.
   107                  mstore(32, 32)
   108                  // Store the string.
   109                  //
   110                  // SAFETY:
   111                  // - We pad the beginning of the string with two zero bytes as well as the
   112                  // length (24) to ensure that we override the free memory pointer at offset
   113                  // 0x40. This is necessary because the free memory pointer is likely to
   114                  // be greater than 1 byte when this function is called, but it is incredibly
   115                  // unlikely that it will be greater than 3 bytes. As for the data within
   116                  // 0x60, it is ensured that it is 0 due to 0x60 being the zero offset.
   117                  // - It's fine to clobber the free memory pointer, we're reverting.
   118                  mstore(88, 0x0000185361666543616c6c3a204e6f7420656e6f75676820676173)
   119  
   120                  // Revert with 'Error("SafeCall: Not enough gas")'
   121                  revert(28, 100)
   122              }
   123  
   124              // The call will be supplied at least ((_minGas * 64) / 63) gas due to the
   125              // above assertion. This ensures that, in all circumstances (except for when the
   126              // `_minGas` does not account for the `memory_expansion_cost` and `code_execution_cost`
   127              // factors of the dynamic cost of the `CALL` opcode), the call will receive at least
   128              // the minimum amount of gas specified.
   129              _success :=
   130                  call(
   131                      gas(), // gas
   132                      _target, // recipient
   133                      _value, // ether value
   134                      add(_calldata, 32), // inloc
   135                      mload(_calldata), // inlen
   136                      0x00, // outloc
   137                      0x00 // outlen
   138                  )
   139          }
   140          return _success;
   141      }
   142  }