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::MemDb},
15};
16use alloy_consensus::BlockHeader;
17use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams};
18use alloy_evm::EvmEnv;
19use alloy_genesis::Genesis;
20use alloy_network::{AnyNetwork, BlockResponse, TransactionResponse};
21use alloy_primitives::{BlockNumber, TxHash, U256, hex, map::HashMap, utils::Unit};
22use alloy_provider::Provider;
23use alloy_rpc_types::BlockNumberOrTag;
24use alloy_signer::Signer;
25use alloy_signer_local::{
26 MnemonicBuilder, PrivateKeySigner,
27 coins_bip39::{English, Mnemonic},
28};
29use alloy_transport::TransportError;
30use anvil_server::ServerConfig;
31use eyre::{Context, Result};
32use foundry_common::{
33 ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT,
34 provider::{ProviderBuilder, RetryProvider},
35};
36use foundry_config::Config;
37use foundry_evm::{
38 backend::{BlockchainDb, BlockchainDbMeta, SharedBackend},
39 constants::DEFAULT_CREATE2_DEPLOYER,
40 hardfork::{FoundryHardfork, OpHardfork},
41 utils::{
42 apply_chain_and_block_specific_env_changes, block_env_from_header,
43 get_blob_base_fee_update_fraction,
44 },
45};
46use foundry_primitives::FoundryTxEnvelope;
47use itertools::Itertools;
48use parking_lot::RwLock;
49use rand_08::thread_rng;
50use revm::{
51 context::{BlockEnv, CfgEnv},
52 context_interface::block::BlobExcessGasAndPrice,
53 primitives::hardfork::SpecId,
54};
55use serde_json::{Value, json};
56use std::{
57 fmt::Write as FmtWrite,
58 net::{IpAddr, Ipv4Addr},
59 path::PathBuf,
60 sync::Arc,
61 time::Duration,
62};
63use tempo_chainspec::hardfork::TempoHardfork;
64use tokio::sync::RwLock as TokioRwLock;
65use yansi::Paint;
66
67pub use foundry_common::version::SHORT_VERSION as VERSION_MESSAGE;
68use foundry_evm::{
69 traces::{CallTraceDecoderBuilder, identifier::SignaturesIdentifier},
70 utils::get_blob_params,
71};
72use foundry_evm_networks::NetworkConfigs;
73
74pub const NODE_PORT: u16 = 8545;
76pub const CHAIN_ID: u64 = 31337;
78pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000;
80pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
82
83pub const DEFAULT_IPC_ENDPOINT: &str =
85 if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };
86
87const BANNER: &str = r"
88 _ _
89 (_) | |
90 __ _ _ __ __ __ _ | |
91 / _` | | '_ \ \ \ / / | | | |
92 | (_| | | | | | \ V / | | | |
93 \__,_| |_| |_| \_/ |_| |_|
94";
95
96#[derive(Clone, Debug)]
98pub struct NodeConfig {
99 pub chain_id: Option<u64>,
101 pub gas_limit: Option<u64>,
103 pub disable_block_gas_limit: bool,
105 pub enable_tx_gas_limit: bool,
107 pub gas_price: Option<u128>,
109 pub base_fee: Option<u64>,
111 pub disable_min_priority_fee: bool,
113 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
115 pub hardfork: Option<FoundryHardfork>,
117 pub genesis_accounts: Vec<PrivateKeySigner>,
119 pub genesis_balance: U256,
121 pub genesis_timestamp: Option<u64>,
123 pub genesis_block_number: Option<u64>,
125 pub signer_accounts: Vec<PrivateKeySigner>,
127 pub block_time: Option<Duration>,
129 pub no_mining: bool,
131 pub mixed_mining: bool,
133 pub port: u16,
135 pub max_transactions: usize,
137 pub eth_rpc_url: Option<String>,
139 pub fork_choice: Option<ForkChoice>,
141 pub fork_headers: Vec<String>,
143 pub fork_chain_id: Option<U256>,
145 pub account_generator: Option<AccountGenerator>,
147 pub enable_tracing: bool,
149 pub no_storage_caching: bool,
151 pub server_config: ServerConfig,
153 pub host: Vec<IpAddr>,
155 pub transaction_order: TransactionOrder,
157 pub config_out: Option<PathBuf>,
159 pub genesis: Option<Genesis>,
161 pub fork_request_timeout: Duration,
163 pub fork_request_retries: u32,
165 pub fork_retry_backoff: Duration,
167 pub compute_units_per_second: u64,
169 pub ipc_path: Option<Option<String>>,
171 pub enable_steps_tracing: bool,
173 pub print_logs: bool,
175 pub print_traces: bool,
177 pub enable_auto_impersonate: bool,
179 pub code_size_limit: Option<usize>,
181 pub prune_history: PruneStateHistoryConfig,
185 pub max_persisted_states: Option<usize>,
187 pub init_state: Option<SerializableState>,
189 pub transaction_block_keeper: Option<usize>,
191 pub disable_default_create2_deployer: bool,
193 pub disable_pool_balance_checks: bool,
195 pub slots_in_an_epoch: u64,
197 pub memory_limit: Option<u64>,
199 pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
201 pub networks: NetworkConfigs,
203 pub silent: bool,
205 pub cache_path: Option<PathBuf>,
208}
209
210impl NodeConfig {
211 fn as_string(&self, fork: Option<&ClientFork>) -> String {
212 let mut s: String = String::new();
213 let _ = write!(s, "\n{}", BANNER.green());
214 let _ = write!(s, "\n {VERSION_MESSAGE}");
215 let _ = write!(s, "\n {}", "https://github.com/foundry-rs/foundry".green());
216
217 let _ = write!(
218 s,
219 r#"
220
221Available Accounts
222==================
223"#
224 );
225 let balance = alloy_primitives::utils::format_ether(self.genesis_balance);
226 for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
227 write!(s, "\n({idx}) {} ({balance} ETH)", wallet.address()).unwrap();
228 }
229
230 let _ = write!(
231 s,
232 r#"
233
234Private Keys
235==================
236"#
237 );
238
239 for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
240 let hex = hex::encode(wallet.credential().to_bytes());
241 let _ = write!(s, "\n({idx}) 0x{hex}");
242 }
243
244 if let Some(generator) = &self.account_generator {
245 let _ = write!(
246 s,
247 r#"
248
249Wallet
250==================
251Mnemonic: {}
252Derivation path: {}
253"#,
254 generator.phrase,
255 generator.get_derivation_path()
256 );
257 }
258
259 if let Some(fork) = fork {
260 let _ = write!(
261 s,
262 r#"
263
264Fork
265==================
266Endpoint: {}
267Block number: {}
268Block hash: {:?}
269Chain ID: {}
270"#,
271 fork.eth_rpc_url(),
272 fork.block_number(),
273 fork.block_hash(),
274 fork.chain_id()
275 );
276
277 if let Some(tx_hash) = fork.transaction_hash() {
278 let _ = writeln!(s, "Transaction hash: {tx_hash}");
279 }
280 } else {
281 let _ = write!(
282 s,
283 r#"
284
285Chain ID
286==================
287
288{}
289"#,
290 self.get_chain_id().green()
291 );
292 }
293
294 if (SpecId::from(self.get_hardfork()) as u8) < (SpecId::LONDON as u8) {
295 let _ = write!(
296 s,
297 r#"
298Gas Price
299==================
300
301{}
302"#,
303 self.get_gas_price().green()
304 );
305 } else {
306 let _ = write!(
307 s,
308 r#"
309Base Fee
310==================
311
312{}
313"#,
314 self.get_base_fee().green()
315 );
316 }
317
318 let _ = write!(
319 s,
320 r#"
321Gas Limit
322==================
323
324{}
325"#,
326 {
327 if self.disable_block_gas_limit {
328 "Disabled".to_string()
329 } else {
330 self.gas_limit.map(|l| l.to_string()).unwrap_or_else(|| {
331 if self.fork_choice.is_some() {
332 "Forked".to_string()
333 } else {
334 DEFAULT_GAS_LIMIT.to_string()
335 }
336 })
337 }
338 }
339 .green()
340 );
341
342 let _ = write!(
343 s,
344 r#"
345Genesis Timestamp
346==================
347
348{}
349"#,
350 self.get_genesis_timestamp().green()
351 );
352
353 let _ = write!(
354 s,
355 r#"
356Genesis Number
357==================
358
359{}
360"#,
361 self.get_genesis_number().green()
362 );
363
364 s
365 }
366
367 fn as_json(&self, fork: Option<&ClientFork>) -> Value {
368 let mut wallet_description = HashMap::new();
369 let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
370 let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
371
372 for wallet in &self.genesis_accounts {
373 available_accounts.push(format!("{:?}", wallet.address()));
374 private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
375 }
376
377 if let Some(generator) = &self.account_generator {
378 let phrase = generator.get_phrase().to_string();
379 let derivation_path = generator.get_derivation_path().to_string();
380
381 wallet_description.insert("derivation_path".to_string(), derivation_path);
382 wallet_description.insert("mnemonic".to_string(), phrase);
383 };
384
385 let gas_limit = match self.gas_limit {
386 Some(_) | None if self.disable_block_gas_limit => Some(u64::MAX.to_string()),
388 Some(limit) => Some(limit.to_string()),
389 _ => None,
390 };
391
392 if let Some(fork) = fork {
393 json!({
394 "available_accounts": available_accounts,
395 "private_keys": private_keys,
396 "endpoint": fork.eth_rpc_url(),
397 "block_number": fork.block_number(),
398 "block_hash": fork.block_hash(),
399 "chain_id": fork.chain_id(),
400 "wallet": wallet_description,
401 "base_fee": format!("{}", self.get_base_fee()),
402 "gas_price": format!("{}", self.get_gas_price()),
403 "gas_limit": gas_limit,
404 })
405 } else {
406 json!({
407 "available_accounts": available_accounts,
408 "private_keys": private_keys,
409 "wallet": wallet_description,
410 "base_fee": format!("{}", self.get_base_fee()),
411 "gas_price": format!("{}", self.get_gas_price()),
412 "gas_limit": gas_limit,
413 "genesis_timestamp": format!("{}", self.get_genesis_timestamp()),
414 })
415 }
416 }
417}
418
419impl NodeConfig {
420 #[doc(hidden)]
423 pub fn test() -> Self {
424 Self { enable_tracing: true, port: 0, silent: true, ..Default::default() }
425 }
426
427 #[doc(hidden)]
429 pub fn test_tempo() -> Self {
430 Self { networks: NetworkConfigs::with_tempo(), ..Self::test() }
431 }
432
433 pub fn empty_state() -> Self {
435 Self {
436 genesis_accounts: vec![],
437 signer_accounts: vec![],
438 disable_default_create2_deployer: true,
439 ..Default::default()
440 }
441 }
442}
443
444impl Default for NodeConfig {
445 fn default() -> Self {
446 let genesis_accounts = AccountGenerator::new(10)
448 .phrase(DEFAULT_MNEMONIC)
449 .generate()
450 .expect("Invalid mnemonic.");
451 Self {
452 chain_id: None,
453 gas_limit: None,
454 disable_block_gas_limit: false,
455 enable_tx_gas_limit: false,
456 gas_price: None,
457 hardfork: None,
458 signer_accounts: genesis_accounts.clone(),
459 genesis_timestamp: None,
460 genesis_block_number: None,
461 genesis_accounts,
462 genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)),
464 block_time: None,
465 no_mining: false,
466 mixed_mining: false,
467 port: NODE_PORT,
468 max_transactions: 1_000,
469 eth_rpc_url: None,
470 fork_choice: None,
471 account_generator: None,
472 base_fee: None,
473 disable_min_priority_fee: false,
474 blob_excess_gas_and_price: None,
475 enable_tracing: true,
476 enable_steps_tracing: false,
477 print_logs: true,
478 print_traces: false,
479 enable_auto_impersonate: false,
480 no_storage_caching: false,
481 server_config: Default::default(),
482 host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
483 transaction_order: Default::default(),
484 config_out: None,
485 genesis: None,
486 fork_request_timeout: REQUEST_TIMEOUT,
487 fork_headers: vec![],
488 fork_request_retries: 5,
489 fork_retry_backoff: Duration::from_millis(1_000),
490 fork_chain_id: None,
491 compute_units_per_second: ALCHEMY_FREE_TIER_CUPS,
493 ipc_path: None,
494 code_size_limit: None,
495 prune_history: Default::default(),
496 max_persisted_states: None,
497 init_state: None,
498 transaction_block_keeper: None,
499 disable_default_create2_deployer: false,
500 disable_pool_balance_checks: false,
501 slots_in_an_epoch: 32,
502 memory_limit: None,
503 precompile_factory: None,
504 networks: Default::default(),
505 silent: false,
506 cache_path: None,
507 }
508 }
509}
510
511impl NodeConfig {
512 #[must_use]
514 pub fn with_memory_limit(mut self, mems_value: Option<u64>) -> Self {
515 self.memory_limit = mems_value;
516 self
517 }
518
519 pub fn get_base_fee(&self) -> u64 {
523 let default = if self.networks.is_tempo() {
524 TempoHardfork::from(self.get_hardfork()).base_fee()
525 } else {
526 INITIAL_BASE_FEE
527 };
528 self.base_fee
529 .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64)))
530 .unwrap_or(default)
531 }
532
533 pub fn get_gas_price(&self) -> u128 {
537 let default = if self.networks.is_tempo() {
538 TempoHardfork::from(self.get_hardfork()).base_fee() as u128
539 } else {
540 INITIAL_GAS_PRICE
541 };
542 self.gas_price.unwrap_or(default)
543 }
544
545 pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice {
546 if let Some(value) = self.blob_excess_gas_and_price {
547 value
548 } else {
549 let excess_blob_gas =
550 self.genesis.as_ref().and_then(|g| g.excess_blob_gas).unwrap_or(0);
551 BlobExcessGasAndPrice::new(
552 excess_blob_gas,
553 get_blob_base_fee_update_fraction(
554 self.get_chain_id(),
555 self.get_genesis_timestamp(),
556 ),
557 )
558 }
559 }
560
561 pub fn get_blob_params(&self) -> BlobParams {
563 get_blob_params(self.get_chain_id(), self.get_genesis_timestamp())
564 }
565
566 pub fn get_hardfork(&self) -> FoundryHardfork {
568 if let Some(hardfork) = self.hardfork {
569 return hardfork;
570 }
571 if self.networks.is_optimism() {
572 return OpHardfork::default().into();
573 }
574 if self.networks.is_tempo() {
575 return TempoHardfork::default().into();
576 }
577 EthereumHardfork::default().into()
578 }
579
580 #[must_use]
582 pub fn with_code_size_limit(mut self, code_size_limit: Option<usize>) -> Self {
583 self.code_size_limit = code_size_limit;
584 self
585 }
586 #[must_use]
588 pub fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self {
589 if disable_code_size_limit {
590 self.code_size_limit = Some(usize::MAX);
591 }
592 self
593 }
594
595 #[must_use]
597 pub fn with_init_state(mut self, init_state: Option<SerializableState>) -> Self {
598 self.init_state = init_state;
599 self
600 }
601
602 #[must_use]
604 #[cfg(feature = "cmd")]
605 pub fn with_init_state_path(mut self, path: impl AsRef<std::path::Path>) -> Self {
606 self.init_state = crate::cmd::StateFile::parse_path(path).ok().and_then(|file| file.state);
607 self
608 }
609
610 #[must_use]
612 pub fn with_chain_id<U: Into<u64>>(mut self, chain_id: Option<U>) -> Self {
613 self.set_chain_id(chain_id);
614 self
615 }
616
617 pub fn get_chain_id(&self) -> u64 {
619 self.chain_id
620 .or_else(|| self.genesis.as_ref().map(|g| g.config.chain_id))
621 .unwrap_or(CHAIN_ID)
622 }
623
624 pub fn set_chain_id(&mut self, chain_id: Option<impl Into<u64>>) {
626 self.chain_id = chain_id.map(Into::into);
627 let chain_id = self.get_chain_id();
628 self.networks.with_chain_id(chain_id);
629 self.genesis_accounts.iter_mut().for_each(|wallet| {
630 *wallet = wallet.clone().with_chain_id(Some(chain_id));
631 });
632 self.signer_accounts.iter_mut().for_each(|wallet| {
633 *wallet = wallet.clone().with_chain_id(Some(chain_id));
634 })
635 }
636
637 #[must_use]
639 pub fn with_gas_limit(mut self, gas_limit: Option<u64>) -> Self {
640 self.gas_limit = gas_limit;
641 self
642 }
643
644 #[must_use]
648 pub fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self {
649 self.disable_block_gas_limit = disable_block_gas_limit;
650 self
651 }
652
653 #[must_use]
657 pub fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self {
658 self.enable_tx_gas_limit = enable_tx_gas_limit;
659 self
660 }
661
662 #[must_use]
664 pub fn with_gas_price(mut self, gas_price: Option<u128>) -> Self {
665 self.gas_price = gas_price;
666 self
667 }
668
669 #[must_use]
671 pub fn set_pruned_history(mut self, prune_history: Option<Option<usize>>) -> Self {
672 self.prune_history = PruneStateHistoryConfig::from_args(prune_history);
673 self
674 }
675
676 #[must_use]
678 pub fn with_max_persisted_states<U: Into<usize>>(
679 mut self,
680 max_persisted_states: Option<U>,
681 ) -> Self {
682 self.max_persisted_states = max_persisted_states.map(Into::into);
683 self
684 }
685
686 #[must_use]
688 pub fn with_max_transactions(mut self, max_transactions: Option<usize>) -> Self {
689 if let Some(max_transactions) = max_transactions {
690 self.max_transactions = max_transactions;
691 }
692 self
693 }
694
695 #[must_use]
697 pub fn with_transaction_block_keeper<U: Into<usize>>(
698 mut self,
699 transaction_block_keeper: Option<U>,
700 ) -> Self {
701 self.transaction_block_keeper = transaction_block_keeper.map(Into::into);
702 self
703 }
704
705 #[must_use]
707 pub fn with_base_fee(mut self, base_fee: Option<u64>) -> Self {
708 self.base_fee = base_fee;
709 self
710 }
711
712 #[must_use]
714 pub fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self {
715 self.disable_min_priority_fee = disable_min_priority_fee;
716 self
717 }
718
719 #[must_use]
721 pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
722 self.genesis = genesis;
723 self
724 }
725
726 pub fn get_genesis_timestamp(&self) -> u64 {
728 self.genesis_timestamp
729 .or_else(|| self.genesis.as_ref().map(|g| g.timestamp))
730 .unwrap_or_else(|| duration_since_unix_epoch().as_secs())
731 }
732
733 #[must_use]
735 pub fn with_genesis_timestamp<U: Into<u64>>(mut self, timestamp: Option<U>) -> Self {
736 if let Some(timestamp) = timestamp {
737 self.genesis_timestamp = Some(timestamp.into());
738 }
739 self
740 }
741
742 #[must_use]
744 pub fn with_genesis_block_number<U: Into<u64>>(mut self, number: Option<U>) -> Self {
745 if let Some(number) = number {
746 self.genesis_block_number = Some(number.into());
747 }
748 self
749 }
750
751 pub fn get_genesis_number(&self) -> u64 {
753 self.genesis_block_number
754 .or_else(|| self.genesis.as_ref().and_then(|g| g.number))
755 .unwrap_or(0)
756 }
757
758 #[must_use]
760 pub fn with_hardfork(mut self, hardfork: Option<FoundryHardfork>) -> Self {
761 self.hardfork = hardfork;
762 self
763 }
764
765 #[must_use]
767 pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
768 self.genesis_accounts = accounts;
769 self
770 }
771
772 #[must_use]
774 pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
775 self.signer_accounts = accounts;
776 self
777 }
778
779 pub fn with_account_generator(mut self, generator: AccountGenerator) -> eyre::Result<Self> {
782 let accounts = generator.generate()?;
783 self.account_generator = Some(generator);
784 Ok(self.with_signer_accounts(accounts.clone()).with_genesis_accounts(accounts))
785 }
786
787 #[must_use]
789 pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
790 self.genesis_balance = balance.into();
791 self
792 }
793
794 #[must_use]
796 pub fn with_blocktime<D: Into<Duration>>(mut self, block_time: Option<D>) -> Self {
797 self.block_time = block_time.map(Into::into);
798 self
799 }
800
801 #[must_use]
802 pub fn with_mixed_mining<D: Into<Duration>>(
803 mut self,
804 mixed_mining: bool,
805 block_time: Option<D>,
806 ) -> Self {
807 self.block_time = block_time.map(Into::into);
808 self.mixed_mining = mixed_mining;
809 self
810 }
811
812 #[must_use]
814 pub fn with_no_mining(mut self, no_mining: bool) -> Self {
815 self.no_mining = no_mining;
816 self
817 }
818
819 #[must_use]
821 pub fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self {
822 self.slots_in_an_epoch = slots_in_an_epoch;
823 self
824 }
825
826 #[must_use]
828 pub fn with_port(mut self, port: u16) -> Self {
829 self.port = port;
830 self
831 }
832
833 #[must_use]
840 pub fn with_ipc(mut self, ipc_path: Option<Option<String>>) -> Self {
841 self.ipc_path = ipc_path;
842 self
843 }
844
845 #[must_use]
847 pub fn set_config_out(mut self, config_out: Option<PathBuf>) -> Self {
848 self.config_out = config_out;
849 self
850 }
851
852 #[must_use]
853 pub fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self {
854 self.no_storage_caching = no_storage_caching;
855 self
856 }
857
858 #[must_use]
860 pub fn with_eth_rpc_url<U: Into<String>>(mut self, eth_rpc_url: Option<U>) -> Self {
861 self.eth_rpc_url = eth_rpc_url.map(Into::into);
862 self
863 }
864
865 #[must_use]
867 pub fn with_fork_block_number<U: Into<u64>>(self, fork_block_number: Option<U>) -> Self {
868 self.with_fork_choice(fork_block_number.map(Into::into))
869 }
870
871 #[must_use]
873 pub fn with_fork_transaction_hash<U: Into<TxHash>>(
874 self,
875 fork_transaction_hash: Option<U>,
876 ) -> Self {
877 self.with_fork_choice(fork_transaction_hash.map(Into::into))
878 }
879
880 #[must_use]
882 pub fn with_fork_choice<U: Into<ForkChoice>>(mut self, fork_choice: Option<U>) -> Self {
883 self.fork_choice = fork_choice.map(Into::into);
884 self
885 }
886
887 #[must_use]
889 pub fn with_fork_chain_id(mut self, fork_chain_id: Option<U256>) -> Self {
890 self.fork_chain_id = fork_chain_id;
891 self
892 }
893
894 #[must_use]
896 pub fn with_fork_headers(mut self, headers: Vec<String>) -> Self {
897 self.fork_headers = headers;
898 self
899 }
900
901 #[must_use]
903 pub fn fork_request_timeout(mut self, fork_request_timeout: Option<Duration>) -> Self {
904 if let Some(fork_request_timeout) = fork_request_timeout {
905 self.fork_request_timeout = fork_request_timeout;
906 }
907 self
908 }
909
910 #[must_use]
912 pub fn fork_request_retries(mut self, fork_request_retries: Option<u32>) -> Self {
913 if let Some(fork_request_retries) = fork_request_retries {
914 self.fork_request_retries = fork_request_retries;
915 }
916 self
917 }
918
919 #[must_use]
921 pub fn fork_retry_backoff(mut self, fork_retry_backoff: Option<Duration>) -> Self {
922 if let Some(fork_retry_backoff) = fork_retry_backoff {
923 self.fork_retry_backoff = fork_retry_backoff;
924 }
925 self
926 }
927
928 #[must_use]
932 pub fn fork_compute_units_per_second(mut self, compute_units_per_second: Option<u64>) -> Self {
933 if let Some(compute_units_per_second) = compute_units_per_second {
934 self.compute_units_per_second = compute_units_per_second;
935 }
936 self
937 }
938
939 #[must_use]
941 pub fn with_tracing(mut self, enable_tracing: bool) -> Self {
942 self.enable_tracing = enable_tracing;
943 self
944 }
945
946 #[must_use]
948 pub fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self {
949 self.enable_steps_tracing = enable_steps_tracing;
950 self
951 }
952
953 #[must_use]
955 pub fn with_print_logs(mut self, print_logs: bool) -> Self {
956 self.print_logs = print_logs;
957 self
958 }
959
960 #[must_use]
962 pub fn with_print_traces(mut self, print_traces: bool) -> Self {
963 self.print_traces = print_traces;
964 self
965 }
966
967 #[must_use]
969 pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
970 self.enable_auto_impersonate = enable_auto_impersonate;
971 self
972 }
973
974 #[must_use]
975 pub fn with_server_config(mut self, config: ServerConfig) -> Self {
976 self.server_config = config;
977 self
978 }
979
980 #[must_use]
982 pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
983 self.host = if host.is_empty() { vec![IpAddr::V4(Ipv4Addr::LOCALHOST)] } else { host };
984 self
985 }
986
987 #[must_use]
988 pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
989 self.transaction_order = transaction_order;
990 self
991 }
992
993 pub fn get_ipc_path(&self) -> Option<String> {
995 match &self.ipc_path {
996 Some(path) => path.clone().or_else(|| Some(DEFAULT_IPC_ENDPOINT.to_string())),
997 None => None,
998 }
999 }
1000
1001 pub fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
1003 if let Some(path) = &self.config_out {
1004 let value = self.as_json(fork);
1005 foundry_common::fs::write_json_file(path, &value).wrap_err("failed writing JSON")?;
1006 }
1007 if !self.silent {
1008 sh_println!("{}", self.as_string(fork))?;
1009 }
1010 Ok(())
1011 }
1012
1013 pub fn block_cache_path(&self, block: u64) -> Option<PathBuf> {
1017 if self.no_storage_caching || self.eth_rpc_url.is_none() {
1018 return None;
1019 }
1020 let chain_id = self.get_chain_id();
1021
1022 Config::foundry_block_cache_file(chain_id, block)
1023 }
1024
1025 #[must_use]
1027 pub fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self {
1028 self.disable_default_create2_deployer = yes;
1029 self
1030 }
1031
1032 #[must_use]
1034 pub fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self {
1035 self.disable_pool_balance_checks = yes;
1036 self
1037 }
1038
1039 #[must_use]
1041 pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self {
1042 self.precompile_factory = Some(Arc::new(factory));
1043 self
1044 }
1045
1046 #[must_use]
1048 pub fn with_networks(mut self, networks: NetworkConfigs) -> Self {
1049 self.networks = networks;
1050 self
1051 }
1052
1053 #[must_use]
1055 pub fn with_tempo(mut self) -> Self {
1056 self.networks = NetworkConfigs::with_tempo();
1057 self
1058 }
1059
1060 #[must_use]
1062 pub fn with_optimism(mut self) -> Self {
1063 self.networks = NetworkConfigs::with_optimism();
1064 self
1065 }
1066
1067 #[must_use]
1069 pub fn silent(self) -> Self {
1070 self.set_silent(true)
1071 }
1072
1073 #[must_use]
1074 pub fn set_silent(mut self, silent: bool) -> Self {
1075 self.silent = silent;
1076 self
1077 }
1078
1079 #[must_use]
1084 pub fn with_cache_path(mut self, cache_path: Option<PathBuf>) -> Self {
1085 self.cache_path = cache_path;
1086 self
1087 }
1088
1089 pub(crate) async fn setup<N>(&mut self) -> Result<mem::Backend<N>>
1094 where
1095 N: alloy_network::Network<
1096 TxEnvelope = foundry_primitives::FoundryTxEnvelope,
1097 ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope,
1098 >,
1099 {
1100 let mut cfg = CfgEnv::default();
1103 cfg.spec = self.get_hardfork().into();
1104
1105 cfg.chain_id = self.get_chain_id();
1106 cfg.limit_contract_code_size = self.code_size_limit;
1107 cfg.disable_eip3607 = true;
1111 cfg.disable_block_gas_limit = self.disable_block_gas_limit;
1112
1113 if !self.enable_tx_gas_limit {
1114 cfg.tx_gas_limit_cap = Some(u64::MAX);
1115 }
1116
1117 if let Some(value) = self.memory_limit {
1118 cfg.memory_limit = value;
1119 }
1120
1121 let spec_id = cfg.spec;
1122 let mut evm_env = EvmEnv::new(
1123 cfg,
1124 BlockEnv {
1125 gas_limit: self.gas_limit(),
1126 basefee: self.get_base_fee(),
1127 ..Default::default()
1128 },
1129 );
1130
1131 let base_fee_params: BaseFeeParams =
1132 self.networks.base_fee_params(self.get_genesis_timestamp());
1133
1134 let fees = FeeManager::new(
1135 spec_id,
1136 self.get_base_fee(),
1137 !self.disable_min_priority_fee,
1138 self.get_gas_price(),
1139 self.get_blob_excess_gas_and_price(),
1140 self.get_blob_params(),
1141 base_fee_params,
1142 );
1143
1144 let (db, fork): (Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>) =
1145 if let Some(eth_rpc_url) = self.eth_rpc_url.clone() {
1146 self.setup_fork_db(eth_rpc_url, &mut evm_env, &fees).await?
1147 } else {
1148 (Arc::new(TokioRwLock::new(Box::<MemDb>::default())), None)
1149 };
1150
1151 if let Some(ref genesis) = self.genesis {
1153 if self.chain_id.is_none() {
1156 evm_env.cfg_env.chain_id = genesis.config.chain_id;
1157 }
1158 evm_env.block_env.timestamp = U256::from(genesis.timestamp);
1159 if let Some(base_fee) = genesis.base_fee_per_gas {
1160 evm_env.block_env.basefee = base_fee.try_into()?;
1161 }
1162 if let Some(number) = genesis.number {
1163 evm_env.block_env.number = U256::from(number);
1164 }
1165 evm_env.block_env.beneficiary = genesis.coinbase;
1166 }
1167
1168 let genesis = GenesisConfig {
1169 number: self.get_genesis_number(),
1170 timestamp: self.get_genesis_timestamp(),
1171 balance: self.genesis_balance,
1172 accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(),
1173 genesis_init: self.genesis.clone(),
1174 };
1175
1176 let mut decoder_builder = CallTraceDecoderBuilder::new();
1177 if self.print_traces {
1178 if let Ok(identifier) = SignaturesIdentifier::new(false) {
1180 debug!(target: "node", "using signature identifier");
1181 decoder_builder = decoder_builder.with_signature_identifier(identifier);
1182 }
1183 }
1184
1185 let backend = mem::Backend::with_genesis(
1187 db,
1188 Arc::new(RwLock::new(evm_env)),
1189 self.networks,
1190 genesis,
1191 fees,
1192 Arc::new(RwLock::new(fork)),
1193 self.enable_steps_tracing,
1194 self.print_logs,
1195 self.print_traces,
1196 Arc::new(decoder_builder.build()),
1197 self.prune_history,
1198 self.max_persisted_states,
1199 self.transaction_block_keeper,
1200 self.block_time,
1201 self.cache_path.clone(),
1202 Arc::new(TokioRwLock::new(self.clone())),
1203 )
1204 .await?;
1205
1206 if !self.disable_default_create2_deployer && self.eth_rpc_url.is_none() {
1209 backend
1210 .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER)
1211 .await
1212 .wrap_err("failed to create default create2 deployer")?;
1213 }
1214
1215 Ok(backend)
1216 }
1217
1218 pub async fn setup_fork_db(
1225 &mut self,
1226 eth_rpc_url: String,
1227 evm_env: &mut EvmEnv,
1228 fees: &FeeManager,
1229 ) -> Result<(Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>)> {
1230 let (db, config) = self.setup_fork_db_config(eth_rpc_url, evm_env, fees).await?;
1231 let db: Arc<TokioRwLock<Box<dyn Db>>> = Arc::new(TokioRwLock::new(Box::new(db)));
1232 let fork = ClientFork::new(config, Arc::clone(&db));
1233 Ok((db, Some(fork)))
1234 }
1235
1236 pub async fn setup_fork_db_config(
1242 &mut self,
1243 eth_rpc_url: String,
1244 evm_env: &mut EvmEnv,
1245 fees: &FeeManager,
1246 ) -> Result<(ForkedDatabase<AnyNetwork>, ClientForkConfig)> {
1247 debug!(target: "node", ?eth_rpc_url, "setting up fork db");
1248 let provider = Arc::new(
1249 ProviderBuilder::new(ð_rpc_url)
1250 .timeout(self.fork_request_timeout)
1251 .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1252 .compute_units_per_second(self.compute_units_per_second)
1253 .max_retry(self.fork_request_retries)
1254 .headers(self.fork_headers.clone())
1255 .build()
1256 .wrap_err("failed to establish provider to fork url")?,
1257 );
1258
1259 let (fork_block_number, fork_chain_id, force_transactions) = if let Some(fork_choice) =
1260 &self.fork_choice
1261 {
1262 let (fork_block_number, force_transactions) =
1263 derive_block_and_transactions(fork_choice, &provider).await.wrap_err(
1264 "failed to derive fork block number and force transactions from fork choice",
1265 )?;
1266 let chain_id = if let Some(chain_id) = self.fork_chain_id {
1267 Some(chain_id)
1268 } else if self.hardfork.is_none() {
1269 let chain_id =
1270 provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?;
1271 Some(U256::from(chain_id))
1272 } else {
1273 None
1274 };
1275
1276 (fork_block_number, chain_id, force_transactions)
1277 } else {
1278 let bn = find_latest_fork_block(&provider)
1280 .await
1281 .wrap_err("failed to get fork block number")?;
1282 (bn, None, None)
1283 };
1284
1285 let block = provider
1286 .get_block(BlockNumberOrTag::Number(fork_block_number).into())
1287 .await
1288 .wrap_err("failed to get fork block")?;
1289
1290 let block = if let Some(block) = block {
1291 block
1292 } else {
1293 if let Ok(latest_block) = provider.get_block_number().await {
1294 let mut message = format!(
1295 "Failed to get block for block number: {fork_block_number}\n\
1296latest block number: {latest_block}"
1297 );
1298 if fork_block_number <= latest_block {
1302 message.push_str(&format!("\n{NON_ARCHIVE_NODE_WARNING}"));
1303 }
1304 eyre::bail!("{message}");
1305 }
1306 eyre::bail!("failed to get block for block number: {fork_block_number}")
1307 };
1308
1309 let gas_limit = self.fork_gas_limit(&block);
1310 self.gas_limit = Some(gas_limit);
1311
1312 evm_env.block_env = BlockEnv {
1313 gas_limit,
1314 beneficiary: evm_env.block_env.beneficiary,
1316 basefee: evm_env.block_env.basefee,
1317 ..block_env_from_header(&block.header)
1318 };
1319
1320 let chain_id = if let Some(chain_id) = self.chain_id {
1322 chain_id
1323 } else {
1324 let chain_id = if let Some(fork_chain_id) = fork_chain_id {
1325 fork_chain_id.to()
1326 } else {
1327 provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?
1328 };
1329
1330 self.set_chain_id(Some(chain_id));
1332 evm_env.cfg_env.chain_id = chain_id;
1333 chain_id
1334 };
1335
1336 if self.hardfork.is_none()
1338 && let Some(hardfork) =
1339 FoundryHardfork::from_chain_and_timestamp(chain_id, block.header.timestamp())
1340 {
1341 evm_env.cfg_env.spec = SpecId::from(hardfork);
1342 self.hardfork = Some(hardfork);
1343 }
1344
1345 if self.base_fee.is_none()
1347 && let Some(base_fee) = block.header.base_fee_per_gas()
1348 {
1349 self.base_fee = Some(base_fee);
1350 evm_env.block_env.basefee = base_fee;
1351 let next_block_base_fee = fees.get_next_block_base_fee_per_gas(
1354 block.header.gas_used(),
1355 gas_limit,
1356 block.header.base_fee_per_gas().unwrap_or_default(),
1357 );
1358
1359 fees.set_base_fee(next_block_base_fee);
1361 }
1362
1363 if let (Some(blob_excess_gas), Some(blob_gas_used)) =
1364 (block.header.excess_blob_gas(), block.header.blob_gas_used())
1365 {
1366 let blob_params = get_blob_params(chain_id, block.header.timestamp());
1368
1369 evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
1370 blob_excess_gas,
1371 blob_params.update_fraction as u64,
1372 ));
1373
1374 fees.set_blob_params(blob_params);
1375
1376 let next_block_blob_excess_gas =
1377 fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used);
1378 fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
1379 next_block_blob_excess_gas,
1380 blob_params.update_fraction as u64,
1381 ));
1382 }
1383
1384 if self.gas_price.is_none()
1386 && let Ok(gas_price) = provider.get_gas_price().await
1387 {
1388 self.gas_price = Some(gas_price);
1389 fees.set_gas_price(gas_price);
1390 }
1391
1392 let block_hash = block.header.hash;
1393
1394 let override_chain_id = self.chain_id;
1395 apply_chain_and_block_specific_env_changes::<AnyNetwork, _, _>(
1397 evm_env,
1398 &block,
1399 self.networks,
1400 );
1401
1402 let meta = BlockchainDbMeta::new(evm_env.block_env.clone(), eth_rpc_url.clone());
1403 let block_chain_db = if self.fork_chain_id.is_some() {
1404 BlockchainDb::new_skip_check(meta, self.block_cache_path(fork_block_number))
1405 } else {
1406 BlockchainDb::new(meta, self.block_cache_path(fork_block_number))
1407 };
1408
1409 let backend = SharedBackend::spawn_backend(
1412 Arc::clone(&provider),
1413 block_chain_db.clone(),
1414 Some(fork_block_number.into()),
1415 )
1416 .await;
1417
1418 let config = ClientForkConfig {
1419 eth_rpc_url,
1420 block_number: fork_block_number,
1421 block_hash,
1422 transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()),
1423 provider,
1424 chain_id,
1425 override_chain_id,
1426 timestamp: block.header.timestamp(),
1427 base_fee: block.header.base_fee_per_gas().map(|g| g as u128),
1428 timeout: self.fork_request_timeout,
1429 retries: self.fork_request_retries,
1430 backoff: self.fork_retry_backoff,
1431 compute_units_per_second: self.compute_units_per_second,
1432 total_difficulty: block.header.total_difficulty.unwrap_or_default(),
1433 blob_gas_used: block.header.blob_gas_used().map(|g| g as u128),
1434 blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price,
1435 force_transactions,
1436 };
1437
1438 debug!(target: "node", fork_number=config.block_number, fork_hash=%config.block_hash, "set up fork db");
1439
1440 let mut db = ForkedDatabase::new(backend, block_chain_db);
1441
1442 db.insert_block_hash(U256::from(config.block_number), config.block_hash);
1444
1445 Ok((db, config))
1446 }
1447
1448 pub(crate) fn fork_gas_limit<B: BlockResponse<Header: BlockHeader>>(&self, block: &B) -> u64 {
1452 if !self.disable_block_gas_limit {
1453 if let Some(gas_limit) = self.gas_limit {
1454 return gas_limit;
1455 } else if block.header().gas_limit() > 0 {
1456 return block.header().gas_limit();
1457 }
1458 }
1459
1460 u64::MAX
1461 }
1462
1463 pub(crate) fn gas_limit(&self) -> u64 {
1467 if self.disable_block_gas_limit {
1468 return u64::MAX;
1469 }
1470
1471 self.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT)
1472 }
1473}
1474
1475async fn derive_block_and_transactions(
1480 fork_choice: &ForkChoice,
1481 provider: &Arc<RetryProvider>,
1482) -> eyre::Result<(BlockNumber, Option<Vec<PoolTransaction<FoundryTxEnvelope>>>)> {
1483 match fork_choice {
1484 ForkChoice::Block(block_number) => {
1485 let block_number = *block_number;
1486 if block_number >= 0 {
1487 return Ok((block_number as u64, None));
1488 }
1489 let latest = provider.get_block_number().await?;
1491
1492 Ok((block_number.saturating_add(latest as i128) as u64, None))
1493 }
1494 ForkChoice::Transaction(transaction_hash) => {
1495 let transaction = provider
1497 .get_transaction_by_hash(transaction_hash.0.into())
1498 .await?
1499 .ok_or_else(|| eyre::eyre!("failed to get fork transaction by hash"))?;
1500 let transaction_block_number = transaction.block_number().ok_or_else(|| {
1501 eyre::eyre!("fork transaction is not mined yet (no block number)")
1502 })?;
1503
1504 let transaction_block = provider
1506 .get_block_by_number(transaction_block_number.into())
1507 .full()
1508 .await?
1509 .ok_or_else(|| eyre::eyre!("failed to get fork block by number"))?;
1510
1511 let filtered_transactions = transaction_block
1513 .transactions
1514 .as_transactions()
1515 .ok_or_else(|| eyre::eyre!("failed to get transactions from full fork block"))?
1516 .iter()
1517 .take_while_inclusive(|&transaction| transaction.tx_hash() != transaction_hash.0)
1518 .collect::<Vec<_>>();
1519
1520 let force_transactions = filtered_transactions
1522 .iter()
1523 .map(|&transaction| PoolTransaction::try_from(transaction.clone()))
1524 .collect::<Result<Vec<_>, _>>()
1525 .map_err(|e| eyre::eyre!("Err converting to pool transactions {e}"))?;
1526 Ok((transaction_block_number.saturating_sub(1), Some(force_transactions)))
1527 }
1528 }
1529}
1530
1531#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1533pub enum ForkChoice {
1534 Block(i128),
1538 Transaction(TxHash),
1540}
1541
1542impl ForkChoice {
1543 pub fn block_number(&self) -> Option<i128> {
1545 match self {
1546 Self::Block(block_number) => Some(*block_number),
1547 Self::Transaction(_) => None,
1548 }
1549 }
1550
1551 pub fn transaction_hash(&self) -> Option<TxHash> {
1553 match self {
1554 Self::Block(_) => None,
1555 Self::Transaction(transaction_hash) => Some(*transaction_hash),
1556 }
1557 }
1558}
1559
1560impl From<TxHash> for ForkChoice {
1562 fn from(tx_hash: TxHash) -> Self {
1563 Self::Transaction(tx_hash)
1564 }
1565}
1566
1567impl From<u64> for ForkChoice {
1569 fn from(block: u64) -> Self {
1570 Self::Block(block as i128)
1571 }
1572}
1573
1574#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1575pub struct PruneStateHistoryConfig {
1576 pub enabled: bool,
1577 pub max_memory_history: Option<usize>,
1578}
1579
1580impl PruneStateHistoryConfig {
1581 pub fn is_state_history_supported(&self) -> bool {
1583 !self.enabled || self.max_memory_history.is_some()
1584 }
1585
1586 pub fn is_config_enabled(&self) -> bool {
1588 self.enabled
1589 }
1590
1591 pub fn from_args(val: Option<Option<usize>>) -> Self {
1592 val.map(|max_memory_history| Self { enabled: true, max_memory_history }).unwrap_or_default()
1593 }
1594}
1595
1596#[derive(Clone, Debug)]
1598pub struct AccountGenerator {
1599 chain_id: u64,
1600 amount: usize,
1601 phrase: String,
1602 derivation_path: Option<String>,
1603}
1604
1605impl AccountGenerator {
1606 pub fn new(amount: usize) -> Self {
1607 Self {
1608 chain_id: CHAIN_ID,
1609 amount,
1610 phrase: Mnemonic::<English>::new(&mut thread_rng()).to_phrase(),
1611 derivation_path: None,
1612 }
1613 }
1614
1615 #[must_use]
1616 pub fn phrase(mut self, phrase: impl Into<String>) -> Self {
1617 self.phrase = phrase.into();
1618 self
1619 }
1620
1621 fn get_phrase(&self) -> &str {
1622 &self.phrase
1623 }
1624
1625 #[must_use]
1626 pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
1627 self.chain_id = chain_id.into();
1628 self
1629 }
1630
1631 #[must_use]
1632 pub fn derivation_path(mut self, derivation_path: impl Into<String>) -> Self {
1633 let mut derivation_path = derivation_path.into();
1634 if !derivation_path.ends_with('/') {
1635 derivation_path.push('/');
1636 }
1637 self.derivation_path = Some(derivation_path);
1638 self
1639 }
1640
1641 fn get_derivation_path(&self) -> &str {
1642 self.derivation_path.as_deref().unwrap_or("m/44'/60'/0'/0/")
1643 }
1644}
1645
1646impl AccountGenerator {
1647 pub fn generate(&self) -> eyre::Result<Vec<PrivateKeySigner>> {
1648 let builder = MnemonicBuilder::<English>::default().phrase(self.phrase.as_str());
1649
1650 let derivation_path = self.get_derivation_path();
1652
1653 let mut wallets = Vec::with_capacity(self.amount);
1654 for idx in 0..self.amount {
1655 let builder =
1656 builder.clone().derivation_path(format!("{derivation_path}{idx}")).unwrap();
1657 let wallet = builder.build()?.with_chain_id(Some(self.chain_id));
1658 wallets.push(wallet)
1659 }
1660 Ok(wallets)
1661 }
1662}
1663
1664pub fn anvil_dir() -> Option<PathBuf> {
1666 Config::foundry_dir().map(|p| p.join("anvil"))
1667}
1668
1669pub fn anvil_tmp_dir() -> Option<PathBuf> {
1671 anvil_dir().map(|p| p.join("tmp"))
1672}
1673
1674async fn find_latest_fork_block<P: Provider<AnyNetwork>>(
1679 provider: P,
1680) -> Result<u64, TransportError> {
1681 let mut num = provider.get_block_number().await?;
1682
1683 for _ in 0..2 {
1686 if let Some(block) = provider.get_block(num.into()).await?
1687 && !block.header.hash.is_zero()
1688 {
1689 break;
1690 }
1691 num = num.saturating_sub(1)
1693 }
1694
1695 Ok(num)
1696}
1697
1698#[cfg(test)]
1699mod tests {
1700 use super::*;
1701
1702 #[test]
1703 fn test_prune_history() {
1704 let config = PruneStateHistoryConfig::default();
1705 assert!(config.is_state_history_supported());
1706 let config = PruneStateHistoryConfig::from_args(Some(None));
1707 assert!(!config.is_state_history_supported());
1708 let config = PruneStateHistoryConfig::from_args(Some(Some(10)));
1709 assert!(config.is_state_history_supported());
1710 }
1711}