Skip to main content

anvil/eth/backend/
db.rs

1//! Helper types for working with [revm](foundry_evm::revm)
2
3use std::{
4    collections::BTreeMap,
5    fmt::{self, Debug},
6    path::Path,
7};
8
9use alloy_consensus::{BlockBody, Header};
10use alloy_eips::eip4895::Withdrawals;
11use alloy_evm::block::StateDB;
12use alloy_primitives::{
13    Address, B256, Bytes, U256, keccak256,
14    map::{AddressMap, HashMap},
15};
16use alloy_rpc_types::BlockId;
17use anvil_core::eth::{
18    block::Block,
19    transaction::{MaybeImpersonatedTransaction, TransactionInfo},
20};
21use foundry_common::errors::FsPathError;
22use foundry_evm::backend::{
23    BlockchainDb, DatabaseError, DatabaseResult, MemDb, RevertStateSnapshotAction, StateSnapshot,
24};
25use foundry_primitives::{FoundryNetwork, FoundryReceiptEnvelope, FoundryTxEnvelope};
26use revm::{
27    Database, DatabaseCommit,
28    bytecode::Bytecode,
29    context::BlockEnv,
30    context_interface::block::BlobExcessGasAndPrice,
31    database::{CacheDB, DatabaseRef, DbAccount},
32    primitives::{KECCAK_EMPTY, eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE},
33    state::AccountInfo,
34};
35use serde::{
36    Deserialize, Deserializer, Serialize,
37    de::{Error as DeError, MapAccess, Visitor},
38};
39use serde_json::Value;
40
41use crate::mem::storage::MinedTransaction;
42
43/// Helper trait get access to the full state data of the database
44pub trait MaybeFullDatabase: DatabaseRef<Error = DatabaseError> + Debug {
45    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
46        None
47    }
48
49    /// Clear the state and move it into a new `StateSnapshot`.
50    fn clear_into_state_snapshot(&mut self) -> StateSnapshot;
51
52    /// Read the state snapshot.
53    ///
54    /// This clones all the states and returns a new `StateSnapshot`.
55    fn read_as_state_snapshot(&self) -> StateSnapshot;
56
57    /// Clears the entire database
58    fn clear(&mut self);
59
60    /// Reverses `clear_into_snapshot` by initializing the db's state with the state snapshot.
61    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot);
62}
63
64impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T
65where
66    &'a T: DatabaseRef<Error = DatabaseError>,
67{
68    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
69        T::maybe_as_full_db(self)
70    }
71
72    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
73        unreachable!("never called for DatabaseRef")
74    }
75
76    fn read_as_state_snapshot(&self) -> StateSnapshot {
77        unreachable!("never called for DatabaseRef")
78    }
79
80    fn clear(&mut self) {}
81
82    fn init_from_state_snapshot(&mut self, _state_snapshot: StateSnapshot) {}
83}
84
85/// Helper trait to reset the DB if it's forked
86pub trait MaybeForkedDatabase {
87    fn maybe_reset(&mut self, _url: Option<String>, block_number: BlockId) -> Result<(), String>;
88
89    fn maybe_flush_cache(&self) -> Result<(), String>;
90
91    fn maybe_inner(&self) -> Result<&BlockchainDb, String>;
92}
93
94/// `dyn Db` satisfies all `alloy_evm::Database` requirements via its supertraits, but the
95/// blanket impl has an implicit `Sized` bound. Provide an explicit impl.
96impl alloy_evm::Database for dyn Db {}
97
98impl StateDB for dyn Db {
99    fn set_state_clear_flag(&mut self, _has_state_clear: bool) {
100        // Anvil does not use the revm State wrapper, so this is a no-op.
101    }
102}
103
104/// A wrapper around [`CacheDB`] that implements [`StateDB`].
105///
106/// `StateDB` is a foreign trait with an orphan-rule constraint, so we cannot
107/// implement it directly for `CacheDB<T>`.  This newtype sidesteps the orphan
108/// rule while delegating all [`Database`], [`DatabaseCommit`] and
109/// [`DatabaseRef`] calls to the inner `CacheDB`.
110#[derive(Debug)]
111pub struct AnvilCacheDB<T>(pub CacheDB<T>);
112
113impl<T: DatabaseRef<Error = DatabaseError>> AnvilCacheDB<T> {
114    pub fn new(inner: T) -> Self {
115        Self(CacheDB::new(inner))
116    }
117}
118
119impl<T: DatabaseRef<Error = DatabaseError>> std::ops::Deref for AnvilCacheDB<T> {
120    type Target = CacheDB<T>;
121    fn deref(&self) -> &Self::Target {
122        &self.0
123    }
124}
125
126impl<T: DatabaseRef<Error = DatabaseError>> std::ops::DerefMut for AnvilCacheDB<T> {
127    fn deref_mut(&mut self) -> &mut Self::Target {
128        &mut self.0
129    }
130}
131
132impl<T: DatabaseRef<Error = DatabaseError> + fmt::Debug> Database for AnvilCacheDB<T> {
133    type Error = DatabaseError;
134
135    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
136        self.0.basic(address)
137    }
138
139    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
140        self.0.code_by_hash(code_hash)
141    }
142
143    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
144        self.0.storage(address, index)
145    }
146
147    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
148        self.0.block_hash(number)
149    }
150}
151
152impl<T: DatabaseRef<Error = DatabaseError>> DatabaseRef for AnvilCacheDB<T> {
153    type Error = DatabaseError;
154
155    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
156        self.0.basic_ref(address)
157    }
158
159    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
160        self.0.code_by_hash_ref(code_hash)
161    }
162
163    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
164        self.0.storage_ref(address, index)
165    }
166
167    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
168        self.0.block_hash_ref(number)
169    }
170}
171
172impl<T: DatabaseRef<Error = DatabaseError> + fmt::Debug> DatabaseCommit for AnvilCacheDB<T> {
173    fn commit(&mut self, changes: revm::state::EvmState) {
174        self.0.commit(changes)
175    }
176}
177
178impl<T: DatabaseRef<Error = DatabaseError> + fmt::Debug> StateDB for AnvilCacheDB<T> {
179    fn set_state_clear_flag(&mut self, _has_state_clear: bool) {
180        // Anvil does not use the revm State wrapper, so this is a no-op.
181    }
182}
183
184/// This bundles all required revm traits
185pub trait Db:
186    DatabaseRef<Error = DatabaseError>
187    + Database<Error = DatabaseError>
188    + DatabaseCommit
189    + MaybeFullDatabase
190    + MaybeForkedDatabase
191    + fmt::Debug
192    + Send
193    + Sync
194{
195    /// Inserts an account
196    fn insert_account(&mut self, address: Address, account: AccountInfo);
197
198    /// Sets the nonce of the given address
199    fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> {
200        let mut info = self.basic(address)?.unwrap_or_default();
201        info.nonce = nonce;
202        self.insert_account(address, info);
203        Ok(())
204    }
205
206    /// Sets the balance of the given address
207    fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> {
208        let mut info = self.basic(address)?.unwrap_or_default();
209        info.balance = balance;
210        self.insert_account(address, info);
211        Ok(())
212    }
213
214    /// Sets the code of the given address
215    fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> {
216        let mut info = self.basic(address)?.unwrap_or_default();
217        let code_hash = if code.as_ref().is_empty() {
218            KECCAK_EMPTY
219        } else {
220            B256::from_slice(&keccak256(code.as_ref())[..])
221        };
222        info.code_hash = code_hash;
223        info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(code.0)));
224        self.insert_account(address, info);
225        Ok(())
226    }
227
228    /// Sets the storage value at the given slot for the address
229    fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()>;
230
231    /// inserts a blockhash for the given number
232    fn insert_block_hash(&mut self, number: U256, hash: B256);
233
234    /// Write all chain data to serialized bytes buffer
235    fn dump_state(
236        &self,
237        at: BlockEnv,
238        best_number: u64,
239        blocks: Vec<SerializableBlock>,
240        transactions: Vec<SerializableTransaction>,
241        historical_states: Option<SerializableHistoricalStates>,
242    ) -> DatabaseResult<Option<SerializableState>>;
243
244    /// Deserialize and add all chain data to the backend storage
245    fn load_state(&mut self, state: SerializableState) -> DatabaseResult<bool> {
246        for (addr, account) in state.accounts.into_iter() {
247            let old_account_nonce = DatabaseRef::basic_ref(self, addr)
248                .ok()
249                .and_then(|acc| acc.map(|acc| acc.nonce))
250                .unwrap_or_default();
251            // use max nonce in case account is imported multiple times with difference
252            // nonces to prevent collisions
253            let nonce = std::cmp::max(old_account_nonce, account.nonce);
254
255            self.insert_account(
256                addr,
257                AccountInfo {
258                    balance: account.balance,
259                    code_hash: KECCAK_EMPTY, // will be set automatically
260                    code: if account.code.0.is_empty() {
261                        None
262                    } else {
263                        Some(Bytecode::new_raw(alloy_primitives::Bytes(account.code.0)))
264                    },
265                    nonce,
266                    account_id: None,
267                },
268            );
269
270            for (k, v) in account.storage.into_iter() {
271                self.set_storage_at(addr, k, v)?;
272            }
273        }
274        Ok(true)
275    }
276
277    /// Creates a new state snapshot.
278    fn snapshot_state(&mut self) -> U256;
279
280    /// Reverts a state snapshot.
281    ///
282    /// Returns `true` if the state snapshot was reverted.
283    fn revert_state(&mut self, state_snapshot: U256, action: RevertStateSnapshotAction) -> bool;
284
285    /// Returns the state root if possible to compute
286    fn maybe_state_root(&self) -> Option<B256> {
287        None
288    }
289
290    /// Returns the current, standalone state of the Db
291    fn current_state(&self) -> StateDb;
292}
293
294/// Convenience impl only used to use any `Db` on the fly as the db layer for revm's CacheDB
295/// This is useful to create blocks without actually writing to the `Db`, but rather in the cache of
296/// the `CacheDB` see also
297/// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block())
298impl<T: DatabaseRef<Error = DatabaseError> + Send + Sync + Clone + fmt::Debug> Db for CacheDB<T> {
299    fn insert_account(&mut self, address: Address, account: AccountInfo) {
300        self.insert_account_info(address, account)
301    }
302
303    fn set_storage_at(&mut self, address: Address, slot: B256, val: B256) -> DatabaseResult<()> {
304        self.insert_account_storage(address, slot.into(), val.into())
305    }
306
307    fn insert_block_hash(&mut self, number: U256, hash: B256) {
308        self.cache.block_hashes.insert(number, hash);
309    }
310
311    fn dump_state(
312        &self,
313        _at: BlockEnv,
314        _best_number: u64,
315        _blocks: Vec<SerializableBlock>,
316        _transaction: Vec<SerializableTransaction>,
317        _historical_states: Option<SerializableHistoricalStates>,
318    ) -> DatabaseResult<Option<SerializableState>> {
319        Ok(None)
320    }
321
322    fn snapshot_state(&mut self) -> U256 {
323        U256::ZERO
324    }
325
326    fn revert_state(&mut self, _state_snapshot: U256, _action: RevertStateSnapshotAction) -> bool {
327        false
328    }
329
330    fn current_state(&self) -> StateDb {
331        StateDb::new(MemDb::default())
332    }
333}
334
335impl<T: DatabaseRef<Error = DatabaseError> + Debug> MaybeFullDatabase for CacheDB<T> {
336    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
337        Some(&self.cache.accounts)
338    }
339
340    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
341        let db_accounts = std::mem::take(&mut self.cache.accounts);
342        let mut accounts = HashMap::default();
343        let mut account_storage = HashMap::default();
344
345        for (addr, mut acc) in db_accounts {
346            account_storage.insert(addr, std::mem::take(&mut acc.storage));
347            let mut info = acc.info;
348            info.code = self.cache.contracts.remove(&info.code_hash);
349            accounts.insert(addr, info);
350        }
351        let block_hashes = std::mem::take(&mut self.cache.block_hashes);
352        StateSnapshot { accounts, storage: account_storage, block_hashes }
353    }
354
355    fn read_as_state_snapshot(&self) -> StateSnapshot {
356        let mut accounts = HashMap::default();
357        let mut account_storage = HashMap::default();
358
359        for (addr, acc) in &self.cache.accounts {
360            account_storage.insert(*addr, acc.storage.clone());
361            let mut info = acc.info.clone();
362            info.code = self.cache.contracts.get(&info.code_hash).cloned();
363            accounts.insert(*addr, info);
364        }
365
366        let block_hashes = self.cache.block_hashes.clone();
367        StateSnapshot { accounts, storage: account_storage, block_hashes }
368    }
369
370    fn clear(&mut self) {
371        self.clear_into_state_snapshot();
372    }
373
374    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
375        let StateSnapshot { accounts, mut storage, block_hashes } = state_snapshot;
376
377        for (addr, mut acc) in accounts {
378            if let Some(code) = acc.code.take() {
379                self.cache.contracts.insert(acc.code_hash, code);
380            }
381            self.cache.accounts.insert(
382                addr,
383                DbAccount {
384                    info: acc,
385                    storage: storage.remove(&addr).unwrap_or_default(),
386                    ..Default::default()
387                },
388            );
389        }
390        self.cache.block_hashes = block_hashes;
391    }
392}
393
394impl<T: DatabaseRef<Error = DatabaseError>> MaybeForkedDatabase for CacheDB<T> {
395    fn maybe_reset(&mut self, _url: Option<String>, _block_number: BlockId) -> Result<(), String> {
396        Err("not supported".to_string())
397    }
398
399    fn maybe_flush_cache(&self) -> Result<(), String> {
400        Err("not supported".to_string())
401    }
402
403    fn maybe_inner(&self) -> Result<&BlockchainDb, String> {
404        Err("not supported".to_string())
405    }
406}
407
408/// Represents a state at certain point
409#[derive(Debug)]
410pub struct StateDb(pub(crate) Box<dyn MaybeFullDatabase + Send + Sync>);
411
412impl StateDb {
413    pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self {
414        Self(Box::new(db))
415    }
416
417    pub fn serialize_state(&mut self) -> StateSnapshot {
418        // Using read_as_snapshot makes sures we don't clear the historical state from the current
419        // instance.
420        self.read_as_state_snapshot()
421    }
422}
423
424impl DatabaseRef for StateDb {
425    type Error = DatabaseError;
426    fn basic_ref(&self, address: Address) -> DatabaseResult<Option<AccountInfo>> {
427        self.0.basic_ref(address)
428    }
429
430    fn code_by_hash_ref(&self, code_hash: B256) -> DatabaseResult<Bytecode> {
431        self.0.code_by_hash_ref(code_hash)
432    }
433
434    fn storage_ref(&self, address: Address, index: U256) -> DatabaseResult<U256> {
435        self.0.storage_ref(address, index)
436    }
437
438    fn block_hash_ref(&self, number: u64) -> DatabaseResult<B256> {
439        self.0.block_hash_ref(number)
440    }
441}
442
443impl MaybeFullDatabase for StateDb {
444    fn maybe_as_full_db(&self) -> Option<&AddressMap<DbAccount>> {
445        self.0.maybe_as_full_db()
446    }
447
448    fn clear_into_state_snapshot(&mut self) -> StateSnapshot {
449        self.0.clear_into_state_snapshot()
450    }
451
452    fn read_as_state_snapshot(&self) -> StateSnapshot {
453        self.0.read_as_state_snapshot()
454    }
455
456    fn clear(&mut self) {
457        self.0.clear()
458    }
459
460    fn init_from_state_snapshot(&mut self, state_snapshot: StateSnapshot) {
461        self.0.init_from_state_snapshot(state_snapshot)
462    }
463}
464
465/// Legacy block environment from before v1.3.
466#[derive(Debug, Deserialize)]
467#[serde(rename_all = "snake_case")]
468pub struct LegacyBlockEnv {
469    pub number: Option<StringOrU64>,
470    #[serde(alias = "coinbase")]
471    pub beneficiary: Option<Address>,
472    pub timestamp: Option<StringOrU64>,
473    pub gas_limit: Option<StringOrU64>,
474    pub basefee: Option<StringOrU64>,
475    pub difficulty: Option<StringOrU64>,
476    pub prevrandao: Option<B256>,
477    pub blob_excess_gas_and_price: Option<LegacyBlobExcessGasAndPrice>,
478}
479
480/// Legacy blob excess gas and price structure from before v1.3.
481#[derive(Debug, Deserialize)]
482pub struct LegacyBlobExcessGasAndPrice {
483    pub excess_blob_gas: u64,
484    pub blob_gasprice: u64,
485}
486
487/// Legacy string or u64 type from before v1.3.
488#[derive(Debug, Deserialize)]
489#[serde(untagged)]
490pub enum StringOrU64 {
491    Hex(String),
492    Dec(u64),
493}
494
495impl StringOrU64 {
496    pub fn to_u64(&self) -> Option<u64> {
497        match self {
498            Self::Dec(n) => Some(*n),
499            Self::Hex(s) => s.strip_prefix("0x").and_then(|s| u64::from_str_radix(s, 16).ok()),
500        }
501    }
502
503    pub fn to_u256(&self) -> Option<U256> {
504        match self {
505            Self::Dec(n) => Some(U256::from(*n)),
506            Self::Hex(s) => s.strip_prefix("0x").and_then(|s| U256::from_str_radix(s, 16).ok()),
507        }
508    }
509}
510
511/// Converts a `LegacyBlockEnv` to a `BlockEnv`, handling the conversion of legacy fields.
512impl TryFrom<LegacyBlockEnv> for BlockEnv {
513    type Error = &'static str;
514
515    fn try_from(legacy: LegacyBlockEnv) -> Result<Self, Self::Error> {
516        Ok(Self {
517            number: legacy.number.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO),
518            beneficiary: legacy.beneficiary.unwrap_or(Address::ZERO),
519            timestamp: legacy.timestamp.and_then(|v| v.to_u256()).unwrap_or(U256::ONE),
520            gas_limit: legacy.gas_limit.and_then(|v| v.to_u64()).unwrap_or(u64::MAX),
521            basefee: legacy.basefee.and_then(|v| v.to_u64()).unwrap_or(0),
522            difficulty: legacy.difficulty.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO),
523            prevrandao: legacy.prevrandao.or(Some(B256::ZERO)),
524            blob_excess_gas_and_price: legacy
525                .blob_excess_gas_and_price
526                .map(|v| BlobExcessGasAndPrice::new(v.excess_blob_gas, v.blob_gasprice))
527                .or_else(|| {
528                    Some(BlobExcessGasAndPrice::new(0, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE))
529                }),
530        })
531    }
532}
533
534/// Custom deserializer for `BlockEnv` that handles both v1.2 and v1.3+ formats.
535fn deserialize_block_env_compat<'de, D>(deserializer: D) -> Result<Option<BlockEnv>, D::Error>
536where
537    D: Deserializer<'de>,
538{
539    let value: Option<Value> = Option::deserialize(deserializer)?;
540    let Some(value) = value else {
541        return Ok(None);
542    };
543
544    if let Ok(env) = BlockEnv::deserialize(&value) {
545        return Ok(Some(env));
546    }
547
548    let legacy: LegacyBlockEnv = serde_json::from_value(value).map_err(|e| {
549        D::Error::custom(format!("Legacy deserialization of `BlockEnv` failed: {e}"))
550    })?;
551
552    Ok(Some(BlockEnv::try_from(legacy).map_err(D::Error::custom)?))
553}
554
555/// Custom deserializer for `best_block_number` that handles both v1.2 and v1.3+ formats.
556fn deserialize_best_block_number_compat<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
557where
558    D: Deserializer<'de>,
559{
560    let value: Option<Value> = Option::deserialize(deserializer)?;
561    let Some(value) = value else {
562        return Ok(None);
563    };
564
565    let number = match value {
566        Value::Number(n) => n.as_u64(),
567        Value::String(s) => {
568            if let Some(s) = s.strip_prefix("0x") {
569                u64::from_str_radix(s, 16).ok()
570            } else {
571                s.parse().ok()
572            }
573        }
574        _ => None,
575    };
576
577    Ok(number)
578}
579
580#[derive(Clone, Debug, Default, Serialize, Deserialize)]
581pub struct SerializableState {
582    /// The block number of the state
583    ///
584    /// Note: This is an Option for backwards compatibility: <https://github.com/foundry-rs/foundry/issues/5460>
585    #[serde(deserialize_with = "deserialize_block_env_compat")]
586    pub block: Option<BlockEnv>,
587    pub accounts: BTreeMap<Address, SerializableAccountRecord>,
588    /// The best block number of the state, can be different from block number (Arbitrum chain).
589    #[serde(deserialize_with = "deserialize_best_block_number_compat")]
590    pub best_block_number: Option<u64>,
591    #[serde(default)]
592    pub blocks: Vec<SerializableBlock>,
593    #[serde(default)]
594    pub transactions: Vec<SerializableTransaction>,
595    /// Historical states of accounts and storage at particular block hashes.
596    ///
597    /// Note: This is an Option for backwards compatibility.
598    #[serde(default)]
599    pub historical_states: Option<SerializableHistoricalStates>,
600}
601
602impl SerializableState {
603    /// Loads the `Genesis` object from the given json file path
604    pub fn load(path: impl AsRef<Path>) -> Result<Self, FsPathError> {
605        let path = path.as_ref();
606        if path.is_dir() {
607            foundry_common::fs::read_json_file(&path.join("state.json"))
608        } else {
609            foundry_common::fs::read_json_file(path)
610        }
611    }
612
613    /// This is used as the clap `value_parser` implementation
614    #[cfg(feature = "cmd")]
615    pub(crate) fn parse(path: &str) -> Result<Self, String> {
616        Self::load(path).map_err(|err| err.to_string())
617    }
618}
619
620#[derive(Clone, Debug, Serialize, Deserialize)]
621pub struct SerializableAccountRecord {
622    pub nonce: u64,
623    pub balance: U256,
624    pub code: Bytes,
625
626    #[serde(deserialize_with = "deserialize_btree")]
627    pub storage: BTreeMap<B256, B256>,
628}
629
630fn deserialize_btree<'de, D>(deserializer: D) -> Result<BTreeMap<B256, B256>, D::Error>
631where
632    D: Deserializer<'de>,
633{
634    struct BTreeVisitor;
635
636    impl<'de> Visitor<'de> for BTreeVisitor {
637        type Value = BTreeMap<B256, B256>;
638
639        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
640            formatter.write_str("a mapping of hex encoded storage slots to hex encoded state data")
641        }
642
643        fn visit_map<M>(self, mut mapping: M) -> Result<BTreeMap<B256, B256>, M::Error>
644        where
645            M: MapAccess<'de>,
646        {
647            let mut btree = BTreeMap::new();
648            while let Some((key, value)) = mapping.next_entry::<U256, U256>()? {
649                btree.insert(B256::from(key), B256::from(value));
650            }
651
652            Ok(btree)
653        }
654    }
655
656    deserializer.deserialize_map(BTreeVisitor)
657}
658
659/// Defines a backwards-compatible enum for transactions.
660/// This is essential for maintaining compatibility with state dumps
661/// created before the changes introduced in PR #8411.
662///
663/// The enum can represent either a `TypedTransaction` or a `MaybeImpersonatedTransaction`,
664/// depending on the data being deserialized. This flexibility ensures that older state
665/// dumps can still be loaded correctly, even after the changes in #8411.
666#[derive(Clone, Debug, Serialize, Deserialize)]
667#[serde(untagged)]
668pub enum SerializableTransactionType {
669    TypedTransaction(FoundryTxEnvelope),
670    MaybeImpersonatedTransaction(MaybeImpersonatedTransaction<FoundryTxEnvelope>),
671}
672
673#[derive(Clone, Debug, Serialize, Deserialize)]
674pub struct SerializableBlock {
675    pub header: Header,
676    pub transactions: Vec<SerializableTransactionType>,
677    pub ommers: Vec<Header>,
678    #[serde(default)]
679    pub withdrawals: Option<Withdrawals>,
680}
681
682impl From<Block> for SerializableBlock {
683    fn from(block: Block) -> Self {
684        Self {
685            header: block.header,
686            transactions: block.body.transactions.into_iter().map(Into::into).collect(),
687            ommers: block.body.ommers.into_iter().collect(),
688            withdrawals: block.body.withdrawals,
689        }
690    }
691}
692
693impl From<SerializableBlock> for Block {
694    fn from(block: SerializableBlock) -> Self {
695        let transactions = block.transactions.into_iter().map(Into::into).collect();
696        let ommers = block.ommers;
697        let body = BlockBody { transactions, ommers, withdrawals: block.withdrawals };
698        Self::new(block.header, body)
699    }
700}
701
702impl From<MaybeImpersonatedTransaction<FoundryTxEnvelope>> for SerializableTransactionType {
703    fn from(transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>) -> Self {
704        Self::MaybeImpersonatedTransaction(transaction)
705    }
706}
707
708impl From<SerializableTransactionType> for MaybeImpersonatedTransaction<FoundryTxEnvelope> {
709    fn from(transaction: SerializableTransactionType) -> Self {
710        match transaction {
711            SerializableTransactionType::TypedTransaction(tx) => Self::new(tx),
712            SerializableTransactionType::MaybeImpersonatedTransaction(tx) => tx,
713        }
714    }
715}
716
717#[derive(Clone, Debug, Serialize, Deserialize)]
718pub struct SerializableTransaction {
719    pub info: TransactionInfo,
720    pub receipt: FoundryReceiptEnvelope,
721    pub block_hash: B256,
722    pub block_number: u64,
723}
724
725impl From<MinedTransaction<FoundryNetwork>> for SerializableTransaction {
726    fn from(transaction: MinedTransaction<FoundryNetwork>) -> Self {
727        Self {
728            info: transaction.info,
729            receipt: transaction.receipt,
730            block_hash: transaction.block_hash,
731            block_number: transaction.block_number,
732        }
733    }
734}
735
736impl From<SerializableTransaction> for MinedTransaction<FoundryNetwork> {
737    fn from(transaction: SerializableTransaction) -> Self {
738        Self {
739            info: transaction.info,
740            receipt: transaction.receipt,
741            block_hash: transaction.block_hash,
742            block_number: transaction.block_number,
743        }
744    }
745}
746
747#[derive(Clone, Debug, Serialize, Deserialize, Default)]
748pub struct SerializableHistoricalStates(Vec<(B256, StateSnapshot)>);
749
750impl SerializableHistoricalStates {
751    pub const fn new(states: Vec<(B256, StateSnapshot)>) -> Self {
752        Self(states)
753    }
754}
755
756impl IntoIterator for SerializableHistoricalStates {
757    type Item = (B256, StateSnapshot);
758    type IntoIter = std::vec::IntoIter<Self::Item>;
759
760    fn into_iter(self) -> Self::IntoIter {
761        self.0.into_iter()
762    }
763}
764
765#[cfg(test)]
766mod test {
767    use super::*;
768
769    #[test]
770    fn test_deser_block() {
771        let block = r#"{
772            "header": {
773                "parentHash": "0xceb0fe420d6f14a8eeec4319515b89acbb0bb4861cad9983d529ab4b1e4af929",
774                "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
775                "miner": "0x0000000000000000000000000000000000000000",
776                "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1",
777                "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988",
778                "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa",
779                "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
780                "difficulty": "0x0",
781                "number": "0x2",
782                "gasLimit": "0x1c9c380",
783                "gasUsed": "0x5208",
784                "timestamp": "0x66cdc823",
785                "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
786                "nonce": "0x0000000000000000",
787                "baseFeePerGas": "0x342a1c58",
788                "blobGasUsed": "0x0",
789                "excessBlobGas": "0x0",
790                "extraData": "0x"
791            },
792            "transactions": [
793                {
794                    "type": "0x2",
795                    "chainId": "0x7a69",
796                    "nonce": "0x0",
797                    "gas": "0x5209",
798                    "maxFeePerGas": "0x77359401",
799                    "maxPriorityFeePerGas": "0x1",
800                    "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
801                    "value": "0x0",
802                    "accessList": [],
803                    "input": "0x",
804                    "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0",
805                    "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd",
806                    "yParity": "0x0",
807                    "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"
808                }
809            ],
810            "ommers": []
811        }
812        "#;
813
814        let _block: SerializableBlock = serde_json::from_str(block).unwrap();
815    }
816
817    #[test]
818    fn test_block_withdrawals_preserved() {
819        use alloy_eips::eip4895::Withdrawal;
820
821        // create a block with withdrawals (like post-Shanghai blocks)
822        let withdrawal = Withdrawal {
823            index: 42,
824            validator_index: 123,
825            address: Address::repeat_byte(1),
826            amount: 1000,
827        };
828
829        let header = Header::default();
830        let body = BlockBody {
831            transactions: vec![],
832            ommers: vec![],
833            withdrawals: Some(vec![withdrawal].into()),
834        };
835        let block = Block::new(header, body);
836
837        // convert to SerializableBlock and back
838        let serializable = SerializableBlock::from(block);
839        let restored = Block::from(serializable);
840
841        // withdrawals should be preserved
842        assert!(restored.body.withdrawals.is_some());
843        let withdrawals = restored.body.withdrawals.unwrap();
844        assert_eq!(withdrawals.len(), 1);
845        assert_eq!(withdrawals[0].index, 42);
846        assert_eq!(withdrawals[0].validator_index, 123);
847    }
848}