Skip to main content

anvil/
config.rs

1use crate::{
2    FeeManager, PrecompileFactory,
3    eth::{
4        backend::{
5            db::{Db, SerializableState},
6            fork::{
7                ClientFork, ClientForkConfig, ForkEndpointIdentity, ensure_fork_network_supported,
8            },
9            genesis::GenesisConfig,
10            mem::fork_db::ForkedDatabase,
11            time::duration_since_unix_epoch,
12        },
13        fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE},
14        miner::INSTANT_COALESCE_WINDOW,
15        pool::transactions::TransactionOrder,
16    },
17    mem::{self, in_memory_db::StateRootDb},
18};
19use alloy_chains::Chain;
20use alloy_consensus::BlockHeader;
21use alloy_eips::{BlockId, eip1559::BaseFeeParams, eip7840::BlobParams};
22use alloy_evm::EvmEnv;
23use alloy_genesis::Genesis;
24use alloy_network::{AnyNetwork, AnyRpcBlock, BlockResponse, TransactionResponse};
25use alloy_primitives::{
26    Address, B256, BlockNumber, TxHash, U256, hex, keccak256, map::HashMap, utils::Unit,
27};
28use alloy_provider::Provider;
29use alloy_rpc_types::anvil::{Metadata, NodeInfo};
30use alloy_signer::Signer;
31use alloy_signer_local::{
32    MnemonicBuilder, PrivateKeySigner,
33    coins_bip39::{English, Mnemonic},
34};
35use alloy_transport::TransportError;
36use anvil_server::ServerConfig;
37use eyre::{Context, Result};
38use foundry_common::{
39    ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT,
40    provider::{ProviderBuilder, RetryProvider, is_rpc_method_not_found, redact_url},
41};
42use foundry_config::Config;
43use foundry_evm::{
44    backend::{
45        BlockchainDb, BlockchainDbMeta, ForkBlock, SharedBackend, account_fetch_policy_for_source,
46    },
47    constants::DEFAULT_CREATE2_DEPLOYER,
48    hardfork::FoundryHardfork,
49    traces::{CallTraceDecoderBuilder, identifier::SignaturesIdentifier},
50    utils::{
51        apply_chain_and_block_specific_env_changes_for_chain, block_env_from_header,
52        get_blob_params, get_blob_params_by_hardfork,
53    },
54};
55use foundry_evm_networks::{NetworkConfigs, NetworkVariant};
56use parking_lot::RwLock;
57use rand_08::thread_rng;
58use revm::{
59    context::{BlockEnv, CfgEnv},
60    context_interface::block::BlobExcessGasAndPrice,
61    primitives::hardfork::SpecId,
62};
63use serde_json::{Value, json};
64use std::{
65    fmt::Write as FmtWrite,
66    net::{IpAddr, Ipv4Addr},
67    path::PathBuf,
68    sync::Arc,
69    time::Duration,
70};
71use tempo_hardfork::{
72    TempoHardfork,
73    constants::gas::{TEMPO_T0_BASE_FEE, TEMPO_T1_BASE_FEE},
74};
75use tempo_precompiles::TIP_FEE_MANAGER_ADDRESS;
76use tokio::sync::RwLock as TokioRwLock;
77use yansi::Paint;
78
79#[cfg(feature = "base")]
80use alloy_chains::NamedChain;
81
82pub use foundry_common::version::SHORT_VERSION as VERSION_MESSAGE;
83
84/// Default port the rpc will open
85pub const NODE_PORT: u16 = 8545;
86/// Default chain id of the node
87pub const CHAIN_ID: u64 = 31337;
88/// The default gas limit for all transactions
89pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000;
90/// The default number of slots in an epoch used for safe/finalized block tags.
91pub const DEFAULT_SLOTS_IN_AN_EPOCH: u64 = 32;
92/// Default mnemonic for dev accounts
93pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
94
95/// Resolves source-chain upgrades without changing another execution family's historical lookup.
96pub(crate) fn source_hardfork(
97    _execution_network: NetworkVariant,
98    chain_id: u64,
99    timestamp: u64,
100) -> Option<FoundryHardfork> {
101    #[cfg(feature = "base")]
102    if _execution_network.is_base() {
103        return _execution_network.historical_hardfork(chain_id, timestamp);
104    }
105    #[cfg(feature = "optimism")]
106    if _execution_network.is_optimism()
107        && let Some(hardfork) = _execution_network.historical_hardfork(chain_id, timestamp)
108    {
109        return Some(hardfork);
110    }
111    #[cfg(feature = "base")]
112    if matches!(NamedChain::try_from(chain_id), Ok(NamedChain::Base | NamedChain::BaseSepolia)) {
113        #[cfg(feature = "optimism")]
114        return foundry_evm::hardfork::OpHardfork::from_chain_and_timestamp(
115            Chain::from_id(chain_id),
116            timestamp,
117        )
118        .map(FoundryHardfork::Optimism);
119        #[cfg(not(feature = "optimism"))]
120        return None;
121    }
122    FoundryHardfork::from_chain_and_timestamp(chain_id, timestamp)
123}
124
125#[derive(Clone, Copy, Debug)]
126struct ForkOverrides {
127    gas_limit: Option<u64>,
128    gas_price: Option<u128>,
129    base_fee: Option<u64>,
130}
131
132struct StableForkSnapshot {
133    endpoint_identity: ForkEndpointIdentity,
134    state_is_mutable: bool,
135    block_number: u64,
136    transaction_replay: Option<ForkTransactionReplay>,
137    block: Option<AnyRpcBlock>,
138    gas_price: u128,
139}
140
141/// Keep optional identity probes from delaying startup on RPCs that stall on unknown methods.
142const FORK_IDENTITY_PROBE_TIMEOUT: Duration = Duration::from_millis(500);
143
144/// Best-effort Anvil detection that becomes strict after positive identification.
145///
146/// Before the first successful `anvil_nodeInfo` response, any probe failure means that the
147/// optional capability is unavailable. Mandatory standard RPC reads still expose endpoint-wide
148/// failures. Once a response or cached endpoint identity identifies Anvil, every later probe
149/// RPC failure is returned so it cannot hide an endpoint reset or execution-profile change.
150/// Probe timeouts always mean that the optional capability is unavailable.
151#[derive(Clone, Copy, Debug, Default)]
152struct AnvilNodeInfoProbe {
153    identified: bool,
154    skip: bool,
155    /// A failed probe could not distinguish an unsupported method from a mutable Anvil source.
156    inconclusive: bool,
157}
158
159impl AnvilNodeInfoProbe {
160    const fn new(identified: bool, skip: bool) -> Self {
161        Self { identified, skip, inconclusive: false }
162    }
163
164    async fn request(&mut self, provider: &RetryProvider) -> Result<Option<NodeInfo>> {
165        if self.skip {
166            return Ok(None);
167        }
168        let Ok(response) = tokio::time::timeout(
169            FORK_IDENTITY_PROBE_TIMEOUT,
170            provider.raw_request::<_, NodeInfo>("anvil_nodeInfo".into(), ()),
171        )
172        .await
173        else {
174            self.inconclusive = true;
175            return Ok(None);
176        };
177        match response {
178            Ok(node_info) => {
179                self.identified = true;
180                Ok(Some(node_info))
181            }
182            Err(error) if !self.identified => {
183                self.inconclusive |= !is_rpc_method_not_found(&error);
184                Ok(None)
185            }
186            Err(error) => {
187                Err(error).wrap_err("failed to determine network family from fork endpoint")
188            }
189        }
190    }
191}
192
193/// One-shot source data for a transaction-hash fork replay.
194#[derive(Clone, Debug)]
195pub(crate) struct ForkTransactionReplay {
196    pub(crate) source_block: AnyRpcBlock,
197    pub(crate) target_index: usize,
198}
199
200/// The default IPC endpoint
201pub const DEFAULT_IPC_ENDPOINT: &str =
202    if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };
203
204const BANNER: &str = r"
205                             _   _
206                            (_) | |
207      __ _   _ __   __   __  _  | |
208     / _` | | '_ \  \ \ / / | | | |
209    | (_| | | | | |  \ V /  | | | |
210     \__,_| |_| |_|   \_/   |_| |_|
211";
212
213fn fork_source_id(urls: &[String], headers: &[String]) -> B256 {
214    let mut encoded = Vec::new();
215    for parts in [urls, headers] {
216        encoded.extend_from_slice(&(parts.len() as u64).to_be_bytes());
217        for part in parts {
218            encoded.extend_from_slice(&(part.len() as u64).to_be_bytes());
219            encoded.extend_from_slice(part.as_bytes());
220        }
221    }
222    keccak256(encoded)
223}
224
225/// Configurations of the EVM node
226#[derive(Clone, Debug)]
227pub struct NodeConfig {
228    /// Chain ID of the EVM chain
229    pub chain_id: Option<u64>,
230    /// Default gas limit for all txs
231    pub gas_limit: Option<u64>,
232    /// If set to `true`, disables the block gas limit
233    pub disable_block_gas_limit: bool,
234    /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825)
235    pub enable_tx_gas_limit: bool,
236    /// Default gas price for all txs
237    pub gas_price: Option<u128>,
238    /// Default base fee
239    pub base_fee: Option<u64>,
240    /// If set to `true`, disables the enforcement of a minimum suggested priority fee
241    pub disable_min_priority_fee: bool,
242    /// Default blob excess gas and price
243    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
244    /// The hardfork to force, or `None` to infer it from chain activation data.
245    pub hardfork: Option<FoundryHardfork>,
246    /// Signer accounts that will be initialised with `genesis_balance` in the genesis block
247    pub genesis_accounts: Vec<PrivateKeySigner>,
248    /// Native token balance of every genesis account in the genesis block
249    pub genesis_balance: U256,
250    /// Genesis block timestamp
251    pub genesis_timestamp: Option<u64>,
252    /// Genesis block number
253    pub genesis_block_number: Option<u64>,
254    /// Signer accounts that can sign messages/transactions from the EVM node
255    pub signer_accounts: Vec<PrivateKeySigner>,
256    /// Configured block time for the EVM chain. Use `None` for instant/auto mining.
257    pub block_time: Option<Duration>,
258    /// Window for grouping ready transactions in auto mining; zero disables coalescing.
259    pub transaction_coalescing_window: Duration,
260    /// Disable auto and interval mining mode and use `MiningMode::None` instead.
261    pub no_mining: bool,
262    /// Enables auto and interval mining mode
263    pub mixed_mining: bool,
264    /// port to use for the server
265    pub port: u16,
266    /// maximum number of transactions in a block
267    pub max_transactions: usize,
268    /// Fork URLs for RPC calls. The first entry is the primary endpoint.
269    /// When multiple URLs are provided, requests are distributed using
270    /// round-robin load balancing with retry-based failover.
271    pub fork_urls: Vec<String>,
272    /// pins the block number or transaction hash for the state fork
273    pub fork_choice: Option<ForkChoice>,
274    /// headers to use with fork RPC endpoints
275    pub fork_headers: Vec<String>,
276    /// specifies chain id for cache to skip fetching from remote in offline-start mode
277    pub fork_chain_id: Option<U256>,
278    /// Skip `anvil_nodeInfo` / `anvil_metadata` probes against the fork URL.
279    pub no_fork_node_info: bool,
280    /// Disables opportunistic BAL post-state prefill of the remote fork cache.
281    pub no_bal: bool,
282    /// Address fork state reads by block number instead of by block hash.
283    pub fork_state_by_number: bool,
284    /// Chain ID discovered from the active fork source.
285    pub fork_source_chain_id: Option<u64>,
286    /// Chain ID exposed by the active fork endpoint.
287    pub fork_execution_chain_id: Option<u64>,
288    /// Whether the active fork endpoint has positively identified itself as Anvil.
289    pub(crate) fork_endpoint_is_anvil: bool,
290    /// Network family most recently inferred from a fork endpoint.
291    inferred_fork_network: Option<NetworkVariant>,
292    /// Network configuration replaced by chain-ID inference, if any.
293    chain_id_network_base: Option<NetworkConfigs>,
294    /// User-provided gas settings captured before fork-derived values are materialized.
295    fork_overrides: Option<ForkOverrides>,
296    /// The generator used to generate the dev accounts
297    pub account_generator: Option<AccountGenerator>,
298    /// whether to enable tracing
299    pub enable_tracing: bool,
300    /// Explicitly disables the use of RPC caching.
301    pub no_storage_caching: bool,
302    /// How to configure the server
303    pub server_config: ServerConfig,
304    /// The host the server will listen on
305    pub host: Vec<IpAddr>,
306    /// How transactions are sorted in the mempool
307    pub transaction_order: TransactionOrder,
308    /// Filename to write anvil output as json
309    pub config_out: Option<PathBuf>,
310    /// The genesis to use to initialize the node
311    pub genesis: Option<Genesis>,
312    /// Timeout in for requests sent to remote JSON-RPC server in forking mode
313    pub fork_request_timeout: Duration,
314    /// Number of request retries for spurious networks
315    pub fork_request_retries: u32,
316    /// The initial retry backoff
317    pub fork_retry_backoff: Duration,
318    /// available CUPS
319    pub compute_units_per_second: u64,
320    /// The ipc path
321    pub ipc_path: Option<Option<String>>,
322    /// Enable transaction/call steps tracing for debug calls returning geth-style traces
323    pub enable_steps_tracing: bool,
324    /// Enable printing of `console.log` invocations.
325    pub print_logs: bool,
326    /// Enable printing of traces.
327    pub print_traces: bool,
328    /// Enable auto impersonation of accounts on startup
329    pub enable_auto_impersonate: bool,
330    /// Configure the code size limit
331    pub code_size_limit: Option<usize>,
332    /// Configures how to remove historic state.
333    ///
334    /// If set to `Some(num)` keep latest num state in memory only.
335    pub prune_history: PruneStateHistoryConfig,
336    /// Max number of states cached on disk.
337    pub max_persisted_states: Option<usize>,
338    /// The initial state to apply and consume during startup.
339    pub init_state: Option<SerializableState>,
340    /// max number of blocks with transactions in memory
341    pub transaction_block_keeper: Option<usize>,
342    /// Disable the default CREATE2 deployer
343    pub disable_default_create2_deployer: bool,
344    /// Disable pool balance checks
345    pub disable_pool_balance_checks: bool,
346    /// Slots in an epoch
347    pub slots_in_an_epoch: u64,
348    /// The memory limit per EVM execution in bytes.
349    pub memory_limit: Option<u64>,
350    /// Factory used by `anvil` to extend the EVM's precompiles.
351    pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
352    /// Networks to enable features for.
353    pub networks: NetworkConfigs,
354    /// Overrides the Base activation-registry administrator.
355    #[cfg(feature = "base")]
356    pub base_activation_admin: Option<Address>,
357    /// The account used to sponsor Tempo fee-payer requests.
358    ///
359    /// Must be an unlocked signer account. Defaults to the last dev account on Tempo networks.
360    pub tempo_fee_payer: Option<Address>,
361    /// Do not print log messages.
362    pub silent: bool,
363    /// The path where persisted states are cached (used with `max_persisted_states`).
364    /// This does not affect the fork RPC cache location.
365    pub cache_path: Option<PathBuf>,
366    /// Accounts to fund with specific balances on startup (address -> balance in wei).
367    pub funded_accounts: HashMap<Address, U256>,
368}
369
370impl NodeConfig {
371    fn as_string(&self, fork: Option<&ClientFork>) -> String {
372        let mut s: String = String::new();
373        let _ = write!(s, "\n{}", BANNER.green());
374        let _ = write!(s, "\n    {VERSION_MESSAGE}");
375        let _ = write!(s, "\n    {}", "https://github.com/foundry-rs/foundry".green());
376
377        let _ = write!(
378            s,
379            r#"
380
381Available Accounts
382==================
383"#
384        );
385        let balance = alloy_primitives::utils::format_ether(self.genesis_balance);
386        for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
387            write!(s, "\n({idx}) {} ({balance} ETH)", wallet.address()).unwrap();
388        }
389
390        let _ = write!(
391            s,
392            r#"
393
394Private Keys
395==================
396"#
397        );
398
399        for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
400            let hex = hex::encode(wallet.credential().to_bytes());
401            let _ = write!(s, "\n({idx}) 0x{hex}");
402        }
403
404        if let Some(generator) = &self.account_generator {
405            let _ = write!(
406                s,
407                r#"
408
409Wallet
410==================
411Mnemonic:          {}
412Derivation path:   {}
413"#,
414                generator.phrase,
415                generator.get_derivation_path()
416            );
417        }
418
419        if let Some(fee_payer) = self.tempo_fee_payer_address() {
420            let _ = write!(
421                s,
422                r#"
423
424Tempo Fee Payer
425==================
426{fee_payer}
427"#
428            );
429        }
430
431        if let Some(fork) = fork {
432            let _ = write!(
433                s,
434                r#"
435
436Fork
437==================
438Endpoint:       {}
439Block number:   {}
440Block hash:     {:?}
441Chain ID:       {}
442"#,
443                fork.eth_rpc_url().as_deref().map(redact_url).unwrap_or_else(|| "none".to_string()),
444                fork.block_number(),
445                fork.block_hash(),
446                fork.execution_chain_id()
447            );
448            if fork.chain_id() != fork.execution_chain_id() {
449                let _ = writeln!(s, "Source chain ID: {}", fork.chain_id());
450            }
451
452            if self.fork_urls.len() > 1 {
453                let _ = writeln!(s, "Endpoints:      {}", self.fork_urls.len());
454                for (i, url) in self.fork_urls.iter().enumerate() {
455                    let _ = writeln!(s, "  ({i}) {}", redact_url(url));
456                }
457            }
458
459            if let Some(tx_hash) = fork.transaction_hash() {
460                let _ = writeln!(s, "Transaction hash: {tx_hash}");
461            }
462        } else {
463            let _ = write!(
464                s,
465                r#"
466
467Chain ID
468==================
469
470{}
471"#,
472                self.get_chain_id().green()
473            );
474        }
475
476        if (SpecId::from(self.get_hardfork()) as u8) < (SpecId::LONDON as u8) {
477            let _ = write!(
478                s,
479                r#"
480Gas Price
481==================
482
483{}
484"#,
485                self.get_gas_price().green()
486            );
487        } else {
488            let _ = write!(
489                s,
490                r#"
491Base Fee
492==================
493
494{}
495"#,
496                self.get_base_fee().green()
497            );
498        }
499
500        let _ = write!(
501            s,
502            r#"
503Gas Limit
504==================
505
506{}
507"#,
508            {
509                if self.disable_block_gas_limit {
510                    "Disabled".to_string()
511                } else {
512                    self.gas_limit.map(|l| l.to_string()).unwrap_or_else(|| {
513                        if self.fork_choice.is_some() {
514                            "Forked".to_string()
515                        } else {
516                            DEFAULT_GAS_LIMIT.to_string()
517                        }
518                    })
519                }
520            }
521            .green()
522        );
523
524        let _ = write!(
525            s,
526            r#"
527Genesis Timestamp
528==================
529
530{}
531"#,
532            self.get_genesis_timestamp().green()
533        );
534
535        let _ = write!(
536            s,
537            r#"
538Genesis Number
539==================
540
541{}
542"#,
543            self.get_genesis_number().green()
544        );
545
546        s
547    }
548
549    fn as_json(&self, fork: Option<&ClientFork>) -> Value {
550        let mut wallet_description = HashMap::new();
551        let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
552        let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
553
554        for wallet in &self.genesis_accounts {
555            available_accounts.push(format!("{:?}", wallet.address()));
556            private_keys.push(hex::encode_prefixed(wallet.credential().to_bytes()));
557        }
558
559        if let Some(generator) = &self.account_generator {
560            let phrase = generator.get_phrase().to_string();
561            let derivation_path = generator.get_derivation_path().to_string();
562
563            wallet_description.insert("derivation_path".to_string(), derivation_path);
564            wallet_description.insert("mnemonic".to_string(), phrase);
565        };
566
567        let gas_limit = match self.gas_limit {
568            // if we have a disabled flag we should max out the limit
569            Some(_) | None if self.disable_block_gas_limit => Some(u64::MAX.to_string()),
570            Some(limit) => Some(limit.to_string()),
571            _ => None,
572        };
573
574        if let Some(fork) = fork {
575            json!({
576              "available_accounts": available_accounts,
577              "private_keys": private_keys,
578              "endpoint": fork.eth_rpc_url().as_deref().map(redact_url).unwrap_or_default(),
579              "block_number": fork.block_number(),
580              "block_hash": fork.block_hash(),
581              "chain_id": fork.execution_chain_id(),
582              "source_chain_id": fork.chain_id(),
583              "wallet": wallet_description,
584              "base_fee": format!("{}", self.get_base_fee()),
585              "gas_price": format!("{}", self.get_gas_price()),
586              "gas_limit": gas_limit,
587            })
588        } else {
589            json!({
590              "available_accounts": available_accounts,
591              "private_keys": private_keys,
592              "wallet": wallet_description,
593              "base_fee": format!("{}", self.get_base_fee()),
594              "gas_price": format!("{}", self.get_gas_price()),
595              "gas_limit": gas_limit,
596              "genesis_timestamp": format!("{}", self.get_genesis_timestamp()),
597            })
598        }
599    }
600}
601
602impl NodeConfig {
603    /// Returns a new config intended to be used in tests, which does not print and binds to a
604    /// random, free port by setting it to `0`
605    #[doc(hidden)]
606    pub fn test() -> Self {
607        Self { enable_tracing: true, port: 0, silent: true, ..Default::default() }
608    }
609
610    /// Returns a test config with Tempo network enabled.
611    #[doc(hidden)]
612    pub fn test_tempo() -> Self {
613        Self { networks: NetworkConfigs::with_tempo(), ..Self::test() }
614    }
615
616    /// Returns a test config with Monad network enabled.
617    #[cfg(feature = "monad")]
618    #[doc(hidden)]
619    pub fn test_monad() -> Self {
620        Self { networks: NetworkConfigs::with_monad(), ..Self::test() }
621    }
622
623    /// Returns a test config with Base network enabled.
624    #[cfg(feature = "base")]
625    #[doc(hidden)]
626    pub fn test_base() -> Self {
627        Self::test()
628            .with_networks(NetworkConfigs::with_base())
629            .with_chain_id(Some(NamedChain::Base as u64))
630    }
631
632    /// Returns a new config which does not initialize any accounts on node startup.
633    pub fn empty_state() -> Self {
634        Self {
635            genesis_accounts: vec![],
636            signer_accounts: vec![],
637            disable_default_create2_deployer: true,
638            ..Default::default()
639        }
640    }
641}
642
643impl Default for NodeConfig {
644    fn default() -> Self {
645        // generate some random wallets
646        let genesis_accounts = AccountGenerator::new(10)
647            .phrase(DEFAULT_MNEMONIC)
648            .generate()
649            .expect("Invalid mnemonic.");
650        Self {
651            chain_id: None,
652            gas_limit: None,
653            disable_block_gas_limit: false,
654            enable_tx_gas_limit: false,
655            gas_price: None,
656            hardfork: None,
657            signer_accounts: genesis_accounts.clone(),
658            genesis_timestamp: None,
659            genesis_block_number: None,
660            genesis_accounts,
661            // 100ETH default balance
662            genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)),
663            block_time: None,
664            transaction_coalescing_window: INSTANT_COALESCE_WINDOW,
665            no_mining: false,
666            mixed_mining: false,
667            port: NODE_PORT,
668            max_transactions: 1_000,
669            fork_urls: vec![],
670            fork_choice: None,
671            account_generator: None,
672            base_fee: None,
673            disable_min_priority_fee: false,
674            blob_excess_gas_and_price: None,
675            enable_tracing: true,
676            enable_steps_tracing: false,
677            print_logs: true,
678            print_traces: false,
679            enable_auto_impersonate: false,
680            no_storage_caching: false,
681            server_config: Default::default(),
682            host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
683            transaction_order: Default::default(),
684            config_out: None,
685            genesis: None,
686            fork_request_timeout: REQUEST_TIMEOUT,
687            fork_headers: vec![],
688            fork_request_retries: 5,
689            fork_retry_backoff: Duration::from_millis(1_000),
690            fork_chain_id: None,
691            no_fork_node_info: false,
692            no_bal: false,
693            fork_state_by_number: false,
694            fork_source_chain_id: None,
695            fork_execution_chain_id: None,
696            fork_endpoint_is_anvil: false,
697            inferred_fork_network: None,
698            chain_id_network_base: None,
699            fork_overrides: None,
700            // alchemy max cpus <https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second>
701            compute_units_per_second: ALCHEMY_FREE_TIER_CUPS,
702            ipc_path: None,
703            code_size_limit: None,
704            prune_history: Default::default(),
705            max_persisted_states: None,
706            init_state: None,
707            transaction_block_keeper: None,
708            disable_default_create2_deployer: false,
709            disable_pool_balance_checks: false,
710            slots_in_an_epoch: DEFAULT_SLOTS_IN_AN_EPOCH,
711            memory_limit: None,
712            precompile_factory: None,
713            networks: Default::default(),
714            #[cfg(feature = "base")]
715            base_activation_admin: None,
716            tempo_fee_payer: None,
717            silent: false,
718            cache_path: None,
719            funded_accounts: HashMap::default(),
720        }
721    }
722}
723
724impl NodeConfig {
725    /// Applies Tempo's safe default beneficiary for forked nodes while preserving
726    /// explicit coinbase selections.
727    pub(crate) fn apply_tempo_fork_beneficiary_default<N>(&self, evm_env: &mut EvmEnv<N>) {
728        if self.networks.is_tempo()
729            && !self.fork_urls.is_empty()
730            && evm_env.block_env.beneficiary.is_zero()
731        {
732            // Tempo mainnet maps the zero validator token to a DONOTUSE sentinel.
733            // Forked transactions with the default zero beneficiary can therefore
734            // fail fee collection before producing a receipt. Use the same neutral
735            // fee-recipient sentinel as Tempo's simulation path so validator token
736            // lookup falls back to the default PathUSD token unless the user has
737            // explicitly supplied a non-zero coinbase.
738            evm_env.block_env.beneficiary = TIP_FEE_MANAGER_ADDRESS;
739        }
740    }
741
742    /// Returns the memory limit of the node
743    #[must_use]
744    pub const fn with_memory_limit(mut self, mems_value: Option<u64>) -> Self {
745        self.memory_limit = mems_value;
746        self
747    }
748
749    /// Returns the base fee to use.
750    ///
751    /// In Tempo mode, uses the hardfork-specific base fee (10 gwei pre-T1, 20 gwei T1+).
752    pub fn get_base_fee(&self) -> u64 {
753        let default = if self.networks.is_tempo() {
754            tempo_default_base_fee(TempoHardfork::from(self.get_hardfork()))
755        } else {
756            INITIAL_BASE_FEE
757        };
758        self.base_fee
759            .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64)))
760            .unwrap_or(default)
761    }
762
763    /// Returns the gas price to use.
764    ///
765    /// In Tempo mode, defaults to the hardfork-specific base fee.
766    pub fn get_gas_price(&self) -> u128 {
767        let default = if self.networks.is_tempo() {
768            tempo_default_base_fee(TempoHardfork::from(self.get_hardfork())) as u128
769        } else {
770            INITIAL_GAS_PRICE
771        };
772        self.gas_price.unwrap_or(default)
773    }
774
775    pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice {
776        if let Some(value) = self.blob_excess_gas_and_price {
777            value
778        } else {
779            let excess_blob_gas =
780                self.genesis.as_ref().and_then(|g| g.excess_blob_gas).unwrap_or(0);
781            BlobExcessGasAndPrice::new(
782                excess_blob_gas,
783                self.get_blob_params().update_fraction as u64,
784            )
785        }
786    }
787
788    /// Returns the [`BlobParams`] that should be used.
789    pub fn get_blob_params(&self) -> BlobParams {
790        get_blob_params_by_hardfork(self.get_hardfork())
791    }
792
793    /// Returns the hardfork to use
794    pub fn get_hardfork(&self) -> FoundryHardfork {
795        if let Some(hardfork) = self.hardfork {
796            return hardfork;
797        }
798        self.networks
799            .execution_network()
800            .hardfork_at(self.protocol_chain_id(), self.get_genesis_timestamp())
801    }
802
803    /// Sets a custom code size limit
804    #[must_use]
805    pub const fn with_code_size_limit(mut self, code_size_limit: Option<usize>) -> Self {
806        self.code_size_limit = code_size_limit;
807        self
808    }
809    /// Disables  code size limit
810    #[must_use]
811    pub const fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self {
812        if disable_code_size_limit {
813            self.code_size_limit = Some(usize::MAX);
814        }
815        self
816    }
817
818    /// Sets the init state if any
819    #[must_use]
820    pub fn with_init_state(mut self, init_state: Option<SerializableState>) -> Self {
821        self.init_state = init_state;
822        self
823    }
824
825    /// Loads the init state from a file if it exists
826    #[must_use]
827    #[cfg(feature = "cmd")]
828    pub fn with_init_state_path(mut self, path: impl AsRef<std::path::Path>) -> Self {
829        self.init_state = crate::cmd::StateFile::parse_path(path).ok().and_then(|file| file.state);
830        self
831    }
832
833    /// Sets the chain ID
834    #[must_use]
835    pub fn with_chain_id<U: Into<u64>>(mut self, chain_id: Option<U>) -> Self {
836        self.set_chain_id(chain_id);
837        self
838    }
839
840    /// Returns the chain ID to use
841    pub fn get_chain_id(&self) -> u64 {
842        self.chain_id
843            .or(self.fork_execution_chain_id)
844            .or_else(|| self.genesis.as_ref().map(|g| g.config.chain_id))
845            .unwrap_or(CHAIN_ID)
846    }
847
848    /// Returns the chain ID that defines protocol behavior.
849    fn protocol_chain_id(&self) -> u64 {
850        self.fork_source_chain_id.unwrap_or_else(|| self.get_chain_id())
851    }
852
853    /// Sets the chain id and updates all wallets
854    pub fn set_chain_id(&mut self, chain_id: Option<impl Into<u64>>) {
855        if let Some(base) = self.chain_id_network_base.take() {
856            self.networks = base;
857        }
858        self.chain_id = chain_id.map(Into::into);
859        let chain_id = self.get_chain_id();
860        let base = self.networks;
861        let inferred = base.with_chain_id(chain_id);
862        if !base.has_network_selection() && inferred.has_network_selection() {
863            self.chain_id_network_base = Some(base);
864        }
865        self.networks = inferred;
866        self.update_wallet_chain_id(chain_id);
867    }
868
869    pub(crate) fn update_wallet_chain_id(&mut self, chain_id: u64) {
870        self.genesis_accounts.iter_mut().for_each(|wallet| {
871            *wallet = wallet.clone().with_chain_id(Some(chain_id));
872        });
873        self.signer_accounts.iter_mut().for_each(|wallet| {
874            *wallet = wallet.clone().with_chain_id(Some(chain_id));
875        })
876    }
877
878    /// Sets the gas limit
879    #[must_use]
880    pub const fn with_gas_limit(mut self, gas_limit: Option<u64>) -> Self {
881        self.gas_limit = gas_limit;
882        self
883    }
884
885    /// Disable block gas limit check
886    ///
887    /// If set to `true` block gas limit will not be enforced
888    #[must_use]
889    pub const fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self {
890        self.disable_block_gas_limit = disable_block_gas_limit;
891        self
892    }
893
894    /// Enable tx gas limit check
895    ///
896    /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825)
897    #[must_use]
898    pub const fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self {
899        self.enable_tx_gas_limit = enable_tx_gas_limit;
900        self
901    }
902
903    /// Sets the gas price
904    #[must_use]
905    pub const fn with_gas_price(mut self, gas_price: Option<u128>) -> Self {
906        self.gas_price = gas_price;
907        self
908    }
909
910    /// Sets prune history status.
911    #[must_use]
912    pub fn set_pruned_history(mut self, prune_history: Option<Option<usize>>) -> Self {
913        self.prune_history = PruneStateHistoryConfig::from_args(prune_history);
914        self
915    }
916
917    /// Sets max number of states to cache on disk.
918    #[must_use]
919    pub fn with_max_persisted_states<U: Into<usize>>(
920        mut self,
921        max_persisted_states: Option<U>,
922    ) -> Self {
923        self.max_persisted_states = max_persisted_states.map(Into::into);
924        self
925    }
926
927    /// Sets the max number of transactions in a block
928    #[must_use]
929    pub const fn with_max_transactions(mut self, max_transactions: Option<usize>) -> Self {
930        if let Some(max_transactions) = max_transactions {
931            self.max_transactions = max_transactions;
932        }
933        self
934    }
935
936    /// Sets max number of blocks with transactions to keep in memory
937    #[must_use]
938    pub fn with_transaction_block_keeper<U: Into<usize>>(
939        mut self,
940        transaction_block_keeper: Option<U>,
941    ) -> Self {
942        self.transaction_block_keeper = transaction_block_keeper.map(Into::into);
943        self
944    }
945
946    /// Sets the base fee
947    #[must_use]
948    pub const fn with_base_fee(mut self, base_fee: Option<u64>) -> Self {
949        self.base_fee = base_fee;
950        self
951    }
952
953    /// Disable the enforcement of a minimum suggested priority fee
954    #[must_use]
955    pub const fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self {
956        self.disable_min_priority_fee = disable_min_priority_fee;
957        self
958    }
959
960    /// Sets the init genesis (genesis.json)
961    #[must_use]
962    pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
963        self.genesis = genesis;
964        self
965    }
966
967    /// Returns the genesis timestamp to use
968    pub fn get_genesis_timestamp(&self) -> u64 {
969        self.genesis_timestamp
970            .or_else(|| self.genesis.as_ref().map(|g| g.timestamp))
971            .unwrap_or_else(|| duration_since_unix_epoch().as_secs())
972    }
973
974    /// Sets the genesis timestamp
975    #[must_use]
976    pub fn with_genesis_timestamp<U: Into<u64>>(mut self, timestamp: Option<U>) -> Self {
977        if let Some(timestamp) = timestamp {
978            self.genesis_timestamp = Some(timestamp.into());
979        }
980        self
981    }
982
983    /// Sets the genesis number
984    #[must_use]
985    pub fn with_genesis_block_number<U: Into<u64>>(mut self, number: Option<U>) -> Self {
986        if let Some(number) = number {
987            self.genesis_block_number = Some(number.into());
988        }
989        self
990    }
991
992    /// Returns the genesis number
993    pub fn get_genesis_number(&self) -> u64 {
994        self.genesis_block_number
995            .or_else(|| self.genesis.as_ref().and_then(|g| g.number))
996            .unwrap_or(0)
997    }
998
999    /// Sets the hardfork
1000    #[must_use]
1001    pub const fn with_hardfork(mut self, hardfork: Option<FoundryHardfork>) -> Self {
1002        self.hardfork = hardfork;
1003        self
1004    }
1005
1006    /// Sets the genesis accounts
1007    #[must_use]
1008    pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
1009        self.genesis_accounts = accounts;
1010        self
1011    }
1012
1013    /// Sets the signer accounts
1014    #[must_use]
1015    pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
1016        self.signer_accounts = accounts;
1017        self
1018    }
1019
1020    /// Sets both the genesis accounts and the signer accounts
1021    /// so that `genesis_accounts == accounts`
1022    pub fn with_account_generator(mut self, generator: AccountGenerator) -> eyre::Result<Self> {
1023        let accounts = generator.generate()?;
1024        self.account_generator = Some(generator);
1025        Ok(self.with_signer_accounts(accounts.clone()).with_genesis_accounts(accounts))
1026    }
1027
1028    /// Sets the balance of the genesis accounts in the genesis block
1029    #[must_use]
1030    pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
1031        self.genesis_balance = balance.into();
1032        self
1033    }
1034
1035    /// Sets the block time to automine blocks
1036    #[must_use]
1037    pub fn with_blocktime<D: Into<Duration>>(mut self, block_time: Option<D>) -> Self {
1038        self.block_time = block_time.map(Into::into);
1039        self
1040    }
1041
1042    #[must_use]
1043    pub fn with_mixed_mining<D: Into<Duration>>(
1044        mut self,
1045        mixed_mining: bool,
1046        block_time: Option<D>,
1047    ) -> Self {
1048        self.block_time = block_time.map(Into::into);
1049        self.mixed_mining = mixed_mining;
1050        self
1051    }
1052
1053    /// Sets the auto-mining coalescing window. Zero disables the delay.
1054    #[must_use]
1055    pub const fn with_transaction_coalescing_window(mut self, window: Duration) -> Self {
1056        self.transaction_coalescing_window = window;
1057        self
1058    }
1059
1060    /// If set to `true` auto mining will be disabled
1061    #[must_use]
1062    pub const fn with_no_mining(mut self, no_mining: bool) -> Self {
1063        self.no_mining = no_mining;
1064        self
1065    }
1066
1067    /// Sets the slots in an epoch
1068    #[must_use]
1069    pub const fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self {
1070        self.slots_in_an_epoch = slots_in_an_epoch;
1071        self
1072    }
1073
1074    /// Sets the port to use
1075    #[must_use]
1076    pub const fn with_port(mut self, port: u16) -> Self {
1077        self.port = port;
1078        self
1079    }
1080
1081    /// Sets the ipc path to use
1082    ///
1083    /// Note: this is a double Option for
1084    ///     - `None` -> no ipc
1085    ///     - `Some(None)` -> use default path
1086    ///     - `Some(Some(path))` -> use custom path
1087    #[must_use]
1088    pub fn with_ipc(mut self, ipc_path: Option<Option<String>>) -> Self {
1089        self.ipc_path = ipc_path;
1090        self
1091    }
1092
1093    /// Sets the file path to write the Anvil node's config info to.
1094    #[must_use]
1095    pub fn set_config_out(mut self, config_out: Option<PathBuf>) -> Self {
1096        self.config_out = config_out;
1097        self
1098    }
1099
1100    #[must_use]
1101    pub const fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self {
1102        self.no_storage_caching = no_storage_caching;
1103        self
1104    }
1105
1106    /// Sets the `eth_rpc_url` to use when forking (single endpoint convenience).
1107    #[must_use]
1108    pub fn with_eth_rpc_url<U: Into<String>>(mut self, eth_rpc_url: Option<U>) -> Self {
1109        if let Some(url) = eth_rpc_url {
1110            let fork_urls = vec![url.into()];
1111            if self.fork_urls != fork_urls {
1112                self.fork_endpoint_is_anvil = false;
1113            }
1114            self.fork_urls = fork_urls;
1115        }
1116        self
1117    }
1118
1119    /// Sets the fork URLs for load-balanced multi-endpoint forking.
1120    #[must_use]
1121    pub fn with_fork_urls(mut self, fork_urls: Vec<String>) -> Self {
1122        if self.fork_urls != fork_urls {
1123            self.fork_endpoint_is_anvil = false;
1124        }
1125        self.fork_urls = fork_urls;
1126        self
1127    }
1128
1129    /// Sets the `fork_choice` to use to fork off from based on a block number
1130    #[must_use]
1131    pub fn with_fork_block_number<U: Into<u64>>(self, fork_block_number: Option<U>) -> Self {
1132        self.with_fork_choice(fork_block_number.map(Into::into))
1133    }
1134
1135    /// Sets the `fork_choice` to use to fork off from based on a transaction hash
1136    #[must_use]
1137    pub fn with_fork_transaction_hash<U: Into<TxHash>>(
1138        self,
1139        fork_transaction_hash: Option<U>,
1140    ) -> Self {
1141        self.with_fork_choice(fork_transaction_hash.map(Into::into))
1142    }
1143
1144    /// Sets the `fork_choice` to use to fork off from
1145    #[must_use]
1146    pub fn with_fork_choice<U: Into<ForkChoice>>(mut self, fork_choice: Option<U>) -> Self {
1147        self.fork_choice = fork_choice.map(Into::into);
1148        self
1149    }
1150
1151    /// Sets whether fork state reads are addressed by block number instead of by block hash.
1152    #[must_use]
1153    pub const fn with_fork_state_by_number(mut self, fork_state_by_number: bool) -> Self {
1154        self.fork_state_by_number = fork_state_by_number;
1155        self
1156    }
1157
1158    /// Disables opportunistic BAL post-state prefill of the remote fork cache.
1159    #[must_use]
1160    pub const fn with_no_bal(mut self, no_bal: bool) -> Self {
1161        self.no_bal = no_bal;
1162        self
1163    }
1164
1165    /// Sets the `fork_chain_id` to use to fork off local cache from
1166    #[must_use]
1167    pub const fn with_fork_chain_id(mut self, fork_chain_id: Option<U256>) -> Self {
1168        self.fork_chain_id = fork_chain_id;
1169        self
1170    }
1171
1172    /// Skip `anvil_nodeInfo` / `anvil_metadata` probes against the fork URL.
1173    #[must_use]
1174    pub const fn with_no_fork_node_info(mut self, no_fork_node_info: bool) -> Self {
1175        self.no_fork_node_info = no_fork_node_info;
1176        self
1177    }
1178
1179    const fn node_info_probe(&self, identified: bool) -> AnvilNodeInfoProbe {
1180        AnvilNodeInfoProbe::new(identified, self.no_fork_node_info)
1181    }
1182
1183    /// Sets the `fork_headers` to use with fork RPC endpoints
1184    #[must_use]
1185    pub fn with_fork_headers(mut self, headers: Vec<String>) -> Self {
1186        self.fork_headers = headers;
1187        self
1188    }
1189
1190    /// Sets the `fork_request_timeout` to use for requests
1191    #[must_use]
1192    pub const fn fork_request_timeout(mut self, fork_request_timeout: Option<Duration>) -> Self {
1193        if let Some(fork_request_timeout) = fork_request_timeout {
1194            self.fork_request_timeout = fork_request_timeout;
1195        }
1196        self
1197    }
1198
1199    /// Sets the `fork_request_retries` to use for spurious networks
1200    #[must_use]
1201    pub const fn fork_request_retries(mut self, fork_request_retries: Option<u32>) -> Self {
1202        if let Some(fork_request_retries) = fork_request_retries {
1203            self.fork_request_retries = fork_request_retries;
1204        }
1205        self
1206    }
1207
1208    /// Sets the initial `fork_retry_backoff` for rate limits
1209    #[must_use]
1210    pub const fn fork_retry_backoff(mut self, fork_retry_backoff: Option<Duration>) -> Self {
1211        if let Some(fork_retry_backoff) = fork_retry_backoff {
1212            self.fork_retry_backoff = fork_retry_backoff;
1213        }
1214        self
1215    }
1216
1217    /// Sets the number of assumed available compute units per second
1218    ///
1219    /// See also, <https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second>
1220    #[must_use]
1221    pub const fn fork_compute_units_per_second(
1222        mut self,
1223        compute_units_per_second: Option<u64>,
1224    ) -> Self {
1225        if let Some(compute_units_per_second) = compute_units_per_second {
1226            self.compute_units_per_second = compute_units_per_second;
1227        }
1228        self
1229    }
1230
1231    /// Sets whether to enable tracing
1232    #[must_use]
1233    pub const fn with_tracing(mut self, enable_tracing: bool) -> Self {
1234        self.enable_tracing = enable_tracing;
1235        self
1236    }
1237
1238    /// Sets whether to enable steps tracing
1239    #[must_use]
1240    pub const fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self {
1241        self.enable_steps_tracing = enable_steps_tracing;
1242        self
1243    }
1244
1245    /// Sets whether to print `console.log` invocations to stdout.
1246    #[must_use]
1247    pub const fn with_print_logs(mut self, print_logs: bool) -> Self {
1248        self.print_logs = print_logs;
1249        self
1250    }
1251
1252    /// Sets whether to print traces to stdout.
1253    #[must_use]
1254    pub const fn with_print_traces(mut self, print_traces: bool) -> Self {
1255        self.print_traces = print_traces;
1256        self
1257    }
1258
1259    /// Sets whether to enable autoImpersonate
1260    #[must_use]
1261    pub const fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
1262        self.enable_auto_impersonate = enable_auto_impersonate;
1263        self
1264    }
1265
1266    #[must_use]
1267    pub fn with_server_config(mut self, config: ServerConfig) -> Self {
1268        self.server_config = config;
1269        self
1270    }
1271
1272    /// Sets the host the server will listen on
1273    #[must_use]
1274    pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
1275        self.host = if host.is_empty() { vec![IpAddr::V4(Ipv4Addr::LOCALHOST)] } else { host };
1276        self
1277    }
1278
1279    #[must_use]
1280    pub const fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
1281        self.transaction_order = transaction_order;
1282        self
1283    }
1284
1285    /// Returns the ipc path for the ipc endpoint if any
1286    pub fn get_ipc_path(&self) -> Option<String> {
1287        match &self.ipc_path {
1288            Some(path) => path.clone().or_else(|| Some(DEFAULT_IPC_ENDPOINT.to_string())),
1289            None => None,
1290        }
1291    }
1292
1293    /// Prints the config info
1294    pub fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
1295        if let Some(path) = &self.config_out {
1296            let value = self.as_json(fork);
1297            foundry_common::fs::write_sensitive_json_file(path, &value)
1298                .wrap_err("failed writing JSON")?;
1299        }
1300        if !self.silent {
1301            sh_println!("{}", self.as_string(fork))?;
1302            if self.networks.is_celo() {
1303                foundry_common::sh_warn!(
1304                    "CIP-64 uses native fee accounting; feeCurrency is preserved but token fees are not charged"
1305                )?;
1306            }
1307        }
1308        Ok(())
1309    }
1310
1311    /// Returns the endpoint-specific path where the cache file should be stored.
1312    ///
1313    /// See also [`Config::foundry_block_cache_file`].
1314    pub fn block_cache_path(&self, block: u64) -> Option<PathBuf> {
1315        self.block_cache_path_for_rpc(self.protocol_chain_id(), block, self.fork_urls.first()?)
1316    }
1317
1318    fn block_cache_path_for_rpc(
1319        &self,
1320        source_chain_id: u64,
1321        block: u64,
1322        rpc_url: &str,
1323    ) -> Option<PathBuf> {
1324        if self.no_storage_caching || self.fork_urls.is_empty() {
1325            return None;
1326        }
1327
1328        let rpc_url_hash = hex::encode(keccak256(rpc_url));
1329        Some(
1330            Config::foundry_block_cache_file(source_chain_id, block)?
1331                .with_file_name(format!("storage-{rpc_url_hash}.json")),
1332        )
1333    }
1334
1335    /// Sets whether to disable the default create2 deployer
1336    #[must_use]
1337    pub const fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self {
1338        self.disable_default_create2_deployer = yes;
1339        self
1340    }
1341
1342    /// Sets whether to disable pool balance checks
1343    #[must_use]
1344    pub const fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self {
1345        self.disable_pool_balance_checks = yes;
1346        self
1347    }
1348
1349    /// Injects precompiles to `anvil`'s EVM.
1350    #[must_use]
1351    pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self {
1352        self.precompile_factory = Some(Arc::new(factory));
1353        self
1354    }
1355
1356    /// Enable features for provided networks.
1357    #[must_use]
1358    pub const fn with_networks(mut self, networks: NetworkConfigs) -> Self {
1359        self.networks = networks;
1360        self.inferred_fork_network = None;
1361        self.chain_id_network_base = None;
1362        self
1363    }
1364
1365    /// Enable Tempo network features.
1366    #[must_use]
1367    pub fn with_tempo(mut self) -> Self {
1368        self.networks = NetworkConfigs::with_tempo();
1369        self.inferred_fork_network = None;
1370        self.chain_id_network_base = None;
1371        self
1372    }
1373
1374    /// Sets the account used to sponsor Tempo fee-payer requests.
1375    #[must_use]
1376    pub const fn with_tempo_fee_payer(mut self, fee_payer: Option<Address>) -> Self {
1377        self.tempo_fee_payer = fee_payer;
1378        self
1379    }
1380
1381    /// Returns the effective account used to sponsor Tempo fee-payer requests.
1382    ///
1383    /// Defaults to the last dev account so it rarely collides with the sender accounts commonly
1384    /// used in tests, mirroring the dedicated sponsor account of hosted fee payer services.
1385    /// Returns `None` on non-Tempo networks.
1386    pub fn tempo_fee_payer_address(&self) -> Option<Address> {
1387        if !self.networks.is_tempo() {
1388            return None;
1389        }
1390        self.tempo_fee_payer.or_else(|| self.genesis_accounts.last().map(|wallet| wallet.address()))
1391    }
1392
1393    /// Enable Monad network features.
1394    #[cfg(feature = "monad")]
1395    #[must_use]
1396    pub fn with_monad(mut self) -> Self {
1397        self.networks = NetworkConfigs::with_monad();
1398        self.inferred_fork_network = None;
1399        self.chain_id_network_base = None;
1400        self
1401    }
1402
1403    /// Enable Base network features.
1404    #[cfg(feature = "base")]
1405    #[must_use]
1406    pub fn with_base(mut self) -> Self {
1407        self.networks = NetworkConfigs::with_base();
1408        self.inferred_fork_network = None;
1409        self.chain_id_network_base = None;
1410        self
1411    }
1412
1413    /// Sets the Base activation-registry administrator override.
1414    #[cfg(feature = "base")]
1415    #[must_use]
1416    pub const fn with_base_activation_admin(mut self, admin: Option<Address>) -> Self {
1417        self.base_activation_admin = admin;
1418        self
1419    }
1420
1421    /// Enable Optimism network features.
1422    #[cfg(feature = "optimism")]
1423    #[must_use]
1424    pub fn with_optimism(mut self) -> Self {
1425        self.networks = NetworkConfigs::with_optimism();
1426        self.inferred_fork_network = None;
1427        self.chain_id_network_base = None;
1428        self
1429    }
1430
1431    /// Makes the node silent to not emit anything on stdout
1432    #[must_use]
1433    pub const fn silent(self) -> Self {
1434        self.set_silent(true)
1435    }
1436
1437    #[must_use]
1438    pub const fn set_silent(mut self, silent: bool) -> Self {
1439        self.silent = silent;
1440        self
1441    }
1442
1443    /// Sets the path where persisted states are cached (used with `max_persisted_states`).
1444    ///
1445    /// Note: This does not control the fork RPC cache location, which uses endpoint-specific files
1446    /// under `~/.foundry/cache/rpc/<chain>/<block>/`.
1447    #[must_use]
1448    pub fn with_cache_path(mut self, cache_path: Option<PathBuf>) -> Self {
1449        self.cache_path = cache_path;
1450        self
1451    }
1452
1453    /// Sets accounts to fund with custom balances on startup.
1454    #[must_use]
1455    pub fn with_funded_accounts(mut self, accounts: HashMap<Address, U256>) -> Self {
1456        self.funded_accounts = accounts;
1457        self
1458    }
1459
1460    /// Configures everything related to env, backend and database and returns the
1461    /// [Backend](mem::Backend)
1462    ///
1463    /// *Note*: only memory based backend for now
1464    pub(crate) async fn setup<N>(
1465        &mut self,
1466    ) -> Result<(mem::Backend<N>, Option<ForkTransactionReplay>)>
1467    where
1468        N: alloy_network::Network<
1469                TxEnvelope = foundry_primitives::FoundryTxEnvelope,
1470                ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope,
1471            >,
1472    {
1473        // configure the revm environment
1474
1475        let mut cfg = CfgEnv::new();
1476        cfg.set_spec_and_mainnet_gas_params(self.get_hardfork().into());
1477
1478        cfg.chain_id = self.get_chain_id();
1479        cfg.limit_contract_code_size = self.code_size_limit;
1480        // EIP-3607 rejects transactions from senders with deployed code.
1481        // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the
1482        // caller is a contract. So we disable the check by default.
1483        cfg.disable_eip3607 = true;
1484        cfg.disable_block_gas_limit = self.disable_block_gas_limit;
1485
1486        if !self.enable_tx_gas_limit {
1487            cfg.tx_gas_limit_cap = Some(u64::MAX);
1488        }
1489
1490        if let Some(value) = self.memory_limit {
1491            cfg.memory_limit = value;
1492        }
1493
1494        let spec_id = cfg.spec;
1495        let mut evm_env = EvmEnv::new(
1496            cfg,
1497            BlockEnv {
1498                gas_limit: self.gas_limit(),
1499                basefee: self.get_base_fee(),
1500                ..Default::default()
1501            },
1502        );
1503
1504        self.apply_tempo_fork_beneficiary_default(&mut evm_env);
1505
1506        let genesis_timestamp = self.get_genesis_timestamp();
1507        let base_fee_params: BaseFeeParams = self.networks.base_fee_params(genesis_timestamp);
1508
1509        // On Tempo, the base fee follows the chain's hardfork rules instead of EIP-1559.
1510        let tempo_hardfork =
1511            self.networks.is_tempo().then(|| TempoHardfork::from(self.get_hardfork()));
1512
1513        let fees = FeeManager::new(
1514            spec_id,
1515            self.get_base_fee(),
1516            !self.disable_min_priority_fee,
1517            self.get_gas_price(),
1518            self.get_blob_excess_gas_and_price(),
1519            self.get_blob_params(),
1520            base_fee_params,
1521            tempo_hardfork,
1522        );
1523        #[cfg(feature = "optimism")]
1524        if self.networks.is_optimism() {
1525            fees.set_optimism_hardfork(self.get_hardfork().into());
1526        }
1527
1528        let (db, fork, fork_transaction_replay) =
1529            if let Some(eth_rpc_url) = self.fork_urls.first().cloned() {
1530                self.setup_fork_db_with_replay(eth_rpc_url, &mut evm_env, &fees).await?
1531            } else {
1532                let track_history = self.prune_history.is_state_history_supported();
1533                let db: Arc<TokioRwLock<Box<dyn Db>>> =
1534                    Arc::new(TokioRwLock::new(Box::new(StateRootDb::new(track_history))));
1535                (db, None, None)
1536            };
1537
1538        // if provided use all settings of `genesis.json`
1539        if let Some(ref genesis) = self.genesis {
1540            // --chain-id flag gets precedence over the genesis.json chain id
1541            // <https://github.com/foundry-rs/foundry/issues/10059>
1542            if self.chain_id.is_none() && fork.is_none() {
1543                evm_env.cfg_env.chain_id = genesis.config.chain_id;
1544            }
1545            evm_env.block_env.timestamp = U256::from(genesis.timestamp);
1546            if let Some(base_fee) = genesis.base_fee_per_gas {
1547                evm_env.block_env.basefee = base_fee.try_into()?;
1548            }
1549            if let Some(number) = genesis.number {
1550                evm_env.block_env.number = U256::from(number);
1551            }
1552            evm_env.block_env.beneficiary = genesis.coinbase;
1553        }
1554
1555        // Fork setup initializes its own timestamp. For a local chain, keep the initial EVM and
1556        // genesis block on the same resolved timestamp, so that calls against the latest block
1557        // observe the genesis time, and timestamp-activated precompiles and validity windows
1558        // behave the same before and after the first mined block.
1559        if fork.is_none() {
1560            evm_env.block_env.timestamp = U256::from(genesis_timestamp);
1561        }
1562
1563        self.apply_tempo_fork_beneficiary_default(&mut evm_env);
1564
1565        let genesis = GenesisConfig {
1566            number: self.get_genesis_number(),
1567            timestamp: genesis_timestamp,
1568            balance: self.genesis_balance,
1569            accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(),
1570            genesis_init: self.genesis.clone(),
1571        };
1572
1573        let active_hardfork = fork
1574            .as_ref()
1575            .and_then(|fork| fork.config.read().hardfork)
1576            .unwrap_or_else(|| self.get_hardfork());
1577        let mut decoder_builder = CallTraceDecoderBuilder::new()
1578            .with_networks(self.networks)
1579            .with_hardfork(Some(self.networks.executed_hardfork(active_hardfork)));
1580        if self.print_traces {
1581            // if traces should get printed we configure the decoder with the signatures cache
1582            if let Ok(identifier) = SignaturesIdentifier::new(false) {
1583                debug!(target: "node", "using signature identifier");
1584                decoder_builder = decoder_builder.with_signature_identifier(identifier);
1585            }
1586        }
1587
1588        // only memory based backend for now
1589        let backend = mem::Backend::with_genesis(
1590            db,
1591            Arc::new(RwLock::new(evm_env)),
1592            self.networks,
1593            genesis,
1594            fees,
1595            Arc::new(RwLock::new(fork)),
1596            self.enable_steps_tracing,
1597            self.print_logs,
1598            self.print_traces,
1599            Arc::new(decoder_builder.build()),
1600            self.prune_history,
1601            self.max_persisted_states,
1602            self.transaction_block_keeper,
1603            self.block_time,
1604            self.cache_path.clone(),
1605            Arc::new(TokioRwLock::new(self.clone())),
1606        )
1607        .await?;
1608
1609        // Writes the default create2 deployer to the backend,
1610        // if the option is not disabled and we are not forking.
1611        if !self.disable_default_create2_deployer && self.fork_urls.is_empty() {
1612            backend
1613                .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER)
1614                .await
1615                .wrap_err("failed to create default create2 deployer")?;
1616        }
1617
1618        if let Some(fork) = backend.get_fork() {
1619            let config = fork.config.read().clone();
1620            if !self
1621                .fork_urls_match_context(
1622                    &config.fork_urls,
1623                    config.endpoint_identity,
1624                    config.block_number,
1625                    config.block_hash,
1626                )
1627                .await?
1628            {
1629                eyre::bail!("fork endpoint changed while Anvil was being initialized");
1630            }
1631        }
1632        Ok((backend, fork_transaction_replay))
1633    }
1634
1635    /// Configures everything related to forking based on the passed `eth_rpc_url`:
1636    ///  - returning a tuple of a [ForkedDatabase] wrapped in an [Arc] [RwLock](TokioRwLock) and
1637    ///    [ClientFork] wrapped in an [Option] which can be used in a [Backend](mem::Backend) to
1638    ///    fork from.
1639    ///  - modifying some parameters of the passed `env`
1640    ///  - mutating some members of `self`
1641    pub async fn setup_fork_db(
1642        &mut self,
1643        eth_rpc_url: String,
1644        evm_env: &mut EvmEnv,
1645        fees: &FeeManager,
1646    ) -> Result<(Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>)> {
1647        let (db, fork, replay) = self.setup_fork_db_with_replay(eth_rpc_url, evm_env, fees).await?;
1648        eyre::ensure!(replay.is_none(), "transaction-hash fork replay requires full node startup");
1649        Ok((db, fork))
1650    }
1651
1652    async fn setup_fork_db_with_replay(
1653        &mut self,
1654        eth_rpc_url: String,
1655        evm_env: &mut EvmEnv,
1656        fees: &FeeManager,
1657    ) -> Result<(Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>, Option<ForkTransactionReplay>)>
1658    {
1659        let (db, config, replay) =
1660            self.setup_fork_db_config_with_replay(eth_rpc_url, evm_env, fees, None).await?;
1661        if !self.no_bal && !self.no_fork_node_info {
1662            config.prefill_cache(db.inner()).await;
1663        }
1664        let db: Arc<TokioRwLock<Box<dyn Db>>> = Arc::new(TokioRwLock::new(Box::new(db)));
1665        let fork = ClientFork::new(config, Arc::clone(&db));
1666        Ok((db, Some(fork), replay))
1667    }
1668
1669    fn fork_provider(&self, eth_rpc_url: &str) -> Result<RetryProvider> {
1670        ProviderBuilder::new(eth_rpc_url)
1671            .timeout(self.fork_request_timeout)
1672            .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1673            .compute_units_per_second(self.compute_units_per_second)
1674            .max_retry(self.fork_request_retries)
1675            .headers(self.fork_headers.clone())
1676            .build()
1677            .wrap_err("failed to establish provider to fork url")
1678    }
1679
1680    async fn fork_endpoint_identity(
1681        &self,
1682        provider: &RetryProvider,
1683        fallback_execution_chain_id: u64,
1684        source_chain_id_override: Option<u64>,
1685        node_info_probe: &mut AnvilNodeInfoProbe,
1686    ) -> Result<ForkEndpointIdentity> {
1687        let Some(node_info) = node_info_probe.request(provider).await? else {
1688            let source_chain_id = source_chain_id_override.unwrap_or(fallback_execution_chain_id);
1689            let explicit_fallback = self.has_explicit_network_selection().then_some(self.networks);
1690            let network_profile = NetworkConfigs::from_rpc_identity_profile_with_fallback(
1691                source_chain_id,
1692                None,
1693                explicit_fallback,
1694            )
1695            .map_err(eyre::Report::msg)?;
1696            return Ok(ForkEndpointIdentity {
1697                execution_chain_id: fallback_execution_chain_id,
1698                source_chain_id,
1699                network: network_profile.map(|profile| profile.execution_network()),
1700                network_profile,
1701                hardfork: None,
1702                instance_id: None,
1703                source_fork_block_number: None,
1704                source_fork_block_hash: None,
1705            });
1706        };
1707
1708        let (
1709            execution_chain_id,
1710            source_chain_id,
1711            instance_id,
1712            source_fork_block_number,
1713            source_fork_block_hash,
1714        ) = match tokio::time::timeout(
1715            FORK_IDENTITY_PROBE_TIMEOUT,
1716            provider.raw_request::<_, Metadata>("anvil_metadata".into(), ()),
1717        )
1718        .await
1719        {
1720            Ok(Ok(metadata)) => (
1721                metadata.chain_id,
1722                source_chain_id_override.unwrap_or_else(|| {
1723                    metadata.forked_network.map(|fork| fork.chain_id).unwrap_or(metadata.chain_id)
1724                }),
1725                Some(metadata.instance_id),
1726                metadata.forked_network.map(|fork| fork.fork_block_number),
1727                metadata.forked_network.map(|fork| fork.fork_block_hash),
1728            ),
1729            response => {
1730                if let Ok(Err(error)) = response
1731                    && !is_rpc_method_not_found(&error)
1732                {
1733                    return Err(error).wrap_err("failed to retrieve Anvil fork source identity");
1734                }
1735                (
1736                    fallback_execution_chain_id,
1737                    source_chain_id_override.unwrap_or(fallback_execution_chain_id),
1738                    None,
1739                    None,
1740                    None,
1741                )
1742            }
1743        };
1744        let identity_chain_id =
1745            if node_info.network.is_some() { execution_chain_id } else { source_chain_id };
1746        let explicit_fallback = self.has_explicit_network_selection().then_some(self.networks);
1747        let network_profile = NetworkConfigs::from_rpc_identity_profile_with_fallback(
1748            identity_chain_id,
1749            Some(node_info.network.as_deref()),
1750            explicit_fallback,
1751        )
1752        .map_err(eyre::Report::msg)?
1753        .ok_or_else(|| eyre::eyre!("Anvil metadata did not identify an execution profile"))?;
1754        let network = network_profile.execution_network();
1755        let hardfork = network
1756            .parse_hardfork(&node_info.hard_fork)
1757            .map_err(eyre::Report::msg)
1758            .wrap_err_with(|| {
1759                format!("unsupported hardfork `{}` reported for `{network}`", node_info.hard_fork)
1760            })?;
1761
1762        Ok(ForkEndpointIdentity {
1763            execution_chain_id,
1764            source_chain_id,
1765            network: Some(network),
1766            network_profile: Some(network_profile),
1767            hardfork: Some(hardfork),
1768            instance_id,
1769            source_fork_block_number,
1770            source_fork_block_hash,
1771        })
1772    }
1773
1774    async fn resolved_fork_endpoint_identity(
1775        &self,
1776        provider: &RetryProvider,
1777        node_info_probe: &mut AnvilNodeInfoProbe,
1778    ) -> Result<ForkEndpointIdentity> {
1779        let identity = if let Some(chain_id) = self.fork_chain_id {
1780            let chain_id = chain_id.to();
1781            // `fork_chain_id` avoids depending on `eth_chainId`, but an online Anvil endpoint can
1782            // still expose authoritative family, hardfork, and instance metadata. Probe it so
1783            // mirror validation and reset staging cannot mistake this node for its own upstream.
1784            self.fork_endpoint_identity(provider, chain_id, Some(chain_id), node_info_probe).await
1785        } else {
1786            let execution_chain_id =
1787                provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?;
1788            self.fork_endpoint_identity(provider, execution_chain_id, None, node_info_probe).await
1789        }?;
1790        ensure_fork_network_supported(identity.source_chain_id)?;
1791        Ok(identity)
1792    }
1793
1794    pub(crate) async fn replacement_fork_provider(
1795        &self,
1796        eth_rpc_url: &str,
1797        expected: ForkEndpointIdentity,
1798        block_number: u64,
1799        block_hash: B256,
1800        serving_instance_id: B256,
1801    ) -> Result<(Arc<RetryProvider>, ForkEndpointIdentity)> {
1802        let provider = Arc::new(self.fork_provider(eth_rpc_url)?);
1803        let mut node_info_probe = self.node_info_probe(false);
1804        for _ in 0..3 {
1805            let before =
1806                self.resolved_fork_endpoint_identity(&provider, &mut node_info_probe).await?;
1807            eyre::ensure!(
1808                before.instance_id != Some(serving_instance_id),
1809                "cannot set Anvil's fork provider to its own RPC endpoint"
1810            );
1811            let block = provider
1812                .get_block(BlockId::number(block_number))
1813                .await
1814                .wrap_err("failed to confirm active fork block on replacement endpoint")?;
1815            let after =
1816                self.resolved_fork_endpoint_identity(&provider, &mut node_info_probe).await?;
1817            if before != after {
1818                continue;
1819            }
1820            if !before.context_eq(expected) {
1821                eyre::bail!("replacement fork endpoint has an incompatible execution context");
1822            }
1823            let actual_hash = block.map(|block| block.header.hash);
1824            if actual_hash != Some(block_hash) {
1825                eyre::bail!(
1826                    "replacement fork endpoint does not contain active fork block {block_number} with hash {block_hash}"
1827                );
1828            }
1829            return Ok((provider, before));
1830        }
1831        eyre::bail!(
1832            "fork endpoint changed while its identity and active fork block were being resolved"
1833        );
1834    }
1835
1836    async fn stable_fork_snapshot(
1837        &self,
1838        provider: &Arc<RetryProvider>,
1839        fork_overrides: ForkOverrides,
1840    ) -> Result<StableForkSnapshot> {
1841        let mut node_info_probe = self.node_info_probe(self.fork_endpoint_is_anvil);
1842        for _ in 0..3 {
1843            let before =
1844                self.resolved_fork_endpoint_identity(provider, &mut node_info_probe).await?;
1845            let (block_number, transaction_replay) = if let Some(fork_choice) = &self.fork_choice {
1846                derive_block_and_replay(fork_choice, provider).await.wrap_err(
1847                    "failed to derive fork block and transaction replay from fork choice",
1848                )?
1849            } else {
1850                (
1851                    find_latest_fork_block(provider)
1852                        .await
1853                        .wrap_err("failed to get fork block number")?,
1854                    None,
1855                )
1856            };
1857            let block = provider
1858                .get_block(BlockId::number(block_number))
1859                .await
1860                .wrap_err("failed to get fork block")?;
1861            let gas_price = if let Some(gas_price) = fork_overrides.gas_price {
1862                gas_price
1863            } else {
1864                provider.get_gas_price().await.unwrap_or(INITIAL_GAS_PRICE)
1865            };
1866            let after =
1867                self.resolved_fork_endpoint_identity(provider, &mut node_info_probe).await?;
1868            if before == after {
1869                return Ok(StableForkSnapshot {
1870                    endpoint_identity: before,
1871                    state_is_mutable: node_info_probe.identified || node_info_probe.inconclusive,
1872                    block_number,
1873                    transaction_replay,
1874                    block,
1875                    gas_price,
1876                });
1877            }
1878        }
1879        eyre::bail!(
1880            "fork endpoint changed while its identity and block context were being resolved"
1881        );
1882    }
1883
1884    pub(crate) async fn fork_context_matches(
1885        &self,
1886        eth_rpc_url: &str,
1887        expected: ForkEndpointIdentity,
1888        block_number: u64,
1889        block_hash: B256,
1890    ) -> Result<bool> {
1891        let provider = self.fork_provider(eth_rpc_url)?;
1892        let mut node_info_probe = self.node_info_probe(expected.is_authoritative());
1893        for _ in 0..3 {
1894            let before =
1895                self.resolved_fork_endpoint_identity(&provider, &mut node_info_probe).await?;
1896            let block = provider
1897                .get_block(BlockId::number(block_number))
1898                .await
1899                .wrap_err("failed to confirm fork block context")?;
1900            let after =
1901                self.resolved_fork_endpoint_identity(&provider, &mut node_info_probe).await?;
1902            if before != after {
1903                continue;
1904            }
1905            if before.source_chain_id != expected.source_chain_id {
1906                eyre::bail!(
1907                    "fork endpoints must use the same chain ID: expected {}, got {} from {}",
1908                    expected.source_chain_id,
1909                    before.source_chain_id,
1910                    redact_url(eth_rpc_url)
1911                );
1912            }
1913            return Ok(
1914                before == expected && block.is_some_and(|block| block.header.hash == block_hash)
1915            );
1916        }
1917        Ok(false)
1918    }
1919
1920    pub(crate) async fn fork_urls_match_context(
1921        &self,
1922        fork_urls: &[String],
1923        expected: ForkEndpointIdentity,
1924        block_number: u64,
1925        block_hash: B256,
1926    ) -> Result<bool> {
1927        for eth_rpc_url in Self::fork_urls_requiring_revalidation(fork_urls, expected) {
1928            if !self.fork_context_matches(eth_rpc_url, expected, block_number, block_hash).await? {
1929                return Ok(false);
1930            }
1931        }
1932        Ok(true)
1933    }
1934
1935    const fn fork_urls_requiring_revalidation(
1936        fork_urls: &[String],
1937        endpoint_identity: ForkEndpointIdentity,
1938    ) -> &[String] {
1939        if endpoint_identity.is_authoritative() || fork_urls.len() > 1 { fork_urls } else { &[] }
1940    }
1941
1942    pub(crate) fn has_explicit_network_selection(&self) -> bool {
1943        let effective_network =
1944            self.networks.resolved_network().unwrap_or(NetworkVariant::Ethereum);
1945        self.networks.has_network_selection()
1946            && self.chain_id_network_base.is_none()
1947            && self.inferred_fork_network != Some(effective_network)
1948    }
1949
1950    const fn requires_primary_fork_revalidation(
1951        &self,
1952        endpoint_identity: ForkEndpointIdentity,
1953    ) -> bool {
1954        endpoint_identity.is_authoritative() || self.fork_urls.len() > 1
1955    }
1956
1957    /// Configures everything related to forking based on the passed `eth_rpc_url`:
1958    ///  - returning a tuple of a [ForkedDatabase] and [ClientForkConfig] which can be used to build
1959    ///    a [ClientFork] to fork from.
1960    ///  - modifying some parameters of the passed `env`
1961    ///  - mutating some members of `self`
1962    pub async fn setup_fork_db_config(
1963        &mut self,
1964        eth_rpc_url: String,
1965        evm_env: &mut EvmEnv,
1966        fees: &FeeManager,
1967    ) -> Result<(ForkedDatabase<AnyNetwork>, ClientForkConfig)> {
1968        let (db, config, replay) =
1969            self.setup_fork_db_config_with_replay(eth_rpc_url, evm_env, fees, None).await?;
1970        eyre::ensure!(replay.is_none(), "transaction-hash fork replay requires full node startup");
1971        if !self.no_bal && !self.no_fork_node_info {
1972            config.prefill_cache(db.inner()).await;
1973        }
1974        Ok((db, config))
1975    }
1976
1977    /// Configures a replacement fork while preserving the already-instantiated execution profile.
1978    pub(crate) async fn setup_fork_db_config_for_reset(
1979        &mut self,
1980        eth_rpc_url: String,
1981        evm_env: &mut EvmEnv,
1982        fees: &FeeManager,
1983        execution_profile: NetworkConfigs,
1984    ) -> Result<(ForkedDatabase<AnyNetwork>, ClientForkConfig)> {
1985        let (db, config, replay) = self
1986            .setup_fork_db_config_with_replay(eth_rpc_url, evm_env, fees, Some(execution_profile))
1987            .await?;
1988        eyre::ensure!(replay.is_none(), "transaction-hash fork replay requires full node startup");
1989        Ok((db, config))
1990    }
1991
1992    pub(crate) async fn setup_fork_db_config_with_replay(
1993        &mut self,
1994        eth_rpc_url: String,
1995        evm_env: &mut EvmEnv,
1996        fees: &FeeManager,
1997        fixed_execution_profile: Option<NetworkConfigs>,
1998    ) -> Result<(ForkedDatabase<AnyNetwork>, ClientForkConfig, Option<ForkTransactionReplay>)> {
1999        debug!(target: "node", eth_rpc_url=%redact_url(&eth_rpc_url), "setting up fork db");
2000        if self.fork_chain_id.is_some() {
2001            eyre::ensure!(
2002                self.fork_urls.len() == 1,
2003                "multiple fork URLs cannot be validated with --fork-chain-id; remove \
2004                 --fork-chain-id to validate every endpoint"
2005            );
2006        }
2007        let fork_overrides = *self.fork_overrides.get_or_insert(ForkOverrides {
2008            gas_limit: self.gas_limit,
2009            gas_price: self.gas_price,
2010            base_fee: self.base_fee,
2011        });
2012
2013        // Always bootstrap with the primary URL only to avoid race conditions
2014        // where discovery calls (get_chain_id, find_latest_fork_block, get_block)
2015        // hit different endpoints that may be at different chain tips.
2016        let provider = Arc::new(self.fork_provider(&eth_rpc_url)?);
2017
2018        // Resolve identity, block, and fee data as one stable snapshot. An upstream Anvil can
2019        // reset between any two RPC calls, so verify the endpoint identity on both sides.
2020        let StableForkSnapshot {
2021            endpoint_identity: fork_identity,
2022            state_is_mutable,
2023            block_number: fork_block_number,
2024            transaction_replay: fork_transaction_replay,
2025            block,
2026            gas_price,
2027        } = self.stable_fork_snapshot(&provider, fork_overrides).await?;
2028        self.fork_endpoint_is_anvil = fork_identity.is_authoritative();
2029
2030        let target_network = fork_identity.network.unwrap_or(NetworkVariant::Ethereum);
2031        let target_profile = fork_identity.network_profile.unwrap_or_default();
2032        if let Some(execution_profile) = fixed_execution_profile {
2033            if !self.has_explicit_network_selection() {
2034                eyre::ensure!(
2035                    execution_profile.supports_fork_source(&target_profile),
2036                    "cannot reset Anvil across network families ({} -> {}); start a new instance \
2037                     with matching network configuration",
2038                    execution_profile.execution_profile_name(),
2039                    target_profile.execution_profile_name()
2040                );
2041            }
2042            self.networks = execution_profile;
2043        } else if self.inferred_fork_network.is_some() {
2044            eyre::ensure!(
2045                self.networks.supports_fork_source(&target_profile),
2046                "cannot reset Anvil across network families ({} -> {}); start a new instance \
2047                 with matching network configuration",
2048                self.networks.execution_profile_name(),
2049                target_profile.execution_profile_name()
2050            );
2051        }
2052        let source_chain_id = fork_identity.source_chain_id;
2053        self.fork_source_chain_id = Some(source_chain_id);
2054        self.fork_execution_chain_id = Some(fork_identity.execution_chain_id);
2055        if fixed_execution_profile.is_none() && !self.has_explicit_network_selection() {
2056            self.networks = self.networks.with_rpc_profile(target_profile);
2057            self.inferred_fork_network = Some(target_network);
2058            self.chain_id_network_base = None;
2059        }
2060
2061        let block = if let Some(block) = block {
2062            block
2063        } else {
2064            if let Ok(latest_block) = provider.get_block_number().await {
2065                let mut message = format!(
2066                    "Failed to get block for block number: {fork_block_number}\n\
2067latest block number: {latest_block}"
2068                );
2069                // If the `eth_getBlockByNumber` call succeeds, but returns null instead of
2070                // the block, and the block number is less than equal the latest block, then
2071                // the user is forking from a non-archive node with an older block number.
2072                if fork_block_number <= latest_block {
2073                    message.push_str(&format!("\n{NON_ARCHIVE_NODE_WARNING}"));
2074                }
2075                eyre::bail!("{message}");
2076            }
2077            eyre::bail!("failed to get block for block number: {fork_block_number}");
2078        };
2079
2080        if let Some(replay) = &fork_transaction_replay {
2081            let source_header = replay.source_block.header();
2082            eyre::ensure!(
2083                block.header.hash == source_header.parent_hash,
2084                "fork transaction block {} at {} has parent {}, but fetched fork block at {} has \
2085                 hash {}",
2086                source_header.hash,
2087                source_header.number,
2088                source_header.parent_hash,
2089                block.header.number,
2090                block.header.hash
2091            );
2092            eyre::ensure!(
2093                block.header.number.checked_add(1) == Some(source_header.number),
2094                "fork transaction block {} has number {}, but fetched parent {} has number {}",
2095                source_header.hash,
2096                source_header.number,
2097                block.header.hash,
2098                block.header.number
2099            );
2100        }
2101
2102        let gas_limit = self.fork_gas_limit_with_override(&block, fork_overrides.gas_limit);
2103        self.gas_limit = Some(gas_limit);
2104
2105        // Cache identity must describe the remote fork block, not local execution overrides that
2106        // can change after mining (for example, the locally advanced base fee).
2107        let cache_block_env: BlockEnv = block_env_from_header(&block.header);
2108
2109        evm_env.block_env = BlockEnv {
2110            gas_limit,
2111            // Preserve configured local overrides while replacing fork-derived block values.
2112            beneficiary: evm_env.block_env.beneficiary,
2113            basefee: fork_overrides
2114                .base_fee
2115                .or_else(|| block.header.base_fee_per_gas())
2116                .unwrap_or_default(),
2117            ..block_env_from_header(&block.header)
2118        };
2119
2120        let override_chain_id = self.chain_id;
2121        let execution_chain_id = override_chain_id.unwrap_or(fork_identity.execution_chain_id);
2122        if override_chain_id.is_none() {
2123            // Sign locally produced transactions for the chain ID exposed by the endpoint
2124            // without turning the inferred value into an explicit execution override.
2125            self.update_wallet_chain_id(fork_identity.execution_chain_id);
2126        }
2127        evm_env.cfg_env.chain_id = execution_chain_id;
2128
2129        // Resolve the fork block's hardfork without materializing it into `self.hardfork`.
2130        // That field represents the user's explicit override; keeping inference on the fork
2131        // config lets a later reset re-resolve timestamp-based activations.
2132        let effective_network =
2133            self.networks.resolved_network().unwrap_or(NetworkVariant::Ethereum);
2134        let endpoint_hardfork = fork_identity
2135            .hardfork
2136            .filter(|hardfork| hardfork.namespace() == effective_network.hardfork_namespace());
2137        let inferred_hardfork = endpoint_hardfork.or_else(|| {
2138            effective_network.historical_hardfork(source_chain_id, block.header.timestamp())
2139        });
2140        let source_protocol_hardfork = fork_identity.hardfork.or_else(|| {
2141            source_hardfork(effective_network, source_chain_id, block.header.timestamp())
2142        });
2143        let source_may_omit_blob_fields = source_protocol_hardfork
2144            .map_or(self.hardfork.is_some(), |hardfork| SpecId::from(hardfork) < SpecId::CANCUN);
2145        let fork_hardfork = self.hardfork.or(inferred_hardfork);
2146        let effective_hardfork = fork_hardfork.unwrap_or_else(|| self.get_hardfork());
2147        let effective_spec = SpecId::from(effective_hardfork);
2148        evm_env.cfg_env.set_spec_and_mainnet_gas_params(effective_spec);
2149        fees.set_execution_rules(
2150            effective_spec,
2151            self.networks.base_fee_params(block.header.timestamp()),
2152            self.networks.is_tempo().then(|| TempoHardfork::from(effective_hardfork)),
2153        );
2154        #[cfg(feature = "optimism")]
2155        if self.networks.is_optimism() {
2156            fees.set_optimism_base_fee_rules(block.header.extra_data());
2157        }
2158
2159        // if not set explicitly we use the base fee of the latest block
2160        self.base_fee = fork_overrides.base_fee.or_else(|| block.header.base_fee_per_gas());
2161        if let Some(base_fee) = fork_overrides.base_fee {
2162            fees.set_base_fee(base_fee);
2163        } else if let Some(base_fee) = block.header.base_fee_per_gas() {
2164            // This is the base fee of the current block, but we need the base fee of the next
2165            // block.
2166            fees.set_base_fee(base_fee);
2167            let next_block_base_fee = fees.get_next_block_base_fee_from_header(&block.header);
2168            fees.set_base_fee(next_block_base_fee);
2169        } else {
2170            fees.set_base_fee(self.get_base_fee());
2171        }
2172
2173        // Blob rules and fee state belong to the selected fork context even when the target block
2174        // predates Cancun. Always replace both so a reset cannot retain the previous fork's
2175        // schedule or excess gas.
2176        let blob_params = get_blob_params(source_chain_id, block.header.timestamp());
2177        fees.set_blob_params(blob_params);
2178        let blob_update_fraction = blob_params.update_fraction as u64;
2179        let blob_excess_gas = block.header.excess_blob_gas().or_else(|| {
2180            // Pre-Cancun headers, Polygon Bor headers, and Arbitrum Nitro headers omit the blob
2181            // fields. REVM still requires a valid blob environment when executing with the Cancun
2182            // spec; zero is the neutral excess-gas value. On Nitro this makes `BLOBBASEFEE` return
2183            // `1`, although Nitro rejects the opcode; matching that requires Arbitrum-specific EVM
2184            // handling.
2185            (effective_spec >= SpecId::CANCUN
2186                && ((source_may_omit_blob_fields && block.header.blob_gas_used().is_none())
2187                    || Chain::from_id(source_chain_id).is_polygon()
2188                    || Chain::from_id(source_chain_id).is_arbitrum()))
2189            .then_some(0)
2190        });
2191        evm_env.block_env.blob_excess_gas_and_price =
2192            blob_excess_gas.map(|excess| BlobExcessGasAndPrice::new(excess, blob_update_fraction));
2193        let next_block_blob_excess_gas = blob_excess_gas.map_or(0, |excess| {
2194            self.networks.next_block_blob_excess_gas(
2195                blob_params,
2196                excess,
2197                block.header.blob_gas_used().unwrap_or_default(),
2198                block.header.base_fee_per_gas().unwrap_or_default(),
2199            )
2200        });
2201        fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
2202            next_block_blob_excess_gas,
2203            blob_update_fraction,
2204        ));
2205
2206        // Use the gas price captured in the stable endpoint snapshot.
2207        self.gas_price = Some(gas_price);
2208        fees.set_gas_price(gas_price);
2209
2210        let block_hash = block.header.hash;
2211
2212        // Apply changes such as difficulty -> prevrandao for the remote source chain.
2213        apply_chain_and_block_specific_env_changes_for_chain::<AnyNetwork, _, _>(
2214            evm_env,
2215            &block,
2216            source_chain_id,
2217            self.networks,
2218        );
2219
2220        for mirror_url in self.fork_urls.iter().skip(1) {
2221            if !self
2222                .fork_context_matches(mirror_url, fork_identity, fork_block_number, block_hash)
2223                .await?
2224            {
2225                eyre::bail!(
2226                    "fork fallback endpoint `{}` does not expose the primary endpoint's execution \
2227                     and block context",
2228                    redact_url(mirror_url)
2229                );
2230            }
2231        }
2232        if self.requires_primary_fork_revalidation(fork_identity)
2233            && !self
2234                .fork_context_matches(&eth_rpc_url, fork_identity, fork_block_number, block_hash)
2235                .await?
2236        {
2237            eyre::bail!("primary fork endpoint changed while its context was being validated");
2238        }
2239
2240        let source_id = fork_source_id(&self.fork_urls, &self.fork_headers);
2241        let account_fetch_policy = account_fetch_policy_for_source(source_chain_id, target_profile);
2242        let meta = BlockchainDbMeta::new(cache_block_env, eth_rpc_url.clone())
2243            .with_fork_identity(block_hash, source_id)
2244            .with_account_fetch_policy(account_fetch_policy);
2245        let cache_path =
2246            self.block_cache_path_for_rpc(source_chain_id, fork_block_number, &eth_rpc_url);
2247        let block_chain_db = BlockchainDb::new(meta, cache_path);
2248
2249        // After bootstrap, rebuild the provider with round-robin if multiple URLs are
2250        // configured. This ensures bootstrap used only the primary endpoint for consistency,
2251        // while ongoing requests are distributed across all endpoints.
2252        let provider = if self.fork_urls.len() > 1 {
2253            let urls = self.fork_urls.iter().map(|url| redact_url(url)).collect::<Vec<_>>();
2254            debug!(target: "node", ?urls, "using multi-endpoint round-robin provider");
2255            Arc::new(
2256                ProviderBuilder::new(&eth_rpc_url)
2257                    .timeout(self.fork_request_timeout)
2258                    .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
2259                    .compute_units_per_second(self.compute_units_per_second)
2260                    .max_retry(self.fork_request_retries)
2261                    .headers(self.fork_headers.clone())
2262                    .build_fallback(self.fork_urls.clone())
2263                    .wrap_err("failed to establish round-robin provider to fork urls")?,
2264            )
2265        } else {
2266            provider
2267        };
2268
2269        // This will spawn the background thread that will use the provider to fetch
2270        // blockchain data from the other client
2271        let anchor = ForkBlock::with_rpc_number(
2272            evm_env.block_env.number.saturating_to(),
2273            fork_block_number,
2274            block_hash,
2275        );
2276        let (backend, handler) = if self.fork_state_by_number {
2277            SharedBackend::new_with_anchor_by_number(
2278                Arc::clone(&provider),
2279                block_chain_db.clone(),
2280                anchor,
2281            )?
2282        } else {
2283            SharedBackend::new_with_anchor(Arc::clone(&provider), block_chain_db.clone(), anchor)?
2284        };
2285        // The handler gets its own thread and runtime: `SharedBackend` reads block the calling
2286        // thread via `block_in_place`, so under enough concurrent reads they can occupy every
2287        // thread of the node's runtime and leave none to poll the handler they wait on.
2288        std::thread::Builder::new().name("fork-backend".into()).spawn(move || {
2289            tokio::runtime::Builder::new_current_thread()
2290                .enable_all()
2291                .build()
2292                .expect("failed to build fork backend runtime")
2293                .block_on(handler)
2294        })?;
2295
2296        let config = ClientForkConfig {
2297            fork_urls: self.fork_urls.clone(),
2298            block_number: fork_block_number,
2299            evm_block_number: evm_env.block_env.number.saturating_to(),
2300            block_hash,
2301            transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()),
2302            provider,
2303            chain_id: source_chain_id,
2304            execution_chain_id,
2305            override_chain_id,
2306            fork_chain_id: self.fork_chain_id.map(|chain_id| chain_id.to()),
2307            hardfork: Some(effective_hardfork),
2308            endpoint_identity: fork_identity,
2309            state_is_mutable,
2310            timestamp: block.header.timestamp(),
2311            base_fee: block.header.base_fee_per_gas().map(|g| g as u128),
2312            timeout: self.fork_request_timeout,
2313            retries: self.fork_request_retries,
2314            backoff: self.fork_retry_backoff,
2315            compute_units_per_second: self.compute_units_per_second,
2316            headers: self.fork_headers.clone(),
2317            total_difficulty: block.header.total_difficulty.unwrap_or_default(),
2318            blob_gas_used: block.header.blob_gas_used().map(|g| g as u128),
2319            blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price,
2320        };
2321
2322        debug!(target: "node", fork_number=config.block_number, fork_hash=%config.block_hash, "set up fork db");
2323
2324        let mut db = ForkedDatabase::new(backend, block_chain_db);
2325
2326        // need to insert the forked block's hash
2327        db.insert_block_hash(U256::from(config.block_number), config.block_hash);
2328
2329        Ok((db, config, fork_transaction_replay))
2330    }
2331
2332    /// we only use the gas limit value of the block if it is non-zero and the block gas
2333    /// limit is enabled, since there are networks where this is not used and is always
2334    /// `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also <https://github.com/foundry-rs/foundry/issues/3247>
2335    fn fork_gas_limit_with_override<B: BlockResponse<Header: BlockHeader>>(
2336        &self,
2337        block: &B,
2338        gas_limit: Option<u64>,
2339    ) -> u64 {
2340        if !self.disable_block_gas_limit {
2341            if let Some(gas_limit) = gas_limit {
2342                return gas_limit;
2343            } else if block.header().gas_limit() > 0 {
2344                return block.header().gas_limit();
2345            }
2346        }
2347
2348        u64::MAX
2349    }
2350
2351    /// Restores user-provided gas settings after leaving fork mode.
2352    pub(crate) const fn restore_fork_overrides(&mut self) {
2353        if let Some(overrides) = self.fork_overrides {
2354            self.gas_limit = overrides.gas_limit;
2355            self.gas_price = overrides.gas_price;
2356            self.base_fee = overrides.base_fee;
2357        }
2358    }
2359
2360    /// Returns the gas limit for a non forked anvil instance
2361    ///
2362    /// Checks the config for the `disable_block_gas_limit` flag
2363    pub(crate) fn gas_limit(&self) -> u64 {
2364        if self.disable_block_gas_limit {
2365            return u64::MAX;
2366        }
2367
2368        self.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT)
2369    }
2370}
2371
2372pub(crate) const fn tempo_default_base_fee(hardfork: TempoHardfork) -> u64 {
2373    if hardfork.is_t1() { TEMPO_T1_BASE_FEE } else { TEMPO_T0_BASE_FEE }
2374}
2375
2376/// If the fork choice is a block number, simply return it with an empty list of transactions.
2377/// If the fork choice is a transaction hash, determine the block that the transaction was mined in,
2378/// and return the block number before the fork block along with all transactions in the fork block
2379/// that are before (and including) the fork transaction.
2380async fn derive_block_and_replay(
2381    fork_choice: &ForkChoice,
2382    provider: &Arc<RetryProvider>,
2383) -> eyre::Result<(BlockNumber, Option<ForkTransactionReplay>)> {
2384    match fork_choice {
2385        ForkChoice::Block(block_number) => {
2386            let block_number = *block_number;
2387            if block_number >= 0 {
2388                return Ok((block_number as u64, None));
2389            }
2390            // subtract from latest block number
2391            let latest = provider.get_block_number().await?;
2392
2393            Ok((block_number.saturating_add(latest as i128) as u64, None))
2394        }
2395        ForkChoice::Transaction(transaction_hash) => {
2396            // Determine the block that this transaction was mined in
2397            let transaction = provider
2398                .get_transaction_by_hash(transaction_hash.0.into())
2399                .await?
2400                .ok_or_else(|| eyre::eyre!("fork transaction {transaction_hash} was not found"))?;
2401            let transaction_block_number = transaction.block_number().ok_or_else(|| {
2402                eyre::eyre!("fork transaction {transaction_hash} is not mined (no block number)")
2403            })?;
2404            let transaction_block_hash = transaction.block_hash().ok_or_else(|| {
2405                eyre::eyre!("fork transaction {transaction_hash} is not mined (no block hash)")
2406            })?;
2407
2408            // Get the block pertaining to the fork transaction.
2409            let transaction_block =
2410                provider.get_block_by_hash(transaction_block_hash).full().await?.ok_or_else(
2411                    || {
2412                        eyre::eyre!(
2413                            "failed to get fork block {transaction_block_hash} for transaction \
2414                         {transaction_hash}"
2415                        )
2416                    },
2417                )?;
2418            let replay = validate_fork_transaction_replay(
2419                *transaction_hash,
2420                &transaction,
2421                transaction_block,
2422            )?;
2423            Ok((transaction_block_number.saturating_sub(1), Some(replay)))
2424        }
2425    }
2426}
2427
2428fn validate_fork_transaction_replay(
2429    transaction_hash: TxHash,
2430    transaction: &alloy_network::AnyRpcTransaction,
2431    source_block: AnyRpcBlock,
2432) -> eyre::Result<ForkTransactionReplay> {
2433    let source_hash = source_block.header.hash;
2434    let source_number = source_block.header.number;
2435    let transaction_block_hash = transaction.block_hash().ok_or_else(|| {
2436        eyre::eyre!("fork transaction {transaction_hash} is not mined (no block hash)")
2437    })?;
2438    let transaction_block_number = transaction.block_number().ok_or_else(|| {
2439        eyre::eyre!("fork transaction {transaction_hash} is not mined (no block number)")
2440    })?;
2441
2442    eyre::ensure!(
2443        source_hash == transaction_block_hash,
2444        "fork transaction {transaction_hash} reports block {transaction_block_hash}, but fetched \
2445         block hash is {source_hash}"
2446    );
2447    eyre::ensure!(
2448        source_number == transaction_block_number,
2449        "fork transaction {transaction_hash} reports block number {transaction_block_number}, but \
2450         fetched block {source_hash} has number {source_number}"
2451    );
2452    eyre::ensure!(
2453        source_number > 0,
2454        "fork transaction {transaction_hash} is in genesis block {source_hash}, which has no parent"
2455    );
2456
2457    let transactions = source_block.transactions.as_transactions().ok_or_else(|| {
2458        eyre::eyre!("fork block {source_hash} at {source_number} did not include full transactions")
2459    })?;
2460    let mut matches =
2461        transactions.iter().enumerate().filter(|(_, tx)| tx.tx_hash() == transaction_hash);
2462    let target_index = matches.next().map(|(index, _)| index).ok_or_else(|| {
2463        eyre::eyre!(
2464            "fork transaction {transaction_hash} is absent from block {source_hash} at \
2465             {source_number}"
2466        )
2467    })?;
2468    eyre::ensure!(
2469        matches.next().is_none(),
2470        "fork transaction {transaction_hash} occurs more than once in block {source_hash} at \
2471         {source_number}"
2472    );
2473    if let Some(reported_index) = transaction.transaction_index() {
2474        eyre::ensure!(
2475            reported_index == target_index as u64,
2476            "fork transaction {transaction_hash} reports index {reported_index}, but occurs at \
2477             index {target_index} in block {source_hash}"
2478        );
2479    }
2480
2481    Ok(ForkTransactionReplay { source_block, target_index })
2482}
2483
2484/// Fork delimiter used to specify which block or transaction to fork from.
2485#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2486pub enum ForkChoice {
2487    /// Block number to fork from.
2488    ///
2489    /// If negative, the given value is subtracted from the `latest` block number.
2490    Block(i128),
2491    /// Transaction hash to fork from.
2492    Transaction(TxHash),
2493}
2494
2495impl ForkChoice {
2496    /// Returns the block number to fork from
2497    pub const fn block_number(&self) -> Option<i128> {
2498        match self {
2499            Self::Block(block_number) => Some(*block_number),
2500            Self::Transaction(_) => None,
2501        }
2502    }
2503
2504    /// Returns the transaction hash to fork from
2505    pub const fn transaction_hash(&self) -> Option<TxHash> {
2506        match self {
2507            Self::Block(_) => None,
2508            Self::Transaction(transaction_hash) => Some(*transaction_hash),
2509        }
2510    }
2511}
2512
2513/// Convert a transaction hash into a ForkChoice
2514impl From<TxHash> for ForkChoice {
2515    fn from(tx_hash: TxHash) -> Self {
2516        Self::Transaction(tx_hash)
2517    }
2518}
2519
2520/// Convert a decimal block number into a ForkChoice
2521impl From<u64> for ForkChoice {
2522    fn from(block: u64) -> Self {
2523        Self::Block(block as i128)
2524    }
2525}
2526
2527#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2528pub struct PruneStateHistoryConfig {
2529    pub enabled: bool,
2530    pub max_memory_history: Option<usize>,
2531}
2532
2533impl PruneStateHistoryConfig {
2534    /// Returns `true` if writing state history is supported
2535    pub const fn is_state_history_supported(&self) -> bool {
2536        if !self.enabled {
2537            return true;
2538        }
2539
2540        match self.max_memory_history {
2541            Some(limit) => limit > 0,
2542            None => false,
2543        }
2544    }
2545
2546    /// Returns true if this setting was enabled.
2547    pub const fn is_config_enabled(&self) -> bool {
2548        self.enabled
2549    }
2550
2551    pub fn from_args(val: Option<Option<usize>>) -> Self {
2552        val.map(|max_memory_history| Self {
2553            enabled: true,
2554            max_memory_history: max_memory_history.filter(|limit| *limit > 0),
2555        })
2556        .unwrap_or_default()
2557    }
2558}
2559
2560/// Can create dev accounts
2561#[derive(Clone, Debug)]
2562pub struct AccountGenerator {
2563    chain_id: u64,
2564    amount: usize,
2565    phrase: String,
2566    derivation_path: Option<String>,
2567}
2568
2569impl AccountGenerator {
2570    pub fn new(amount: usize) -> Self {
2571        Self {
2572            chain_id: CHAIN_ID,
2573            amount,
2574            phrase: Mnemonic::<English>::new(&mut thread_rng()).to_phrase(),
2575            derivation_path: None,
2576        }
2577    }
2578
2579    #[must_use]
2580    pub fn phrase(mut self, phrase: impl Into<String>) -> Self {
2581        self.phrase = phrase.into();
2582        self
2583    }
2584
2585    fn get_phrase(&self) -> &str {
2586        &self.phrase
2587    }
2588
2589    #[must_use]
2590    pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
2591        self.chain_id = chain_id.into();
2592        self
2593    }
2594
2595    #[must_use]
2596    pub fn derivation_path(mut self, derivation_path: impl Into<String>) -> Self {
2597        let mut derivation_path = derivation_path.into();
2598        if !derivation_path.ends_with('/') {
2599            derivation_path.push('/');
2600        }
2601        self.derivation_path = Some(derivation_path);
2602        self
2603    }
2604
2605    fn get_derivation_path(&self) -> &str {
2606        self.derivation_path.as_deref().unwrap_or("m/44'/60'/0'/0/")
2607    }
2608}
2609
2610impl AccountGenerator {
2611    pub fn generate(&self) -> eyre::Result<Vec<PrivateKeySigner>> {
2612        let builder = MnemonicBuilder::<English>::default().phrase(self.phrase.as_str());
2613
2614        // use the derivation path
2615        let derivation_path = self.get_derivation_path();
2616        foundry_common::wallet::validate_bip32_path(derivation_path).map_err(|e| eyre::eyre!(e))?;
2617
2618        let mut wallets = Vec::with_capacity(self.amount);
2619        for idx in 0..self.amount {
2620            let idx = u32::try_from(idx).map_err(|_| eyre::eyre!("account index overflows u32"))?;
2621            let full_path = foundry_common::wallet::derive_key_path_checked(derivation_path, idx)
2622                .map_err(|e| eyre::eyre!(e))?;
2623            let builder = builder.clone().derivation_path(full_path)?;
2624            let wallet = builder.build()?.with_chain_id(Some(self.chain_id));
2625            wallets.push(wallet)
2626        }
2627        Ok(wallets)
2628    }
2629}
2630
2631/// Returns the path to anvil dir `~/.foundry/anvil`
2632pub fn anvil_dir() -> Option<PathBuf> {
2633    Config::foundry_dir().map(|p| p.join("anvil"))
2634}
2635
2636/// Returns the root path to anvil's temporary storage `~/.foundry/anvil/`
2637pub fn anvil_tmp_dir() -> Option<PathBuf> {
2638    anvil_dir().map(|p| p.join("tmp"))
2639}
2640
2641/// Finds the latest appropriate block to fork
2642///
2643/// This fetches the "latest" block and checks whether the `Block` is fully populated (`hash` field
2644/// is present). This prevents edge cases where anvil forks the "latest" block but `eth_getBlockByNumber` still returns a pending block, <https://github.com/foundry-rs/foundry/issues/2036>
2645async fn find_latest_fork_block<P: Provider<AnyNetwork>>(
2646    provider: P,
2647) -> Result<u64, TransportError> {
2648    let mut num = provider.get_block_number().await?;
2649
2650    // walk back from the head of the chain, but at most 2 blocks, which should be more than enough
2651    // leeway
2652    for _ in 0..2 {
2653        if let Some(block) = provider.get_block(num.into()).await?
2654            && !block.header.hash.is_zero()
2655        {
2656            break;
2657        }
2658        // block not actually finalized, so we try the block before
2659        num = num.saturating_sub(1)
2660    }
2661
2662    Ok(num)
2663}
2664
2665#[cfg(test)]
2666mod tests {
2667    use super::*;
2668    use foundry_evm::{hardfork::EthereumHardfork, hardforks::latest_active_tempo_hardfork};
2669
2670    #[cfg(unix)]
2671    use std::os::unix::fs::PermissionsExt;
2672
2673    #[cfg(feature = "base")]
2674    use foundry_evm::hardforks::BaseUpgrade;
2675
2676    #[cfg(feature = "optimism")]
2677    use foundry_evm::hardfork::OpHardfork;
2678
2679    #[cfg(not(feature = "base"))]
2680    use alloy_chains::NamedChain;
2681
2682    #[cfg(feature = "base")]
2683    #[tokio::test(flavor = "multi_thread")]
2684    async fn base_chain_inference_uses_native_base_forks() {
2685        for chain_id in [8453u64, 84532] {
2686            let (origin_api, origin) = crate::spawn(
2687                NodeConfig::test()
2688                    .with_chain_id(Some(chain_id))
2689                    .with_genesis_timestamp(Some(1_710_374_401u64)),
2690            )
2691            .await;
2692            assert!(origin_api.backend.is_base());
2693            assert_eq!(origin_api.backend.hardfork(), BaseUpgrade::Ecotone.into());
2694
2695            let anonymous_url = foundry_test_utils::rpc::spawn_rpc_proxy_rejecting_method_after(
2696                origin.http_endpoint(),
2697                "anvil_nodeInfo",
2698                0,
2699            )
2700            .await;
2701            for endpoint in [origin.http_endpoint(), anonymous_url] {
2702                let (api, _handle) =
2703                    crate::spawn(NodeConfig::test().with_eth_rpc_url(Some(endpoint))).await;
2704                assert!(api.backend.is_base());
2705                assert_eq!(api.backend.hardfork(), BaseUpgrade::Ecotone.into());
2706                let fork = api.backend.get_fork().unwrap();
2707                assert_eq!(
2708                    fork.config.read().endpoint_identity.network,
2709                    Some(NetworkVariant::Base)
2710                );
2711            }
2712        }
2713    }
2714
2715    #[tokio::test(flavor = "multi_thread")]
2716    async fn fork_output_redacts_endpoint_credentials() {
2717        let (_api, source) = crate::spawn(NodeConfig::test()).await;
2718        let fork_url = source.http_endpoint().replacen("http://", "http://user:password@", 1)
2719            + "/?api_key=secret";
2720        let mut config = NodeConfig::test().with_eth_rpc_url(Some(fork_url.clone()));
2721        let (api, _handle) = crate::spawn(config.clone()).await;
2722        config.fork_urls.push("https://mirror.example/private-api-key?token=secret".to_string());
2723
2724        let fork = api.backend.get_fork().unwrap();
2725        let node_info = api.anvil_node_info().await.unwrap();
2726        let output = config.as_string(Some(&fork));
2727        let temp = tempfile::tempdir().unwrap();
2728        let config_out = temp.path().join("config.json");
2729        config.config_out = Some(config_out.clone());
2730        config.print(Some(&fork)).unwrap();
2731        let json = serde_json::from_slice::<Value>(&std::fs::read(config_out).unwrap()).unwrap();
2732
2733        assert!(output.contains(&redact_url(&fork_url)));
2734        assert!(output.contains("https://mirror.example/"));
2735        assert!(!output.contains("user"));
2736        assert!(!output.contains("password"));
2737        assert!(!output.contains("private-api-key"));
2738        assert!(!output.contains("secret"));
2739        assert_eq!(json["endpoint"], redact_url(&fork_url));
2740        assert_eq!(node_info.fork_config.fork_url, Some(redact_url(&fork_url)));
2741        assert!(!json.to_string().contains("password"));
2742        assert!(!json.to_string().contains("secret"));
2743    }
2744
2745    #[test]
2746    fn fork_source_identity_includes_all_urls_and_headers() {
2747        let urls = ["http://primary".to_string(), "http://fallback".to_string()];
2748        let headers = ["Authorization: secret".to_string()];
2749        let identity = fork_source_id(&urls, &headers);
2750
2751        assert_ne!(identity, fork_source_id(&urls[..1], &headers));
2752        assert_ne!(identity, fork_source_id(&urls, &[]));
2753        assert_ne!(identity, fork_source_id(&[urls[1].clone(), urls[0].clone()], &headers));
2754    }
2755
2756    #[test]
2757    fn test_prune_history() {
2758        let config = PruneStateHistoryConfig::default();
2759        assert!(config.is_state_history_supported());
2760        let config = PruneStateHistoryConfig::from_args(Some(None));
2761        assert!(!config.is_state_history_supported());
2762        let config = PruneStateHistoryConfig::from_args(Some(Some(0)));
2763        assert!(config.is_config_enabled());
2764        assert!(!config.is_state_history_supported());
2765        let config = PruneStateHistoryConfig::from_args(Some(Some(10)));
2766        assert!(config.is_state_history_supported());
2767    }
2768
2769    #[test]
2770    fn fork_cache_path_can_use_source_chain() {
2771        let rpc_url = "http://localhost:8545";
2772        let mut config = NodeConfig::test()
2773            .with_eth_rpc_url(Some(rpc_url.to_string()))
2774            .with_chain_id(Some(1u64));
2775        let block = 42;
2776        config.fork_source_chain_id = Some(143);
2777        let expected = Config::foundry_block_cache_file(143, block)
2778            .map(|path| path.with_file_name(format!("storage-{:x}.json", keccak256(rpc_url))));
2779
2780        assert_eq!(config.block_cache_path(block), expected);
2781        assert_ne!(
2782            config.block_cache_path_for_rpc(143, block, rpc_url),
2783            config.block_cache_path_for_rpc(143, block, "http://localhost:8546")
2784        );
2785    }
2786
2787    #[test]
2788    fn fork_execution_and_source_chain_ids_remain_distinct() {
2789        let mut config = NodeConfig::test();
2790        config.fork_execution_chain_id = Some(1);
2791        config.fork_source_chain_id = Some(143);
2792
2793        assert_eq!(config.get_chain_id(), 1);
2794        assert_eq!(config.protocol_chain_id(), 143);
2795    }
2796
2797    #[test]
2798    fn fork_chain_id_is_only_an_offline_discovery_hint() {
2799        let mut config = NodeConfig::test()
2800            .with_chain_id(Some(31_337u64))
2801            .with_fork_chain_id(Some(U256::from(143)));
2802
2803        assert_eq!(config.protocol_chain_id(), 31_337);
2804
2805        config.fork_source_chain_id = Some(143);
2806        assert_eq!(config.protocol_chain_id(), 143);
2807
2808        config.fork_source_chain_id = None;
2809        assert_eq!(config.protocol_chain_id(), 31_337);
2810    }
2811
2812    #[test]
2813    fn fork_endpoint_revalidation_requires_authority_or_fallbacks() {
2814        let anonymous = ForkEndpointIdentity {
2815            execution_chain_id: 1,
2816            source_chain_id: 1,
2817            network: Some(NetworkVariant::Ethereum),
2818            network_profile: Some(NetworkConfigs::default()),
2819            hardfork: None,
2820            instance_id: None,
2821            source_fork_block_number: None,
2822            source_fork_block_hash: None,
2823        };
2824        let mut config =
2825            NodeConfig::test().with_eth_rpc_url(Some("http://localhost:8545".to_string()));
2826
2827        assert!(!anonymous.is_authoritative());
2828        assert!(!config.requires_primary_fork_revalidation(anonymous));
2829
2830        config.fork_urls.push("http://localhost:8546".to_string());
2831        assert!(config.requires_primary_fork_revalidation(anonymous));
2832        assert_eq!(
2833            NodeConfig::fork_urls_requiring_revalidation(&config.fork_urls, anonymous),
2834            config.fork_urls
2835        );
2836
2837        config.fork_urls.pop();
2838        let authoritative =
2839            ForkEndpointIdentity { hardfork: Some(EthereumHardfork::Prague.into()), ..anonymous };
2840        assert!(authoritative.is_authoritative());
2841        assert!(config.requires_primary_fork_revalidation(authoritative));
2842        assert_eq!(
2843            NodeConfig::fork_urls_requiring_revalidation(&config.fork_urls, authoritative),
2844            config.fork_urls
2845        );
2846    }
2847
2848    #[tokio::test]
2849    async fn fork_authoritative_identity_keeps_node_info_probe_strict() {
2850        let (_api, origin) =
2851            crate::spawn(NodeConfig::test().with_chain_id(Some(NamedChain::Mainnet as u64))).await;
2852        let fork_url = foundry_test_utils::rpc::spawn_rpc_proxy_rejecting_method_after(
2853            origin.http_endpoint(),
2854            "anvil_nodeInfo",
2855            0,
2856        )
2857        .await;
2858        let expected = ForkEndpointIdentity {
2859            execution_chain_id: NamedChain::Mainnet as u64,
2860            source_chain_id: NamedChain::Mainnet as u64,
2861            network: Some(NetworkVariant::Ethereum),
2862            network_profile: Some(NetworkConfigs::default()),
2863            hardfork: Some(EthereumHardfork::Prague.into()),
2864            instance_id: Some(B256::with_last_byte(1)),
2865            source_fork_block_number: None,
2866            source_fork_block_hash: None,
2867        };
2868
2869        let error = NodeConfig::test()
2870            .fork_context_matches(&fork_url, expected, 0, B256::ZERO)
2871            .await
2872            .unwrap_err();
2873
2874        assert!(
2875            error.to_string().contains("failed to determine network family from fork endpoint"),
2876            "{error}"
2877        );
2878    }
2879
2880    #[cfg(feature = "optimism")]
2881    #[test]
2882    fn set_chain_id_updates_network_config() {
2883        let mut config = NodeConfig::test();
2884        config.set_chain_id(Some(10u64));
2885
2886        assert!(config.networks.is_optimism());
2887    }
2888
2889    #[test]
2890    fn chain_id_network_inference_is_replaceable_and_clearable() {
2891        let mut config = NodeConfig::test();
2892        config.set_chain_id(Some(4217u64));
2893        assert!(config.networks.is_tempo());
2894
2895        config.set_chain_id(Some(NamedChain::Celo as u64));
2896        assert!(config.networks.is_celo());
2897        assert!(!config.networks.is_tempo());
2898
2899        config.set_chain_id(Some(1u64));
2900        assert!(!config.networks.has_network_selection());
2901
2902        config.set_chain_id(Some(4217u64));
2903        config.set_chain_id(None::<u64>);
2904        assert!(!config.networks.has_network_selection());
2905    }
2906
2907    #[test]
2908    fn chain_id_preserves_explicit_network_selection() {
2909        let mut config = NodeConfig::test_tempo();
2910        config.set_chain_id(Some(NamedChain::Celo as u64));
2911
2912        assert!(config.networks.is_tempo());
2913        assert!(!config.networks.is_celo());
2914    }
2915
2916    #[test]
2917    fn get_hardfork_on_tempo_never_returns_non_tempo_variant() {
2918        // Post-Shanghai timestamp on Ethereum mainnet.
2919        let shanghai_ts = 1_681_338_455u64;
2920
2921        let config = NodeConfig::test_tempo()
2922            .with_chain_id(Some(1u64))
2923            .with_genesis_timestamp(Some(shanghai_ts));
2924
2925        assert!(config.networks.is_tempo());
2926        assert!(matches!(config.get_hardfork(), FoundryHardfork::Tempo(_)));
2927    }
2928
2929    #[test]
2930    fn get_hardfork_on_ethereum_uses_genesis_timestamp() {
2931        let timestamp = EthereumHardfork::Shanghai.mainnet_activation_timestamp().unwrap();
2932        let config =
2933            NodeConfig::test().with_chain_id(Some(1u64)).with_genesis_timestamp(Some(timestamp));
2934
2935        assert_eq!(config.get_hardfork(), FoundryHardfork::Ethereum(EthereumHardfork::Shanghai));
2936    }
2937
2938    #[test]
2939    #[cfg(feature = "optimism")]
2940    fn get_hardfork_on_optimism_uses_genesis_timestamp() {
2941        // OP Mainnet Canyon activation timestamp.
2942        let timestamp = 1_704_992_401u64;
2943        let config = NodeConfig::test()
2944            .with_optimism()
2945            .with_chain_id(Some(10u64))
2946            .with_genesis_timestamp(Some(timestamp));
2947
2948        assert_eq!(config.get_hardfork(), FoundryHardfork::Optimism(OpHardfork::Canyon));
2949    }
2950
2951    #[test]
2952    fn get_hardfork_on_local_tempo_defaults_to_latest_active() {
2953        let config = NodeConfig::test_tempo();
2954
2955        assert_eq!(config.get_hardfork(), FoundryHardfork::Tempo(latest_active_tempo_hardfork()));
2956    }
2957
2958    #[test]
2959    #[cfg(feature = "base")]
2960    fn test_base_config_uses_base_network_and_chain_id() {
2961        let config = NodeConfig::test_base();
2962
2963        assert!(config.networks.is_base());
2964        assert_eq!(config.get_chain_id(), NamedChain::Base as u64);
2965    }
2966
2967    #[test]
2968    #[cfg(feature = "base")]
2969    fn get_hardfork_on_base_fork_uses_source_chain_timestamp_mapping() {
2970        let mut config = NodeConfig::test_base()
2971            .with_chain_id(Some(1u64))
2972            .with_genesis_timestamp(Some(u64::MAX));
2973        config.fork_source_chain_id = Some(NamedChain::Base as u64);
2974
2975        assert_eq!(config.get_chain_id(), 1);
2976        assert_eq!(config.get_hardfork(), FoundryHardfork::Base(BaseUpgrade::Beryl));
2977    }
2978
2979    #[test]
2980    #[cfg(feature = "monad")]
2981    fn get_hardfork_on_monad_fork_uses_source_chain_timestamp_mapping() {
2982        let mut config = NodeConfig::test_monad()
2983            .with_chain_id(Some(1u64))
2984            .with_genesis_timestamp(Some(1_763_648_999u64));
2985        config.fork_source_chain_id = Some(143);
2986
2987        assert_eq!(config.get_chain_id(), 1);
2988        assert_eq!(
2989            config.get_hardfork(),
2990            FoundryHardfork::Monad(foundry_evm::hardfork::MonadHardfork::MonadEight)
2991        );
2992    }
2993
2994    #[test]
2995    fn account_generator_rejects_harden_bit_overflow_path() {
2996        let err = AccountGenerator::new(1)
2997            .phrase("test test test test test test test test test test test junk")
2998            .derivation_path("m/44'/60'/0'/0/2147483648'")
2999            .generate()
3000            .unwrap_err()
3001            .to_string();
3002        assert!(err.contains("harden bit"), "{err}");
3003
3004        assert!(
3005            AccountGenerator::new(1)
3006                .phrase("test test test test test test test test test test test junk")
3007                .derivation_path("m/44'/60'/0'/0")
3008                .generate()
3009                .is_ok()
3010        );
3011    }
3012
3013    #[cfg(unix)]
3014    #[test]
3015    fn config_out_has_owner_only_permissions() {
3016        let dir = tempfile::tempdir().unwrap();
3017        let path = dir.path().join("config.json");
3018        let config = NodeConfig::test().set_config_out(Some(path.clone()));
3019
3020        config.print(None).unwrap();
3021
3022        let mode = std::fs::metadata(path).unwrap().permissions().mode();
3023        assert_eq!(mode & 0o777, 0o600);
3024    }
3025}