From 1730a9bdf91bf741dbd29f708a9f8c445673927c Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Thu, 22 Jun 2023 22:05:53 -0500 Subject: [PATCH 01/17] fix: exclude vm address from assumeNoPrecompiles --- src/StdCheats.sol | 2 +- test/StdCheats.t.sol | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index d438098d..da1ab79e 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -228,7 +228,7 @@ abstract contract StdCheatsSafe { // address), but the same rationale for excluding them applies so we include those too. // These should be present on all EVM-compatible chains. - vm.assume(addr < address(0x1) || addr > address(0x9)); + vm.assume((addr < address(0x1) || addr > address(0x9)) && addr != address(vm)); // forgefmt: disable-start if (chainId == 10 || chainId == 420) { diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 6f4d5bb7..e4398ec4 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -4,6 +4,7 @@ pragma solidity >=0.7.0 <0.9.0; import "../src/StdCheats.sol"; import "../src/Test.sol"; import "../src/StdJson.sol"; +import "../src/console.sol"; contract StdCheatsTest is Test { Bar test; @@ -339,7 +340,8 @@ contract StdCheatsTest is Test { assumeNoPrecompiles(addr, getChain("optimism_goerli").chainId); assertTrue( addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) - || addr > address(0x4200000000000000000000000000000000000800) + || (addr > address(0x4200000000000000000000000000000000000800) && addr < address(vm)) + || addr > address(vm) ); } From 934e0ae6483dae79998a2b8c33d81ddb5bc1d105 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Thu, 22 Jun 2023 22:08:02 -0500 Subject: [PATCH 02/17] forge fmt --- test/StdCheats.t.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index e4398ec4..d8b8264d 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -340,8 +340,7 @@ contract StdCheatsTest is Test { assumeNoPrecompiles(addr, getChain("optimism_goerli").chainId); assertTrue( addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) - || (addr > address(0x4200000000000000000000000000000000000800) && addr < address(vm)) - || addr > address(vm) + || (addr > address(0x4200000000000000000000000000000000000800) && addr < address(vm)) || addr > address(vm) ); } From 49bc2a67c4c2f7759872c2a341bc5e5564a5ab9f Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Thu, 22 Jun 2023 22:14:39 -0500 Subject: [PATCH 03/17] oops --- test/StdCheats.t.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index d8b8264d..1af6bb56 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -4,7 +4,6 @@ pragma solidity >=0.7.0 <0.9.0; import "../src/StdCheats.sol"; import "../src/Test.sol"; import "../src/StdJson.sol"; -import "../src/console.sol"; contract StdCheatsTest is Test { Bar test; From 0ab33b18d48ac610a11c71b9f8be5603df3c9913 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:18:21 -0500 Subject: [PATCH 04/17] update assumePayableIsNot --- src/StdCheats.sol | 93 +++++++++++++++++++++++++++++++++++++++----- test/StdCheats.t.sol | 42 ++++++++++++-------- 2 files changed, 110 insertions(+), 25 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index da1ab79e..0a8eb553 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -193,6 +193,14 @@ abstract contract StdCheatsSafe { uint256 key; } + enum AddressType { + Payable, + NonPayable, + ZeroAddress, + Precompiles, + ForgeAddresses + } + // Checks that `addr` is not blacklisted by token contracts that have a blacklist. function assumeNoBlacklisted(address token, address addr) internal virtual { // Nothing to check if `token` is not a contract. @@ -214,13 +222,82 @@ abstract contract StdCheatsSafe { vm.assume(!success || abi.decode(returnData, (bool)) == false); } - function assumeNoPrecompiles(address addr) internal virtual { + function assumeAddressIsNot(AddressType addressType, address addr) internal virtual { // Assembly required since `block.chainid` was introduced in 0.8.0. uint256 chainId; assembly { chainId := chainid() } - assumeNoPrecompiles(addr, chainId); + + if (addressType == AddressType.Payable) { + assumeNoPayable(addr); + } else if (addressType == AddressType.NonPayable) { + assumeNoNonPayable(addr); + } else if (addressType == AddressType.ZeroAddress) { + assumeNoZeroAddress(addr); + } else if (addressType == AddressType.Precompiles) { + assumeNoPrecompiles(addr, chainId); + } else if (addressType == AddressType.ForgeAddresses) { + assumeNoForgeAddresses(addr); + } + } + + function assumeAddressIsNot(AddressType addressType1, AddressType addressType2, address addr) internal virtual { + assumeAddressIsNot(addressType1, addr); + assumeAddressIsNot(addressType2, addr); + } + + function assumeAddressIsNot( + AddressType addressType1, + AddressType addressType2, + AddressType addressType3, + address addr + ) internal virtual { + assumeAddressIsNot(addressType1, addr); + assumeAddressIsNot(addressType2, addr); + assumeAddressIsNot(addressType3, addr); + } + + function assumeAddressIsNot( + AddressType addressType1, + AddressType addressType2, + AddressType addressType3, + AddressType addressType4, + address addr + ) internal virtual { + assumeAddressIsNot(addressType1, addr); + assumeAddressIsNot(addressType2, addr); + assumeAddressIsNot(addressType3, addr); + assumeAddressIsNot(addressType4, addr); + } + + function _isPayable(address addr) private returns (bool) { + if (addr.code.length == 0) { + return false; + } else { + require(addr.balance < type(uint256).max, "balance exceeds max uint256"); + uint256 origBalanceTest = address(this).balance; + uint256 origBalanceAddr = address(addr).balance; + (bool success,) = payable(addr).call{value: 1}(""); + + // reset balances + vm.deal(address(this), origBalanceTest); + vm.deal(addr, origBalanceAddr); + + return success; + } + } + + function assumeNoPayable(address addr) internal virtual { + vm.assume(!_isPayable(addr)); + } + + function assumeNoNonPayable(address addr) internal virtual { + vm.assume(_isPayable(addr)); + } + + function assumeNoZeroAddress(address addr) internal virtual { + vm.assume(addr != address(0)); } function assumeNoPrecompiles(address addr, uint256 chainId) internal pure virtual { @@ -246,6 +323,11 @@ abstract contract StdCheatsSafe { // forgefmt: disable-end } + function assumeNoForgeAddresses(address addr) internal virtual { + // vm and console addresses + vm.assume(addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67); + } + function readEIP1559ScriptArtifact(string memory path) internal view @@ -508,13 +590,6 @@ abstract contract StdCheatsSafe { vm.resumeGasMetering(); } } - - // a cheat for fuzzing addresses that are payable only - // see https://github.com/foundry-rs/foundry/issues/3631 - function assumePayable(address addr) internal virtual { - (bool success,) = payable(addr).call{value: 0}(""); - vm.assume(success); - } } // Wrappers around cheatcodes to avoid footguns diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 1af6bb56..7242b9c1 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -335,36 +335,46 @@ contract StdCheatsTest is Test { return number; } - function testAssumeNoPrecompiles(address addr) external { - assumeNoPrecompiles(addr, getChain("optimism_goerli").chainId); + function testAssumeAddressIsNot(address addr) external { + // skip over Payable and NonPayable enums + for (uint8 i = 2; i < uint8(type(AddressType).max); i++) { + assumeAddressIsNot(AddressType(i), addr); + } + assertTrue(addr != address(0)); assertTrue( addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) - || (addr > address(0x4200000000000000000000000000000000000800) && addr < address(vm)) || addr > address(vm) - ); + || addr > address(0x4200000000000000000000000000000000000800) + ); + assertTrue(addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67); + } + + function testAssumeNoPayable() external { + // all should pass since these addresses are not payable + + // VM address + assumeNoPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + // Console address + assumeNoPayable(0x000000000000000000636F6e736F6c652e6c6f67); + + // Create2Deployer + assumeNoPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); } - function testAssumePayable() external { + function testAssumeNoNonPayable() external { // all should revert since these addresses are not payable // VM address vm.expectRevert(); - assumePayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + assumeNoNonPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); // Console address vm.expectRevert(); - assumePayable(0x000000000000000000636F6e736F6c652e6c6f67); + assumeNoNonPayable(0x000000000000000000636F6e736F6c652e6c6f67); // Create2Deployer vm.expectRevert(); - assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); - } - - function testAssumePayable(address addr) external { - assumePayable(addr); - assertTrue( - addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67 - && addr != 0x4e59b44847b379578588920cA78FbF26c0B4956C - ); + assumeNoNonPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); } } From 9ab202fbe78008f67f757275259074994b610409 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:21:33 -0500 Subject: [PATCH 05/17] forge fmt --- src/StdCheats.sol | 6 ++++-- test/StdCheats.t.sol | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 0a8eb553..21860ce0 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -305,7 +305,7 @@ abstract contract StdCheatsSafe { // address), but the same rationale for excluding them applies so we include those too. // These should be present on all EVM-compatible chains. - vm.assume((addr < address(0x1) || addr > address(0x9)) && addr != address(vm)); + vm.assume(addr < address(0x1) || addr > address(0x9)); // forgefmt: disable-start if (chainId == 10 || chainId == 420) { @@ -325,7 +325,9 @@ abstract contract StdCheatsSafe { function assumeNoForgeAddresses(address addr) internal virtual { // vm and console addresses - vm.assume(addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67); + vm.assume( + addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67 + ); } function readEIP1559ScriptArtifact(string memory path) diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 7242b9c1..ad81f2ac 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -343,9 +343,11 @@ contract StdCheatsTest is Test { assertTrue(addr != address(0)); assertTrue( addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) - || addr > address(0x4200000000000000000000000000000000000800) - ); - assertTrue(addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67); + || addr > address(0x4200000000000000000000000000000000000800) + ); + assertTrue( + addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67 + ); } function testAssumeNoPayable() external { From 0b25de513d822b5a7a0cadfe5ad3abb450aa6805 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:30:24 -0500 Subject: [PATCH 06/17] forge fmt v2 --- src/StdCheats.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index a41f1f55..915e5bc9 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -221,7 +221,7 @@ abstract contract StdCheatsSafe { (success, returnData) = token.staticcall(abi.encodeWithSelector(0xe47d6060, addr)); vm.assume(!success || abi.decode(returnData, (bool)) == false); } - + // Checks that `addr` is not blacklisted by token contracts that have a blacklist. // This is identical to `assumeNotBlacklisted(address,address)` but with a different name, for // backwards compatibility, since this name was used in the original PR which has already has @@ -306,6 +306,7 @@ abstract contract StdCheatsSafe { function assumeNoZeroAddress(address addr) internal virtual { vm.assume(addr != address(0)); + } function assumeNoPrecompiles(address addr) internal pure virtual { assumeNoPrecompiles(addr, _pureChainId()); From 0b3b4384fb544ae522fa177849b645c96b2f723e Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:32:28 -0500 Subject: [PATCH 07/17] remove exposed_assumePayable test --- test/StdCheats.t.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 5fb7dabf..182163c1 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -408,10 +408,6 @@ contract StdCheatsTest is Test { } contract StdCheatsMock is StdCheats { - function exposed_assumePayable(address addr) external { - assumePayable(addr); - } - // We deploy a mock version so we can properly test expected reverts. function exposed_assumeNotBlacklisted(address token, address addr) external view { return assumeNotBlacklisted(token, addr); From 508abbbf723a6e9d9cf0439597d1748f0842b503 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:46:03 -0500 Subject: [PATCH 08/17] modify _isPayable to be compiler agnostic --- src/StdCheats.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 915e5bc9..e6d48804 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -279,8 +279,14 @@ abstract contract StdCheatsSafe { assumeAddressIsNot(addressType4, addr); } - function _isPayable(address addr) private returns (bool) { - if (addr.code.length == 0) { + function _isPayable(address addr) private returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + + if (size == 0) { + // return false if no code return false; } else { require(addr.balance < type(uint256).max, "balance exceeds max uint256"); From cdecd83bf7dfe71809da317c8ba021150c0903d9 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 16:00:29 -0500 Subject: [PATCH 09/17] compiler agnostic v2 --- src/StdCheats.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index e6d48804..666021ec 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -9,6 +9,9 @@ import {Vm} from "./Vm.sol"; abstract contract StdCheatsSafe { Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + uint256 private constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; + bool private gasMeteringOff; // Data structures to parse Transaction objects from the broadcast artifact @@ -279,7 +282,7 @@ abstract contract StdCheatsSafe { assumeAddressIsNot(addressType4, addr); } - function _isPayable(address addr) private returns (bool) { + function _isPayable(address addr) private returns (bool) { uint256 size; assembly { size := extcodesize(addr) @@ -289,7 +292,7 @@ abstract contract StdCheatsSafe { // return false if no code return false; } else { - require(addr.balance < type(uint256).max, "balance exceeds max uint256"); + require(addr.balance < UINT256_MAX, "balance exceeds max uint256"); uint256 origBalanceTest = address(this).balance; uint256 origBalanceAddr = address(addr).balance; (bool success,) = payable(addr).call{value: 1}(""); From 6e10c8823a35370225c81dfcc308e867efb66cfd Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 3 Jul 2023 16:40:23 -0500 Subject: [PATCH 10/17] warnings --- src/StdCheats.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 666021ec..64083f12 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -313,7 +313,7 @@ abstract contract StdCheatsSafe { vm.assume(_isPayable(addr)); } - function assumeNoZeroAddress(address addr) internal virtual { + function assumeNoZeroAddress(address addr) internal pure virtual { vm.assume(addr != address(0)); } @@ -344,7 +344,7 @@ abstract contract StdCheatsSafe { // forgefmt: disable-end } - function assumeNoForgeAddresses(address addr) internal virtual { + function assumeNoForgeAddresses(address addr) internal pure virtual { // vm and console addresses vm.assume( addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67 From 73cca737ffe555dc49b4a8b805f486503aa4667c Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Tue, 4 Jul 2023 07:48:50 -0500 Subject: [PATCH 11/17] minor fixes --- src/StdCheats.sol | 4 ++-- test/StdCheats.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 64083f12..333f9e91 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -247,7 +247,7 @@ abstract contract StdCheatsSafe { } else if (addressType == AddressType.ZeroAddress) { assumeNoZeroAddress(addr); } else if (addressType == AddressType.Precompiles) { - assumeNoPrecompiles(addr, chainId); + assumeNoPrecompiles(addr); } else if (addressType == AddressType.ForgeAddresses) { assumeNoForgeAddresses(addr); } @@ -347,7 +347,7 @@ abstract contract StdCheatsSafe { function assumeNoForgeAddresses(address addr) internal pure virtual { // vm and console addresses vm.assume( - addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67 + addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D || addr != 0x000000000000000000636F6e736F6c652e6c6f67 ); } diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 182163c1..5e0ffc65 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -346,7 +346,7 @@ contract StdCheatsTest is Test { || addr > address(0x4200000000000000000000000000000000000800) ); assertTrue( - addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D && addr != 0x000000000000000000636F6e736F6c652e6c6f67 + addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D || addr != 0x000000000000000000636F6e736F6c652e6c6f67 ); } From c31747d7296f668a2248aeddcb995078593849d0 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Tue, 4 Jul 2023 09:56:28 -0500 Subject: [PATCH 12/17] minor fixes v2 --- src/StdCheats.sol | 6 ------ test/StdCheats.t.sol | 13 +++++++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 333f9e91..32b9d88d 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -234,12 +234,6 @@ abstract contract StdCheatsSafe { } function assumeAddressIsNot(AddressType addressType, address addr) internal virtual { - // Assembly required since `block.chainid` was introduced in 0.8.0. - uint256 chainId; - assembly { - chainId := chainid() - } - if (addressType == AddressType.Payable) { assumeNoPayable(addr); } else if (addressType == AddressType.NonPayable) { diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 5e0ffc65..dc7c2963 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -341,10 +341,7 @@ contract StdCheatsTest is Test { assumeAddressIsNot(AddressType(i), addr); } assertTrue(addr != address(0)); - assertTrue( - addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) - || addr > address(0x4200000000000000000000000000000000000800) - ); + assertTrue(addr < address(1) || addr > address(9)); assertTrue( addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D || addr != 0x000000000000000000636F6e736F6c652e6c6f67 ); @@ -379,6 +376,14 @@ contract StdCheatsTest is Test { assumeNoNonPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); } + function testAssumeNoPrecompiles(address addr) external { + assumeNoPrecompiles(addr, getChain("optimism_goerli").chainId); + assertTrue( + addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) + || addr > address(0x4200000000000000000000000000000000000800) + ); + } + function testCannotDeployCodeTo() external { vm.expectRevert("StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode."); this._revertDeployCodeTo(); From ff9e33ddd88db3a2148b46ec2141be9a80618992 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Sun, 9 Jul 2023 20:47:42 -0500 Subject: [PATCH 13/17] fix: make suggested changes --- src/StdCheats.sol | 60 +++++++++++++++++++---------------- test/StdCheats.t.sol | 75 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 93 insertions(+), 42 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 32b9d88d..026afaca 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -200,8 +200,8 @@ abstract contract StdCheatsSafe { Payable, NonPayable, ZeroAddress, - Precompiles, - ForgeAddresses + Precompile, + ForgeAddress } // Checks that `addr` is not blacklisted by token contracts that have a blacklist. @@ -235,28 +235,28 @@ abstract contract StdCheatsSafe { function assumeAddressIsNot(AddressType addressType, address addr) internal virtual { if (addressType == AddressType.Payable) { - assumeNoPayable(addr); + assumeNotPayable(addr); } else if (addressType == AddressType.NonPayable) { - assumeNoNonPayable(addr); + assumePayable(addr); } else if (addressType == AddressType.ZeroAddress) { - assumeNoZeroAddress(addr); - } else if (addressType == AddressType.Precompiles) { - assumeNoPrecompiles(addr); - } else if (addressType == AddressType.ForgeAddresses) { - assumeNoForgeAddresses(addr); + assumeNotZeroAddress(addr); + } else if (addressType == AddressType.Precompile) { + assumeNotPrecompile(addr); + } else if (addressType == AddressType.ForgeAddress) { + assumeNotForgeAddress(addr); } } - function assumeAddressIsNot(AddressType addressType1, AddressType addressType2, address addr) internal virtual { + function assumeAddressIsNot(address addr, AddressType addressType1, AddressType addressType2) internal virtual { assumeAddressIsNot(addressType1, addr); assumeAddressIsNot(addressType2, addr); } function assumeAddressIsNot( + address addr, AddressType addressType1, AddressType addressType2, - AddressType addressType3, - address addr + AddressType addressType3 ) internal virtual { assumeAddressIsNot(addressType1, addr); assumeAddressIsNot(addressType2, addr); @@ -264,11 +264,11 @@ abstract contract StdCheatsSafe { } function assumeAddressIsNot( + address addr, AddressType addressType1, AddressType addressType2, AddressType addressType3, - AddressType addressType4, - address addr + AddressType addressType4 ) internal virtual { assumeAddressIsNot(addressType1, addr); assumeAddressIsNot(addressType2, addr); @@ -276,6 +276,10 @@ abstract contract StdCheatsSafe { assumeAddressIsNot(addressType4, addr); } + // This function checks whether an address, `addr`, is payable. It returns true if addr has no code in which case + // it is an EOA. It will then send 1 wei to `addr` and check the success return value. There may be state changes + // depending on the fallback/receive logic implemented by `addr` which should be taken into account when + // this function is used. function _isPayable(address addr) private returns (bool) { uint256 size; assembly { @@ -283,12 +287,14 @@ abstract contract StdCheatsSafe { } if (size == 0) { - // return false if no code - return false; + // return true if no code + return true; } else { - require(addr.balance < UINT256_MAX, "balance exceeds max uint256"); + require(addr.balance < UINT256_MAX, "StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds"); uint256 origBalanceTest = address(this).balance; uint256 origBalanceAddr = address(addr).balance; + + vm.deal(address(this), 1); (bool success,) = payable(addr).call{value: 1}(""); // reset balances @@ -299,23 +305,23 @@ abstract contract StdCheatsSafe { } } - function assumeNoPayable(address addr) internal virtual { - vm.assume(!_isPayable(addr)); + function assumePayable(address addr) internal virtual { + vm.assume(_isPayable(addr)); } - function assumeNoNonPayable(address addr) internal virtual { - vm.assume(_isPayable(addr)); + function assumeNotPayable(address addr) internal virtual { + vm.assume(!_isPayable(addr)); } - function assumeNoZeroAddress(address addr) internal pure virtual { + function assumeNotZeroAddress(address addr) internal pure virtual { vm.assume(addr != address(0)); } - function assumeNoPrecompiles(address addr) internal pure virtual { - assumeNoPrecompiles(addr, _pureChainId()); + function assumeNotPrecompile(address addr) internal pure virtual { + assumeNotPrecompile(addr, _pureChainId()); } - function assumeNoPrecompiles(address addr, uint256 chainId) internal pure virtual { + function assumeNotPrecompile(address addr, uint256 chainId) internal pure virtual { // Note: For some chains like Optimism these are technically predeploys (i.e. bytecode placed at a specific // address), but the same rationale for excluding them applies so we include those too. @@ -338,10 +344,10 @@ abstract contract StdCheatsSafe { // forgefmt: disable-end } - function assumeNoForgeAddresses(address addr) internal pure virtual { + function assumeNotForgeAddress(address addr) internal pure virtual { // vm and console addresses vm.assume( - addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D || addr != 0x000000000000000000636F6e736F6c652e6c6f67 + addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67 ); } diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index dc7c2963..90dc3041 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -343,41 +343,74 @@ contract StdCheatsTest is Test { assertTrue(addr != address(0)); assertTrue(addr < address(1) || addr > address(9)); assertTrue( - addr != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D || addr != 0x000000000000000000636F6e736F6c652e6c6f67 + addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67 ); } - function testAssumeNoPayable() external { - // all should pass since these addresses are not payable + function testAssumePayable() external { + // We deploy a mock version so we can properly test the revert. + StdCheatsMock stdCheatsMock = new StdCheatsMock(); + + // all should revert since these addresses are not payable // VM address - assumeNoPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + vm.expectRevert(); + stdCheatsMock.exposed_assumePayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); // Console address - assumeNoPayable(0x000000000000000000636F6e736F6c652e6c6f67); + vm.expectRevert(); + stdCheatsMock.exposed_assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); // Create2Deployer - assumeNoPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); + vm.expectRevert(); + stdCheatsMock.exposed_assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); + + // all should pass since these addresses are payable + + // vitalik.eth + stdCheatsMock.exposed_assumePayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); + + // mock payable contract + address arbitraryAddress = makeAddr("arbitraryAddress"); + deployCodeTo("StdCheats.t.sol:MockContractPayable", arbitraryAddress); + MockContractPayable cp = MockContractPayable(payable(arbitraryAddress)); + + stdCheatsMock.exposed_assumePayable(address(cp)); } - function testAssumeNoNonPayable() external { - // all should revert since these addresses are not payable + function testAssumeNotPayable() external { + // We deploy a mock version so we can properly test the revert. + StdCheatsMock stdCheatsMock = new StdCheatsMock(); + + // should pass since these addresses are not payable // VM address - vm.expectRevert(); - assumeNoNonPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + stdCheatsMock.exposed_assumeNotPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); // Console address - vm.expectRevert(); - assumeNoNonPayable(0x000000000000000000636F6e736F6c652e6c6f67); + stdCheatsMock.exposed_assumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); // Create2Deployer + stdCheatsMock.exposed_assumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); + + // should fail since these addresses are payable + + // vitalik.eth + vm.expectRevert(); + stdCheatsMock.exposed_assumeNotPayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); + + // mock payable contract + address arbitraryAddress = makeAddr("arbitraryAddress"); + deployCodeTo("StdCheats.t.sol:MockContractPayable", arbitraryAddress); + MockContractPayable cp = MockContractPayable(payable(arbitraryAddress)); + vm.expectRevert(); - assumeNoNonPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); + stdCheatsMock.exposed_assumeNotPayable(address(cp)); + } - function testAssumeNoPrecompiles(address addr) external { - assumeNoPrecompiles(addr, getChain("optimism_goerli").chainId); + function testAssumeNotPrecompile(address addr) external { + assumeNotPrecompile(addr, getChain("optimism_goerli").chainId); assertTrue( addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) || addr > address(0x4200000000000000000000000000000000000800) @@ -413,6 +446,14 @@ contract StdCheatsTest is Test { } contract StdCheatsMock is StdCheats { + function exposed_assumePayable(address addr) external { + assumePayable(addr); + } + + function exposed_assumeNotPayable(address addr) external { + assumeNotPayable(addr); + } + // We deploy a mock version so we can properly test expected reverts. function exposed_assumeNotBlacklisted(address token, address addr) external view { return assumeNotBlacklisted(token, addr); @@ -564,3 +605,7 @@ contract MockContractWithConstructorArgs { z = _z; } } + +contract MockContractPayable { + receive() external payable {} +} From 53abaa496d6ceae0c01a40e0be56e240a10d3238 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Sun, 9 Jul 2023 20:49:15 -0500 Subject: [PATCH 14/17] forge fmt --- src/StdCheats.sol | 15 ++++++++------- test/StdCheats.t.sol | 7 ++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index 026afaca..c7457476 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -276,9 +276,9 @@ abstract contract StdCheatsSafe { assumeAddressIsNot(addressType4, addr); } - // This function checks whether an address, `addr`, is payable. It returns true if addr has no code in which case - // it is an EOA. It will then send 1 wei to `addr` and check the success return value. There may be state changes - // depending on the fallback/receive logic implemented by `addr` which should be taken into account when + // This function checks whether an address, `addr`, is payable. It returns true if addr has no code in which case + // it is an EOA. It will then send 1 wei to `addr` and check the success return value. There may be state changes + // depending on the fallback/receive logic implemented by `addr` which should be taken into account when // this function is used. function _isPayable(address addr) private returns (bool) { uint256 size; @@ -290,7 +290,10 @@ abstract contract StdCheatsSafe { // return true if no code return true; } else { - require(addr.balance < UINT256_MAX, "StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds"); + require( + addr.balance < UINT256_MAX, + "StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds" + ); uint256 origBalanceTest = address(this).balance; uint256 origBalanceAddr = address(addr).balance; @@ -346,9 +349,7 @@ abstract contract StdCheatsSafe { function assumeNotForgeAddress(address addr) internal pure virtual { // vm and console addresses - vm.assume( - addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67 - ); + vm.assume(addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67); } function readEIP1559ScriptArtifact(string memory path) diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 90dc3041..a3c339ea 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -342,15 +342,13 @@ contract StdCheatsTest is Test { } assertTrue(addr != address(0)); assertTrue(addr < address(1) || addr > address(9)); - assertTrue( - addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67 - ); + assertTrue(addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67); } function testAssumePayable() external { // We deploy a mock version so we can properly test the revert. StdCheatsMock stdCheatsMock = new StdCheatsMock(); - + // all should revert since these addresses are not payable // VM address @@ -406,7 +404,6 @@ contract StdCheatsTest is Test { vm.expectRevert(); stdCheatsMock.exposed_assumeNotPayable(address(cp)); - } function testAssumeNotPrecompile(address addr) external { From 59d5726cf0d1d0d6c732d500c36b6e92d3ca29b7 Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:25:08 -0500 Subject: [PATCH 15/17] fix: correct use of console address --- test/StdCheats.t.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index a3c339ea..346adbd0 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -355,16 +355,15 @@ contract StdCheatsTest is Test { vm.expectRevert(); stdCheatsMock.exposed_assumePayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - // Console address - vm.expectRevert(); - stdCheatsMock.exposed_assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); - // Create2Deployer vm.expectRevert(); stdCheatsMock.exposed_assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); // all should pass since these addresses are payable + // Console address (no code) + stdCheatsMock.exposed_assumePayable(0x000000000000000000636F6e736F6c652e6c6f67); + // vitalik.eth stdCheatsMock.exposed_assumePayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); @@ -380,18 +379,19 @@ contract StdCheatsTest is Test { // We deploy a mock version so we can properly test the revert. StdCheatsMock stdCheatsMock = new StdCheatsMock(); - // should pass since these addresses are not payable + // all should pass since these addresses are not payable // VM address stdCheatsMock.exposed_assumeNotPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - // Console address - stdCheatsMock.exposed_assumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); - // Create2Deployer stdCheatsMock.exposed_assumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); - // should fail since these addresses are payable + // all should revert since these addresses are payable + + // Console address (no code) + vm.expectRevert(); + stdCheatsMock.exposed_assumeNotPayable(0x000000000000000000636F6e736F6c652e6c6f67); // vitalik.eth vm.expectRevert(); From d9f25df25e084634653c2970cd036e90e17c5f8a Mon Sep 17 00:00:00 2001 From: Sabnock01 <24715302+Sabnock01@users.noreply.github.com> Date: Mon, 10 Jul 2023 09:32:43 -0500 Subject: [PATCH 16/17] fix: remove extcodesize check --- src/StdCheats.sol | 34 ++++++++++++---------------------- test/StdCheats.t.sol | 14 +++++++------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index c7457476..a396d9d6 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -281,31 +281,21 @@ abstract contract StdCheatsSafe { // depending on the fallback/receive logic implemented by `addr` which should be taken into account when // this function is used. function _isPayable(address addr) private returns (bool) { - uint256 size; - assembly { - size := extcodesize(addr) - } - - if (size == 0) { - // return true if no code - return true; - } else { - require( - addr.balance < UINT256_MAX, - "StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds" - ); - uint256 origBalanceTest = address(this).balance; - uint256 origBalanceAddr = address(addr).balance; + require( + addr.balance < UINT256_MAX, + "StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds" + ); + uint256 origBalanceTest = address(this).balance; + uint256 origBalanceAddr = address(addr).balance; - vm.deal(address(this), 1); - (bool success,) = payable(addr).call{value: 1}(""); + vm.deal(address(this), 1); + (bool success,) = payable(addr).call{value: 1}(""); - // reset balances - vm.deal(address(this), origBalanceTest); - vm.deal(addr, origBalanceAddr); + // reset balances + vm.deal(address(this), origBalanceTest); + vm.deal(addr, origBalanceAddr); - return success; - } + return success; } function assumePayable(address addr) internal virtual { diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 346adbd0..2dac7146 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -355,15 +355,16 @@ contract StdCheatsTest is Test { vm.expectRevert(); stdCheatsMock.exposed_assumePayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + // Console address + vm.expectRevert(); + stdCheatsMock.exposed_assumePayable(0x000000000000000000636F6e736F6c652e6c6f67); + // Create2Deployer vm.expectRevert(); stdCheatsMock.exposed_assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); // all should pass since these addresses are payable - // Console address (no code) - stdCheatsMock.exposed_assumePayable(0x000000000000000000636F6e736F6c652e6c6f67); - // vitalik.eth stdCheatsMock.exposed_assumePayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); @@ -384,15 +385,14 @@ contract StdCheatsTest is Test { // VM address stdCheatsMock.exposed_assumeNotPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + // Console address + stdCheatsMock.exposed_assumeNotPayable(0x000000000000000000636F6e736F6c652e6c6f67); + // Create2Deployer stdCheatsMock.exposed_assumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C); // all should revert since these addresses are payable - // Console address (no code) - vm.expectRevert(); - stdCheatsMock.exposed_assumeNotPayable(0x000000000000000000636F6e736F6c652e6c6f67); - // vitalik.eth vm.expectRevert(); stdCheatsMock.exposed_assumeNotPayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); From 3981afcd06c2b523ba4d677a02e8422baff9b077 Mon Sep 17 00:00:00 2001 From: Matt Solomon Date: Tue, 11 Jul 2023 10:40:26 -0700 Subject: [PATCH 17/17] chore: tweak function arg order and other small cleanup --- src/StdCheats.sol | 31 +++++++++++++++++-------------- test/StdCheats.t.sol | 12 +++--------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/StdCheats.sol b/src/StdCheats.sol index a396d9d6..1b01726b 100644 --- a/src/StdCheats.sol +++ b/src/StdCheats.sol @@ -233,7 +233,7 @@ abstract contract StdCheatsSafe { assumeNotBlacklisted(token, addr); } - function assumeAddressIsNot(AddressType addressType, address addr) internal virtual { + function assumeAddressIsNot(address addr, AddressType addressType) internal virtual { if (addressType == AddressType.Payable) { assumeNotPayable(addr); } else if (addressType == AddressType.NonPayable) { @@ -248,8 +248,8 @@ abstract contract StdCheatsSafe { } function assumeAddressIsNot(address addr, AddressType addressType1, AddressType addressType2) internal virtual { - assumeAddressIsNot(addressType1, addr); - assumeAddressIsNot(addressType2, addr); + assumeAddressIsNot(addr, addressType1); + assumeAddressIsNot(addr, addressType2); } function assumeAddressIsNot( @@ -258,9 +258,9 @@ abstract contract StdCheatsSafe { AddressType addressType2, AddressType addressType3 ) internal virtual { - assumeAddressIsNot(addressType1, addr); - assumeAddressIsNot(addressType2, addr); - assumeAddressIsNot(addressType3, addr); + assumeAddressIsNot(addr, addressType1); + assumeAddressIsNot(addr, addressType2); + assumeAddressIsNot(addr, addressType3); } function assumeAddressIsNot( @@ -270,16 +270,16 @@ abstract contract StdCheatsSafe { AddressType addressType3, AddressType addressType4 ) internal virtual { - assumeAddressIsNot(addressType1, addr); - assumeAddressIsNot(addressType2, addr); - assumeAddressIsNot(addressType3, addr); - assumeAddressIsNot(addressType4, addr); + assumeAddressIsNot(addr, addressType1); + assumeAddressIsNot(addr, addressType2); + assumeAddressIsNot(addr, addressType3); + assumeAddressIsNot(addr, addressType4); } - // This function checks whether an address, `addr`, is payable. It returns true if addr has no code in which case - // it is an EOA. It will then send 1 wei to `addr` and check the success return value. There may be state changes - // depending on the fallback/receive logic implemented by `addr` which should be taken into account when - // this function is used. + // This function checks whether an address, `addr`, is payable. It works by sending 1 wei to + // `addr` and checking the `success` return value. + // NOTE: This function may result in state changes depending on the fallback/receive logic + // implemented by `addr`, which should be taken into account when this function is used. function _isPayable(address addr) private returns (bool) { require( addr.balance < UINT256_MAX, @@ -298,6 +298,9 @@ abstract contract StdCheatsSafe { return success; } + // NOTE: This function may result in state changes depending on the fallback/receive logic + // implemented by `addr`, which should be taken into account when this function is used. See the + // `_isPayable` method for more information. function assumePayable(address addr) internal virtual { vm.assume(_isPayable(addr)); } diff --git a/test/StdCheats.t.sol b/test/StdCheats.t.sol index 2dac7146..05a56882 100644 --- a/test/StdCheats.t.sol +++ b/test/StdCheats.t.sol @@ -338,7 +338,7 @@ contract StdCheatsTest is Test { function testAssumeAddressIsNot(address addr) external { // skip over Payable and NonPayable enums for (uint8 i = 2; i < uint8(type(AddressType).max); i++) { - assumeAddressIsNot(AddressType(i), addr); + assumeAddressIsNot(addr, AddressType(i)); } assertTrue(addr != address(0)); assertTrue(addr < address(1) || addr > address(9)); @@ -369,10 +369,7 @@ contract StdCheatsTest is Test { stdCheatsMock.exposed_assumePayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); // mock payable contract - address arbitraryAddress = makeAddr("arbitraryAddress"); - deployCodeTo("StdCheats.t.sol:MockContractPayable", arbitraryAddress); - MockContractPayable cp = MockContractPayable(payable(arbitraryAddress)); - + MockContractPayable cp = new MockContractPayable(); stdCheatsMock.exposed_assumePayable(address(cp)); } @@ -398,10 +395,7 @@ contract StdCheatsTest is Test { stdCheatsMock.exposed_assumeNotPayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); // mock payable contract - address arbitraryAddress = makeAddr("arbitraryAddress"); - deployCodeTo("StdCheats.t.sol:MockContractPayable", arbitraryAddress); - MockContractPayable cp = MockContractPayable(payable(arbitraryAddress)); - + MockContractPayable cp = new MockContractPayable(); vm.expectRevert(); stdCheatsMock.exposed_assumeNotPayable(address(cp)); }