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 }