1
1
//! Helper types for working with [revm](foundry_evm::revm)
2
2
3
- use crate :: mem:: storage:: MinedTransaction ;
3
+ use std:: {
4
+ collections:: BTreeMap ,
5
+ fmt:: { self , Debug } ,
6
+ path:: Path ,
7
+ } ;
8
+
4
9
use alloy_consensus:: Header ;
5
10
use alloy_primitives:: { Address , B256 , Bytes , U256 , keccak256, map:: HashMap } ;
6
11
use alloy_rpc_types:: BlockId ;
@@ -16,19 +21,18 @@ use revm::{
16
21
Database , DatabaseCommit ,
17
22
bytecode:: Bytecode ,
18
23
context:: BlockEnv ,
24
+ context_interface:: block:: BlobExcessGasAndPrice ,
19
25
database:: { CacheDB , DatabaseRef , DbAccount } ,
20
- primitives:: KECCAK_EMPTY ,
26
+ primitives:: { KECCAK_EMPTY , eip4844 :: BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE } ,
21
27
state:: AccountInfo ,
22
28
} ;
23
29
use serde:: {
24
30
Deserialize , Deserializer , Serialize ,
25
- de:: { MapAccess , Visitor } ,
26
- } ;
27
- use std:: {
28
- collections:: BTreeMap ,
29
- fmt:: { self , Debug } ,
30
- path:: Path ,
31
+ de:: { Error as DeError , MapAccess , Visitor } ,
31
32
} ;
33
+ use serde_json:: Value ;
34
+
35
+ use crate :: mem:: storage:: MinedTransaction ;
32
36
33
37
/// Helper trait get access to the full state data of the database
34
38
pub trait MaybeFullDatabase : DatabaseRef < Error = DatabaseError > + Debug {
@@ -385,14 +389,131 @@ impl MaybeFullDatabase for StateDb {
385
389
}
386
390
}
387
391
392
+ /// Legacy block environment from before v1.3.
393
+ #[ derive( Debug , Deserialize ) ]
394
+ #[ serde( rename_all = "snake_case" ) ]
395
+ pub struct LegacyBlockEnv {
396
+ pub number : Option < StringOrU64 > ,
397
+ #[ serde( alias = "coinbase" ) ]
398
+ pub beneficiary : Option < Address > ,
399
+ pub timestamp : Option < StringOrU64 > ,
400
+ pub gas_limit : Option < StringOrU64 > ,
401
+ pub basefee : Option < StringOrU64 > ,
402
+ pub difficulty : Option < StringOrU64 > ,
403
+ pub prevrandao : Option < B256 > ,
404
+ pub blob_excess_gas_and_price : Option < LegacyBlobExcessGasAndPrice > ,
405
+ }
406
+
407
+ /// Legacy blob excess gas and price structure from before v1.3.
408
+ #[ derive( Debug , Deserialize ) ]
409
+ pub struct LegacyBlobExcessGasAndPrice {
410
+ pub excess_blob_gas : u64 ,
411
+ pub blob_gasprice : u64 ,
412
+ }
413
+
414
+ /// Legacy string or u64 type from before v1.3.
415
+ #[ derive( Debug , Deserialize ) ]
416
+ #[ serde( untagged) ]
417
+ pub enum StringOrU64 {
418
+ Hex ( String ) ,
419
+ Dec ( u64 ) ,
420
+ }
421
+
422
+ impl StringOrU64 {
423
+ pub fn to_u64 ( & self ) -> Option < u64 > {
424
+ match self {
425
+ Self :: Dec ( n) => Some ( * n) ,
426
+ Self :: Hex ( s) => s. strip_prefix ( "0x" ) . and_then ( |s| u64:: from_str_radix ( s, 16 ) . ok ( ) ) ,
427
+ }
428
+ }
429
+
430
+ pub fn to_u256 ( & self ) -> Option < U256 > {
431
+ match self {
432
+ Self :: Dec ( n) => Some ( U256 :: from ( * n) ) ,
433
+ Self :: Hex ( s) => s. strip_prefix ( "0x" ) . and_then ( |s| U256 :: from_str_radix ( s, 16 ) . ok ( ) ) ,
434
+ }
435
+ }
436
+ }
437
+
438
+ /// Converts a `LegacyBlockEnv` to a `BlockEnv`, handling the conversion of legacy fields.
439
+ impl TryFrom < LegacyBlockEnv > for BlockEnv {
440
+ type Error = & ' static str ;
441
+
442
+ fn try_from ( legacy : LegacyBlockEnv ) -> Result < Self , Self :: Error > {
443
+ Ok ( Self {
444
+ number : legacy. number . and_then ( |v| v. to_u256 ( ) ) . unwrap_or ( U256 :: ZERO ) ,
445
+ beneficiary : legacy. beneficiary . unwrap_or ( Address :: ZERO ) ,
446
+ timestamp : legacy. timestamp . and_then ( |v| v. to_u256 ( ) ) . unwrap_or ( U256 :: ONE ) ,
447
+ gas_limit : legacy. gas_limit . and_then ( |v| v. to_u64 ( ) ) . unwrap_or ( u64:: MAX ) ,
448
+ basefee : legacy. basefee . and_then ( |v| v. to_u64 ( ) ) . unwrap_or ( 0 ) ,
449
+ difficulty : legacy. difficulty . and_then ( |v| v. to_u256 ( ) ) . unwrap_or ( U256 :: ZERO ) ,
450
+ prevrandao : legacy. prevrandao . or ( Some ( B256 :: ZERO ) ) ,
451
+ blob_excess_gas_and_price : legacy
452
+ . blob_excess_gas_and_price
453
+ . map ( |v| BlobExcessGasAndPrice :: new ( v. excess_blob_gas , v. blob_gasprice ) )
454
+ . or_else ( || {
455
+ Some ( BlobExcessGasAndPrice :: new ( 0 , BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE ) )
456
+ } ) ,
457
+ } )
458
+ }
459
+ }
460
+
461
+ /// Custom deserializer for `BlockEnv` that handles both v1.2 and v1.3+ formats.
462
+ fn deserialize_block_env_compat < ' de , D > ( deserializer : D ) -> Result < Option < BlockEnv > , D :: Error >
463
+ where
464
+ D : Deserializer < ' de > ,
465
+ {
466
+ let value: Option < Value > = Option :: deserialize ( deserializer) ?;
467
+ let Some ( value) = value else {
468
+ return Ok ( None ) ;
469
+ } ;
470
+
471
+ if let Ok ( env) = BlockEnv :: deserialize ( & value) {
472
+ return Ok ( Some ( env) ) ;
473
+ }
474
+
475
+ let legacy: LegacyBlockEnv = serde_json:: from_value ( value) . map_err ( |e| {
476
+ D :: Error :: custom ( format ! ( "Legacy deserialization of `BlockEnv` failed: {e}" ) )
477
+ } ) ?;
478
+
479
+ Ok ( Some ( BlockEnv :: try_from ( legacy) . map_err ( D :: Error :: custom) ?) )
480
+ }
481
+
482
+ /// Custom deserializer for `best_block_number` that handles both v1.2 and v1.3+ formats.
483
+ fn deserialize_best_block_number_compat < ' de , D > ( deserializer : D ) -> Result < Option < u64 > , D :: Error >
484
+ where
485
+ D : Deserializer < ' de > ,
486
+ {
487
+ let value: Option < Value > = Option :: deserialize ( deserializer) ?;
488
+ let Some ( value) = value else {
489
+ return Ok ( None ) ;
490
+ } ;
491
+
492
+ let number = match value {
493
+ Value :: Number ( n) => n. as_u64 ( ) ,
494
+ Value :: String ( s) => {
495
+ if let Some ( s) = s. strip_prefix ( "0x" ) {
496
+ u64:: from_str_radix ( s, 16 ) . ok ( )
497
+ } else {
498
+ s. parse ( ) . ok ( )
499
+ }
500
+ }
501
+ _ => None ,
502
+ } ;
503
+
504
+ Ok ( number)
505
+ }
506
+
388
507
#[ derive( Clone , Debug , Default , Serialize , Deserialize ) ]
389
508
pub struct SerializableState {
390
509
/// The block number of the state
391
510
///
392
511
/// Note: This is an Option for backwards compatibility: <https://github.com/foundry-rs/foundry/issues/5460>
512
+ #[ serde( deserialize_with = "deserialize_block_env_compat" ) ]
393
513
pub block : Option < BlockEnv > ,
394
514
pub accounts : BTreeMap < Address , SerializableAccountRecord > ,
395
515
/// The best block number of the state, can be different from block number (Arbitrum chain).
516
+ #[ serde( deserialize_with = "deserialize_best_block_number_compat" ) ]
396
517
pub best_block_number : Option < u64 > ,
397
518
#[ serde( default ) ]
398
519
pub blocks : Vec < SerializableBlock > ,
0 commit comments