- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2.2k
Description
Component
Forge
Have you ensured that all of these are up to date?
- Foundry
- Foundryup
What version of Foundry are you on?
forge Version: 1.1.0-stable Commit SHA: d484a00 Build Timestamp: 2025-04-30T13:51:59.442211000Z (1746021119) Build Profile: maxperf
What version of Foundryup are you on?
foundryup: 1.0.1
What command(s) is the bug in?
forge test
Operating System
macOS (Intel)
Describe the bug
When using targetContract(address(this)) in a test contract, we need to consider what state-changing functions are considered valid targets.
TLDR: as of #10274, setUp(), test_ and invariant_ functions are all considered targets functions, which I found a little surprising.
My initial assumption was that all these are special functions that should be excluded for target functions by default. The case seems particularly clear for excluding setUp(), but @GalloDaSballo thinks that invariant_ functions could legitimately be considered valid state-changing targets. My bias would be to exclude all special functions by default (setUp(), test_ and invariant_), I don't think they belong in call sequences.
Repro
I use the following test contract to evaluate the behavior and find out which functions are targets:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "forge-std/Test.sol";
contract InvariantTest is Test {
    bool fooCalled;
    bool testSanityCalled;
    uint256 invariantCalledNum;
    uint256 setUpCalledNum;
    function setUp() public {
        targetContract(address(this));
    }
    function foo() public {
        fooCalled = true;
    }
    function test_sanity() public {
        testSanityCalled = true;
    }
    function invariant_foo_called() public view {
        assert(!fooCalled);
    }
    function invariant_testSanity_considered_target() public {
        assertFalse(testSanityCalled);
    }
    function invariant_setUp_considered_target() public {
        setUpCalledNum++;
        assertLt(setUpCalledNum, 3);
    }
    function invariant_considered_target() public {
        // this is evaluated for invariant violations, but also considered a target state-changing function
        invariantCalledNum++;
        assertLt(invariantCalledNum, 3);
    }
}Expected Behavior
I think it would be fair for this test to have output like this:
[FAIL] invariant_foo_called
[PASS] invariant_testSanity_considered_target
[PASS] invariant_setUp_considered_target
[PASS] invariant_considered_target
meaning that foo() would be recognized as the only valid state-changing target function in this contract
Actual Behavior
Full output below, showing all target functions called:
 ft --mc InvariantTest                                         
[⠒] Compiling...
[⠃] Compiling 1 files with Solc 0.8.28
[⠊] Solc 0.8.28 finished in 690.33ms
Compiler run successful with warnings:
Warning (2018): Function state mutability can be restricted to view
  --> test/Invariant.t.sol:28:5:
   |
