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