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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  // Testing utilities
     5  import { Test } from "forge-std/Test.sol";
     6  
     7  // Target contract
     8  import { SafeCall } from "src/libraries/SafeCall.sol";
     9  
    10  contract SafeCall_Test is Test {
    11      /// @dev Tests that the `send` function succeeds.
    12      function testFuzz_send_succeeds(address from, address to, uint256 gas, uint64 value) external {
    13          vm.assume(from.balance == 0);
    14          vm.assume(to.balance == 0);
    15          // no precompiles (mainnet)
    16          assumeNotPrecompile(to);
    17          // don't call the vm
    18          vm.assume(to != address(vm));
    19          vm.assume(from != address(vm));
    20          // don't call the console
    21          vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
    22          // don't call the create2 deployer
    23          vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
    24          vm.assume(to != address(this));
    25  
    26          assertEq(from.balance, 0, "from balance is 0");
    27          vm.deal(from, value);
    28          assertEq(from.balance, value, "from balance not dealt");
    29  
    30          uint256[2] memory balancesBefore = [from.balance, to.balance];
    31  
    32          vm.expectCall(to, value, bytes(""));
    33          vm.prank(from);
    34          bool success = SafeCall.send(to, gas, value);
    35  
    36          assertTrue(success, "send not successful");
    37          if (from == to) {
    38              assertEq(from.balance, balancesBefore[0], "Self-send did not change balance");
    39          } else {
    40              assertEq(from.balance, balancesBefore[0] - value, "from balance not drained");
    41              assertEq(to.balance, balancesBefore[1] + value, "to balance received");
    42          }
    43      }
    44  
    45      /// @dev Tests that `call` succeeds.
    46      function testFuzz_call_succeeds(address from, address to, uint256 gas, uint64 value, bytes memory data) external {
    47          vm.assume(from.balance == 0);
    48          vm.assume(to.balance == 0);
    49          // no precompiles (mainnet)
    50          assumeNotPrecompile(to);
    51          // don't call the vm
    52          vm.assume(to != address(vm));
    53          vm.assume(from != address(vm));
    54          // don't call the console
    55          vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
    56          // don't call the create2 deployer
    57          vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
    58          vm.assume(to != address(this));
    59  
    60          assertEq(from.balance, 0, "from balance is 0");
    61          vm.deal(from, value);
    62          assertEq(from.balance, value, "from balance not dealt");
    63  
    64          uint256[2] memory balancesBefore = [from.balance, to.balance];
    65  
    66          vm.expectCall(to, value, data);
    67          vm.prank(from);
    68          bool success = SafeCall.call(to, gas, value, data);
    69  
    70          assertTrue(success, "call not successful");
    71          if (from == to) {
    72              assertEq(from.balance, balancesBefore[0], "Self-send did not change balance");
    73          } else {
    74              assertEq(from.balance, balancesBefore[0] - value, "from balance not drained");
    75              assertEq(to.balance, balancesBefore[1] + value, "to balance received");
    76          }
    77      }
    78  
    79      /// @dev Tests that `callWithMinGas` succeeds with enough gas.
    80      function testFuzz_callWithMinGas_hasEnough_succeeds(
    81          address from,
    82          address to,
    83          uint64 minGas,
    84          uint64 value,
    85          bytes memory data
    86      )
    87          external
    88      {
    89          vm.assume(from.balance == 0);
    90          vm.assume(to.balance == 0);
    91          // no precompiles (mainnet)
    92          assumeNotPrecompile(to);
    93          // don't call the vm
    94          vm.assume(to != address(vm));
    95          vm.assume(from != address(vm));
    96          // don't call the console
    97          vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
    98          // don't call the create2 deployer
    99          vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
   100          vm.assume(to != address(this));
   101  
   102          assertEq(from.balance, 0, "from balance is 0");
   103          vm.deal(from, value);
   104          assertEq(from.balance, value, "from balance not dealt");
   105  
   106          // Bound minGas to [0, l1_block_gas_limit]
   107          minGas = uint64(bound(minGas, 0, 30_000_000));
   108  
   109          uint256[2] memory balancesBefore = [from.balance, to.balance];
   110  
   111          vm.expectCallMinGas(to, value, minGas, data);
   112          vm.prank(from);
   113          bool success = SafeCall.callWithMinGas(to, minGas, value, data);
   114  
   115          assertTrue(success, "call not successful");
   116          if (from == to) {
   117              assertEq(from.balance, balancesBefore[0], "Self-send did not change balance");
   118          } else {
   119              assertEq(from.balance, balancesBefore[0] - value, "from balance not drained");
   120              assertEq(to.balance, balancesBefore[1] + value, "to balance received");
   121          }
   122      }
   123  
   124      /// @dev Tests that `callWithMinGas` succeeds for the lower gas bounds.
   125      function test_callWithMinGas_noLeakageLow_succeeds() external {
   126          SimpleSafeCaller caller = new SimpleSafeCaller();
   127  
   128          for (uint64 i = 40_000; i < 100_000; i++) {
   129              uint256 snapshot = vm.snapshot();
   130  
   131              // 65_907 is the exact amount of gas required to make the safe call
   132              // successfully.
   133              if (i < 65_907) {
   134                  assertFalse(caller.makeSafeCall(i, 25_000));
   135              } else {
   136                  vm.expectCallMinGas(address(caller), 0, 25_000, abi.encodeWithSelector(caller.setA.selector, 1));
   137                  assertTrue(caller.makeSafeCall(i, 25_000));
   138              }
   139  
   140              assertTrue(vm.revertTo(snapshot));
   141          }
   142      }
   143  
   144      /// @dev Tests that `callWithMinGas` succeeds on the upper gas bounds.
   145      function test_callWithMinGas_noLeakageHigh_succeeds() external {
   146          SimpleSafeCaller caller = new SimpleSafeCaller();
   147  
   148          for (uint64 i = 15_200_000; i < 15_300_000; i++) {
   149              uint256 snapshot = vm.snapshot();
   150  
   151              // 15_278_606 is the exact amount of gas required to make the safe call
   152              // successfully.
   153              if (i < 15_278_606) {
   154                  assertFalse(caller.makeSafeCall(i, 15_000_000));
   155              } else {
   156                  vm.expectCallMinGas(address(caller), 0, 15_000_000, abi.encodeWithSelector(caller.setA.selector, 1));
   157                  assertTrue(caller.makeSafeCall(i, 15_000_000));
   158              }
   159  
   160              assertTrue(vm.revertTo(snapshot));
   161          }
   162      }
   163  }
   164  
   165  contract SimpleSafeCaller {
   166      uint256 public a;
   167  
   168      function makeSafeCall(uint64 gas, uint64 minGas) external returns (bool) {
   169          return SafeCall.call(address(this), gas, 0, abi.encodeWithSelector(this.makeSafeCallMinGas.selector, minGas));
   170      }
   171  
   172      function makeSafeCallMinGas(uint64 minGas) external returns (bool) {
   173          return SafeCall.callWithMinGas(address(this), minGas, 0, abi.encodeWithSelector(this.setA.selector, 1));
   174      }
   175  
   176      function setA(uint256 _a) external {
   177          a = _a;
   178      }
   179  }