28 |     function invariant_testSanity_considered_target() public {
   |     ^ (Relevant source part starts here and spans across multiple lines).
Ran 5 tests for test/Invariant.t.sol:InvariantTest
[FAIL: assertion failed: 3 >= 3]
	[Sequence] (original: 7, shrunk: 2)
		sender=0xDD2073ceDaF7259a8c4C7cAEdba084a2EbbC92bd addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_considered_target() args=[]
		sender=0x0000000000000000000000000000000000000D08 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_considered_target() args=[]
 invariant_considered_target() (runs: 0, calls: 0, reverts: 2)
╭---------------+----------------------------------------+-------+---------+----------╮
| Contract      | Selector                               | Calls | Reverts | Discards |
+=====================================================================================+
| InvariantTest | invariant_considered_target            | 2     | 0       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | invariant_testSanity_considered_target | 2     | 2       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | setUp                                  | 4     | 0       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | test_sanity                            | 1     | 0       | 0        |
╰---------------+----------------------------------------+-------+---------+----------╯
[FAIL: panic: assertion failed (0x01)]
	[Sequence] (original: 5, shrunk: 1)
		sender=0x0000000000000000000000000000000000001134 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=foo() args=[]
 invariant_foo_called() (runs: 0, calls: 0, reverts: 0)
╭---------------+-----------------------------------+-------+---------+----------╮
| Contract      | Selector                          | Calls | Reverts | Discards |
+================================================================================+
| InvariantTest | foo                               | 1     | 0       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | invariant_considered_target       | 1     | 0       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | invariant_setUp_considered_target | 2     | 0       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | setUp                             | 1     | 0       | 0        |
╰---------------+-----------------------------------+-------+---------+----------╯
[FAIL: assertion failed: 3 >= 3]
	[Sequence] (original: 10, shrunk: 2)
		sender=0x0000000000000000000000000000000000000639 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_setUp_considered_target() args=[]
		sender=0x0000000000000000000000000000000000001108 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_setUp_considered_target() args=[]
 invariant_setUp_considered_target() (runs: 0, calls: 0, reverts: 2)
╭---------------+-----------------------------------+-------+---------+----------╮
| Contract      | Selector                          | Calls | Reverts | Discards |
+================================================================================+
| InvariantTest | foo                               | 3     | 0       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | invariant_considered_target       | 4     | 2       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | invariant_setUp_considered_target | 2     | 0       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | setUp                             | 1     | 0       | 0        |
|---------------+-----------------------------------+-------+---------+----------|
| InvariantTest | test_sanity                       | 2     | 0       | 0        |
╰---------------+-----------------------------------+-------+---------+----------╯
[FAIL: assertion failed]
	[Sequence] (original: 15, shrunk: 1)
		sender=0x0000000000000000000000000000000000001856 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=test_sanity() args=[]
 invariant_testSanity_considered_target() (runs: 0, calls: 0, reverts: 4)
╭---------------+----------------------------------------+-------+---------+----------╮
| Contract      | Selector                               | Calls | Reverts | Discards |
+=====================================================================================+
| InvariantTest | foo                                    | 3     | 0       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | invariant_considered_target            | 4     | 2       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | invariant_setUp_considered_target      | 4     | 2       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | invariant_testSanity_considered_target | 1     | 0       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | setUp                                  | 6     | 0       | 0        |
|---------------+----------------------------------------+-------+---------+----------|
| InvariantTest | test_sanity                            | 1     | 0       | 0        |
╰---------------+----------------------------------------+-------+---------+----------╯
[PASS] test_sanity() (gas: 5304)
Suite result: FAILED. 1 passed; 4 failed; 0 skipped; finished in 22.00ms (37.32ms CPU time)
Ran 1 test suite in 406.19ms (22.00ms CPU time): 1 tests passed, 4 failed, 0 skipped (5 total tests)
Failing tests:
Encountered 4 failing tests in test/Invariant.t.sol:InvariantTest
[FAIL: assertion failed: 3 >= 3]
	[Sequence] (original: 7, shrunk: 2)
		sender=0xDD2073ceDaF7259a8c4C7cAEdba084a2EbbC92bd addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_considered_target() args=[]
		sender=0x0000000000000000000000000000000000000D08 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_considered_target() args=[]
 invariant_considered_target() (runs: 0, calls: 0, reverts: 2)
[FAIL: panic: assertion failed (0x01)]
	[Sequence] (original: 5, shrunk: 1)
		sender=0x0000000000000000000000000000000000001134 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=foo() args=[]
 invariant_foo_called() (runs: 0, calls: 0, reverts: 0)
[FAIL: assertion failed: 3 >= 3]
	[Sequence] (original: 10, shrunk: 2)
		sender=0x0000000000000000000000000000000000000639 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_setUp_considered_target() args=[]
		sender=0x0000000000000000000000000000000000001108 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=invariant_setUp_considered_target() args=[]
 invariant_setUp_considered_target() (runs: 0, calls: 0, reverts: 2)
[FAIL: assertion failed]
	[Sequence] (original: 15, shrunk: 1)
		sender=0x0000000000000000000000000000000000001856 addr=[test/Invariant.t.sol:InvariantTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 calldata=test_sanity() args=[]
 invariant_testSanity_considered_target() (runs: 0, calls: 0, reverts: 4)
Encountered a total of 4 failing tests, 1 tests succeeded
Background
Before #10274, selectors had to be explicitly configured for address(this), which was verbose but explicit and understandable with no surprises:
targetContract(address(this));
        selectors = new bytes4[](2);
        selectors[0] = this.foo.selector;
        selectors[1] = this.bar.selector;
        targetSelector(FuzzSelector({
            addr: address(this),
            selectors: selectors
        }));We are trying to implement a similar feature and are trying to be compatible with foundry when possible: a16z/halmos#506
Metadata
Metadata
Assignees
Labels
Type
Projects
Status