Skip to main content

anvil/
config.rs

1use crate::{
2    EthereumHardfork, FeeManager, PrecompileFactory,
3    eth::{
4        backend::{
5            db::{Db, SerializableState},
6            fork::{ClientFork, ClientForkConfig},
7            genesis::GenesisConfig,
8            mem::fork_db::ForkedDatabase,
9            time::duration_since_unix_epoch,
10        },
11        fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE},
12        pool::transactions::{PoolTransaction, TransactionOrder},
13    },
14    mem::{self, in_memory_db::StateRootDb},
15};
16use alloy_chains::NamedChain;
17use alloy_consensus::BlockHeader;
18use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams};
19use alloy_evm::EvmEnv;
20use alloy_genesis::Genesis;
21use alloy_network::{AnyNetwork, BlockResponse, TransactionResponse};
22use alloy_primitives::{Address, BlockNumber, TxHash, U256, hex, map::HashMap, utils::Unit};
23use alloy_provider::Provider;
24use alloy_rpc_types::BlockNumberOrTag;
25use alloy_signer::Signer;
26use alloy_signer_local::{
27    MnemonicBuilder, PrivateKeySigner,
28    coins_bip39::{English, Mnemonic},
29};
30use alloy_transport::TransportError;
31use anvil_server::ServerConfig;
32use eyre::{Context, Result};
33use foundry_common::{
34    ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT,
35    provider::{ProviderBuilder, RetryProvider},
36};
37use foundry_config::Config;
38use foundry_evm::{
39    backend::{BlockchainDb, BlockchainDbMeta, SharedBackend},
40    constants::DEFAULT_CREATE2_DEPLOYER,
41    hardfork::FoundryHardfork,
42    hardforks::latest_active_tempo_hardfork,
43    utils::{
44        apply_chain_and_block_specific_env_changes, block_env_from_header,
45        get_blob_base_fee_update_fraction,
46    },
47};
48use foundry_primitives::FoundryTxEnvelope;
49use itertools::Itertools;
50use parking_lot::RwLock;
51use rand_08::thread_rng;
52use revm::{
53    context::{BlockEnv, CfgEnv},
54    context_interface::block::BlobExcessGasAndPrice,
55    primitives::hardfork::SpecId,
56};
57use serde_json::{Value, json};
58use std::{
59    fmt::Write as FmtWrite,
60    net::{IpAddr, Ipv4Addr},
61    path::PathBuf,
62    sync::Arc,
63    time::Duration,
64};
65use tempo_hardfork::{
66    TempoHardfork,
67    constants::gas::{TEMPO_T0_BASE_FEE, TEMPO_T1_BASE_FEE},
68};
69use tokio::sync::RwLock as TokioRwLock;
70use yansi::Paint;
71
72pub use foundry_common::version::SHORT_VERSION as VERSION_MESSAGE;
73use foundry_evm::{
74    traces::{CallTraceDecoderBuilder, identifier::SignaturesIdentifier},
75    utils::get_blob_params,
76};
77use foundry_evm_networks::NetworkConfigs;
78use tempo_precompiles::TIP_FEE_MANAGER_ADDRESS;
79
80/// Default port the rpc will open
81pub const NODE_PORT: u16 = 8545;
82/// Default chain id of the node
83pub const CHAIN_ID: u64 = 31337;
84/// The default gas limit for all transactions
85pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000;
86/// The default number of slots in an epoch used for safe/finalized block tags.
87pub const DEFAULT_SLOTS_IN_AN_EPOCH: u64 = 32;
88/// Default mnemonic for dev accounts
89pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
90
91/// The default IPC endpoint
92pub const DEFAULT_IPC_ENDPOINT: &str =
93    if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };
94
95const BANNER: &str = r"
96                             _   _
97                            (_) | |
98      __ _   _ __   __   __  _  | |
99     / _` | | '_ \  \ \ / / | | | |
100    | (_| | | | | |  \ V /  | | | |
101     \__,_| |_| |_|   \_/   |_| |_|
102";
103
104/// Configurations of the EVM node
105#[derive(Clone, Debug)]
106pub struct NodeConfig {
107    /// Chain ID of the EVM chain
108    pub chain_id: Option<u64>,
109    /// Default gas limit for all txs
110    pub gas_limit: Option<u64>,
111    /// If set to `true`, disables the block gas limit
112    pub disable_block_gas_limit: bool,
113    /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825)
114    pub enable_tx_gas_limit: bool,
115    /// Default gas price for all txs
116    pub gas_price: Option<u128>,
117    /// Default base fee
118    pub base_fee: Option<u64>,
119    /// If set to `true`, disables the enforcement of a minimum suggested priority fee
120    pub disable_min_priority_fee: bool,
121    /// Default blob excess gas and price
122    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
123    /// The hardfork to use
124    pub hardfork: Option<FoundryHardfork>,
125    /// Signer accounts that will be initialised with `genesis_balance` in the genesis block
126    pub genesis_accounts: Vec<PrivateKeySigner>,
127    /// Native token balance of every genesis account in the genesis block
128    pub genesis_balance: U256,
129    /// Genesis block timestamp
130    pub genesis_timestamp: Option<u64>,
131    /// Genesis block number
132    pub genesis_block_number: Option<u64>,
133    /// Signer accounts that can sign messages/transactions from the EVM node
134    pub signer_accounts: Vec<PrivateKeySigner>,
135    /// Configured block time for the EVM chain. Use `None` for instant/auto mining.
136    pub block_time: Option<Duration>,
137    /// Disable auto and interval mining mode and use `MiningMode::None` instead.
138    pub no_mining: bool,
139    /// Enables auto and interval mining mode
140    pub mixed_mining: bool,
141    /// port to use for the server
142    pub port: u16,
143    /// maximum number of transactions in a block
144    pub max_transactions: usize,
145    /// Fork URLs for RPC calls. The first entry is the primary endpoint.
146    /// When multiple URLs are provided, requests are distributed using
147    /// round-robin load balancing with retry-based failover.
148    pub fork_urls: Vec<String>,
149    /// pins the block number or transaction hash for the state fork
150    pub fork_choice: Option<ForkChoice>,
151    /// headers to use with fork RPC endpoints
152    pub fork_headers: Vec<String>,
153    /// specifies chain id for cache to skip fetching from remote in offline-start mode
154    pub fork_chain_id: Option<U256>,
155    /// The generator used to generate the dev accounts
156    pub account_generator: Option<AccountGenerator>,
157    /// whether to enable tracing
158    pub enable_tracing: bool,
159    /// Explicitly disables the use of RPC caching.
160    pub no_storage_caching: bool,
161    /// How to configure the server
162    pub server_config: ServerConfig,
163    /// The host the server will listen on
164    pub host: Vec<IpAddr>,
165    /// How transactions are sorted in the mempool
166    pub transaction_order: TransactionOrder,
167    /// Filename to write anvil output as json
168    pub config_out: Option<PathBuf>,
169    /// The genesis to use to initialize the node
170    pub genesis: Option<Genesis>,
171    /// Timeout in for requests sent to remote JSON-RPC server in forking mode
172    pub fork_request_timeout: Duration,
173    /// Number of request retries for spurious networks
174    pub fork_request_retries: u32,
175    /// The initial retry backoff
176    pub fork_retry_backoff: Duration,
177    /// available CUPS
178    pub compute_units_per_second: u64,
179    /// The ipc path
180    pub ipc_path: Option<Option<String>>,
181    /// Enable transaction/call steps tracing for debug calls returning geth-style traces
182    pub enable_steps_tracing: bool,
183    /// Enable printing of `console.log` invocations.
184    pub print_logs: bool,
185    /// Enable printing of traces.
186    pub print_traces: bool,
187    /// Enable auto impersonation of accounts on startup
188    pub enable_auto_impersonate: bool,
189    /// Configure the code size limit
190    pub code_size_limit: Option<usize>,
191    /// Configures how to remove historic state.
192    ///
193    /// If set to `Some(num)` keep latest num state in memory only.
194    pub prune_history: PruneStateHistoryConfig,
195    /// Max number of states cached on disk.
196    pub max_persisted_states: Option<usize>,
197    /// The file where to load the state from
198    pub init_state: Option<SerializableState>,
199    /// max number of blocks with transactions in memory
200    pub transaction_block_keeper: Option<usize>,
201    /// Disable the default CREATE2 deployer
202    pub disable_default_create2_deployer: bool,
203    /// Disable pool balance checks
204    pub disable_pool_balance_checks: bool,
205    /// Slots in an epoch
206    pub slots_in_an_epoch: u64,
207    /// The memory limit per EVM execution in bytes.
208    pub memory_limit: Option<u64>,
209    /// Factory used by `anvil` to extend the EVM's precompiles.
210    pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
211    /// Networks to enable features for.
212    pub networks: NetworkConfigs,
213    /// Do not print log messages.
214    pub silent: bool,
215    /// The path where persisted states are cached (used with `max_persisted_states`).
216    /// This does not affect the fork RPC cache location.
217    pub cache_path: Option<PathBuf>,
218    /// Accounts to fund with specific balances on startup (address -> balance in wei).
219    pub funded_accounts: HashMap<Address, U256>,
220}
221
222impl NodeConfig {
223    fn as_string(&self, fork: Option<&ClientFork>) -> String {
224        let mut s: String = String::new();
225        let _ = write!(s, "\n{}", BANNER.green());
226        let _ = write!(s, "\n    {VERSION_MESSAGE}");
227        let _ = write!(s, "\n    {}", "https://github.com/foundry-rs/foundry".green());
228
229        let _ = write!(
230            s,
231            r#"
232
233Available Accounts
234==================
235"#
236        );
237        let balance = alloy_primitives::utils::format_ether(self.genesis_balance);
238        for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
239            write!(s, "\n({idx}) {} ({balance} ETH)", wallet.address()).unwrap();
240        }
241
242        let _ = write!(
243            s,
244            r#"
245
246Private Keys
247==================
248"#
249        );
250
251        for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
252            let hex = hex::encode(wallet.credential().to_bytes());
253            let _ = write!(s, "\n({idx}) 0x{hex}");
254        }
255
256        if let Some(generator) = &self.account_generator {
257            let _ = write!(
258                s,
259                r#"
260
261Wallet
262==================
263Mnemonic:          {}
264Derivation path:   {}
265"#,
266                generator.phrase,
267                generator.get_derivation_path()
268            );
269        }
270
271        if let Some(fork) = fork {
272            let _ = write!(
273                s,
274                r#"
275
276Fork
277==================
278Endpoint:       {}
279Block number:   {}
280Block hash:     {:?}
281Chain ID:       {}
282"#,
283                fork.eth_rpc_url().as_deref().unwrap_or("none"),
284                fork.block_number(),
285                fork.block_hash(),
286                fork.chain_id()
287            );
288
289            if self.fork_urls.len() > 1 {
290                let _ = writeln!(s, "Endpoints:      {}", self.fork_urls.len());
291                for (i, url) in self.fork_urls.iter().enumerate() {
292                    let _ = writeln!(s, "  ({i}) {url}");
293                }
294            }
295
296            if let Some(tx_hash) = fork.transaction_hash() {
297                let _ = writeln!(s, "Transaction hash: {tx_hash}");
298            }
299        } else {
300            let _ = write!(
301                s,
302                r#"
303
304Chain ID
305==================
306
307{}
308"#,
309                self.get_chain_id().green()
310            );
311        }
312
313        if (SpecId::from(self.get_hardfork()) as u8) < (SpecId::LONDON as u8) {
314            let _ = write!(
315                s,
316                r#"
317Gas Price
318==================
319
320{}
321"#,
322                self.get_gas_price().green()
323            );
324        } else {
325            let _ = write!(
326                s,
327                r#"
328Base Fee
329==================
330
331{}
332"#,
333                self.get_base_fee().green()
334            );
335        }
336
337        let _ = write!(
338            s,
339            r#"
340Gas Limit
341==================
342
343{}
344"#,
345            {
346                if self.disable_block_gas_limit {
347                    "Disabled".to_string()
348                } else {
349                    self.gas_limit.map(|l| l.to_string()).unwrap_or_else(|| {
350                        if self.fork_choice.is_some() {
351                            "Forked".to_string()
352                        } else {
353                            DEFAULT_GAS_LIMIT.to_string()
354                        }
355                    })
356                }
357            }
358            .green()
359        );
360
361        let _ = write!(
362            s,
363            r#"
364Genesis Timestamp
365==================
366
367{}
368"#,
369            self.get_genesis_timestamp().green()
370        );
371
372        let _ = write!(
373            s,
374            r#"
375Genesis Number
376==================
377
378{}
379"#,
380            self.get_genesis_number().green()
381        );
382
383        s
384    }
385
386    fn as_json(&self, fork: Option<&ClientFork>) -> Value {
387        let mut wallet_description = HashMap::new();
388        let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
389        let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
390
391        for wallet in &self.genesis_accounts {
392            available_accounts.push(format!("{:?}", wallet.address()));
393            private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
394        }
395
396        if let Some(generator) = &self.account_generator {
397            let phrase = generator.get_phrase().to_string();
398            let derivation_path = generator.get_derivation_path().to_string();
399
400            wallet_description.insert("derivation_path".to_string(), derivation_path);
401            wallet_description.insert("mnemonic".to_string(), phrase);
402        };
403
404        let gas_limit = match self.gas_limit {
405            // if we have a disabled flag we should max out the limit
406            Some(_) | None if self.disable_block_gas_limit => Some(u64::MAX.to_string()),
407            Some(limit) => Some(limit.to_string()),
408            _ => None,
409        };
410
411        if let Some(fork) = fork {
412            json!({
413              "available_accounts": available_accounts,
414              "private_keys": private_keys,
415              "endpoint": fork.eth_rpc_url().unwrap_or_default(),
416              "block_number": fork.block_number(),
417              "block_hash": fork.block_hash(),
418              "chain_id": fork.chain_id(),
419              "wallet": wallet_description,
420              "base_fee": format!("{}", self.get_base_fee()),
421              "gas_price": format!("{}", self.get_gas_price()),
422              "gas_limit": gas_limit,
423            })
424        } else {
425            json!({
426              "available_accounts": available_accounts,
427              "private_keys": private_keys,
428              "wallet": wallet_description,
429              "base_fee": format!("{}", self.get_base_fee()),
430              "gas_price": format!("{}", self.get_gas_price()),
431              "gas_limit": gas_limit,
432              "genesis_timestamp": format!("{}", self.get_genesis_timestamp()),
433            })
434        }
435    }
436}
437
438impl NodeConfig {
439    /// Returns a new config intended to be used in tests, which does not print and binds to a
440    /// random, free port by setting it to `0`
441    #[doc(hidden)]
442    pub fn test() -> Self {
443        Self { enable_tracing: true, port: 0, silent: true, ..Default::default() }
444    }
445
446    /// Returns a test config with Tempo network enabled.
447    #[doc(hidden)]
448    pub fn test_tempo() -> Self {
449        Self { networks: NetworkConfigs::with_tempo(), ..Self::test() }
450    }
451
452    /// Returns a new config which does not initialize any accounts on node startup.
453    pub fn empty_state() -> Self {
454        Self {
455            genesis_accounts: vec![],
456            signer_accounts: vec![],
457            disable_default_create2_deployer: true,
458            ..Default::default()
459        }
460    }
461}
462
463impl Default for NodeConfig {
464    fn default() -> Self {
465        // generate some random wallets
466        let genesis_accounts = AccountGenerator::new(10)
467            .phrase(DEFAULT_MNEMONIC)
468            .generate()
469            .expect("Invalid mnemonic.");
470        Self {
471            chain_id: None,
472            gas_limit: None,
473            disable_block_gas_limit: false,
474            enable_tx_gas_limit: false,
475            gas_price: None,
476            hardfork: None,
477            signer_accounts: genesis_accounts.clone(),
478            genesis_timestamp: None,
479            genesis_block_number: None,
480            genesis_accounts,
481            // 100ETH default balance
482            genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)),
483            block_time: None,
484            no_mining: false,
485            mixed_mining: false,
486            port: NODE_PORT,
487            max_transactions: 1_000,
488            fork_urls: vec![],
489            fork_choice: None,
490            account_generator: None,
491            base_fee: None,
492            disable_min_priority_fee: false,
493            blob_excess_gas_and_price: None,
494            enable_tracing: true,
495            enable_steps_tracing: false,
496            print_logs: true,
497            print_traces: false,
498            enable_auto_impersonate: false,
499            no_storage_caching: false,
500            server_config: Default::default(),
501            host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
502            transaction_order: Default::default(),
503            config_out: None,
504            genesis: None,
505            fork_request_timeout: REQUEST_TIMEOUT,
506            fork_headers: vec![],
507            fork_request_retries: 5,
508            fork_retry_backoff: Duration::from_millis(1_000),
509            fork_chain_id: None,
510            // alchemy max cpus <https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second>
511            compute_units_per_second: ALCHEMY_FREE_TIER_CUPS,
512            ipc_path: None,
513            code_size_limit: None,
514            prune_history: Default::default(),
515            max_persisted_states: None,
516            init_state: None,
517            transaction_block_keeper: None,
518            disable_default_create2_deployer: false,
519            disable_pool_balance_checks: false,
520            slots_in_an_epoch: DEFAULT_SLOTS_IN_AN_EPOCH,
521            memory_limit: None,
522            precompile_factory: None,
523            networks: Default::default(),
524            silent: false,
525            cache_path: None,
526            funded_accounts: HashMap::default(),
527        }
528    }
529}
530
531impl NodeConfig {
532    /// Applies Tempo's safe default beneficiary for forked nodes while preserving
533    /// explicit coinbase selections.
534    pub(crate) fn apply_tempo_fork_beneficiary_default<N>(&self, evm_env: &mut EvmEnv<N>) {
535        if self.networks.is_tempo()
536            && !self.fork_urls.is_empty()
537            && evm_env.block_env.beneficiary.is_zero()
538        {
539            // Tempo mainnet maps the zero validator token to a DONOTUSE sentinel.
540            // Forked transactions with the default zero beneficiary can therefore
541            // fail fee collection before producing a receipt. Use the same neutral
542            // fee-recipient sentinel as Tempo's simulation path so validator token
543            // lookup falls back to the default PathUSD token unless the user has
544            // explicitly supplied a non-zero coinbase.
545            evm_env.block_env.beneficiary = TIP_FEE_MANAGER_ADDRESS;
546        }
547    }
548
549    /// Returns the memory limit of the node
550    #[must_use]
551    pub const fn with_memory_limit(mut self, mems_value: Option<u64>) -> Self {
552        self.memory_limit = mems_value;
553        self
554    }
555
556    /// Returns the base fee to use.
557    ///
558    /// In Tempo mode, uses the hardfork-specific base fee (10 gwei pre-T1, 20 gwei T1+).
559    pub fn get_base_fee(&self) -> u64 {
560        let default = if self.networks.is_tempo() {
561            tempo_default_base_fee(TempoHardfork::from(self.get_hardfork()))
562        } else {
563            INITIAL_BASE_FEE
564        };
565        self.base_fee
566            .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64)))
567            .unwrap_or(default)
568    }
569
570    /// Returns the gas price to use.
571    ///
572    /// In Tempo mode, defaults to the hardfork-specific base fee.
573    pub fn get_gas_price(&self) -> u128 {
574        let default = if self.networks.is_tempo() {
575            tempo_default_base_fee(TempoHardfork::from(self.get_hardfork())) as u128
576        } else {
577            INITIAL_GAS_PRICE
578        };
579        self.gas_price.unwrap_or(default)
580    }
581
582    pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice {
583        if let Some(value) = self.blob_excess_gas_and_price {
584            value
585        } else {
586            let excess_blob_gas =
587                self.genesis.as_ref().and_then(|g| g.excess_blob_gas).unwrap_or(0);
588            BlobExcessGasAndPrice::new(
589                excess_blob_gas,
590                get_blob_base_fee_update_fraction(
591                    self.get_chain_id(),
592                    self.get_genesis_timestamp(),
593                ),
594            )
595        }
596    }
597
598    /// Returns the [`BlobParams`] that should be used.
599    pub fn get_blob_params(&self) -> BlobParams {
600        get_blob_params(self.get_chain_id(), self.get_genesis_timestamp())
601    }
602
603    /// Returns the hardfork to use
604    pub fn get_hardfork(&self) -> FoundryHardfork {
605        if let Some(hardfork) = self.hardfork {
606            return hardfork;
607        }
608        if self.networks.is_tempo()
609            && let Some(hardfork) = TempoHardfork::from_chain_and_timestamp(
610                self.get_chain_id(),
611                self.get_genesis_timestamp(),
612            )
613        {
614            return hardfork.into();
615        }
616        #[cfg(feature = "optimism")]
617        if self.networks.is_optimism() {
618            return foundry_evm::hardforks::OpHardfork::default().into();
619        }
620        if self.networks.is_tempo() {
621            return latest_active_tempo_hardfork().into();
622        }
623        EthereumHardfork::default().into()
624    }
625
626    /// Sets a custom code size limit
627    #[must_use]
628    pub const fn with_code_size_limit(mut self, code_size_limit: Option<usize>) -> Self {
629        self.code_size_limit = code_size_limit;
630        self
631    }
632    /// Disables  code size limit
633    #[must_use]
634    pub const fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self {
635        if disable_code_size_limit {
636            self.code_size_limit = Some(usize::MAX);
637        }
638        self
639    }
640
641    /// Sets the init state if any
642    #[must_use]
643    pub fn with_init_state(mut self, init_state: Option<SerializableState>) -> Self {
644        self.init_state = init_state;
645        self
646    }
647
648    /// Loads the init state from a file if it exists
649    #[must_use]
650    #[cfg(feature = "cmd")]
651    pub fn with_init_state_path(mut self, path: impl AsRef<std::path::Path>) -> Self {
652        self.init_state = crate::cmd::StateFile::parse_path(path).ok().and_then(|file| file.state);
653        self
654    }
655
656    /// Sets the chain ID
657    #[must_use]
658    pub fn with_chain_id<U: Into<u64>>(mut self, chain_id: Option<U>) -> Self {
659        self.set_chain_id(chain_id);
660        self
661    }
662
663    /// Returns the chain ID to use
664    pub fn get_chain_id(&self) -> u64 {
665        self.chain_id
666            .or_else(|| self.genesis.as_ref().map(|g| g.config.chain_id))
667            .unwrap_or(CHAIN_ID)
668    }
669
670    /// Sets the chain id and updates all wallets
671    pub fn set_chain_id(&mut self, chain_id: Option<impl Into<u64>>) {
672        self.chain_id = chain_id.map(Into::into);
673        let chain_id = self.get_chain_id();
674        self.networks = self.networks.with_chain_id(chain_id);
675        self.genesis_accounts.iter_mut().for_each(|wallet| {
676            *wallet = wallet.clone().with_chain_id(Some(chain_id));
677        });
678        self.signer_accounts.iter_mut().for_each(|wallet| {
679            *wallet = wallet.clone().with_chain_id(Some(chain_id));
680        })
681    }
682
683    /// Sets the gas limit
684    #[must_use]
685    pub const fn with_gas_limit(mut self, gas_limit: Option<u64>) -> Self {
686        self.gas_limit = gas_limit;
687        self
688    }
689
690    /// Disable block gas limit check
691    ///
692    /// If set to `true` block gas limit will not be enforced
693    #[must_use]
694    pub const fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self {
695        self.disable_block_gas_limit = disable_block_gas_limit;
696        self
697    }
698
699    /// Enable tx gas limit check
700    ///
701    /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825)
702    #[must_use]
703    pub const fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self {
704        self.enable_tx_gas_limit = enable_tx_gas_limit;
705        self
706    }
707
708    /// Sets the gas price
709    #[must_use]
710    pub const fn with_gas_price(mut self, gas_price: Option<u128>) -> Self {
711        self.gas_price = gas_price;
712        self
713    }
714
715    /// Sets prune history status.
716    #[must_use]
717    pub fn set_pruned_history(mut self, prune_history: Option<Option<usize>>) -> Self {
718        self.prune_history = PruneStateHistoryConfig::from_args(prune_history);
719        self
720    }
721
722    /// Sets max number of states to cache on disk.
723    #[must_use]
724    pub fn with_max_persisted_states<U: Into<usize>>(
725        mut self,
726        max_persisted_states: Option<U>,
727    ) -> Self {
728        self.max_persisted_states = max_persisted_states.map(Into::into);
729        self
730    }
731
732    /// Sets the max number of transactions in a block
733    #[must_use]
734    pub const fn with_max_transactions(mut self, max_transactions: Option<usize>) -> Self {
735        if let Some(max_transactions) = max_transactions {
736            self.max_transactions = max_transactions;
737        }
738        self
739    }
740
741    /// Sets max number of blocks with transactions to keep in memory
742    #[must_use]
743    pub fn with_transaction_block_keeper<U: Into<usize>>(
744        mut self,
745        transaction_block_keeper: Option<U>,
746    ) -> Self {
747        self.transaction_block_keeper = transaction_block_keeper.map(Into::into);
748        self
749    }
750
751    /// Sets the base fee
752    #[must_use]
753    pub const fn with_base_fee(mut self, base_fee: Option<u64>) -> Self {
754        self.base_fee = base_fee;
755        self
756    }
757
758    /// Disable the enforcement of a minimum suggested priority fee
759    #[must_use]
760    pub const fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self {
761        self.disable_min_priority_fee = disable_min_priority_fee;
762        self
763    }
764
765    /// Sets the init genesis (genesis.json)
766    #[must_use]
767    pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
768        self.genesis = genesis;
769        self
770    }
771
772    /// Returns the genesis timestamp to use
773    pub fn get_genesis_timestamp(&self) -> u64 {
774        self.genesis_timestamp
775            .or_else(|| self.genesis.as_ref().map(|g| g.timestamp))
776            .unwrap_or_else(|| duration_since_unix_epoch().as_secs())
777    }
778
779    /// Sets the genesis timestamp
780    #[must_use]
781    pub fn with_genesis_timestamp<U: Into<u64>>(mut self, timestamp: Option<U>) -> Self {
782        if let Some(timestamp) = timestamp {
783            self.genesis_timestamp = Some(timestamp.into());
784        }
785        self
786    }
787
788    /// Sets the genesis number
789    #[must_use]
790    pub fn with_genesis_block_number<U: Into<u64>>(mut self, number: Option<U>) -> Self {
791        if let Some(number) = number {
792            self.genesis_block_number = Some(number.into());
793        }
794        self
795    }
796
797    /// Returns the genesis number
798    pub fn get_genesis_number(&self) -> u64 {
799        self.genesis_block_number
800            .or_else(|| self.genesis.as_ref().and_then(|g| g.number))
801            .unwrap_or(0)
802    }
803
804    /// Sets the hardfork
805    #[must_use]
806    pub const fn with_hardfork(mut self, hardfork: Option<FoundryHardfork>) -> Self {
807        self.hardfork = hardfork;
808        self
809    }
810
811    /// Sets the genesis accounts
812    #[must_use]
813    pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
814        self.genesis_accounts = accounts;
815        self
816    }
817
818    /// Sets the signer accounts
819    #[must_use]
820    pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
821        self.signer_accounts = accounts;
822        self
823    }
824
825    /// Sets both the genesis accounts and the signer accounts
826    /// so that `genesis_accounts == accounts`
827    pub fn with_account_generator(mut self, generator: AccountGenerator) -> eyre::Result<Self> {
828        let accounts = generator.generate()?;
829        self.account_generator = Some(generator);
830        Ok(self.with_signer_accounts(accounts.clone()).with_genesis_accounts(accounts))
831    }
832
833    /// Sets the balance of the genesis accounts in the genesis block
834    #[must_use]
835    pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
836        self.genesis_balance = balance.into();
837        self
838    }
839
840    /// Sets the block time to automine blocks
841    #[must_use]
842    pub fn with_blocktime<D: Into<Duration>>(mut self, block_time: Option<D>) -> Self {
843        self.block_time = block_time.map(Into::into);
844        self
845    }
846
847    #[must_use]
848    pub fn with_mixed_mining<D: Into<Duration>>(
849        mut self,
850        mixed_mining: bool,
851        block_time: Option<D>,
852    ) -> Self {
853        self.block_time = block_time.map(Into::into);
854        self.mixed_mining = mixed_mining;
855        self
856    }
857
858    /// If set to `true` auto mining will be disabled
859    #[must_use]
860    pub const fn with_no_mining(mut self, no_mining: bool) -> Self {
861        self.no_mining = no_mining;
862        self
863    }
864
865    /// Sets the slots in an epoch
866    #[must_use]
867    pub const fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self {
868        self.slots_in_an_epoch = slots_in_an_epoch;
869        self
870    }
871
872    /// Sets the port to use
873    #[must_use]
874    pub const fn with_port(mut self, port: u16) -> Self {
875        self.port = port;
876        self
877    }
878
879    /// Sets the ipc path to use
880    ///
881    /// Note: this is a double Option for
882    ///     - `None` -> no ipc
883    ///     - `Some(None)` -> use default path
884    ///     - `Some(Some(path))` -> use custom path
885    #[must_use]
886    pub fn with_ipc(mut self, ipc_path: Option<Option<String>>) -> Self {
887        self.ipc_path = ipc_path;
888        self
889    }
890
891    /// Sets the file path to write the Anvil node's config info to.
892    #[must_use]
893    pub fn set_config_out(mut self, config_out: Option<PathBuf>) -> Self {
894        self.config_out = config_out;
895        self
896    }
897
898    #[must_use]
899    pub const fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self {
900        self.no_storage_caching = no_storage_caching;
901        self
902    }
903
904    /// Sets the `eth_rpc_url` to use when forking (single endpoint convenience).
905    #[must_use]
906    pub fn with_eth_rpc_url<U: Into<String>>(mut self, eth_rpc_url: Option<U>) -> Self {
907        if let Some(url) = eth_rpc_url {
908            self.fork_urls = vec![url.into()];
909        }
910        self
911    }
912
913    /// Sets the fork URLs for load-balanced multi-endpoint forking.
914    #[must_use]
915    pub fn with_fork_urls(mut self, fork_urls: Vec<String>) -> Self {
916        self.fork_urls = fork_urls;
917        self
918    }
919
920    /// Sets the `fork_choice` to use to fork off from based on a block number
921    #[must_use]
922    pub fn with_fork_block_number<U: Into<u64>>(self, fork_block_number: Option<U>) -> Self {
923        self.with_fork_choice(fork_block_number.map(Into::into))
924    }
925
926    /// Sets the `fork_choice` to use to fork off from based on a transaction hash
927    #[must_use]
928    pub fn with_fork_transaction_hash<U: Into<TxHash>>(
929        self,
930        fork_transaction_hash: Option<U>,
931    ) -> Self {
932        self.with_fork_choice(fork_transaction_hash.map(Into::into))
933    }
934
935    /// Sets the `fork_choice` to use to fork off from
936    #[must_use]
937    pub fn with_fork_choice<U: Into<ForkChoice>>(mut self, fork_choice: Option<U>) -> Self {
938        self.fork_choice = fork_choice.map(Into::into);
939        self
940    }
941
942    /// Sets the `fork_chain_id` to use to fork off local cache from
943    #[must_use]
944    pub const fn with_fork_chain_id(mut self, fork_chain_id: Option<U256>) -> Self {
945        self.fork_chain_id = fork_chain_id;
946        self
947    }
948
949    /// Sets the `fork_headers` to use with fork RPC endpoints
950    #[must_use]
951    pub fn with_fork_headers(mut self, headers: Vec<String>) -> Self {
952        self.fork_headers = headers;
953        self
954    }
955
956    /// Sets the `fork_request_timeout` to use for requests
957    #[must_use]
958    pub const fn fork_request_timeout(mut self, fork_request_timeout: Option<Duration>) -> Self {
959        if let Some(fork_request_timeout) = fork_request_timeout {
960            self.fork_request_timeout = fork_request_timeout;
961        }
962        self
963    }
964
965    /// Sets the `fork_request_retries` to use for spurious networks
966    #[must_use]
967    pub const fn fork_request_retries(mut self, fork_request_retries: Option<u32>) -> Self {
968        if let Some(fork_request_retries) = fork_request_retries {
969            self.fork_request_retries = fork_request_retries;
970        }
971        self
972    }
973
974    /// Sets the initial `fork_retry_backoff` for rate limits
975    #[must_use]
976    pub const fn fork_retry_backoff(mut self, fork_retry_backoff: Option<Duration>) -> Self {
977        if let Some(fork_retry_backoff) = fork_retry_backoff {
978            self.fork_retry_backoff = fork_retry_backoff;
979        }
980        self
981    }
982
983    /// Sets the number of assumed available compute units per second
984    ///
985    /// See also, <https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second>
986    #[must_use]
987    pub const fn fork_compute_units_per_second(
988        mut self,
989        compute_units_per_second: Option<u64>,
990    ) -> Self {
991        if let Some(compute_units_per_second) = compute_units_per_second {
992            self.compute_units_per_second = compute_units_per_second;
993        }
994        self
995    }
996
997    /// Sets whether to enable tracing
998    #[must_use]
999    pub const fn with_tracing(mut self, enable_tracing: bool) -> Self {
1000        self.enable_tracing = enable_tracing;
1001        self
1002    }
1003
1004    /// Sets whether to enable steps tracing
1005    #[must_use]
1006    pub const fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self {
1007        self.enable_steps_tracing = enable_steps_tracing;
1008        self
1009    }
1010
1011    /// Sets whether to print `console.log` invocations to stdout.
1012    #[must_use]
1013    pub const fn with_print_logs(mut self, print_logs: bool) -> Self {
1014        self.print_logs = print_logs;
1015        self
1016    }
1017
1018    /// Sets whether to print traces to stdout.
1019    #[must_use]
1020    pub const fn with_print_traces(mut self, print_traces: bool) -> Self {
1021        self.print_traces = print_traces;
1022        self
1023    }
1024
1025    /// Sets whether to enable autoImpersonate
1026    #[must_use]
1027    pub const fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
1028        self.enable_auto_impersonate = enable_auto_impersonate;
1029        self
1030    }
1031
1032    #[must_use]
1033    pub fn with_server_config(mut self, config: ServerConfig) -> Self {
1034        self.server_config = config;
1035        self
1036    }
1037
1038    /// Sets the host the server will listen on
1039    #[must_use]
1040    pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
1041        self.host = if host.is_empty() { vec![IpAddr::V4(Ipv4Addr::LOCALHOST)] } else { host };
1042        self
1043    }
1044
1045    #[must_use]
1046    pub const fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
1047        self.transaction_order = transaction_order;
1048        self
1049    }
1050
1051    /// Returns the ipc path for the ipc endpoint if any
1052    pub fn get_ipc_path(&self) -> Option<String> {
1053        match &self.ipc_path {
1054            Some(path) => path.clone().or_else(|| Some(DEFAULT_IPC_ENDPOINT.to_string())),
1055            None => None,
1056        }
1057    }
1058
1059    /// Prints the config info
1060    pub fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
1061        if let Some(path) = &self.config_out {
1062            let value = self.as_json(fork);
1063            foundry_common::fs::write_json_file(path, &value).wrap_err("failed writing JSON")?;
1064        }
1065        if !self.silent {
1066            sh_println!("{}", self.as_string(fork))?;
1067        }
1068        Ok(())
1069    }
1070
1071    /// Returns the path where the cache file should be stored
1072    ///
1073    /// See also [ Config::foundry_block_cache_file()]
1074    pub fn block_cache_path(&self, block: u64) -> Option<PathBuf> {
1075        if self.no_storage_caching || self.fork_urls.is_empty() {
1076            return None;
1077        }
1078        let chain_id = self.get_chain_id();
1079
1080        Config::foundry_block_cache_file(chain_id, block)
1081    }
1082
1083    /// Sets whether to disable the default create2 deployer
1084    #[must_use]
1085    pub const fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self {
1086        self.disable_default_create2_deployer = yes;
1087        self
1088    }
1089
1090    /// Sets whether to disable pool balance checks
1091    #[must_use]
1092    pub const fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self {
1093        self.disable_pool_balance_checks = yes;
1094        self
1095    }
1096
1097    /// Injects precompiles to `anvil`'s EVM.
1098    #[must_use]
1099    pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self {
1100        self.precompile_factory = Some(Arc::new(factory));
1101        self
1102    }
1103
1104    /// Enable features for provided networks.
1105    #[must_use]
1106    pub const fn with_networks(mut self, networks: NetworkConfigs) -> Self {
1107        self.networks = networks;
1108        self
1109    }
1110
1111    /// Enable Tempo network features.
1112    #[must_use]
1113    pub fn with_tempo(mut self) -> Self {
1114        self.networks = NetworkConfigs::with_tempo();
1115        self
1116    }
1117
1118    /// Enable Optimism network features.
1119    #[cfg(feature = "optimism")]
1120    #[must_use]
1121    pub fn with_optimism(mut self) -> Self {
1122        self.networks = NetworkConfigs::with_optimism();
1123        self
1124    }
1125
1126    /// Makes the node silent to not emit anything on stdout
1127    #[must_use]
1128    pub const fn silent(self) -> Self {
1129        self.set_silent(true)
1130    }
1131
1132    #[must_use]
1133    pub const fn set_silent(mut self, silent: bool) -> Self {
1134        self.silent = silent;
1135        self
1136    }
1137
1138    /// Sets the path where persisted states are cached (used with `max_persisted_states`).
1139    ///
1140    /// Note: This does not control the fork RPC cache location (`storage.json`), which uses
1141    /// `~/.foundry/cache/rpc/<chain>/<block>/` via [`Config::foundry_block_cache_file`].
1142    #[must_use]
1143    pub fn with_cache_path(mut self, cache_path: Option<PathBuf>) -> Self {
1144        self.cache_path = cache_path;
1145        self
1146    }
1147
1148    /// Sets accounts to fund with custom balances on startup.
1149    #[must_use]
1150    pub fn with_funded_accounts(mut self, accounts: HashMap<Address, U256>) -> Self {
1151        self.funded_accounts = accounts;
1152        self
1153    }
1154
1155    /// Configures everything related to env, backend and database and returns the
1156    /// [Backend](mem::Backend)
1157    ///
1158    /// *Note*: only memory based backend for now
1159    pub(crate) async fn setup<N>(&mut self) -> Result<mem::Backend<N>>
1160    where
1161        N: alloy_network::Network<
1162                TxEnvelope = foundry_primitives::FoundryTxEnvelope,
1163                ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope,
1164            >,
1165    {
1166        // configure the revm environment
1167
1168        let mut cfg = CfgEnv::default();
1169        cfg.spec = self.get_hardfork().into();
1170
1171        cfg.chain_id = self.get_chain_id();
1172        cfg.limit_contract_code_size = self.code_size_limit;
1173        // EIP-3607 rejects transactions from senders with deployed code.
1174        // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the
1175        // caller is a contract. So we disable the check by default.
1176        cfg.disable_eip3607 = true;
1177        cfg.disable_block_gas_limit = self.disable_block_gas_limit;
1178
1179        if !self.enable_tx_gas_limit {
1180            cfg.tx_gas_limit_cap = Some(u64::MAX);
1181        }
1182
1183        if let Some(value) = self.memory_limit {
1184            cfg.memory_limit = value;
1185        }
1186
1187        let spec_id = cfg.spec;
1188        let mut evm_env = EvmEnv::new(
1189            cfg,
1190            BlockEnv {
1191                gas_limit: self.gas_limit(),
1192                basefee: self.get_base_fee(),
1193                ..Default::default()
1194            },
1195        );
1196
1197        self.apply_tempo_fork_beneficiary_default(&mut evm_env);
1198
1199        let genesis_timestamp = self.get_genesis_timestamp();
1200        let base_fee_params: BaseFeeParams = self.networks.base_fee_params(genesis_timestamp);
1201
1202        // On Tempo, the base fee follows the chain's hardfork rules instead of EIP-1559.
1203        let tempo_hardfork =
1204            self.networks.is_tempo().then(|| TempoHardfork::from(self.get_hardfork()));
1205
1206        let fees = FeeManager::new(
1207            spec_id,
1208            self.get_base_fee(),
1209            !self.disable_min_priority_fee,
1210            self.get_gas_price(),
1211            self.get_blob_excess_gas_and_price(),
1212            self.get_blob_params(),
1213            base_fee_params,
1214            tempo_hardfork,
1215        );
1216
1217        let (db, fork): (Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>) =
1218            if let Some(eth_rpc_url) = self.fork_urls.first().cloned() {
1219                self.setup_fork_db(eth_rpc_url, &mut evm_env, &fees).await?
1220            } else {
1221                let track_history = self.prune_history.is_state_history_supported();
1222                (Arc::new(TokioRwLock::new(Box::new(StateRootDb::new(track_history)))), None)
1223            };
1224
1225        // if provided use all settings of `genesis.json`
1226        if let Some(ref genesis) = self.genesis {
1227            // --chain-id flag gets precedence over the genesis.json chain id
1228            // <https://github.com/foundry-rs/foundry/issues/10059>
1229            if self.chain_id.is_none() {
1230                evm_env.cfg_env.chain_id = genesis.config.chain_id;
1231            }
1232            evm_env.block_env.timestamp = U256::from(genesis.timestamp);
1233            if let Some(base_fee) = genesis.base_fee_per_gas {
1234                evm_env.block_env.basefee = base_fee.try_into()?;
1235            }
1236            if let Some(number) = genesis.number {
1237                evm_env.block_env.number = U256::from(number);
1238            }
1239            evm_env.block_env.beneficiary = genesis.coinbase;
1240        }
1241
1242        // Fork setup initializes its own timestamp. For a local BSC chain, keep the initial EVM
1243        // and genesis block on the same resolved timestamp so chain precompiles are available
1244        // immediately. Preserve the default timestamp behavior for all other local chains.
1245        let is_bsc = matches!(
1246            NamedChain::try_from(evm_env.cfg_env.chain_id),
1247            Ok(NamedChain::BinanceSmartChain | NamedChain::BinanceSmartChainTestnet)
1248        );
1249        if fork.is_none() && (self.genesis_timestamp.is_some() || is_bsc) {
1250            evm_env.block_env.timestamp = U256::from(genesis_timestamp);
1251        }
1252
1253        self.apply_tempo_fork_beneficiary_default(&mut evm_env);
1254
1255        let genesis = GenesisConfig {
1256            number: self.get_genesis_number(),
1257            timestamp: genesis_timestamp,
1258            balance: self.genesis_balance,
1259            accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(),
1260            genesis_init: self.genesis.clone(),
1261        };
1262
1263        let mut decoder_builder = CallTraceDecoderBuilder::new().with_tempo_hardfork(
1264            self.networks.is_tempo().then(|| TempoHardfork::from(self.get_hardfork())),
1265        );
1266        if self.print_traces {
1267            // if traces should get printed we configure the decoder with the signatures cache
1268            if let Ok(identifier) = SignaturesIdentifier::new(false) {
1269                debug!(target: "node", "using signature identifier");
1270                decoder_builder = decoder_builder.with_signature_identifier(identifier);
1271            }
1272        }
1273
1274        // only memory based backend for now
1275        let backend = mem::Backend::with_genesis(
1276            db,
1277            Arc::new(RwLock::new(evm_env)),
1278            self.networks,
1279            genesis,
1280            fees,
1281            Arc::new(RwLock::new(fork)),
1282            self.enable_steps_tracing,
1283            self.print_logs,
1284            self.print_traces,
1285            Arc::new(decoder_builder.build()),
1286            self.prune_history,
1287            self.max_persisted_states,
1288            self.transaction_block_keeper,
1289            self.block_time,
1290            self.cache_path.clone(),
1291            Arc::new(TokioRwLock::new(self.clone())),
1292        )
1293        .await?;
1294
1295        // Writes the default create2 deployer to the backend,
1296        // if the option is not disabled and we are not forking.
1297        if !self.disable_default_create2_deployer && self.fork_urls.is_empty() {
1298            backend
1299                .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER)
1300                .await
1301                .wrap_err("failed to create default create2 deployer")?;
1302        }
1303
1304        if !self.funded_accounts.is_empty() {
1305            for (address, balance) in &self.funded_accounts {
1306                backend
1307                    .set_balance(*address, *balance)
1308                    .await
1309                    .wrap_err_with(|| format!("failed to fund account {address}"))?;
1310            }
1311        }
1312
1313        Ok(backend)
1314    }
1315
1316    /// Configures everything related to forking based on the passed `eth_rpc_url`:
1317    ///  - returning a tuple of a [ForkedDatabase] wrapped in an [Arc] [RwLock](TokioRwLock) and
1318    ///    [ClientFork] wrapped in an [Option] which can be used in a [Backend](mem::Backend) to
1319    ///    fork from.
1320    ///  - modifying some parameters of the passed `env`
1321    ///  - mutating some members of `self`
1322    pub async fn setup_fork_db(
1323        &mut self,
1324        eth_rpc_url: String,
1325        evm_env: &mut EvmEnv,
1326        fees: &FeeManager,
1327    ) -> Result<(Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>)> {
1328        let (db, config) = self.setup_fork_db_config(eth_rpc_url, evm_env, fees).await?;
1329        let db: Arc<TokioRwLock<Box<dyn Db>>> = Arc::new(TokioRwLock::new(Box::new(db)));
1330        let fork = ClientFork::new(config, Arc::clone(&db));
1331        Ok((db, Some(fork)))
1332    }
1333
1334    /// Configures everything related to forking based on the passed `eth_rpc_url`:
1335    ///  - returning a tuple of a [ForkedDatabase] and [ClientForkConfig] which can be used to build
1336    ///    a [ClientFork] to fork from.
1337    ///  - modifying some parameters of the passed `env`
1338    ///  - mutating some members of `self`
1339    pub async fn setup_fork_db_config(
1340        &mut self,
1341        eth_rpc_url: String,
1342        evm_env: &mut EvmEnv,
1343        fees: &FeeManager,
1344    ) -> Result<(ForkedDatabase<AnyNetwork>, ClientForkConfig)> {
1345        debug!(target: "node", ?eth_rpc_url, "setting up fork db");
1346
1347        // Always bootstrap with the primary URL only to avoid race conditions
1348        // where discovery calls (get_chain_id, find_latest_fork_block, get_block)
1349        // hit different endpoints that may be at different chain tips.
1350        let provider = Arc::new(
1351            ProviderBuilder::new(&eth_rpc_url)
1352                .timeout(self.fork_request_timeout)
1353                .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1354                .compute_units_per_second(self.compute_units_per_second)
1355                .max_retry(self.fork_request_retries)
1356                .headers(self.fork_headers.clone())
1357                .build()
1358                .wrap_err("failed to establish provider to fork url")?,
1359        );
1360
1361        let (fork_block_number, fork_chain_id, force_transactions) = if let Some(fork_choice) =
1362            &self.fork_choice
1363        {
1364            let (fork_block_number, force_transactions) =
1365                derive_block_and_transactions(fork_choice, &provider).await.wrap_err(
1366                    "failed to derive fork block number and force transactions from fork choice",
1367                )?;
1368            let chain_id = if let Some(chain_id) = self.fork_chain_id {
1369                Some(chain_id)
1370            } else if self.hardfork.is_none() {
1371                let chain_id =
1372                    provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?;
1373                Some(U256::from(chain_id))
1374            } else {
1375                None
1376            };
1377
1378            (fork_block_number, chain_id, force_transactions)
1379        } else {
1380            // pick the last block number but also ensure it's not pending anymore
1381            let bn = find_latest_fork_block(&provider)
1382                .await
1383                .wrap_err("failed to get fork block number")?;
1384            (bn, None, None)
1385        };
1386
1387        let block = provider
1388            .get_block(BlockNumberOrTag::Number(fork_block_number).into())
1389            .await
1390            .wrap_err("failed to get fork block")?;
1391
1392        let block = if let Some(block) = block {
1393            block
1394        } else {
1395            if let Ok(latest_block) = provider.get_block_number().await {
1396                let mut message = format!(
1397                    "Failed to get block for block number: {fork_block_number}\n\
1398latest block number: {latest_block}"
1399                );
1400                // If the `eth_getBlockByNumber` call succeeds, but returns null instead of
1401                // the block, and the block number is less than equal the latest block, then
1402                // the user is forking from a non-archive node with an older block number.
1403                if fork_block_number <= latest_block {
1404                    message.push_str(&format!("\n{NON_ARCHIVE_NODE_WARNING}"));
1405                }
1406                eyre::bail!("{message}");
1407            }
1408            eyre::bail!("failed to get block for block number: {fork_block_number}");
1409        };
1410
1411        let gas_limit = self.fork_gas_limit(&block);
1412        self.gas_limit = Some(gas_limit);
1413
1414        // Cache identity must describe the remote fork block, not local execution overrides that
1415        // can change after mining (for example, the locally advanced base fee).
1416        let cache_block_env: BlockEnv = block_env_from_header(&block.header);
1417
1418        evm_env.block_env = BlockEnv {
1419            gas_limit,
1420            // Keep previous `coinbase` and `basefee` value
1421            beneficiary: evm_env.block_env.beneficiary,
1422            basefee: evm_env.block_env.basefee,
1423            ..block_env_from_header(&block.header)
1424        };
1425
1426        // Determine chain_id early so we can use it consistently
1427        let chain_id = if let Some(chain_id) = self.chain_id {
1428            chain_id
1429        } else {
1430            let chain_id = if let Some(fork_chain_id) = fork_chain_id {
1431                fork_chain_id.to()
1432            } else {
1433                provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?
1434            };
1435
1436            // need to update the dev signers and env with the chain id
1437            self.set_chain_id(Some(chain_id));
1438            evm_env.cfg_env.chain_id = chain_id;
1439            chain_id
1440        };
1441
1442        // Auto-detect hardfork from chain activation data if not explicitly set.
1443        if self.hardfork.is_none()
1444            && let Some(hardfork) =
1445                FoundryHardfork::from_chain_and_timestamp(chain_id, block.header.timestamp())
1446        {
1447            evm_env.cfg_env.spec = SpecId::from(hardfork);
1448            self.hardfork = Some(hardfork);
1449        }
1450
1451        // The fee manager was built before the fork hardfork was known, so refresh the Tempo
1452        // hardfork it uses for base fee calculations.
1453        if self.networks.is_tempo() {
1454            fees.set_tempo_hardfork(Some(TempoHardfork::from(self.get_hardfork())));
1455        }
1456
1457        // if not set explicitly we use the base fee of the latest block
1458        if self.base_fee.is_none()
1459            && let Some(base_fee) = block.header.base_fee_per_gas()
1460        {
1461            self.base_fee = Some(base_fee);
1462            evm_env.block_env.basefee = base_fee;
1463            // this is the base fee of the current block, but we need the base fee of
1464            // the next block
1465            let next_block_base_fee = fees.get_next_block_base_fee_per_gas(
1466                block.header.gas_used(),
1467                gas_limit,
1468                block.header.base_fee_per_gas().unwrap_or_default(),
1469            );
1470
1471            // update next base fee
1472            fees.set_base_fee(next_block_base_fee);
1473        }
1474
1475        if let (Some(blob_excess_gas), Some(blob_gas_used)) =
1476            (block.header.excess_blob_gas(), block.header.blob_gas_used())
1477        {
1478            // Derive blob params using the fork block timestamp regardless of explicit base fee.
1479            let blob_params = get_blob_params(chain_id, block.header.timestamp());
1480
1481            evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
1482                blob_excess_gas,
1483                blob_params.update_fraction as u64,
1484            ));
1485
1486            fees.set_blob_params(blob_params);
1487
1488            let next_block_blob_excess_gas =
1489                fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used);
1490            fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
1491                next_block_blob_excess_gas,
1492                blob_params.update_fraction as u64,
1493            ));
1494        }
1495
1496        // use remote gas price
1497        if self.gas_price.is_none()
1498            && let Ok(gas_price) = provider.get_gas_price().await
1499        {
1500            self.gas_price = Some(gas_price);
1501            fees.set_gas_price(gas_price);
1502        }
1503
1504        let block_hash = block.header.hash;
1505
1506        let override_chain_id = self.chain_id;
1507        // apply changes such as difficulty -> prevrandao and chain specifics for current chain id
1508        apply_chain_and_block_specific_env_changes::<AnyNetwork, _, _>(
1509            evm_env,
1510            &block,
1511            self.networks,
1512        );
1513
1514        let meta = BlockchainDbMeta::new(cache_block_env, eth_rpc_url.clone());
1515        let block_chain_db = if self.fork_chain_id.is_some() {
1516            BlockchainDb::new_skip_check(meta, self.block_cache_path(fork_block_number))
1517        } else {
1518            BlockchainDb::new(meta, self.block_cache_path(fork_block_number))
1519        };
1520
1521        // After bootstrap, rebuild the provider with round-robin if multiple URLs are
1522        // configured. This ensures bootstrap used only the primary endpoint for consistency,
1523        // while ongoing requests are distributed across all endpoints.
1524        let provider = if self.fork_urls.len() > 1 {
1525            debug!(target: "node", urls=?self.fork_urls, "using multi-endpoint round-robin provider");
1526            Arc::new(
1527                ProviderBuilder::new(&eth_rpc_url)
1528                    .timeout(self.fork_request_timeout)
1529                    .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1530                    .compute_units_per_second(self.compute_units_per_second)
1531                    .max_retry(self.fork_request_retries)
1532                    .headers(self.fork_headers.clone())
1533                    .build_fallback(self.fork_urls.clone())
1534                    .wrap_err("failed to establish round-robin provider to fork urls")?,
1535            )
1536        } else {
1537            provider
1538        };
1539
1540        // This will spawn the background thread that will use the provider to fetch
1541        // blockchain data from the other client
1542        let backend = SharedBackend::spawn_backend(
1543            Arc::clone(&provider),
1544            block_chain_db.clone(),
1545            Some(fork_block_number.into()),
1546        )
1547        .await;
1548
1549        let config = ClientForkConfig {
1550            fork_urls: self.fork_urls.clone(),
1551            block_number: fork_block_number,
1552            block_hash,
1553            transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()),
1554            provider,
1555            chain_id,
1556            override_chain_id,
1557            hardfork: self.hardfork,
1558            timestamp: block.header.timestamp(),
1559            base_fee: block.header.base_fee_per_gas().map(|g| g as u128),
1560            timeout: self.fork_request_timeout,
1561            retries: self.fork_request_retries,
1562            backoff: self.fork_retry_backoff,
1563            compute_units_per_second: self.compute_units_per_second,
1564            headers: self.fork_headers.clone(),
1565            total_difficulty: block.header.total_difficulty.unwrap_or_default(),
1566            blob_gas_used: block.header.blob_gas_used().map(|g| g as u128),
1567            blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price,
1568            force_transactions,
1569        };
1570
1571        debug!(target: "node", fork_number=config.block_number, fork_hash=%config.block_hash, "set up fork db");
1572
1573        let mut db = ForkedDatabase::new(backend, block_chain_db);
1574
1575        // need to insert the forked block's hash
1576        db.insert_block_hash(U256::from(config.block_number), config.block_hash);
1577
1578        Ok((db, config))
1579    }
1580
1581    /// we only use the gas limit value of the block if it is non-zero and the block gas
1582    /// limit is enabled, since there are networks where this is not used and is always
1583    /// `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>
1584    pub(crate) fn fork_gas_limit<B: BlockResponse<Header: BlockHeader>>(&self, block: &B) -> u64 {
1585        if !self.disable_block_gas_limit {
1586            if let Some(gas_limit) = self.gas_limit {
1587                return gas_limit;
1588            } else if block.header().gas_limit() > 0 {
1589                return block.header().gas_limit();
1590            }
1591        }
1592
1593        u64::MAX
1594    }
1595
1596    /// Returns the gas limit for a non forked anvil instance
1597    ///
1598    /// Checks the config for the `disable_block_gas_limit` flag
1599    pub(crate) fn gas_limit(&self) -> u64 {
1600        if self.disable_block_gas_limit {
1601            return u64::MAX;
1602        }
1603
1604        self.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT)
1605    }
1606}
1607
1608pub(crate) const fn tempo_default_base_fee(hardfork: TempoHardfork) -> u64 {
1609    if hardfork.is_t1() { TEMPO_T1_BASE_FEE } else { TEMPO_T0_BASE_FEE }
1610}
1611
1612/// If the fork choice is a block number, simply return it with an empty list of transactions.
1613/// If the fork choice is a transaction hash, determine the block that the transaction was mined in,
1614/// and return the block number before the fork block along with all transactions in the fork block
1615/// that are before (and including) the fork transaction.
1616async fn derive_block_and_transactions(
1617    fork_choice: &ForkChoice,
1618    provider: &Arc<RetryProvider>,
1619) -> eyre::Result<(BlockNumber, Option<Vec<PoolTransaction<FoundryTxEnvelope>>>)> {
1620    match fork_choice {
1621        ForkChoice::Block(block_number) => {
1622            let block_number = *block_number;
1623            if block_number >= 0 {
1624                return Ok((block_number as u64, None));
1625            }
1626            // subtract from latest block number
1627            let latest = provider.get_block_number().await?;
1628
1629            Ok((block_number.saturating_add(latest as i128) as u64, None))
1630        }
1631        ForkChoice::Transaction(transaction_hash) => {
1632            // Determine the block that this transaction was mined in
1633            let transaction = provider
1634                .get_transaction_by_hash(transaction_hash.0.into())
1635                .await?
1636                .ok_or_else(|| eyre::eyre!("failed to get fork transaction by hash"))?;
1637            let transaction_block_number = transaction.block_number().ok_or_else(|| {
1638                eyre::eyre!("fork transaction is not mined yet (no block number)")
1639            })?;
1640
1641            // Get the block pertaining to the fork transaction
1642            let transaction_block = provider
1643                .get_block_by_number(transaction_block_number.into())
1644                .full()
1645                .await?
1646                .ok_or_else(|| eyre::eyre!("failed to get fork block by number"))?;
1647
1648            // Filter out transactions that are after the fork transaction
1649            let filtered_transactions = transaction_block
1650                .transactions
1651                .as_transactions()
1652                .ok_or_else(|| eyre::eyre!("failed to get transactions from full fork block"))?
1653                .iter()
1654                .take_while_inclusive(|&transaction| transaction.tx_hash() != transaction_hash.0)
1655                .collect::<Vec<_>>();
1656
1657            // Convert the transactions to PoolTransactions
1658            let force_transactions = filtered_transactions
1659                .iter()
1660                .map(|&transaction| PoolTransaction::try_from(transaction.clone()))
1661                .collect::<Result<Vec<_>, _>>()
1662                .map_err(|e| eyre::eyre!("Err converting to pool transactions {e}"))?;
1663            Ok((transaction_block_number.saturating_sub(1), Some(force_transactions)))
1664        }
1665    }
1666}
1667
1668/// Fork delimiter used to specify which block or transaction to fork from.
1669#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1670pub enum ForkChoice {
1671    /// Block number to fork from.
1672    ///
1673    /// If negative, the given value is subtracted from the `latest` block number.
1674    Block(i128),
1675    /// Transaction hash to fork from.
1676    Transaction(TxHash),
1677}
1678
1679impl ForkChoice {
1680    /// Returns the block number to fork from
1681    pub const fn block_number(&self) -> Option<i128> {
1682        match self {
1683            Self::Block(block_number) => Some(*block_number),
1684            Self::Transaction(_) => None,
1685        }
1686    }
1687
1688    /// Returns the transaction hash to fork from
1689    pub const fn transaction_hash(&self) -> Option<TxHash> {
1690        match self {
1691            Self::Block(_) => None,
1692            Self::Transaction(transaction_hash) => Some(*transaction_hash),
1693        }
1694    }
1695}
1696
1697/// Convert a transaction hash into a ForkChoice
1698impl From<TxHash> for ForkChoice {
1699    fn from(tx_hash: TxHash) -> Self {
1700        Self::Transaction(tx_hash)
1701    }
1702}
1703
1704/// Convert a decimal block number into a ForkChoice
1705impl From<u64> for ForkChoice {
1706    fn from(block: u64) -> Self {
1707        Self::Block(block as i128)
1708    }
1709}
1710
1711#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1712pub struct PruneStateHistoryConfig {
1713    pub enabled: bool,
1714    pub max_memory_history: Option<usize>,
1715}
1716
1717impl PruneStateHistoryConfig {
1718    /// Returns `true` if writing state history is supported
1719    pub const fn is_state_history_supported(&self) -> bool {
1720        if !self.enabled {
1721            return true;
1722        }
1723
1724        match self.max_memory_history {
1725            Some(limit) => limit > 0,
1726            None => false,
1727        }
1728    }
1729
1730    /// Returns true if this setting was enabled.
1731    pub const fn is_config_enabled(&self) -> bool {
1732        self.enabled
1733    }
1734
1735    pub fn from_args(val: Option<Option<usize>>) -> Self {
1736        val.map(|max_memory_history| Self {
1737            enabled: true,
1738            max_memory_history: max_memory_history.filter(|limit| *limit > 0),
1739        })
1740        .unwrap_or_default()
1741    }
1742}
1743
1744/// Can create dev accounts
1745#[derive(Clone, Debug)]
1746pub struct AccountGenerator {
1747    chain_id: u64,
1748    amount: usize,
1749    phrase: String,
1750    derivation_path: Option<String>,
1751}
1752
1753impl AccountGenerator {
1754    pub fn new(amount: usize) -> Self {
1755        Self {
1756            chain_id: CHAIN_ID,
1757            amount,
1758            phrase: Mnemonic::<English>::new(&mut thread_rng()).to_phrase(),
1759            derivation_path: None,
1760        }
1761    }
1762
1763    #[must_use]
1764    pub fn phrase(mut self, phrase: impl Into<String>) -> Self {
1765        self.phrase = phrase.into();
1766        self
1767    }
1768
1769    fn get_phrase(&self) -> &str {
1770        &self.phrase
1771    }
1772
1773    #[must_use]
1774    pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
1775        self.chain_id = chain_id.into();
1776        self
1777    }
1778
1779    #[must_use]
1780    pub fn derivation_path(mut self, derivation_path: impl Into<String>) -> Self {
1781        let mut derivation_path = derivation_path.into();
1782        if !derivation_path.ends_with('/') {
1783            derivation_path.push('/');
1784        }
1785        self.derivation_path = Some(derivation_path);
1786        self
1787    }
1788
1789    fn get_derivation_path(&self) -> &str {
1790        self.derivation_path.as_deref().unwrap_or("m/44'/60'/0'/0/")
1791    }
1792}
1793
1794impl AccountGenerator {
1795    pub fn generate(&self) -> eyre::Result<Vec<PrivateKeySigner>> {
1796        let builder = MnemonicBuilder::<English>::default().phrase(self.phrase.as_str());
1797
1798        // use the derivation path
1799        let derivation_path = self.get_derivation_path();
1800
1801        let mut wallets = Vec::with_capacity(self.amount);
1802        for idx in 0..self.amount {
1803            let builder =
1804                builder.clone().derivation_path(format!("{derivation_path}{idx}")).unwrap();
1805            let wallet = builder.build()?.with_chain_id(Some(self.chain_id));
1806            wallets.push(wallet)
1807        }
1808        Ok(wallets)
1809    }
1810}
1811
1812/// Returns the path to anvil dir `~/.foundry/anvil`
1813pub fn anvil_dir() -> Option<PathBuf> {
1814    Config::foundry_dir().map(|p| p.join("anvil"))
1815}
1816
1817/// Returns the root path to anvil's temporary storage `~/.foundry/anvil/`
1818pub fn anvil_tmp_dir() -> Option<PathBuf> {
1819    anvil_dir().map(|p| p.join("tmp"))
1820}
1821
1822/// Finds the latest appropriate block to fork
1823///
1824/// This fetches the "latest" block and checks whether the `Block` is fully populated (`hash` field
1825/// 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>
1826async fn find_latest_fork_block<P: Provider<AnyNetwork>>(
1827    provider: P,
1828) -> Result<u64, TransportError> {
1829    let mut num = provider.get_block_number().await?;
1830
1831    // walk back from the head of the chain, but at most 2 blocks, which should be more than enough
1832    // leeway
1833    for _ in 0..2 {
1834        if let Some(block) = provider.get_block(num.into()).await?
1835            && !block.header.hash.is_zero()
1836        {
1837            break;
1838        }
1839        // block not actually finalized, so we try the block before
1840        num = num.saturating_sub(1)
1841    }
1842
1843    Ok(num)
1844}
1845
1846#[cfg(test)]
1847mod tests {
1848    use super::*;
1849
1850    #[test]
1851    fn test_prune_history() {
1852        let config = PruneStateHistoryConfig::default();
1853        assert!(config.is_state_history_supported());
1854        let config = PruneStateHistoryConfig::from_args(Some(None));
1855        assert!(!config.is_state_history_supported());
1856        let config = PruneStateHistoryConfig::from_args(Some(Some(0)));
1857        assert!(config.is_config_enabled());
1858        assert!(!config.is_state_history_supported());
1859        let config = PruneStateHistoryConfig::from_args(Some(Some(10)));
1860        assert!(config.is_state_history_supported());
1861    }
1862
1863    #[cfg(feature = "optimism")]
1864    #[test]
1865    fn set_chain_id_updates_network_config() {
1866        let mut config = NodeConfig::test();
1867        config.set_chain_id(Some(10u64));
1868
1869        assert!(config.networks.is_optimism());
1870    }
1871
1872    #[test]
1873    fn get_hardfork_on_tempo_never_returns_non_tempo_variant() {
1874        // Post-Shanghai timestamp on Ethereum mainnet.
1875        let shanghai_ts = 1_681_338_455u64;
1876
1877        let config = NodeConfig::test_tempo()
1878            .with_chain_id(Some(1u64))
1879            .with_genesis_timestamp(Some(shanghai_ts));
1880
1881        assert!(config.networks.is_tempo());
1882        assert!(matches!(config.get_hardfork(), FoundryHardfork::Tempo(_)));
1883    }
1884
1885    #[test]
1886    fn get_hardfork_on_local_tempo_defaults_to_latest_active() {
1887        let config = NodeConfig::test_tempo();
1888
1889        assert_eq!(config.get_hardfork(), FoundryHardfork::Tempo(latest_active_tempo_hardfork()));
1890    }
1891}