Skip to main content

tycho_simulation/evm/engine_db/
tycho_db.rs

1use std::{
2    collections::HashMap,
3    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
4};
5
6use alloy::primitives::{Address, Bytes as AlloyBytes, B256, U256};
7use revm::{
8    context::DBErrorMarker,
9    primitives::KECCAK_EMPTY,
10    state::{AccountInfo, Bytecode},
11    DatabaseRef,
12};
13use thiserror::Error;
14use tracing::{debug, error, instrument, warn};
15use tycho_client::feed::BlockHeader;
16
17use crate::evm::{
18    account_storage::{AccountStorage, StateUpdate},
19    engine_db::engine_db_interface::EngineDatabaseInterface,
20    tycho_models::{AccountUpdate, ChangeType},
21};
22
23#[derive(Error, Debug)]
24pub enum TychoClientError {
25    #[error("Failed to parse URI: {0}. Error: {1}")]
26    UriParsing(String, String),
27    #[error("Failed to format request: {0}")]
28    FormatRequest(String),
29    #[error("Unexpected HTTP client error: {0}")]
30    HttpClient(String),
31    #[error("Failed to parse response: {0}")]
32    ParseResponse(String),
33}
34
35#[derive(Error, Debug)]
36pub enum PreCachedDBError {
37    #[error("Account {0} not found")]
38    MissingAccount(Address),
39    #[error("Bad account update: {0} - {1:?}")]
40    BadUpdate(String, Box<AccountUpdate>),
41    #[error("Block needs to be set")]
42    BlockNotSet(),
43    #[error("Tycho Client error: {0}")]
44    TychoClientError(#[from] TychoClientError),
45    #[error("{0}")]
46    Fatal(String),
47}
48
49impl DBErrorMarker for PreCachedDBError {}
50
51#[derive(Clone, Debug)]
52pub struct PreCachedDBInner {
53    /// Storage for accounts
54    accounts: AccountStorage,
55    /// Current block
56    block: Option<BlockHeader>,
57}
58
59#[derive(Clone, Debug)]
60pub struct PreCachedDB {
61    /// Cached inner data
62    ///
63    /// `inner` encapsulates `PreCachedDBInner` using `RwLock` for safe concurrent read or
64    /// exclusive write access to the data and `Arc` for shared ownership of the lock across
65    /// threads.
66    pub inner: Arc<RwLock<PreCachedDBInner>>,
67}
68
69impl PreCachedDB {
70    /// Create a new PreCachedDB instance
71    pub fn new() -> Result<Self, PreCachedDBError> {
72        Ok(PreCachedDB {
73            inner: Arc::new(RwLock::new(PreCachedDBInner {
74                accounts: AccountStorage::new(),
75                block: None,
76            })),
77        })
78    }
79
80    #[instrument(skip_all)]
81    pub fn update(
82        &self,
83        account_updates: Vec<AccountUpdate>,
84        block: Option<BlockHeader>,
85    ) -> Result<(), PreCachedDBError> {
86        // Hold the write lock for the duration of the function so that no other thread can
87        // write to the storage.
88        let mut write_guard = self.write_inner()?;
89
90        write_guard.block = block;
91
92        for update in account_updates {
93            match update.change {
94                ChangeType::Update => {
95                    debug!(%update.address, "Updating account");
96
97                    // If the account is not present, the internal storage will handle throwing
98                    // an exception.
99                    write_guard.accounts.update_account(
100                        &update.address,
101                        &StateUpdate {
102                            storage: Some(update.slots.clone()),
103                            balance: update.balance,
104                        },
105                    );
106                }
107                ChangeType::Deletion => {
108                    debug!(%update.address, "Deleting account");
109
110                    warn!(%update.address, "Deletion not implemented");
111                }
112                ChangeType::Creation => {
113                    debug!(%update.address, "Creating account");
114
115                    // We expect the code to be present.
116                    let code = Bytecode::new_raw(AlloyBytes::from(
117                        update.code.clone().ok_or_else(|| {
118                            error!(%update.address, "MissingCode");
119                            PreCachedDBError::BadUpdate(
120                                "MissingCode".into(),
121                                Box::new(update.clone()),
122                            )
123                        })?,
124                    ));
125                    // If the balance is not present, we set it to zero.
126                    let balance = update.balance.unwrap_or(U256::ZERO);
127
128                    // Initialize the account.
129                    write_guard.accounts.init_account(
130                        update.address,
131                        AccountInfo::new(balance, 0, code.hash_slow(), code),
132                        Some(update.slots.clone()),
133                        true, /* Flag all accounts in TychoDB mocked to sign that we cannot
134                               * call an RPC provider for an update */
135                    );
136                }
137                ChangeType::Unspecified => {
138                    warn!(%update.address, "Unspecified change type");
139                }
140            }
141        }
142        Ok(())
143    }
144
145    /// Like [`update`] but unconditionally overwrites existing accounts on `Creation` updates.
146    ///
147    /// Use only for authoritative proxy-token accounts that must win over placeholder entries
148    /// inserted by other decoders' snapshot loops. Generic callers should use [`update`].
149    pub fn force_update_accounts(
150        &self,
151        account_updates: Vec<AccountUpdate>,
152    ) -> Result<(), PreCachedDBError> {
153        let mut write_guard = self.write_inner()?;
154
155        for update in account_updates {
156            if matches!(update.change, ChangeType::Creation) {
157                let code =
158                    Bytecode::new_raw(AlloyBytes::from(update.code.clone().ok_or_else(|| {
159                        error!(%update.address, "MissingCode");
160                        PreCachedDBError::BadUpdate("MissingCode".into(), Box::new(update.clone()))
161                    })?));
162                let balance = update.balance.unwrap_or(U256::ZERO);
163
164                write_guard.accounts.overwrite_account(
165                    update.address,
166                    AccountInfo::new(balance, 0, code.hash_slow(), code),
167                    Some(update.slots.clone()),
168                    true,
169                );
170            } else {
171                warn!(%update.address, "force_update_accounts called with non-Creation update; ignoring");
172            }
173        }
174        Ok(())
175    }
176
177    /// Retrieves the storage value at the specified index for the given account, if it exists.
178    ///
179    /// If the account exists in the storage, the storage value at the specified `index` is returned
180    /// as a reference. Temp storage takes priority over permanent storage.
181    /// If the account does not exist, `None` is returned.
182    ///
183    /// # Arguments
184    ///
185    /// * `address`: A reference to the address of the account to retrieve the storage value from.
186    /// * `index`: A reference to the index of the storage value to retrieve.
187    ///
188    /// # Returns
189    ///
190    /// Returns an `Option` containing a reference to the storage value if it exists, otherwise
191    /// returns `None`.
192    pub fn get_storage(&self, address: &Address, index: &U256) -> Option<U256> {
193        self.inner
194            .read()
195            .unwrap()
196            .accounts
197            .get_storage(address, index)
198    }
199
200    /// Update the simulation state.
201    ///
202    /// This method modifies the current state of the simulation by applying the provided updates to
203    /// the accounts in the smart contract storage. These changes correspond to a particular
204    /// block in the blockchain.
205    ///
206    /// # Arguments
207    ///
208    /// * `new_state`: A struct containing all the state changes for a particular block.
209    pub fn update_state(
210        &mut self,
211        updates: &HashMap<Address, StateUpdate>,
212        block: BlockHeader,
213    ) -> Result<HashMap<Address, StateUpdate>, PreCachedDBError> {
214        // Hold the write lock for the duration of the function so that no other thread can
215        // write to the storage.
216        let mut write_guard = self.write_inner()?;
217
218        let mut revert_updates = HashMap::new();
219        write_guard.block = Some(block);
220
221        for (address, update_info) in updates.iter() {
222            let mut revert_entry = StateUpdate::default();
223
224            if let Some(current_account) = write_guard
225                .accounts
226                .get_account_info(address)
227            {
228                revert_entry.balance = Some(current_account.balance);
229            }
230
231            if let Some(storage) = &update_info.storage {
232                let mut revert_storage = HashMap::default();
233                for index in storage.keys() {
234                    if let Some(s) = write_guard
235                        .accounts
236                        .get_storage(address, index)
237                    {
238                        revert_storage.insert(*index, s);
239                    }
240                }
241                revert_entry.storage = Some(revert_storage);
242            }
243            revert_updates.insert(*address, revert_entry);
244            write_guard
245                .accounts
246                .update_account(address, update_info);
247        }
248
249        Ok(revert_updates)
250    }
251
252    #[cfg(test)]
253    pub fn get_account_storage(&self) -> Result<AccountStorage, PreCachedDBError> {
254        self.read_inner()
255            .map(|guard| guard.accounts.clone())
256    }
257
258    /// If block is set, returns the number. Otherwise returns None.
259    pub fn block_number(&self) -> Result<Option<u64>, PreCachedDBError> {
260        self.read_inner().map(|guard| {
261            guard
262                .block
263                .as_ref()
264                .map(|header| header.number)
265        })
266    }
267
268    /// Clear all state from the database.
269    pub fn clear(&self) -> Result<(), PreCachedDBError> {
270        let mut write_guard = self.write_inner()?;
271        write_guard.accounts.clear();
272        write_guard.block = None;
273        Ok(())
274    }
275
276    fn read_inner(&self) -> Result<RwLockReadGuard<'_, PreCachedDBInner>, PreCachedDBError> {
277        self.inner
278            .read()
279            .map_err(|_| PreCachedDBError::Fatal("Tycho state db lock poisoned".into()))
280    }
281
282    fn write_inner(&self) -> Result<RwLockWriteGuard<'_, PreCachedDBInner>, PreCachedDBError> {
283        self.inner
284            .write()
285            .map_err(|_| PreCachedDBError::Fatal("Tycho state db lock poisoned".into()))
286    }
287}
288
289impl EngineDatabaseInterface for PreCachedDB {
290    type Error = PreCachedDBError;
291
292    /// Sets up a single account
293    ///
294    /// Full control over setting up an accounts. Allows to set up EOAs as well as smart contracts.
295    ///
296    /// # Arguments
297    ///
298    /// * `address` - Address of the account
299    /// * `account` - The account information
300    /// * `permanent_storage` - Storage to init the account with, this storage can only be updated
301    ///   manually
302    fn init_account(
303        &self,
304        address: Address,
305        account: AccountInfo,
306        permanent_storage: Option<HashMap<U256, U256>>,
307        _mocked: bool,
308    ) -> Result<(), <Self as EngineDatabaseInterface>::Error> {
309        if account.code.is_none() && account.code_hash != KECCAK_EMPTY {
310            warn!("Code is None for account {address} but code hash is not KECCAK_EMPTY");
311        } else if account.code.is_some() && account.code_hash == KECCAK_EMPTY {
312            warn!("Code is Some for account {address} but code hash is KECCAK_EMPTY");
313        }
314
315        self.write_inner()?
316            .accounts
317            .init_account(address, account, permanent_storage, true);
318
319        Ok(())
320    }
321
322    /// Deprecated in TychoDB
323    fn clear_temp_storage(&mut self) -> Result<(), <Self as EngineDatabaseInterface>::Error> {
324        debug!("Temp storage in TychoDB is never set, nothing to clear");
325
326        Ok(())
327    }
328
329    fn get_current_block(&self) -> Option<BlockHeader> {
330        self.inner.read().unwrap().block.clone()
331    }
332}
333
334impl DatabaseRef for PreCachedDB {
335    type Error = PreCachedDBError;
336    /// Retrieves basic information about an account.
337    ///
338    /// This function retrieves the basic account information for the specified address.
339    ///
340    /// # Arguments
341    ///
342    /// * `address`: The address of the account to retrieve the information for.
343    ///
344    /// # Returns
345    ///
346    /// Returns a `Result` containing the account information or an error if the account is not
347    /// found.
348    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
349        self.read_inner()?
350            .accounts
351            .get_account_info(&address)
352            .map(|acc| Some(acc.clone()))
353            .ok_or(PreCachedDBError::MissingAccount(address))
354    }
355
356    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
357        Err(PreCachedDBError::Fatal(format!("Code by hash not supported: {code_hash}")))
358    }
359
360    /// Retrieves the storage value at the specified address and index.
361    ///
362    /// # Arguments
363    ///
364    /// * `address`: The address of the contract to retrieve the storage value from.
365    /// * `index`: The index of the storage value to retrieve.
366    ///
367    /// # Returns
368    ///
369    /// Returns a `Result` containing the storage value if it exists.
370    ///
371    /// # Errors
372    ///
373    /// Returns an error if the storage value is not found.
374    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
375        debug!(%address, %index, "Requested storage of account");
376        let read_guard = self.read_inner()?;
377        if let Some(storage_value) = read_guard
378            .accounts
379            .get_storage(&address, &index)
380        {
381            debug!(%address, %index, %storage_value, "Got value locally");
382            Ok(storage_value)
383        } else {
384            // At this point we either don't know this address or we don't have anything at this
385            if read_guard
386                .accounts
387                .account_present(&address)
388            {
389                // As we only store non-zero values, if the account is present it means this
390                // slot is zero.
391                debug!(%address, %index, "Account found, but slot is zero");
392                Ok(U256::ZERO)
393            } else {
394                // At this point we know we don't have data for this address.
395                debug!(%address, %index, "Account not found");
396                Err(PreCachedDBError::MissingAccount(address))
397            }
398        }
399    }
400
401    /// If block header is set, returns the hash. Otherwise, returns a zero hash.
402    fn block_hash_ref(&self, _number: u64) -> Result<B256, Self::Error> {
403        match self.read_inner()?.block.clone() {
404            Some(header) => Ok(B256::from_slice(&header.hash)),
405            None => Ok(B256::default()),
406        }
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use std::{error::Error, str::FromStr};
413
414    use revm::primitives::U256;
415    use rstest::{fixture, rstest};
416    use tycho_common::Bytes;
417
418    use super::*;
419    use crate::evm::tycho_models::{AccountUpdate, Chain, ChangeType};
420
421    #[fixture]
422    pub fn mock_db() -> PreCachedDB {
423        PreCachedDB {
424            inner: Arc::new(RwLock::new(PreCachedDBInner {
425                accounts: AccountStorage::new(),
426                block: None,
427            })),
428        }
429    }
430
431    #[rstest]
432    #[tokio::test]
433    async fn test_account_get_acc_info(mock_db: PreCachedDB) -> Result<(), Box<dyn Error>> {
434        // Tests if the provider has not been queried.
435        // Querying the mocked provider would cause a panic, therefore no assert is needed.
436        let mock_acc_address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
437        mock_db
438            .init_account(mock_acc_address, AccountInfo::default(), None, false)
439            .expect("Account init should succeed");
440
441        let acc_info = mock_db
442            .basic_ref(mock_acc_address)
443            .unwrap()
444            .unwrap();
445
446        assert_eq!(
447            mock_db
448                .basic_ref(mock_acc_address)
449                .unwrap()
450                .unwrap(),
451            acc_info
452        );
453        Ok(())
454    }
455
456    #[rstest]
457    fn test_account_storage(mock_db: PreCachedDB) -> Result<(), Box<dyn Error>> {
458        let mock_acc_address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
459        let storage_address = U256::from(1);
460        let mut permanent_storage: HashMap<U256, U256> = HashMap::new();
461        permanent_storage.insert(storage_address, U256::from(10));
462        mock_db
463            .init_account(mock_acc_address, AccountInfo::default(), Some(permanent_storage), false)
464            .expect("Account init should succeed");
465
466        let storage = mock_db
467            .storage_ref(mock_acc_address, storage_address)
468            .unwrap();
469
470        assert_eq!(storage, U256::from(10));
471        Ok(())
472    }
473
474    #[rstest]
475    fn test_account_storage_zero(mock_db: PreCachedDB) -> Result<(), Box<dyn Error>> {
476        let mock_acc_address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
477        let storage_address = U256::from(1);
478        mock_db
479            .init_account(mock_acc_address, AccountInfo::default(), None, false)
480            .expect("Account init should succeed");
481
482        let storage = mock_db
483            .storage_ref(mock_acc_address, storage_address)
484            .unwrap();
485
486        assert_eq!(storage, U256::ZERO);
487        Ok(())
488    }
489
490    #[rstest]
491    #[should_panic(
492        expected = "called `Result::unwrap()` on an `Err` value: MissingAccount(0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc)"
493    )]
494    fn test_account_storage_missing(mock_db: PreCachedDB) {
495        let mock_acc_address =
496            Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
497        let storage_address = U256::from(1);
498
499        // This will panic because this account isn't initialized
500        mock_db
501            .storage_ref(mock_acc_address, storage_address)
502            .unwrap();
503    }
504
505    #[rstest]
506    #[tokio::test]
507    async fn test_update_state(mut mock_db: PreCachedDB) -> Result<(), Box<dyn Error>> {
508        let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
509        mock_db
510            .init_account(address, AccountInfo::default(), None, false)
511            .expect("Account init should succeed");
512
513        let mut new_storage = HashMap::default();
514        let new_storage_value_index = U256::from_limbs_slice(&[123]);
515        new_storage.insert(new_storage_value_index, new_storage_value_index);
516        let new_balance = U256::from_limbs_slice(&[500]);
517        let update = StateUpdate { storage: Some(new_storage), balance: Some(new_balance) };
518        let new_block = BlockHeader {
519            number: 1,
520            hash: Bytes::from_str(
521                "0xc6b994ec855fb2b31013c7ae65074406fac46679b5b963469104e0bfeddd66d9",
522            )
523            .unwrap(),
524            timestamp: 123,
525            ..Default::default()
526        };
527        let mut updates = HashMap::default();
528        updates.insert(address, update);
529
530        mock_db
531            .update_state(&updates, new_block)
532            .expect("State update should succeed");
533
534        assert_eq!(
535            mock_db
536                .get_storage(&address, &new_storage_value_index)
537                .unwrap(),
538            new_storage_value_index
539        );
540        let account_info = mock_db
541            .basic_ref(address)
542            .unwrap()
543            .unwrap();
544        assert_eq!(account_info.balance, new_balance);
545        let block = mock_db
546            .inner
547            .read()
548            .unwrap()
549            .block
550            .clone()
551            .expect("block is Some");
552        assert_eq!(block.number, 1);
553
554        Ok(())
555    }
556
557    #[rstest]
558    #[tokio::test]
559    async fn test_block_number_getter(mut mock_db: PreCachedDB) -> Result<(), Box<dyn Error>> {
560        let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
561        mock_db
562            .init_account(address, AccountInfo::default(), None, false)
563            .expect("Account init should succeed");
564
565        let new_block = BlockHeader {
566            number: 1,
567            hash: Bytes::from_str(
568                "0xc6b994ec855fb2b31013c7ae65074406fac46679b5b963469104e0bfeddd66d9",
569            )
570            .unwrap(),
571            timestamp: 123,
572            ..Default::default()
573        };
574        let updates = HashMap::default();
575
576        mock_db
577            .update_state(&updates, new_block)
578            .expect("State update should succeed");
579
580        let block_number = mock_db.block_number();
581        assert_eq!(block_number.unwrap().unwrap(), 1);
582
583        Ok(())
584    }
585
586    #[rstest]
587    #[tokio::test]
588    async fn test_update() {
589        let mock_db = PreCachedDB {
590            inner: Arc::new(RwLock::new(PreCachedDBInner {
591                accounts: AccountStorage::new(),
592                block: None,
593            })),
594        };
595
596        let account_update = AccountUpdate::new(
597            Address::from_str("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D").unwrap(),
598            Chain::Ethereum,
599            HashMap::new(),
600            Some(U256::from(500)),
601            Some(Vec::<u8>::new()),
602            ChangeType::Creation,
603        );
604
605        let new_block = BlockHeader {
606            number: 1,
607            hash: Bytes::from_str(
608                "0xc6b994ec855fb2b31013c7ae65074406fac46679b5b963469104e0bfeddd66d9",
609            )
610            .unwrap(),
611            timestamp: 123,
612            ..Default::default()
613        };
614
615        mock_db
616            .update(vec![account_update], Some(new_block))
617            .unwrap();
618
619        let account_info = mock_db
620            .basic_ref(Address::from_str("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D").unwrap())
621            .unwrap()
622            .unwrap();
623
624        assert_eq!(
625            account_info,
626            AccountInfo {
627                nonce: 0,
628                balance: U256::from(500),
629                code_hash: B256::from_str(
630                    "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
631                )
632                .unwrap(),
633                code: Some(Bytecode::default()),
634            }
635        );
636
637        assert_eq!(
638            mock_db
639                .inner
640                .read()
641                .unwrap()
642                .block
643                .clone()
644                .expect("block is Some")
645                .number,
646            1
647        );
648    }
649
650    #[rstest]
651    fn test_force_update_accounts_overwrites_existing(mock_db: PreCachedDB) {
652        let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
653
654        // Simulate a placeholder proxy inserted by another decoder's snapshot loop.
655        mock_db
656            .init_account(address, AccountInfo::default(), None, true)
657            .expect("placeholder init should succeed");
658
659        // Now force-overwrite with the real proxy (authoritative vm_storage data).
660        let storage_slot = U256::from(1);
661        let storage_value = U256::from(42);
662        let mut slots = HashMap::new();
663        slots.insert(storage_slot, storage_value);
664        let update = AccountUpdate::new(
665            address,
666            Chain::Ethereum,
667            slots,
668            Some(U256::from(100)),
669            Some(Vec::<u8>::new()),
670            ChangeType::Creation,
671        );
672        mock_db
673            .force_update_accounts(vec![update])
674            .expect("force update should succeed");
675
676        let info = mock_db
677            .basic_ref(address)
678            .unwrap()
679            .unwrap();
680        assert_eq!(info.balance, U256::from(100));
681        assert_eq!(
682            mock_db
683                .get_storage(&address, &storage_slot)
684                .unwrap(),
685            storage_value
686        );
687    }
688
689    #[rstest]
690    fn test_force_update_accounts_non_creation_ignored(mock_db: PreCachedDB) {
691        let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
692        let original_balance = U256::from(10);
693
694        mock_db
695            .init_account(
696                address,
697                AccountInfo { balance: original_balance, ..Default::default() },
698                None,
699                true,
700            )
701            .expect("init should succeed");
702
703        // A non-Creation update should be ignored by force_update_accounts.
704        let update = AccountUpdate::new(
705            address,
706            Chain::Ethereum,
707            HashMap::new(),
708            Some(U256::from(999)),
709            None,
710            ChangeType::Update,
711        );
712        mock_db
713            .force_update_accounts(vec![update])
714            .expect("force update should succeed");
715
716        // Balance must remain unchanged.
717        let info = mock_db
718            .basic_ref(address)
719            .unwrap()
720            .unwrap();
721        assert_eq!(info.balance, original_balance);
722    }
723
724    /// This test requires a running TychoDB instance.
725    ///
726    /// To run this test, start TychoDB with the following command:
727    /// ```bash
728    /// cargo run --release -- \
729    //     --endpoint https://mainnet.eth.streamingfast.io:443 \
730    //     --module map_changes \
731    //     --spkg substreams/ethereum-ambient/substreams-ethereum-ambient-v0.3.0.spkg
732    /// ```
733    /// 
734    /// Then run the test with:
735    /// ```bash
736    /// cargo test --package src --lib -- --ignored --exact --nocapture
737    /// evm::engine_db::tycho_db::tests::test_tycho_db_connection
738    /// ```
739    #[ignore]
740    #[rstest]
741    fn test_tycho_db_connection() {
742        tracing_subscriber::fmt()
743            .with_env_filter("debug")
744            .init();
745
746        let ambient_contract =
747            Address::from_str("0xaaaaaaaaa24eeeb8d57d431224f73832bc34f688").unwrap();
748
749        let db = PreCachedDB::new().expect("db should initialize");
750
751        let acc_info = db
752            .basic_ref(ambient_contract)
753            .unwrap()
754            .unwrap();
755
756        debug!(?acc_info, "Account info");
757    }
758}