foundry_cheatcodes/
external_storage.rs1use alloy_primitives::{
8 Address,
9 map::{AddressMap, HashMap},
10};
11use foundry_common::external_storage::{fetch_external_storage_layouts, lock_until};
12use foundry_compilers::artifacts::StorageLayout;
13use foundry_config::Chain;
14use foundry_evm_traces::identifier::{ExternalIdentifier, ExternalIdentifierConfig};
15use std::{
16 sync::{Arc, LazyLock, Mutex},
17 time::Instant,
18};
19
20type Identifiers = HashMap<u64, Option<Arc<Mutex<ExternalIdentifier>>>>;
22
23static IDENTIFIERS: LazyLock<Mutex<Identifiers>> = LazyLock::new(Default::default);
29
30pub(crate) fn storage_layouts(
35 sources: &ExternalIdentifierConfig,
36 chain: Chain,
37 addresses: Vec<Address>,
38) -> AddressMap<(String, Arc<StorageLayout>)> {
39 fetch_external_storage_layouts(
40 chain,
41 addresses,
42 sources.storage_timeout(),
43 |unresolved, timeout| {
44 let deadline = Instant::now().checked_add(timeout).unwrap_or_else(Instant::now);
45 let Some(identifier) = identifier(sources, chain, deadline) else {
46 return Default::default();
47 };
48 let Some(mut identifier) = lock_until(&identifier, deadline) else {
49 return Default::default();
50 };
51 let remaining = deadline.saturating_duration_since(Instant::now());
52 foundry_common::block_on(identifier.get_metadata(unresolved, remaining))
53 },
54 )
55}
56
57fn identifier(
62 sources: &ExternalIdentifierConfig,
63 chain: Chain,
64 deadline: Instant,
65) -> Option<Arc<Mutex<ExternalIdentifier>>> {
66 let mut identifiers = lock_until(&IDENTIFIERS, deadline)?;
67 identifiers
68 .entry(chain.id())
69 .or_insert_with(|| match sources.storage_identifier(chain) {
70 Some(identifier) => Some(Arc::new(Mutex::new(identifier))),
71 None => {
72 let _ = sh_warn!(
73 "cannot decode external storage on chain {chain}: no matching block explorer \
74 is configured"
75 );
76 None
77 }
78 })
79 .clone()
80}