Skip to content

Commit b75625a

Browse files
authored
fix(forge): ensure selected fork contains init state for persisted accounts (#10301)
1 parent 8d5c36f commit b75625a

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

Diff for: crates/evm/core/src/backend/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,14 @@ impl DatabaseExt for Backend {
11021102
// update the shared state and track
11031103
let mut fork = self.inner.take_fork(idx);
11041104

1105+
// Make sure all persistent accounts on the newly selected fork starts from the init
1106+
// state (from setup).
1107+
for addr in &self.inner.persistent_accounts {
1108+
if let Some(account) = self.fork_init_journaled_state.state.get(addr) {
1109+
fork.journaled_state.state.insert(*addr, account.clone());
1110+
}
1111+
}
1112+
11051113
// since all forks handle their state separately, the depth can drift
11061114
// this is a handover where the target fork starts at the same depth where it was
11071115
// selected. This ensures that there are no gaps in depth which would

Diff for: crates/forge/tests/cli/test_cmd.rs

+79
Original file line numberDiff line numberDiff line change
@@ -3521,3 +3521,82 @@ contract InterceptInitcodeTest is DSTest {
35213521
.unwrap();
35223522
cmd.args(["test", "-vvvvv"]).assert_success();
35233523
});
3524+
3525+
// <https://github.com/foundry-rs/foundry/issues/10296>
3526+
forgetest_init!(should_preserve_fork_state_setup, |prj, cmd| {
3527+
prj.wipe_contracts();
3528+
prj.add_test(
3529+
"Counter.t.sol",
3530+
r#"
3531+
import "forge-std/Test.sol";
3532+
import {StdChains} from "forge-std/StdChains.sol";
3533+
3534+
contract CounterTest is Test {
3535+
struct Domain {
3536+
StdChains.Chain chain;
3537+
uint256 forkId;
3538+
}
3539+
3540+
struct Bridge {
3541+
Domain source;
3542+
Domain destination;
3543+
uint256 someVal;
3544+
}
3545+
3546+
struct SomeStruct {
3547+
Domain domain;
3548+
Bridge[] bridges;
3549+
}
3550+
3551+
mapping(uint256 => SomeStruct) internal data;
3552+
3553+
function setUp() public {
3554+
StdChains.Chain memory chain1 = getChain("mainnet");
3555+
StdChains.Chain memory chain2 = getChain("base");
3556+
Domain memory domain1 = Domain(chain1, vm.createFork(chain1.rpcUrl, 22253716));
3557+
Domain memory domain2 = Domain(chain2, vm.createFork(chain2.rpcUrl, 28839981));
3558+
data[1].domain = domain1;
3559+
data[2].domain = domain2;
3560+
3561+
vm.selectFork(domain1.forkId);
3562+
3563+
data[2].bridges.push(Bridge(domain1, domain2, 123));
3564+
vm.selectFork(data[2].domain.forkId);
3565+
vm.selectFork(data[1].domain.forkId);
3566+
data[2].bridges.push(Bridge(domain1, domain2, 456));
3567+
3568+
assertEq(data[2].bridges.length, 2);
3569+
}
3570+
3571+
function test_assert_storage() public {
3572+
vm.selectFork(data[2].domain.forkId);
3573+
assertEq(data[2].bridges.length, 2);
3574+
}
3575+
3576+
function test_modify_and_storage() public {
3577+
data[3].domain = Domain(getChain("base"), vm.createFork(getChain("base").rpcUrl, 28839981));
3578+
data[3].bridges.push(Bridge(data[1].domain, data[2].domain, 123));
3579+
data[3].bridges.push(Bridge(data[1].domain, data[2].domain, 456));
3580+
3581+
vm.selectFork(data[2].domain.forkId);
3582+
assertEq(data[3].bridges.length, 2);
3583+
}
3584+
}
3585+
"#,
3586+
)
3587+
.unwrap();
3588+
3589+
cmd.args(["test", "--mc", "CounterTest"]).assert_success().stdout_eq(str![[r#"
3590+
[COMPILING_FILES] with [SOLC_VERSION]
3591+
[SOLC_VERSION] [ELAPSED]
3592+
Compiler run successful!
3593+
3594+
Ran 2 tests for test/Counter.t.sol:CounterTest
3595+
[PASS] test_assert_storage() ([GAS])
3596+
[PASS] test_modify_and_storage() ([GAS])
3597+
Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED]
3598+
3599+
Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests)
3600+
3601+
"#]]);
3602+
});

0 commit comments

Comments
 (0)