From 5add458c57897e2f5fa5d3fa3ffecb228bfed453 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Fri, 29 Nov 2024 15:36:56 +0200 Subject: [PATCH 1/9] feat(cheatcodes): add vm.getStateDiff() to get state diffs as string --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++ crates/cheatcodes/spec/src/vm.rs | 4 ++ crates/cheatcodes/src/evm.rs | 50 +++++++++++++++++++ testdata/cheats/Vm.sol | 1 + .../cheats/RecordAccountAccesses.t.sol | 13 +++++ 5 files changed, 88 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d8f8d21df67b6..7acb6fa4c91f1 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5671,6 +5671,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getStateDiff", + "description": "Returns state diffs from current `vm.startStateDiffRecording` session.", + "declaration": "function getStateDiff() external view returns (string memory diff);", + "visibility": "external", + "mutability": "view", + "signature": "getStateDiff()", + "selector": "0x80df01cc", + "selectorBytes": [ + 128, + 223, + 1, + 204 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "getWallets", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 6b66d31ddb502..73884b9ffb5c8 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -384,6 +384,10 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function stopAndReturnStateDiff() external returns (AccountAccess[] memory accountAccesses); + /// Returns state diffs from current `vm.startStateDiffRecording` session. + #[cheatcode(group = Evm, safety = Safe)] + function getStateDiff() external view returns (string memory diff); + // -------- Recording Map Writes -------- /// Starts recording all map SSTOREs for later retrieval. diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 723beb1cfc8ab..8702424cfbb10 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -683,6 +683,56 @@ impl Cheatcode for stopAndReturnStateDiffCall { } } +/// State diffs to be displayed. +/// (changed account -> (change slot -> vec of (initial value, current value) tuples)) +type StateDiffs = BTreeMap>>; + +impl Cheatcode for getStateDiffCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self {} = self; + + let mut diffs = String::new(); + let address_label = + |addr: &Address| state.labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); + + let mut changes_map: StateDiffs = BTreeMap::default(); + if let Some(records) = &state.recorded_account_diffs_stack { + records + .iter() + .flatten() + .filter(|account_access| !account_access.storageAccesses.is_empty()) + .for_each(|account_access| { + for storage_access in &account_access.storageAccesses { + if storage_access.isWrite && !storage_access.reverted { + changes_map + .entry(storage_access.account) + .or_default() + .entry(storage_access.slot.into()) + .or_default() + .push((storage_access.previousValue, storage_access.newValue)); + } + } + }); + + for change in changes_map { + // Print changed account. + diffs.push_str(&format!("{}:\n", &address_label(&change.0)).to_string()); + for slot_changes in change.1 { + // For each slot we print the initial value from first storage change recorded + // and the new value from last storage change recorded. + let initial_value = slot_changes.1.first().unwrap().0; + let current_value = slot_changes.1.last().unwrap().1; + diffs.push_str( + &format!("@ {}: {} -> {}\n", slot_changes.0, initial_value, current_value) + .to_string(), + ); + } + } + } + Ok(diffs.abi_encode()) + } +} + impl Cheatcode for broadcastRawTransactionCall { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let tx = TxEnvelope::decode(&mut self.data.as_ref()) diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index f6f66969f99fe..575eff4216191 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -277,6 +277,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); + function getStateDiff() external view returns (string memory diff); function getWallets() external returns (address[] memory wallets); function indexOf(string calldata input, string calldata key) external pure returns (uint256); function isContext(ForgeContext context) external view returns (bool result); diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index 98b5843b2a7f8..d912ca75131dd 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; +import "../logs/console.sol"; /// @notice Helper contract with a construction that makes a call to itself then /// optionally reverts if zero-length data is passed @@ -261,6 +262,11 @@ contract RecordAccountAccessesTest is DSTest { two.write(bytes32(uint256(5678)), bytes32(uint256(123469))); two.write(bytes32(uint256(5678)), bytes32(uint256(1234))); + string memory diffs = cheats.getStateDiff(); + assertEq( + "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9:\n@ 1235: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x000000000000000000000000000000000000000000000000000000000000162e\n0xc7183455a4C133Ae270771860664b6B7ec320bB1:\n@ 5678: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x00000000000000000000000000000000000000000000000000000000000004d2\n", + diffs + ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 4, "incorrect length"); @@ -332,6 +338,7 @@ contract RecordAccountAccessesTest is DSTest { // contract calls to self in constructor SelfCaller caller = new SelfCaller{value: 2 ether}("hello2 world2"); + assertEq("", cheats.getStateDiff()); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 6); assertEq( @@ -451,6 +458,7 @@ contract RecordAccountAccessesTest is DSTest { uint256 initBalance = address(this).balance; cheats.startStateDiffRecording(); try this.revertingCall{value: 1 ether}(address(1234), "") {} catch {} + assertEq("", cheats.getStateDiff()); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 2); assertEq( @@ -768,6 +776,11 @@ contract RecordAccountAccessesTest is DSTest { function testNestedStorage() public { cheats.startStateDiffRecording(); nestedStorer.run(); + cheats.label(address(nestedStorer), "NestedStorer"); + assertEq( + "NestedStorer:\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n", + cheats.getStateDiff() + ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 3, "incorrect account access length"); From df7f8f0a99e15a1fe7678a59903182f6c821dd85 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Fri, 29 Nov 2024 17:33:15 +0200 Subject: [PATCH 2/9] Nit arrow --- crates/cheatcodes/src/evm.rs | 2 +- testdata/default/cheats/RecordAccountAccesses.t.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 8702424cfbb10..05a78ed0ae508 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -723,7 +723,7 @@ impl Cheatcode for getStateDiffCall { let initial_value = slot_changes.1.first().unwrap().0; let current_value = slot_changes.1.last().unwrap().1; diffs.push_str( - &format!("@ {}: {} -> {}\n", slot_changes.0, initial_value, current_value) + &format!("@ {}: {} → {}\n", slot_changes.0, initial_value, current_value) .to_string(), ); } diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index d912ca75131dd..e7033ea516d8c 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -264,7 +264,7 @@ contract RecordAccountAccessesTest is DSTest { string memory diffs = cheats.getStateDiff(); assertEq( - "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9:\n@ 1235: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x000000000000000000000000000000000000000000000000000000000000162e\n0xc7183455a4C133Ae270771860664b6B7ec320bB1:\n@ 5678: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x00000000000000000000000000000000000000000000000000000000000004d2\n", + "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9:\n@ 1235: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x000000000000000000000000000000000000000000000000000000000000162e\n0xc7183455a4C133Ae270771860664b6B7ec320bB1:\n@ 5678: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x00000000000000000000000000000000000000000000000000000000000004d2\n", diffs ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -778,7 +778,7 @@ contract RecordAccountAccessesTest is DSTest { nestedStorer.run(); cheats.label(address(nestedStorer), "NestedStorer"); assertEq( - "NestedStorer:\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 -> 0x0000000000000000000000000000000000000000000000000000000000000001\n", + "NestedStorer:\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n", cheats.getStateDiff() ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); From 66a159324c140e6dbbb08db1bd247c61ad2735e4 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Mon, 2 Dec 2024 13:16:04 +0200 Subject: [PATCH 3/9] Add json output --- crates/cheatcodes/assets/cheatcodes.json | 14 +-- crates/cheatcodes/spec/src/vm.rs | 2 +- crates/cheatcodes/src/evm.rs | 97 +++++++++++-------- testdata/cheats/Vm.sol | 2 +- .../cheats/RecordAccountAccesses.t.sol | 17 +++- 5 files changed, 81 insertions(+), 51 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 7acb6fa4c91f1..168a956220e6e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5675,16 +5675,16 @@ "func": { "id": "getStateDiff", "description": "Returns state diffs from current `vm.startStateDiffRecording` session.", - "declaration": "function getStateDiff() external view returns (string memory diff);", + "declaration": "function getStateDiff(bool json) external view returns (string memory diff);", "visibility": "external", "mutability": "view", - "signature": "getStateDiff()", - "selector": "0x80df01cc", + "signature": "getStateDiff(bool)", + "selector": "0x935efbe7", "selectorBytes": [ - 128, - 223, - 1, - 204 + 147, + 94, + 251, + 231 ] }, "group": "evm", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 73884b9ffb5c8..da456a505f8e4 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -386,7 +386,7 @@ interface Vm { /// Returns state diffs from current `vm.startStateDiffRecording` session. #[cheatcode(group = Evm, safety = Safe)] - function getStateDiff() external view returns (string memory diff); + function getStateDiff(bool json) external view returns (string memory diff); // -------- Recording Map Writes -------- diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 05a78ed0ae508..bc0a8454a346f 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -683,54 +683,75 @@ impl Cheatcode for stopAndReturnStateDiffCall { } } -/// State diffs to be displayed. -/// (changed account -> (change slot -> vec of (initial value, current value) tuples)) -type StateDiffs = BTreeMap>>; - impl Cheatcode for getStateDiffCall { fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self {} = self; + let Self { json } = self; + + let state_diffs = get_recorded_state_diffs(state); + if *json { + return Ok(serde_json::to_string(&state_diffs)?.abi_encode()) + } let mut diffs = String::new(); - let address_label = - |addr: &Address| state.labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); + for (address, recorded_changes) in state_diffs { + // Print changed account. + diffs.push_str(&format!("{address}:\n")); + for (slot, (initial_value, current_value)) in recorded_changes { + diffs.push_str(&format!("@ {slot}: {initial_value} → {current_value}\n")); + } + } - let mut changes_map: StateDiffs = BTreeMap::default(); - if let Some(records) = &state.recorded_account_diffs_stack { - records - .iter() - .flatten() - .filter(|account_access| !account_access.storageAccesses.is_empty()) - .for_each(|account_access| { - for storage_access in &account_access.storageAccesses { - if storage_access.isWrite && !storage_access.reverted { - changes_map - .entry(storage_access.account) - .or_default() - .entry(storage_access.slot.into()) - .or_default() - .push((storage_access.previousValue, storage_access.newValue)); - } + Ok(diffs.abi_encode()) + } +} + +/// Returns state diffs to be displayed. +/// (changed account or label -> (changed slot -> (initial value, current value)) +fn get_recorded_state_diffs( + state: &mut Cheatcodes, +) -> BTreeMap> { + let address_label = + |addr: &Address| state.labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); + + let mut state_diffs = BTreeMap::default(); + if let Some(records) = &state.recorded_account_diffs_stack { + let mut recorded_changes: BTreeMap>> = + BTreeMap::default(); + records + .iter() + .flatten() + .filter(|account_access| !account_access.storageAccesses.is_empty()) + .for_each(|account_access| { + for storage_access in &account_access.storageAccesses { + // Retain only records with storage accesses writes that didn't revert. + if storage_access.isWrite && !storage_access.reverted { + recorded_changes + .entry(storage_access.account) + .or_default() + .entry(storage_access.slot.into()) + .or_default() + .push((storage_access.previousValue, storage_access.newValue)); } - }); - - for change in changes_map { - // Print changed account. - diffs.push_str(&format!("{}:\n", &address_label(&change.0)).to_string()); - for slot_changes in change.1 { - // For each slot we print the initial value from first storage change recorded - // and the new value from last storage change recorded. - let initial_value = slot_changes.1.first().unwrap().0; - let current_value = slot_changes.1.last().unwrap().1; - diffs.push_str( - &format!("@ {}: {} → {}\n", slot_changes.0, initial_value, current_value) - .to_string(), - ); } + }); + + for (address, recorded_changes) in recorded_changes { + let mut slot_changes: BTreeMap = BTreeMap::default(); + for (slot, recorded_slot_changes) in recorded_changes { + // For each slot we retain the initial value from first record and the new value + // from last record. + slot_changes.insert( + slot.to_string(), + ( + recorded_slot_changes.first().unwrap().0, + recorded_slot_changes.last().unwrap().1, + ), + ); } + state_diffs.insert(address_label(&address), slot_changes); } - Ok(diffs.abi_encode()) } + state_diffs } impl Cheatcode for broadcastRawTransactionCall { diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 575eff4216191..69f12e7c092ee 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -277,7 +277,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); - function getStateDiff() external view returns (string memory diff); + function getStateDiff(bool json) external view returns (string memory diff); function getWallets() external returns (address[] memory wallets); function indexOf(string calldata input, string calldata key) external pure returns (uint256); function isContext(ForgeContext context) external view returns (bool result); diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index e7033ea516d8c..83f79a643340a 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -262,11 +262,16 @@ contract RecordAccountAccessesTest is DSTest { two.write(bytes32(uint256(5678)), bytes32(uint256(123469))); two.write(bytes32(uint256(5678)), bytes32(uint256(1234))); - string memory diffs = cheats.getStateDiff(); + string memory diffs = cheats.getStateDiff(false); assertEq( "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9:\n@ 1235: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x000000000000000000000000000000000000000000000000000000000000162e\n0xc7183455a4C133Ae270771860664b6B7ec320bB1:\n@ 5678: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x00000000000000000000000000000000000000000000000000000000000004d2\n", diffs ); + string memory diffsJson = cheats.getStateDiff(true); + assertEq( + "{\"0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9\":{\"1235\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x000000000000000000000000000000000000000000000000000000000000162e\"]},\"0xc7183455a4C133Ae270771860664b6B7ec320bB1\":{\"5678\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x00000000000000000000000000000000000000000000000000000000000004d2\"]}}", + diffsJson + ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 4, "incorrect length"); @@ -338,7 +343,7 @@ contract RecordAccountAccessesTest is DSTest { // contract calls to self in constructor SelfCaller caller = new SelfCaller{value: 2 ether}("hello2 world2"); - assertEq("", cheats.getStateDiff()); + assertEq("", cheats.getStateDiff(false)); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 6); assertEq( @@ -458,7 +463,7 @@ contract RecordAccountAccessesTest is DSTest { uint256 initBalance = address(this).balance; cheats.startStateDiffRecording(); try this.revertingCall{value: 1 ether}(address(1234), "") {} catch {} - assertEq("", cheats.getStateDiff()); + assertEq("", cheats.getStateDiff(false)); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 2); assertEq( @@ -779,7 +784,11 @@ contract RecordAccountAccessesTest is DSTest { cheats.label(address(nestedStorer), "NestedStorer"); assertEq( "NestedStorer:\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n", - cheats.getStateDiff() + cheats.getStateDiff(false) + ); + assertEq( + "{\"NestedStorer\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"]}}", + cheats.getStateDiff(true) ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 3, "incorrect account access length"); From d46080ccc67e244a04e58a51d81f3e7e3ecb314e Mon Sep 17 00:00:00 2001 From: grandizzy Date: Mon, 2 Dec 2024 16:21:08 +0200 Subject: [PATCH 4/9] Better json format --- Cargo.lock | 1 + crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/src/evm.rs | 134 +++++++++++------- .../cheats/RecordAccountAccesses.t.sol | 6 +- 4 files changed, 86 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f8fa5bb58ef3..750e1b6a3ea28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3675,6 +3675,7 @@ dependencies = [ "revm", "revm-inspectors", "semver 1.0.23", + "serde", "serde_json", "thiserror 1.0.69", "toml 0.8.19", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 97b22b16f6e3f..8177bba1dd0f5 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -67,3 +67,4 @@ toml = { workspace = true, features = ["preserve_order"] } tracing.workspace = true walkdir.workspace = true proptest.workspace = true +serde.workspace = true diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index bc0a8454a346f..8261a732908f7 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -22,6 +22,7 @@ use std::{collections::BTreeMap, path::Path}; mod record_debug_step; use record_debug_step::{convert_call_trace_to_debug_step, flatten_call_trace}; +use serde::Serialize; mod fork; pub(crate) mod mapping; @@ -76,6 +77,29 @@ pub struct DealRecord { pub new_balance: U256, } +/// Recorded state diffs, for each changed address. +type StateDiffs = BTreeMap; + +/// Account state diff info. +#[derive(Serialize)] +struct AccountStateDiffs { + /// Address label, if any set. + #[serde(skip_serializing_if = "Option::is_none")] + label: Option, + /// State changes, per slot. + changes: BTreeMap, +} + +/// Storage slot diff info. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct SlotStateDiff { + /// Initial storage value. + previous_value: B256, + /// Current storage value. + new_value: B256, +} + impl Cheatcode for addrCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey } = self; @@ -693,11 +717,18 @@ impl Cheatcode for getStateDiffCall { } let mut diffs = String::new(); - for (address, recorded_changes) in state_diffs { + for (address, state_diffs) in state_diffs { // Print changed account. - diffs.push_str(&format!("{address}:\n")); - for (slot, (initial_value, current_value)) in recorded_changes { - diffs.push_str(&format!("@ {slot}: {initial_value} → {current_value}\n")); + if let Some(label) = state_diffs.label { + diffs.push_str(&format!("{address} ({label}):\n")); + } else { + diffs.push_str(&format!("{address}:\n")); + } + for (slot, slot_changes) in state_diffs.changes { + diffs.push_str(&format!( + "@ {slot}: {} → {}\n", + slot_changes.previous_value, slot_changes.new_value + )); } } @@ -705,55 +736,6 @@ impl Cheatcode for getStateDiffCall { } } -/// Returns state diffs to be displayed. -/// (changed account or label -> (changed slot -> (initial value, current value)) -fn get_recorded_state_diffs( - state: &mut Cheatcodes, -) -> BTreeMap> { - let address_label = - |addr: &Address| state.labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); - - let mut state_diffs = BTreeMap::default(); - if let Some(records) = &state.recorded_account_diffs_stack { - let mut recorded_changes: BTreeMap>> = - BTreeMap::default(); - records - .iter() - .flatten() - .filter(|account_access| !account_access.storageAccesses.is_empty()) - .for_each(|account_access| { - for storage_access in &account_access.storageAccesses { - // Retain only records with storage accesses writes that didn't revert. - if storage_access.isWrite && !storage_access.reverted { - recorded_changes - .entry(storage_access.account) - .or_default() - .entry(storage_access.slot.into()) - .or_default() - .push((storage_access.previousValue, storage_access.newValue)); - } - } - }); - - for (address, recorded_changes) in recorded_changes { - let mut slot_changes: BTreeMap = BTreeMap::default(); - for (slot, recorded_slot_changes) in recorded_changes { - // For each slot we retain the initial value from first record and the new value - // from last record. - slot_changes.insert( - slot.to_string(), - ( - recorded_slot_changes.first().unwrap().0, - recorded_slot_changes.last().unwrap().1, - ), - ); - } - state_diffs.insert(address_label(&address), slot_changes); - } - } - state_diffs -} - impl Cheatcode for broadcastRawTransactionCall { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let tx = TxEnvelope::decode(&mut self.data.as_ref()) @@ -1115,3 +1097,49 @@ fn genesis_account(account: &Account) -> GenesisAccount { private_key: None, } } + +/// Helper function to returns state diffs recorded for each changed account. +fn get_recorded_state_diffs(state: &mut Cheatcodes) -> StateDiffs { + let mut state_diffs: StateDiffs = BTreeMap::default(); + if let Some(records) = &state.recorded_account_diffs_stack { + let mut recorded_changes: BTreeMap>> = + BTreeMap::default(); + records + .iter() + .flatten() + .filter(|account_access| !account_access.storageAccesses.is_empty()) + .for_each(|account_access| { + for storage_access in &account_access.storageAccesses { + // Retain only records with storage accesses writes that didn't revert. + if storage_access.isWrite && !storage_access.reverted { + recorded_changes + .entry(storage_access.account) + .or_default() + .entry(storage_access.slot.into()) + .or_default() + .push((storage_access.previousValue, storage_access.newValue)); + } + } + }); + + for (address, recorded_changes) in recorded_changes { + let mut changes = BTreeMap::default(); + for (slot, recorded_slot_changes) in recorded_changes { + // For each slot we retain the initial value from first state change and the new + // value from last state change. + changes.insert( + slot.to_string(), + SlotStateDiff { + previous_value: recorded_slot_changes.first().unwrap().0, + new_value: recorded_slot_changes.last().unwrap().1, + }, + ); + } + state_diffs.insert( + address, + AccountStateDiffs { label: state.labels.get(&address).cloned(), changes }, + ); + } + } + state_diffs +} diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index 83f79a643340a..ed734e4e3300f 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -269,7 +269,7 @@ contract RecordAccountAccessesTest is DSTest { ); string memory diffsJson = cheats.getStateDiff(true); assertEq( - "{\"0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9\":{\"1235\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x000000000000000000000000000000000000000000000000000000000000162e\"]},\"0xc7183455a4C133Ae270771860664b6B7ec320bB1\":{\"5678\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x00000000000000000000000000000000000000000000000000000000000004d2\"]}}", + "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"changes\":{\"1235\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"changes\":{\"5678\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", diffsJson ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -783,11 +783,11 @@ contract RecordAccountAccessesTest is DSTest { nestedStorer.run(); cheats.label(address(nestedStorer), "NestedStorer"); assertEq( - "NestedStorer:\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n", + "0x2e234DAe75C793f67A35089C9d99245E1C58470b (NestedStorer):\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n", cheats.getStateDiff(false) ); assertEq( - "{\"NestedStorer\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"],\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000000000000000000000000001\"]}}", + "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"changes\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", cheats.getStateDiff(true) ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); From 1536119e2d9bb02929048e408c4023061ad4b6d3 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Mon, 2 Dec 2024 21:04:56 +0200 Subject: [PATCH 5/9] Rename to original and dirty --- crates/cheatcodes/src/evm.rs | 11 +++++------ testdata/default/cheats/RecordAccountAccesses.t.sol | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 8261a732908f7..03428c0bd9f74 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -92,12 +92,11 @@ struct AccountStateDiffs { /// Storage slot diff info. #[derive(Serialize)] -#[serde(rename_all = "camelCase")] struct SlotStateDiff { /// Initial storage value. - previous_value: B256, + original: B256, /// Current storage value. - new_value: B256, + dirty: B256, } impl Cheatcode for addrCall { @@ -727,7 +726,7 @@ impl Cheatcode for getStateDiffCall { for (slot, slot_changes) in state_diffs.changes { diffs.push_str(&format!( "@ {slot}: {} → {}\n", - slot_changes.previous_value, slot_changes.new_value + slot_changes.original, slot_changes.dirty )); } } @@ -1130,8 +1129,8 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> StateDiffs { changes.insert( slot.to_string(), SlotStateDiff { - previous_value: recorded_slot_changes.first().unwrap().0, - new_value: recorded_slot_changes.last().unwrap().1, + original: recorded_slot_changes.first().unwrap().0, + dirty: recorded_slot_changes.last().unwrap().1, }, ); } diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index ed734e4e3300f..ec256e6cddcbd 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -269,7 +269,7 @@ contract RecordAccountAccessesTest is DSTest { ); string memory diffsJson = cheats.getStateDiff(true); assertEq( - "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"changes\":{\"1235\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"changes\":{\"5678\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", + "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"changes\":{\"1235\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"changes\":{\"5678\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", diffsJson ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -787,7 +787,7 @@ contract RecordAccountAccessesTest is DSTest { cheats.getStateDiff(false) ); assertEq( - "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"changes\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", + "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"changes\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", cheats.getStateDiff(true) ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); From 4e684f8eca67ad28e3c7d85e53706581907f189f Mon Sep 17 00:00:00 2001 From: grandizzy Date: Tue, 3 Dec 2024 13:05:46 +0200 Subject: [PATCH 6/9] Changes after review: split in 2 cheatcodes, rename to prev/newValues --- crates/cheatcodes/assets/cheatcodes.json | 34 +++++++++++++++---- crates/cheatcodes/spec/src/vm.rs | 6 +++- crates/cheatcodes/src/evm.rs | 26 +++++++------- testdata/cheats/Vm.sol | 3 +- .../cheats/RecordAccountAccesses.t.sol | 16 ++++----- 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 168a956220e6e..a3723547f604a 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5675,16 +5675,36 @@ "func": { "id": "getStateDiff", "description": "Returns state diffs from current `vm.startStateDiffRecording` session.", - "declaration": "function getStateDiff(bool json) external view returns (string memory diff);", + "declaration": "function getStateDiff() external view returns (string memory diff);", "visibility": "external", "mutability": "view", - "signature": "getStateDiff(bool)", - "selector": "0x935efbe7", + "signature": "getStateDiff()", + "selector": "0x80df01cc", "selectorBytes": [ - 147, - 94, - 251, - 231 + 128, + 223, + 1, + 204 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "getStateDiffJson", + "description": "Returns state diffs from current `vm.startStateDiffRecording` session, in json format.", + "declaration": "function getStateDiffJson() external view returns (string memory diff);", + "visibility": "external", + "mutability": "view", + "signature": "getStateDiffJson()", + "selector": "0xf54fe009", + "selectorBytes": [ + 245, + 79, + 224, + 9 ] }, "group": "evm", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index da456a505f8e4..9addf5b599f20 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -386,7 +386,11 @@ interface Vm { /// Returns state diffs from current `vm.startStateDiffRecording` session. #[cheatcode(group = Evm, safety = Safe)] - function getStateDiff(bool json) external view returns (string memory diff); + function getStateDiff() external view returns (string memory diff); + + /// Returns state diffs from current `vm.startStateDiffRecording` session, in json format. + #[cheatcode(group = Evm, safety = Safe)] + function getStateDiffJson() external view returns (string memory diff); // -------- Recording Map Writes -------- diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 03428c0bd9f74..280c02b1e4e19 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -92,11 +92,12 @@ struct AccountStateDiffs { /// Storage slot diff info. #[derive(Serialize)] +#[serde(rename_all = "camelCase")] struct SlotStateDiff { /// Initial storage value. - original: B256, + previous_value: B256, /// Current storage value. - dirty: B256, + new_value: B256, } impl Cheatcode for addrCall { @@ -708,14 +709,8 @@ impl Cheatcode for stopAndReturnStateDiffCall { impl Cheatcode for getStateDiffCall { fn apply(&self, state: &mut Cheatcodes) -> Result { - let Self { json } = self; - - let state_diffs = get_recorded_state_diffs(state); - if *json { - return Ok(serde_json::to_string(&state_diffs)?.abi_encode()) - } - let mut diffs = String::new(); + let state_diffs = get_recorded_state_diffs(state); for (address, state_diffs) in state_diffs { // Print changed account. if let Some(label) = state_diffs.label { @@ -726,7 +721,7 @@ impl Cheatcode for getStateDiffCall { for (slot, slot_changes) in state_diffs.changes { diffs.push_str(&format!( "@ {slot}: {} → {}\n", - slot_changes.original, slot_changes.dirty + slot_changes.previous_value, slot_changes.new_value )); } } @@ -735,6 +730,13 @@ impl Cheatcode for getStateDiffCall { } } +impl Cheatcode for getStateDiffJsonCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let state_diffs = get_recorded_state_diffs(state); + Ok(serde_json::to_string(&state_diffs)?.abi_encode()) + } +} + impl Cheatcode for broadcastRawTransactionCall { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let tx = TxEnvelope::decode(&mut self.data.as_ref()) @@ -1129,8 +1131,8 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> StateDiffs { changes.insert( slot.to_string(), SlotStateDiff { - original: recorded_slot_changes.first().unwrap().0, - dirty: recorded_slot_changes.last().unwrap().1, + previous_value: recorded_slot_changes.first().unwrap().0, + new_value: recorded_slot_changes.last().unwrap().1, }, ); } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 69f12e7c092ee..6003339311a9d 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -277,7 +277,8 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); - function getStateDiff(bool json) external view returns (string memory diff); + function getStateDiff() external view returns (string memory diff); + function getStateDiffJson() external view returns (string memory diff); function getWallets() external returns (address[] memory wallets); function indexOf(string calldata input, string calldata key) external pure returns (uint256); function isContext(ForgeContext context) external view returns (bool result); diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index ec256e6cddcbd..4e6e771005197 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -262,14 +262,14 @@ contract RecordAccountAccessesTest is DSTest { two.write(bytes32(uint256(5678)), bytes32(uint256(123469))); two.write(bytes32(uint256(5678)), bytes32(uint256(1234))); - string memory diffs = cheats.getStateDiff(false); + string memory diffs = cheats.getStateDiff(); assertEq( "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9:\n@ 1235: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x000000000000000000000000000000000000000000000000000000000000162e\n0xc7183455a4C133Ae270771860664b6B7ec320bB1:\n@ 5678: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x00000000000000000000000000000000000000000000000000000000000004d2\n", diffs ); - string memory diffsJson = cheats.getStateDiff(true); + string memory diffsJson = cheats.getStateDiffJson(); assertEq( - "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"changes\":{\"1235\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"changes\":{\"5678\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", + "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"changes\":{\"1235\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"changes\":{\"5678\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", diffsJson ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -343,7 +343,7 @@ contract RecordAccountAccessesTest is DSTest { // contract calls to self in constructor SelfCaller caller = new SelfCaller{value: 2 ether}("hello2 world2"); - assertEq("", cheats.getStateDiff(false)); + assertEq("", cheats.getStateDiff()); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 6); assertEq( @@ -463,7 +463,7 @@ contract RecordAccountAccessesTest is DSTest { uint256 initBalance = address(this).balance; cheats.startStateDiffRecording(); try this.revertingCall{value: 1 ether}(address(1234), "") {} catch {} - assertEq("", cheats.getStateDiff(false)); + assertEq("", cheats.getStateDiff()); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 2); assertEq( @@ -784,11 +784,11 @@ contract RecordAccountAccessesTest is DSTest { cheats.label(address(nestedStorer), "NestedStorer"); assertEq( "0x2e234DAe75C793f67A35089C9d99245E1C58470b (NestedStorer):\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n", - cheats.getStateDiff(false) + cheats.getStateDiff() ); assertEq( - "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"changes\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":{\"original\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"dirty\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", - cheats.getStateDiff(true) + "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"changes\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", + cheats.getStateDiffJson() ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 3, "incorrect account access length"); From 4517474a2bb238ac7c9c959217ff9e96a34742e1 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 4 Dec 2024 08:57:24 +0200 Subject: [PATCH 7/9] Slots as hex strings, add balance diffs, cleanup --- crates/cheatcodes/src/evm.rs | 152 +++++++++++------- .../cheats/RecordAccountAccesses.t.sol | 18 ++- 2 files changed, 107 insertions(+), 63 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 280c02b1e4e19..a7e7bec8ce409 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -18,7 +18,11 @@ use foundry_evm_core::{ use foundry_evm_traces::StackSnapshotType; use rand::Rng; use revm::primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}; -use std::{collections::BTreeMap, path::Path}; +use std::{ + collections::{btree_map::Entry, BTreeMap}, + fmt::Display, + path::Path, +}; mod record_debug_step; use record_debug_step::{convert_call_trace_to_debug_step, flatten_call_trace}; @@ -77,27 +81,70 @@ pub struct DealRecord { pub new_balance: U256, } -/// Recorded state diffs, for each changed address. -type StateDiffs = BTreeMap; +/// Storage slot diff info. +#[derive(Serialize, Default)] +#[serde(rename_all = "camelCase")] +struct SlotStateDiff { + /// Initial storage value. + previous_value: B256, + /// Current storage value. + new_value: B256, +} + +/// Balance diff info. +#[derive(Serialize, Default)] +#[serde(rename_all = "camelCase")] +struct BalanceDiff { + /// Initial storage value. + previous_value: U256, + /// Current storage value. + new_value: U256, +} /// Account state diff info. -#[derive(Serialize)] +#[derive(Serialize, Default)] +#[serde(rename_all = "camelCase")] struct AccountStateDiffs { /// Address label, if any set. #[serde(skip_serializing_if = "Option::is_none")] label: Option, + /// Account balance changes. + #[serde(skip_serializing_if = "Option::is_none")] + balance_diff: Option, /// State changes, per slot. - changes: BTreeMap, + state_diff: BTreeMap, } -/// Storage slot diff info. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct SlotStateDiff { - /// Initial storage value. - previous_value: B256, - /// Current storage value. - new_value: B256, +impl Display for AccountStateDiffs { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> eyre::Result<(), std::fmt::Error> { + // Print changed account. + if let Some(label) = &self.label { + writeln!(f, "label: {label}")?; + } + // Print balance diff if changed. + if let Some(balance_diff) = &self.balance_diff { + if balance_diff.previous_value != balance_diff.new_value { + writeln!( + f, + "- balance diff: {} → {}", + balance_diff.previous_value, balance_diff.new_value + )?; + } + } + // Print state diff if any. + if !&self.state_diff.is_empty() { + writeln!(f, "- state diff:")?; + for (slot, slot_changes) in &self.state_diff { + writeln!( + f, + "@ {slot}: {} → {}", + slot_changes.previous_value, slot_changes.new_value + )?; + } + } + + Ok(()) + } } impl Cheatcode for addrCall { @@ -712,20 +759,9 @@ impl Cheatcode for getStateDiffCall { let mut diffs = String::new(); let state_diffs = get_recorded_state_diffs(state); for (address, state_diffs) in state_diffs { - // Print changed account. - if let Some(label) = state_diffs.label { - diffs.push_str(&format!("{address} ({label}):\n")); - } else { - diffs.push_str(&format!("{address}:\n")); - } - for (slot, slot_changes) in state_diffs.changes { - diffs.push_str(&format!( - "@ {slot}: {} → {}\n", - slot_changes.previous_value, slot_changes.new_value - )); - } + diffs.push_str(&format!("{address}\n")); + diffs.push_str(&format!("{state_diffs}\n")); } - Ok(diffs.abi_encode()) } } @@ -1100,47 +1136,49 @@ fn genesis_account(account: &Account) -> GenesisAccount { } /// Helper function to returns state diffs recorded for each changed account. -fn get_recorded_state_diffs(state: &mut Cheatcodes) -> StateDiffs { - let mut state_diffs: StateDiffs = BTreeMap::default(); +fn get_recorded_state_diffs(state: &mut Cheatcodes) -> BTreeMap { + let mut state_diffs: BTreeMap = BTreeMap::default(); if let Some(records) = &state.recorded_account_diffs_stack { - let mut recorded_changes: BTreeMap>> = - BTreeMap::default(); records .iter() .flatten() - .filter(|account_access| !account_access.storageAccesses.is_empty()) + .filter(|account_access| { + !account_access.storageAccesses.is_empty() || + account_access.oldBalance != account_access.newBalance + }) .for_each(|account_access| { + let account_diff = + state_diffs.entry(account_access.account).or_insert(AccountStateDiffs { + label: state.labels.get(&account_access.account).cloned(), + ..Default::default() + }); + // Update balance diff. Do not overwrite the initial balance if already set. + if let Some(diff) = &mut account_diff.balance_diff { + diff.new_value = account_access.newBalance; + } else { + account_diff.balance_diff = Some(BalanceDiff { + previous_value: account_access.oldBalance, + new_value: account_access.newBalance, + }); + } + for storage_access in &account_access.storageAccesses { - // Retain only records with storage accesses writes that didn't revert. if storage_access.isWrite && !storage_access.reverted { - recorded_changes - .entry(storage_access.account) - .or_default() - .entry(storage_access.slot.into()) - .or_default() - .push((storage_access.previousValue, storage_access.newValue)); + // Update state diff. Do not overwrite the initial value if already set. + match account_diff.state_diff.entry(storage_access.slot) { + Entry::Vacant(slot_state_diff) => { + slot_state_diff.insert(SlotStateDiff { + previous_value: storage_access.previousValue, + new_value: storage_access.newValue, + }); + } + Entry::Occupied(mut slot_state_diff) => { + slot_state_diff.get_mut().new_value = storage_access.newValue; + } + } } } }); - - for (address, recorded_changes) in recorded_changes { - let mut changes = BTreeMap::default(); - for (slot, recorded_slot_changes) in recorded_changes { - // For each slot we retain the initial value from first state change and the new - // value from last state change. - changes.insert( - slot.to_string(), - SlotStateDiff { - previous_value: recorded_slot_changes.first().unwrap().0, - new_value: recorded_slot_changes.last().unwrap().1, - }, - ); - } - state_diffs.insert( - address, - AccountStateDiffs { label: state.labels.get(&address).cloned(), changes }, - ); - } } state_diffs } diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index 4e6e771005197..9d8d50ae7421d 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -264,12 +264,12 @@ contract RecordAccountAccessesTest is DSTest { string memory diffs = cheats.getStateDiff(); assertEq( - "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9:\n@ 1235: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x000000000000000000000000000000000000000000000000000000000000162e\n0xc7183455a4C133Ae270771860664b6B7ec320bB1:\n@ 5678: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x00000000000000000000000000000000000000000000000000000000000004d2\n", + "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9\n- state diff:\n@ 0x00000000000000000000000000000000000000000000000000000000000004d3: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x000000000000000000000000000000000000000000000000000000000000162e\n\n0xc7183455a4C133Ae270771860664b6B7ec320bB1\n- state diff:\n@ 0x000000000000000000000000000000000000000000000000000000000000162e: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x00000000000000000000000000000000000000000000000000000000000004d2\n\n", diffs ); string memory diffsJson = cheats.getStateDiffJson(); assertEq( - "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"changes\":{\"1235\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"changes\":{\"5678\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", + "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x0\"},\"stateDiff\":{\"0x00000000000000000000000000000000000000000000000000000000000004d3\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x0\"},\"stateDiff\":{\"0x000000000000000000000000000000000000000000000000000000000000162e\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", diffsJson ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -343,7 +343,10 @@ contract RecordAccountAccessesTest is DSTest { // contract calls to self in constructor SelfCaller caller = new SelfCaller{value: 2 ether}("hello2 world2"); - assertEq("", cheats.getStateDiff()); + assertEq( + "0x000000000000000000000000000000000000162e\n- balance diff: 0 \xE2\x86\x92 1000000000000000000\n\n0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n- balance diff: 0 \xE2\x86\x92 2000000000000000000\n\n", + cheats.getStateDiff() + ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 6); assertEq( @@ -463,7 +466,10 @@ contract RecordAccountAccessesTest is DSTest { uint256 initBalance = address(this).balance; cheats.startStateDiffRecording(); try this.revertingCall{value: 1 ether}(address(1234), "") {} catch {} - assertEq("", cheats.getStateDiff()); + assertEq( + "0x00000000000000000000000000000000000004d2\n- balance diff: 0 \xE2\x86\x92 100000000000000000\n\n", + cheats.getStateDiff() + ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); assertEq(called.length, 2); assertEq( @@ -783,11 +789,11 @@ contract RecordAccountAccessesTest is DSTest { nestedStorer.run(); cheats.label(address(nestedStorer), "NestedStorer"); assertEq( - "0x2e234DAe75C793f67A35089C9d99245E1C58470b (NestedStorer):\n@ 31391530734884398925509096751136955997235046655136458338700630915422204365175: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 86546418208203448386783321347074308435724792809315873744194221534962779865098: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 89735575844917174604881245405098157398514761457822262993733937076486162048205: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 99655811014363889343382125167956395016210879868288374279890486979400290732814: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n", + "0x2e234DAe75C793f67A35089C9d99245E1C58470b\nlabel: NestedStorer\n- state diff:\n@ 0x4566fa0cd03218c55bba914d793f5e6b9113172c1f684bb5f464c08c867e8977: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 0xbf57896b60daefa2c41de2feffecfc11debd98ea8c913a5170f60e53959ac00a: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 0xc664893a982d78bbeab379feef216ff517b7ea73626b280723be1ace370364cd: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 0xdc5330afa9872081253545dca3f448752688ff1b098b38c1abe4c4cdff4b0b0e: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n\n", cheats.getStateDiff() ); assertEq( - "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"changes\":{\"31391530734884398925509096751136955997235046655136458338700630915422204365175\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"86546418208203448386783321347074308435724792809315873744194221534962779865098\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"89735575844917174604881245405098157398514761457822262993733937076486162048205\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"99655811014363889343382125167956395016210879868288374279890486979400290732814\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", + "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x0\"},\"stateDiff\":{\"0x4566fa0cd03218c55bba914d793f5e6b9113172c1f684bb5f464c08c867e8977\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xbf57896b60daefa2c41de2feffecfc11debd98ea8c913a5170f60e53959ac00a\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xc664893a982d78bbeab379feef216ff517b7ea73626b280723be1ace370364cd\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xdc5330afa9872081253545dca3f448752688ff1b098b38c1abe4c4cdff4b0b0e\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", cheats.getStateDiffJson() ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); From d0b0d81e492aff33a4f3e2e0a005f114a6b03807 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 4 Dec 2024 13:38:36 +0200 Subject: [PATCH 8/9] Record balance diffs only if changed. Add nonce diff placeholder --- crates/cheatcodes/src/evm.rs | 25 +++++++++++-------- .../cheats/RecordAccountAccesses.t.sol | 13 ++++++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index a7e7bec8ce409..2031a5416b47b 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -106,11 +106,11 @@ struct BalanceDiff { #[serde(rename_all = "camelCase")] struct AccountStateDiffs { /// Address label, if any set. - #[serde(skip_serializing_if = "Option::is_none")] label: Option, /// Account balance changes. - #[serde(skip_serializing_if = "Option::is_none")] balance_diff: Option, + /// Placeholder for nonce diff, to be implemented. + nonce_diff: Option<(U256, U256)>, /// State changes, per slot. state_diff: BTreeMap, } @@ -1152,16 +1152,21 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> BTreeMap Date: Wed, 4 Dec 2024 15:52:20 +0200 Subject: [PATCH 9/9] Backoff nonce placeholder --- crates/cheatcodes/src/evm.rs | 2 -- testdata/default/cheats/RecordAccountAccesses.t.sol | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 2031a5416b47b..6c1a4718512d7 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -109,8 +109,6 @@ struct AccountStateDiffs { label: Option, /// Account balance changes. balance_diff: Option, - /// Placeholder for nonce diff, to be implemented. - nonce_diff: Option<(U256, U256)>, /// State changes, per slot. state_diff: BTreeMap, } diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index ec8e458505a4c..8de7bcdc5bd31 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -269,7 +269,7 @@ contract RecordAccountAccessesTest is DSTest { ); string memory diffsJson = cheats.getStateDiffJson(); assertEq( - "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"label\":null,\"balanceDiff\":null,\"nonceDiff\":null,\"stateDiff\":{\"0x00000000000000000000000000000000000000000000000000000000000004d3\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"label\":null,\"balanceDiff\":null,\"nonceDiff\":null,\"stateDiff\":{\"0x000000000000000000000000000000000000000000000000000000000000162e\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", + "{\"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\":{\"label\":null,\"balanceDiff\":null,\"stateDiff\":{\"0x00000000000000000000000000000000000000000000000000000000000004d3\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000162e\"}}},\"0xc7183455a4c133ae270771860664b6b7ec320bb1\":{\"label\":null,\"balanceDiff\":null,\"stateDiff\":{\"0x000000000000000000000000000000000000000000000000000000000000162e\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x00000000000000000000000000000000000000000000000000000000000004d2\"}}}}", diffsJson ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -348,7 +348,7 @@ contract RecordAccountAccessesTest is DSTest { cheats.getStateDiff() ); assertEq( - "{\"0x000000000000000000000000000000000000162e\":{\"label\":null,\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0xde0b6b3a7640000\"},\"nonceDiff\":null,\"stateDiff\":{}},\"0x1d1499e622d69689cdf9004d05ec547d650ff211\":{\"label\":null,\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x1bc16d674ec80000\"},\"nonceDiff\":null,\"stateDiff\":{}}}", + "{\"0x000000000000000000000000000000000000162e\":{\"label\":null,\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0xde0b6b3a7640000\"},\"stateDiff\":{}},\"0x1d1499e622d69689cdf9004d05ec547d650ff211\":{\"label\":null,\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x1bc16d674ec80000\"},\"stateDiff\":{}}}", cheats.getStateDiffJson() ); @@ -476,7 +476,7 @@ contract RecordAccountAccessesTest is DSTest { cheats.getStateDiff() ); assertEq( - "{\"0x00000000000000000000000000000000000004d2\":{\"label\":null,\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x16345785d8a0000\"},\"nonceDiff\":null,\"stateDiff\":{}}}", + "{\"0x00000000000000000000000000000000000004d2\":{\"label\":null,\"balanceDiff\":{\"previousValue\":\"0x0\",\"newValue\":\"0x16345785d8a0000\"},\"stateDiff\":{}}}", cheats.getStateDiffJson() ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); @@ -802,7 +802,7 @@ contract RecordAccountAccessesTest is DSTest { cheats.getStateDiff() ); assertEq( - "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"balanceDiff\":null,\"nonceDiff\":null,\"stateDiff\":{\"0x4566fa0cd03218c55bba914d793f5e6b9113172c1f684bb5f464c08c867e8977\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xbf57896b60daefa2c41de2feffecfc11debd98ea8c913a5170f60e53959ac00a\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xc664893a982d78bbeab379feef216ff517b7ea73626b280723be1ace370364cd\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xdc5330afa9872081253545dca3f448752688ff1b098b38c1abe4c4cdff4b0b0e\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", + "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":\"NestedStorer\",\"balanceDiff\":null,\"stateDiff\":{\"0x4566fa0cd03218c55bba914d793f5e6b9113172c1f684bb5f464c08c867e8977\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xbf57896b60daefa2c41de2feffecfc11debd98ea8c913a5170f60e53959ac00a\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xc664893a982d78bbeab379feef216ff517b7ea73626b280723be1ace370364cd\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"0xdc5330afa9872081253545dca3f448752688ff1b098b38c1abe4c4cdff4b0b0e\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"}}}}", cheats.getStateDiffJson() ); Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff());