@@ -27,7 +27,7 @@ use revm::{
27
27
state:: Account ,
28
28
} ;
29
29
use std:: {
30
- collections:: { BTreeMap , btree_map:: Entry } ,
30
+ collections:: { BTreeMap , HashSet , btree_map:: Entry } ,
31
31
fmt:: Display ,
32
32
path:: Path ,
33
33
} ;
@@ -131,6 +131,8 @@ struct NonceDiff {
131
131
struct AccountStateDiffs {
132
132
/// Address label, if any set.
133
133
label : Option < String > ,
134
+ /// Contract identifier from artifact. e.g "src/Counter.sol:Counter"
135
+ contract : Option < String > ,
134
136
/// Account balance changes.
135
137
balance_diff : Option < BalanceDiff > ,
136
138
/// Account nonce changes.
@@ -145,6 +147,9 @@ impl Display for AccountStateDiffs {
145
147
if let Some ( label) = & self . label {
146
148
writeln ! ( f, "label: {label}" ) ?;
147
149
}
150
+ if let Some ( contract) = & self . contract {
151
+ writeln ! ( f, "contract: {contract}" ) ?;
152
+ }
148
153
// Print balance diff if changed.
149
154
if let Some ( balance_diff) = & self . balance_diff
150
155
&& balance_diff. previous_value != balance_diff. new_value
@@ -832,9 +837,9 @@ impl Cheatcode for stopAndReturnStateDiffCall {
832
837
}
833
838
834
839
impl Cheatcode for getStateDiffCall {
835
- fn apply ( & self , state : & mut Cheatcodes ) -> Result {
840
+ fn apply_stateful ( & self , ccx : & mut CheatsCtxt ) -> Result {
836
841
let mut diffs = String :: new ( ) ;
837
- let state_diffs = get_recorded_state_diffs ( state ) ;
842
+ let state_diffs = get_recorded_state_diffs ( ccx ) ;
838
843
for ( address, state_diffs) in state_diffs {
839
844
diffs. push_str ( & format ! ( "{address}\n " ) ) ;
840
845
diffs. push_str ( & format ! ( "{state_diffs}\n " ) ) ;
@@ -844,8 +849,8 @@ impl Cheatcode for getStateDiffCall {
844
849
}
845
850
846
851
impl Cheatcode for getStateDiffJsonCall {
847
- fn apply ( & self , state : & mut Cheatcodes ) -> Result {
848
- let state_diffs = get_recorded_state_diffs ( state ) ;
852
+ fn apply_stateful ( & self , ccx : & mut CheatsCtxt ) -> Result {
853
+ let state_diffs = get_recorded_state_diffs ( ccx ) ;
849
854
Ok ( serde_json:: to_string ( & state_diffs) ?. abi_encode ( ) )
850
855
}
851
856
}
@@ -1218,9 +1223,36 @@ fn genesis_account(account: &Account) -> GenesisAccount {
1218
1223
}
1219
1224
1220
1225
/// Helper function to returns state diffs recorded for each changed account.
1221
- fn get_recorded_state_diffs ( state : & mut Cheatcodes ) -> BTreeMap < Address , AccountStateDiffs > {
1226
+ fn get_recorded_state_diffs ( ccx : & mut CheatsCtxt ) -> BTreeMap < Address , AccountStateDiffs > {
1222
1227
let mut state_diffs: BTreeMap < Address , AccountStateDiffs > = BTreeMap :: default ( ) ;
1223
- if let Some ( records) = & state. recorded_account_diffs_stack {
1228
+
1229
+ // First, collect all unique addresses we need to look up
1230
+ let mut addresses_to_lookup = HashSet :: new ( ) ;
1231
+ if let Some ( records) = & ccx. state . recorded_account_diffs_stack {
1232
+ for account_access in records. iter ( ) . flatten ( ) {
1233
+ if !account_access. storageAccesses . is_empty ( )
1234
+ || account_access. oldBalance != account_access. newBalance
1235
+ {
1236
+ addresses_to_lookup. insert ( account_access. account ) ;
1237
+ for storage_access in & account_access. storageAccesses {
1238
+ if storage_access. isWrite && !storage_access. reverted {
1239
+ addresses_to_lookup. insert ( storage_access. account ) ;
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+
1246
+ // Look up contract names for all addresses
1247
+ let mut contract_names = HashMap :: new ( ) ;
1248
+ for address in addresses_to_lookup {
1249
+ if let Some ( name) = get_contract_name ( ccx, address) {
1250
+ contract_names. insert ( address, name) ;
1251
+ }
1252
+ }
1253
+
1254
+ // Now process the records
1255
+ if let Some ( records) = & ccx. state . recorded_account_diffs_stack {
1224
1256
records
1225
1257
. iter ( )
1226
1258
. flatten ( )
@@ -1235,7 +1267,8 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> BTreeMap<Address, Account
1235
1267
let account_diff =
1236
1268
state_diffs. entry ( account_access. account ) . or_insert_with ( || {
1237
1269
AccountStateDiffs {
1238
- label : state. labels . get ( & account_access. account ) . cloned ( ) ,
1270
+ label : ccx. state . labels . get ( & account_access. account ) . cloned ( ) ,
1271
+ contract : contract_names. get ( & account_access. account ) . cloned ( ) ,
1239
1272
..Default :: default ( )
1240
1273
}
1241
1274
} ) ;
@@ -1255,7 +1288,8 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> BTreeMap<Address, Account
1255
1288
let account_diff =
1256
1289
state_diffs. entry ( account_access. account ) . or_insert_with ( || {
1257
1290
AccountStateDiffs {
1258
- label : state. labels . get ( & account_access. account ) . cloned ( ) ,
1291
+ label : ccx. state . labels . get ( & account_access. account ) . cloned ( ) ,
1292
+ contract : contract_names. get ( & account_access. account ) . cloned ( ) ,
1259
1293
..Default :: default ( )
1260
1294
}
1261
1295
} ) ;
@@ -1276,7 +1310,8 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> BTreeMap<Address, Account
1276
1310
let account_diff = state_diffs
1277
1311
. entry ( storage_access. account )
1278
1312
. or_insert_with ( || AccountStateDiffs {
1279
- label : state. labels . get ( & storage_access. account ) . cloned ( ) ,
1313
+ label : ccx. state . labels . get ( & storage_access. account ) . cloned ( ) ,
1314
+ contract : contract_names. get ( & storage_access. account ) . cloned ( ) ,
1280
1315
..Default :: default ( )
1281
1316
} ) ;
1282
1317
// Update state diff. Do not overwrite the initial value if already set.
@@ -1298,6 +1333,34 @@ fn get_recorded_state_diffs(state: &mut Cheatcodes) -> BTreeMap<Address, Account
1298
1333
state_diffs
1299
1334
}
1300
1335
1336
+ /// Helper function to get the contract name from the deployed code.
1337
+ fn get_contract_name ( ccx : & mut CheatsCtxt , address : Address ) -> Option < String > {
1338
+ // Check if we have available artifacts to match against
1339
+ let artifacts = ccx. state . config . available_artifacts . as_ref ( ) ?;
1340
+
1341
+ // Try to load the account and get its code
1342
+ let account = ccx. ecx . journaled_state . load_account ( address) . ok ( ) ?;
1343
+ let code = account. info . code . as_ref ( ) ?;
1344
+
1345
+ // Skip if code is empty
1346
+ if code. is_empty ( ) {
1347
+ return None ;
1348
+ }
1349
+
1350
+ // Try to find the artifact by deployed code
1351
+ let code_bytes = code. original_bytes ( ) ;
1352
+ if let Some ( ( artifact_id, _) ) = artifacts. find_by_deployed_code_exact ( & code_bytes) {
1353
+ return Some ( artifact_id. identifier ( ) ) ;
1354
+ }
1355
+
1356
+ // Fallback to fuzzy matching if exact match fails
1357
+ if let Some ( ( artifact_id, _) ) = artifacts. find_by_deployed_code ( & code_bytes) {
1358
+ return Some ( artifact_id. identifier ( ) ) ;
1359
+ }
1360
+
1361
+ None
1362
+ }
1363
+
1301
1364
/// Helper function to set / unset cold storage slot of the target address.
1302
1365
fn set_cold_slot ( ccx : & mut CheatsCtxt , target : Address , slot : U256 , cold : bool ) {
1303
1366
if let Some ( account) = ccx. ecx . journaled_state . state . get_mut ( & target)
0 commit comments