@@ -354,15 +354,15 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
354354 }
355355 t if using_revive && is :: < rollCall > ( t) => {
356356 let & rollCall { newHeight } = cheatcode. as_any ( ) . downcast_ref ( ) . unwrap ( ) ;
357- let new_block_number: u64 = newHeight. try_into ( ) . expect ( "Block number exceeds u32" ) ;
357+ let new_block_number: u64 = newHeight. saturating_to ( ) ;
358358
359359 // blockhash should be the same on both revive and revm sides, so fetch it before
360360 // changing the block number.
361361 let prev_new_height_hash = ccx
362362 . ecx
363363 . journaled_state
364364 . database
365- . block_hash ( new_block_number - 1 )
365+ . block_hash ( new_block_number. saturating_sub ( 1 ) )
366366 . expect ( "Should not fail" ) ;
367367 let new_height_hash = ccx
368368 . ecx
@@ -695,10 +695,11 @@ fn select_revive(
695695 }
696696
697697 for address in accounts {
698- tracing:: info!( "Migrating account {:?} (is_test_contract: {})" , address, test_contract_addr == Some ( address) ) ;
698+ tracing:: info!( "Migrating account {:?} (is_test_contract: {})" , address, test_contract_addr == Some ( address) ) ;
699699 let acc = data. journaled_state . load_account ( address) . expect ( "failed to load account" ) ;
700- let amount = acc. data . info . balance ;
701- let nonce = acc. data . info . nonce ;
700+
701+ let amount = acc. info . balance ;
702+ let nonce = acc. info . nonce ;
702703 let account = H160 :: from_slice ( address. as_slice ( ) ) ;
703704 let account_id =
704705 AccountId32Mapper :: < Runtime > :: to_fallback_account_id ( & account) ;
@@ -710,7 +711,7 @@ fn select_revive(
710711 a. nonce = nonce. min ( u32:: MAX . into ( ) ) . try_into ( ) . expect ( "shouldn't happen" ) ;
711712 } ) ;
712713
713- if let Some ( bytecode) = acc. data . info . code . as_ref ( ) {
714+ if let Some ( bytecode) = acc. info . code . as_ref ( ) {
714715 let account_h160 = H160 :: from_slice ( address. as_slice ( ) ) ;
715716
716717 // Skip if contract already exists in pallet-revive
@@ -825,26 +826,73 @@ fn select_revive(
825826 }
826827 }
827828 }
828- if AccountInfo :: < Runtime > :: load_contract ( & account_h160) . is_some ( ) {
829- // Migrate complete account state (storage) for newly created/existing contract
830- for ( slot, storage_slot) in & acc. data . storage {
831- let slot_bytes = slot. to_be_bytes :: < 32 > ( ) ;
832- let value_bytes = storage_slot. present_value . to_be_bytes :: < 32 > ( ) ;
833-
834- if !storage_slot. present_value . is_zero ( ) {
835- let _ = Pallet :: < Runtime > :: set_storage (
836- account_h160,
837- slot_bytes,
838- Some ( value_bytes. to_vec ( ) ) ,
839- ) ;
840- }
841- }
829+ if AccountInfo :: < Runtime > :: load_contract ( & account_h160) . is_some ( ) {
830+ migrate_contract_storage ( data, address, account_h160) ;
842831 }
843832 }
844833 }
845834 } ) ;
846835}
847836
837+ /// Migrates contract storage from REVM state to pallet-revive.
838+ ///
839+ /// Merges storage from two sources:
840+ /// 1. Journaled state: most recent storage values from current execution
841+ /// 2. Database cache: storage written before startup migration, which is run as separate
842+ /// transaction, already committed to cache
843+ ///
844+ /// The journaled state takes precedence - cache values are only used for slots
845+ /// not present in the journaled state.
846+ fn migrate_contract_storage ( data : Ecx < ' _ , ' _ , ' _ > , address : Address , account_h160 : H160 ) {
847+ use std:: collections:: HashSet ;
848+
849+ // Track which slots we've already migrated from the journaled state
850+ let mut migrated_slots: HashSet < U256 > = HashSet :: new ( ) ;
851+
852+ // First, migrate storage from the journaled state (most up-to-date values)
853+ if let Some ( account_state) = data. journaled_state . state . get ( & address) {
854+ for ( slot, storage_slot) in & account_state. storage {
855+ migrated_slots. insert ( * slot) ;
856+
857+ let slot_bytes = slot. to_be_bytes :: < 32 > ( ) ;
858+ let value = storage_slot. present_value ;
859+
860+ if !value. is_zero ( ) {
861+ let _ = Pallet :: < Runtime > :: set_storage (
862+ account_h160,
863+ slot_bytes,
864+ Some ( value. to_be_bytes :: < 32 > ( ) . to_vec ( ) ) ,
865+ ) ;
866+ } else {
867+ // Handle case where storage was explicitly cleared
868+ let _ = Pallet :: < Runtime > :: set_storage ( account_h160, slot_bytes, None ) ;
869+ }
870+ }
871+ }
872+
873+ // Then, migrate storage from the database cache for slots NOT in journaled state
874+ if let Some ( cached_storage) = data. journaled_state . database . cached_storage ( address) {
875+ for ( slot, value) in cached_storage {
876+ // Skip slots already migrated from the journaled state
877+ if migrated_slots. contains ( & slot) {
878+ continue ;
879+ }
880+
881+ let slot_bytes = slot. to_be_bytes :: < 32 > ( ) ;
882+ if !value. is_zero ( ) {
883+ let _ = Pallet :: < Runtime > :: set_storage (
884+ account_h160,
885+ slot_bytes,
886+ Some ( value. to_be_bytes :: < 32 > ( ) . to_vec ( ) ) ,
887+ ) ;
888+ } else {
889+ // Handle case where storage was explicitly cleared
890+ let _ = Pallet :: < Runtime > :: set_storage ( account_h160, slot_bytes, None ) ;
891+ }
892+ }
893+ }
894+ }
895+
848896fn select_evm ( ctx : & mut PvmCheatcodeInspectorStrategyContext , data : Ecx < ' _ , ' _ , ' _ > ) {
849897 if !ctx. using_revive {
850898 tracing:: info!( "already using REVM" ) ;
@@ -991,7 +1039,7 @@ impl foundry_cheatcodes::CheatcodeInspectorStrategyExt for PvmCheatcodeInspector
9911039 ( contract. resolc_bytecode . as_bytes ( ) . unwrap ( ) . to_vec ( ) , constructor_args. to_vec ( ) )
9921040 }
9931041 crate :: ReviveRuntimeMode :: Evm => {
994- // EVM mode: use EVM bytecode directly
1042+ // EVM mode: use EVM bytecode directly (includes constructor args)
9951043 tracing:: info!( "running create in EVM mode with EVM bytecode" ) ;
9961044 ( init_code. 0 . to_vec ( ) , vec ! [ ] )
9971045 }
0 commit comments