anvil/
config.rs

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