1use crate::{
2 EthereumHardfork, FeeManager, PrecompileFactory,
3 eth::{
4 backend::{
5 db::{Db, SerializableState},
6 fork::{ClientFork, ClientForkConfig},
7 genesis::GenesisConfig,
8 mem::fork_db::ForkedDatabase,
9 time::duration_since_unix_epoch,
10 },
11 fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE},
12 pool::transactions::{PoolTransaction, TransactionOrder},
13 },
14 mem::{self, in_memory_db::StateRootDb},
15};
16use alloy_chains::NamedChain;
17use alloy_consensus::BlockHeader;
18use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams};
19use alloy_evm::EvmEnv;
20use alloy_genesis::Genesis;
21use alloy_network::{AnyNetwork, BlockResponse, TransactionResponse};
22use alloy_primitives::{Address, BlockNumber, TxHash, U256, hex, map::HashMap, utils::Unit};
23use alloy_provider::Provider;
24use alloy_rpc_types::BlockNumberOrTag;
25use alloy_signer::Signer;
26use alloy_signer_local::{
27 MnemonicBuilder, PrivateKeySigner,
28 coins_bip39::{English, Mnemonic},
29};
30use alloy_transport::TransportError;
31use anvil_server::ServerConfig;
32use eyre::{Context, Result};
33use foundry_common::{
34 ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT,
35 provider::{ProviderBuilder, RetryProvider},
36};
37use foundry_config::Config;
38use foundry_evm::{
39 backend::{BlockchainDb, BlockchainDbMeta, SharedBackend},
40 constants::DEFAULT_CREATE2_DEPLOYER,
41 hardfork::FoundryHardfork,
42 hardforks::latest_active_tempo_hardfork,
43 utils::{
44 apply_chain_and_block_specific_env_changes, block_env_from_header,
45 get_blob_base_fee_update_fraction,
46 },
47};
48use foundry_primitives::FoundryTxEnvelope;
49use itertools::Itertools;
50use parking_lot::RwLock;
51use rand_08::thread_rng;
52use revm::{
53 context::{BlockEnv, CfgEnv},
54 context_interface::block::BlobExcessGasAndPrice,
55 primitives::hardfork::SpecId,
56};
57use serde_json::{Value, json};
58use std::{
59 fmt::Write as FmtWrite,
60 net::{IpAddr, Ipv4Addr},
61 path::PathBuf,
62 sync::Arc,
63 time::Duration,
64};
65use tempo_hardfork::{
66 TempoHardfork,
67 constants::gas::{TEMPO_T0_BASE_FEE, TEMPO_T1_BASE_FEE},
68};
69use tokio::sync::RwLock as TokioRwLock;
70use yansi::Paint;
71
72pub use foundry_common::version::SHORT_VERSION as VERSION_MESSAGE;
73use foundry_evm::{
74 traces::{CallTraceDecoderBuilder, identifier::SignaturesIdentifier},
75 utils::get_blob_params,
76};
77use foundry_evm_networks::NetworkConfigs;
78use tempo_precompiles::TIP_FEE_MANAGER_ADDRESS;
79
80pub const NODE_PORT: u16 = 8545;
82pub const CHAIN_ID: u64 = 31337;
84pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000;
86pub const DEFAULT_SLOTS_IN_AN_EPOCH: u64 = 32;
88pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
90
91pub const DEFAULT_IPC_ENDPOINT: &str =
93 if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };
94
95const BANNER: &str = r"
96 _ _
97 (_) | |
98 __ _ _ __ __ __ _ | |
99 / _` | | '_ \ \ \ / / | | | |
100 | (_| | | | | | \ V / | | | |
101 \__,_| |_| |_| \_/ |_| |_|
102";
103
104#[derive(Clone, Debug)]
106pub struct NodeConfig {
107 pub chain_id: Option<u64>,
109 pub gas_limit: Option<u64>,
111 pub disable_block_gas_limit: bool,
113 pub enable_tx_gas_limit: bool,
115 pub gas_price: Option<u128>,
117 pub base_fee: Option<u64>,
119 pub disable_min_priority_fee: bool,
121 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
123 pub hardfork: Option<FoundryHardfork>,
125 pub genesis_accounts: Vec<PrivateKeySigner>,
127 pub genesis_balance: U256,
129 pub genesis_timestamp: Option<u64>,
131 pub genesis_block_number: Option<u64>,
133 pub signer_accounts: Vec<PrivateKeySigner>,
135 pub block_time: Option<Duration>,
137 pub no_mining: bool,
139 pub mixed_mining: bool,
141 pub port: u16,
143 pub max_transactions: usize,
145 pub fork_urls: Vec<String>,
149 pub fork_choice: Option<ForkChoice>,
151 pub fork_headers: Vec<String>,
153 pub fork_chain_id: Option<U256>,
155 pub account_generator: Option<AccountGenerator>,
157 pub enable_tracing: bool,
159 pub no_storage_caching: bool,
161 pub server_config: ServerConfig,
163 pub host: Vec<IpAddr>,
165 pub transaction_order: TransactionOrder,
167 pub config_out: Option<PathBuf>,
169 pub genesis: Option<Genesis>,
171 pub fork_request_timeout: Duration,
173 pub fork_request_retries: u32,
175 pub fork_retry_backoff: Duration,
177 pub compute_units_per_second: u64,
179 pub ipc_path: Option<Option<String>>,
181 pub enable_steps_tracing: bool,
183 pub print_logs: bool,
185 pub print_traces: bool,
187 pub enable_auto_impersonate: bool,
189 pub code_size_limit: Option<usize>,
191 pub prune_history: PruneStateHistoryConfig,
195 pub max_persisted_states: Option<usize>,
197 pub init_state: Option<SerializableState>,
199 pub transaction_block_keeper: Option<usize>,
201 pub disable_default_create2_deployer: bool,
203 pub disable_pool_balance_checks: bool,
205 pub slots_in_an_epoch: u64,
207 pub memory_limit: Option<u64>,
209 pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
211 pub networks: NetworkConfigs,
213 pub silent: bool,
215 pub cache_path: Option<PathBuf>,
218 pub funded_accounts: HashMap<Address, U256>,
220}
221
222impl NodeConfig {
223 fn as_string(&self, fork: Option<&ClientFork>) -> String {
224 let mut s: String = String::new();
225 let _ = write!(s, "\n{}", BANNER.green());
226 let _ = write!(s, "\n {VERSION_MESSAGE}");
227 let _ = write!(s, "\n {}", "https://github.com/foundry-rs/foundry".green());
228
229 let _ = write!(
230 s,
231 r#"
232
233Available Accounts
234==================
235"#
236 );
237 let balance = alloy_primitives::utils::format_ether(self.genesis_balance);
238 for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
239 write!(s, "\n({idx}) {} ({balance} ETH)", wallet.address()).unwrap();
240 }
241
242 let _ = write!(
243 s,
244 r#"
245
246Private Keys
247==================
248"#
249 );
250
251 for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
252 let hex = hex::encode(wallet.credential().to_bytes());
253 let _ = write!(s, "\n({idx}) 0x{hex}");
254 }
255
256 if let Some(generator) = &self.account_generator {
257 let _ = write!(
258 s,
259 r#"
260
261Wallet
262==================
263Mnemonic: {}
264Derivation path: {}
265"#,
266 generator.phrase,
267 generator.get_derivation_path()
268 );
269 }
270
271 if let Some(fork) = fork {
272 let _ = write!(
273 s,
274 r#"
275
276Fork
277==================
278Endpoint: {}
279Block number: {}
280Block hash: {:?}
281Chain ID: {}
282"#,
283 fork.eth_rpc_url().as_deref().unwrap_or("none"),
284 fork.block_number(),
285 fork.block_hash(),
286 fork.chain_id()
287 );
288
289 if self.fork_urls.len() > 1 {
290 let _ = writeln!(s, "Endpoints: {}", self.fork_urls.len());
291 for (i, url) in self.fork_urls.iter().enumerate() {
292 let _ = writeln!(s, " ({i}) {url}");
293 }
294 }
295
296 if let Some(tx_hash) = fork.transaction_hash() {
297 let _ = writeln!(s, "Transaction hash: {tx_hash}");
298 }
299 } else {
300 let _ = write!(
301 s,
302 r#"
303
304Chain ID
305==================
306
307{}
308"#,
309 self.get_chain_id().green()
310 );
311 }
312
313 if (SpecId::from(self.get_hardfork()) as u8) < (SpecId::LONDON as u8) {
314 let _ = write!(
315 s,
316 r#"
317Gas Price
318==================
319
320{}
321"#,
322 self.get_gas_price().green()
323 );
324 } else {
325 let _ = write!(
326 s,
327 r#"
328Base Fee
329==================
330
331{}
332"#,
333 self.get_base_fee().green()
334 );
335 }
336
337 let _ = write!(
338 s,
339 r#"
340Gas Limit
341==================
342
343{}
344"#,
345 {
346 if self.disable_block_gas_limit {
347 "Disabled".to_string()
348 } else {
349 self.gas_limit.map(|l| l.to_string()).unwrap_or_else(|| {
350 if self.fork_choice.is_some() {
351 "Forked".to_string()
352 } else {
353 DEFAULT_GAS_LIMIT.to_string()
354 }
355 })
356 }
357 }
358 .green()
359 );
360
361 let _ = write!(
362 s,
363 r#"
364Genesis Timestamp
365==================
366
367{}
368"#,
369 self.get_genesis_timestamp().green()
370 );
371
372 let _ = write!(
373 s,
374 r#"
375Genesis Number
376==================
377
378{}
379"#,
380 self.get_genesis_number().green()
381 );
382
383 s
384 }
385
386 fn as_json(&self, fork: Option<&ClientFork>) -> Value {
387 let mut wallet_description = HashMap::new();
388 let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
389 let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
390
391 for wallet in &self.genesis_accounts {
392 available_accounts.push(format!("{:?}", wallet.address()));
393 private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
394 }
395
396 if let Some(generator) = &self.account_generator {
397 let phrase = generator.get_phrase().to_string();
398 let derivation_path = generator.get_derivation_path().to_string();
399
400 wallet_description.insert("derivation_path".to_string(), derivation_path);
401 wallet_description.insert("mnemonic".to_string(), phrase);
402 };
403
404 let gas_limit = match self.gas_limit {
405 Some(_) | None if self.disable_block_gas_limit => Some(u64::MAX.to_string()),
407 Some(limit) => Some(limit.to_string()),
408 _ => None,
409 };
410
411 if let Some(fork) = fork {
412 json!({
413 "available_accounts": available_accounts,
414 "private_keys": private_keys,
415 "endpoint": fork.eth_rpc_url().unwrap_or_default(),
416 "block_number": fork.block_number(),
417 "block_hash": fork.block_hash(),
418 "chain_id": fork.chain_id(),
419 "wallet": wallet_description,
420 "base_fee": format!("{}", self.get_base_fee()),
421 "gas_price": format!("{}", self.get_gas_price()),
422 "gas_limit": gas_limit,
423 })
424 } else {
425 json!({
426 "available_accounts": available_accounts,
427 "private_keys": private_keys,
428 "wallet": wallet_description,
429 "base_fee": format!("{}", self.get_base_fee()),
430 "gas_price": format!("{}", self.get_gas_price()),
431 "gas_limit": gas_limit,
432 "genesis_timestamp": format!("{}", self.get_genesis_timestamp()),
433 })
434 }
435 }
436}
437
438impl NodeConfig {
439 #[doc(hidden)]
442 pub fn test() -> Self {
443 Self { enable_tracing: true, port: 0, silent: true, ..Default::default() }
444 }
445
446 #[doc(hidden)]
448 pub fn test_tempo() -> Self {
449 Self { networks: NetworkConfigs::with_tempo(), ..Self::test() }
450 }
451
452 pub fn empty_state() -> Self {
454 Self {
455 genesis_accounts: vec![],
456 signer_accounts: vec![],
457 disable_default_create2_deployer: true,
458 ..Default::default()
459 }
460 }
461}
462
463impl Default for NodeConfig {
464 fn default() -> Self {
465 let genesis_accounts = AccountGenerator::new(10)
467 .phrase(DEFAULT_MNEMONIC)
468 .generate()
469 .expect("Invalid mnemonic.");
470 Self {
471 chain_id: None,
472 gas_limit: None,
473 disable_block_gas_limit: false,
474 enable_tx_gas_limit: false,
475 gas_price: None,
476 hardfork: None,
477 signer_accounts: genesis_accounts.clone(),
478 genesis_timestamp: None,
479 genesis_block_number: None,
480 genesis_accounts,
481 genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)),
483 block_time: None,
484 no_mining: false,
485 mixed_mining: false,
486 port: NODE_PORT,
487 max_transactions: 1_000,
488 fork_urls: vec![],
489 fork_choice: None,
490 account_generator: None,
491 base_fee: None,
492 disable_min_priority_fee: false,
493 blob_excess_gas_and_price: None,
494 enable_tracing: true,
495 enable_steps_tracing: false,
496 print_logs: true,
497 print_traces: false,
498 enable_auto_impersonate: false,
499 no_storage_caching: false,
500 server_config: Default::default(),
501 host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
502 transaction_order: Default::default(),
503 config_out: None,
504 genesis: None,
505 fork_request_timeout: REQUEST_TIMEOUT,
506 fork_headers: vec![],
507 fork_request_retries: 5,
508 fork_retry_backoff: Duration::from_millis(1_000),
509 fork_chain_id: None,
510 compute_units_per_second: ALCHEMY_FREE_TIER_CUPS,
512 ipc_path: None,
513 code_size_limit: None,
514 prune_history: Default::default(),
515 max_persisted_states: None,
516 init_state: None,
517 transaction_block_keeper: None,
518 disable_default_create2_deployer: false,
519 disable_pool_balance_checks: false,
520 slots_in_an_epoch: DEFAULT_SLOTS_IN_AN_EPOCH,
521 memory_limit: None,
522 precompile_factory: None,
523 networks: Default::default(),
524 silent: false,
525 cache_path: None,
526 funded_accounts: HashMap::default(),
527 }
528 }
529}
530
531impl NodeConfig {
532 pub(crate) fn apply_tempo_fork_beneficiary_default<N>(&self, evm_env: &mut EvmEnv<N>) {
535 if self.networks.is_tempo()
536 && !self.fork_urls.is_empty()
537 && evm_env.block_env.beneficiary.is_zero()
538 {
539 evm_env.block_env.beneficiary = TIP_FEE_MANAGER_ADDRESS;
546 }
547 }
548
549 #[must_use]
551 pub const fn with_memory_limit(mut self, mems_value: Option<u64>) -> Self {
552 self.memory_limit = mems_value;
553 self
554 }
555
556 pub fn get_base_fee(&self) -> u64 {
560 let default = if self.networks.is_tempo() {
561 tempo_default_base_fee(TempoHardfork::from(self.get_hardfork()))
562 } else {
563 INITIAL_BASE_FEE
564 };
565 self.base_fee
566 .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64)))
567 .unwrap_or(default)
568 }
569
570 pub fn get_gas_price(&self) -> u128 {
574 let default = if self.networks.is_tempo() {
575 tempo_default_base_fee(TempoHardfork::from(self.get_hardfork())) as u128
576 } else {
577 INITIAL_GAS_PRICE
578 };
579 self.gas_price.unwrap_or(default)
580 }
581
582 pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice {
583 if let Some(value) = self.blob_excess_gas_and_price {
584 value
585 } else {
586 let excess_blob_gas =
587 self.genesis.as_ref().and_then(|g| g.excess_blob_gas).unwrap_or(0);
588 BlobExcessGasAndPrice::new(
589 excess_blob_gas,
590 get_blob_base_fee_update_fraction(
591 self.get_chain_id(),
592 self.get_genesis_timestamp(),
593 ),
594 )
595 }
596 }
597
598 pub fn get_blob_params(&self) -> BlobParams {
600 get_blob_params(self.get_chain_id(), self.get_genesis_timestamp())
601 }
602
603 pub fn get_hardfork(&self) -> FoundryHardfork {
605 if let Some(hardfork) = self.hardfork {
606 return hardfork;
607 }
608 if self.networks.is_tempo()
609 && let Some(hardfork) = TempoHardfork::from_chain_and_timestamp(
610 self.get_chain_id(),
611 self.get_genesis_timestamp(),
612 )
613 {
614 return hardfork.into();
615 }
616 #[cfg(feature = "optimism")]
617 if self.networks.is_optimism() {
618 return foundry_evm::hardforks::OpHardfork::default().into();
619 }
620 if self.networks.is_tempo() {
621 return latest_active_tempo_hardfork().into();
622 }
623 EthereumHardfork::default().into()
624 }
625
626 #[must_use]
628 pub const fn with_code_size_limit(mut self, code_size_limit: Option<usize>) -> Self {
629 self.code_size_limit = code_size_limit;
630 self
631 }
632 #[must_use]
634 pub const fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self {
635 if disable_code_size_limit {
636 self.code_size_limit = Some(usize::MAX);
637 }
638 self
639 }
640
641 #[must_use]
643 pub fn with_init_state(mut self, init_state: Option<SerializableState>) -> Self {
644 self.init_state = init_state;
645 self
646 }
647
648 #[must_use]
650 #[cfg(feature = "cmd")]
651 pub fn with_init_state_path(mut self, path: impl AsRef<std::path::Path>) -> Self {
652 self.init_state = crate::cmd::StateFile::parse_path(path).ok().and_then(|file| file.state);
653 self
654 }
655
656 #[must_use]
658 pub fn with_chain_id<U: Into<u64>>(mut self, chain_id: Option<U>) -> Self {
659 self.set_chain_id(chain_id);
660 self
661 }
662
663 pub fn get_chain_id(&self) -> u64 {
665 self.chain_id
666 .or_else(|| self.genesis.as_ref().map(|g| g.config.chain_id))
667 .unwrap_or(CHAIN_ID)
668 }
669
670 pub fn set_chain_id(&mut self, chain_id: Option<impl Into<u64>>) {
672 self.chain_id = chain_id.map(Into::into);
673 let chain_id = self.get_chain_id();
674 self.networks = self.networks.with_chain_id(chain_id);
675 self.genesis_accounts.iter_mut().for_each(|wallet| {
676 *wallet = wallet.clone().with_chain_id(Some(chain_id));
677 });
678 self.signer_accounts.iter_mut().for_each(|wallet| {
679 *wallet = wallet.clone().with_chain_id(Some(chain_id));
680 })
681 }
682
683 #[must_use]
685 pub const fn with_gas_limit(mut self, gas_limit: Option<u64>) -> Self {
686 self.gas_limit = gas_limit;
687 self
688 }
689
690 #[must_use]
694 pub const fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self {
695 self.disable_block_gas_limit = disable_block_gas_limit;
696 self
697 }
698
699 #[must_use]
703 pub const fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self {
704 self.enable_tx_gas_limit = enable_tx_gas_limit;
705 self
706 }
707
708 #[must_use]
710 pub const fn with_gas_price(mut self, gas_price: Option<u128>) -> Self {
711 self.gas_price = gas_price;
712 self
713 }
714
715 #[must_use]
717 pub fn set_pruned_history(mut self, prune_history: Option<Option<usize>>) -> Self {
718 self.prune_history = PruneStateHistoryConfig::from_args(prune_history);
719 self
720 }
721
722 #[must_use]
724 pub fn with_max_persisted_states<U: Into<usize>>(
725 mut self,
726 max_persisted_states: Option<U>,
727 ) -> Self {
728 self.max_persisted_states = max_persisted_states.map(Into::into);
729 self
730 }
731
732 #[must_use]
734 pub const fn with_max_transactions(mut self, max_transactions: Option<usize>) -> Self {
735 if let Some(max_transactions) = max_transactions {
736 self.max_transactions = max_transactions;
737 }
738 self
739 }
740
741 #[must_use]
743 pub fn with_transaction_block_keeper<U: Into<usize>>(
744 mut self,
745 transaction_block_keeper: Option<U>,
746 ) -> Self {
747 self.transaction_block_keeper = transaction_block_keeper.map(Into::into);
748 self
749 }
750
751 #[must_use]
753 pub const fn with_base_fee(mut self, base_fee: Option<u64>) -> Self {
754 self.base_fee = base_fee;
755 self
756 }
757
758 #[must_use]
760 pub const fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self {
761 self.disable_min_priority_fee = disable_min_priority_fee;
762 self
763 }
764
765 #[must_use]
767 pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
768 self.genesis = genesis;
769 self
770 }
771
772 pub fn get_genesis_timestamp(&self) -> u64 {
774 self.genesis_timestamp
775 .or_else(|| self.genesis.as_ref().map(|g| g.timestamp))
776 .unwrap_or_else(|| duration_since_unix_epoch().as_secs())
777 }
778
779 #[must_use]
781 pub fn with_genesis_timestamp<U: Into<u64>>(mut self, timestamp: Option<U>) -> Self {
782 if let Some(timestamp) = timestamp {
783 self.genesis_timestamp = Some(timestamp.into());
784 }
785 self
786 }
787
788 #[must_use]
790 pub fn with_genesis_block_number<U: Into<u64>>(mut self, number: Option<U>) -> Self {
791 if let Some(number) = number {
792 self.genesis_block_number = Some(number.into());
793 }
794 self
795 }
796
797 pub fn get_genesis_number(&self) -> u64 {
799 self.genesis_block_number
800 .or_else(|| self.genesis.as_ref().and_then(|g| g.number))
801 .unwrap_or(0)
802 }
803
804 #[must_use]
806 pub const fn with_hardfork(mut self, hardfork: Option<FoundryHardfork>) -> Self {
807 self.hardfork = hardfork;
808 self
809 }
810
811 #[must_use]
813 pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
814 self.genesis_accounts = accounts;
815 self
816 }
817
818 #[must_use]
820 pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
821 self.signer_accounts = accounts;
822 self
823 }
824
825 pub fn with_account_generator(mut self, generator: AccountGenerator) -> eyre::Result<Self> {
828 let accounts = generator.generate()?;
829 self.account_generator = Some(generator);
830 Ok(self.with_signer_accounts(accounts.clone()).with_genesis_accounts(accounts))
831 }
832
833 #[must_use]
835 pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
836 self.genesis_balance = balance.into();
837 self
838 }
839
840 #[must_use]
842 pub fn with_blocktime<D: Into<Duration>>(mut self, block_time: Option<D>) -> Self {
843 self.block_time = block_time.map(Into::into);
844 self
845 }
846
847 #[must_use]
848 pub fn with_mixed_mining<D: Into<Duration>>(
849 mut self,
850 mixed_mining: bool,
851 block_time: Option<D>,
852 ) -> Self {
853 self.block_time = block_time.map(Into::into);
854 self.mixed_mining = mixed_mining;
855 self
856 }
857
858 #[must_use]
860 pub const fn with_no_mining(mut self, no_mining: bool) -> Self {
861 self.no_mining = no_mining;
862 self
863 }
864
865 #[must_use]
867 pub const fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self {
868 self.slots_in_an_epoch = slots_in_an_epoch;
869 self
870 }
871
872 #[must_use]
874 pub const fn with_port(mut self, port: u16) -> Self {
875 self.port = port;
876 self
877 }
878
879 #[must_use]
886 pub fn with_ipc(mut self, ipc_path: Option<Option<String>>) -> Self {
887 self.ipc_path = ipc_path;
888 self
889 }
890
891 #[must_use]
893 pub fn set_config_out(mut self, config_out: Option<PathBuf>) -> Self {
894 self.config_out = config_out;
895 self
896 }
897
898 #[must_use]
899 pub const fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self {
900 self.no_storage_caching = no_storage_caching;
901 self
902 }
903
904 #[must_use]
906 pub fn with_eth_rpc_url<U: Into<String>>(mut self, eth_rpc_url: Option<U>) -> Self {
907 if let Some(url) = eth_rpc_url {
908 self.fork_urls = vec![url.into()];
909 }
910 self
911 }
912
913 #[must_use]
915 pub fn with_fork_urls(mut self, fork_urls: Vec<String>) -> Self {
916 self.fork_urls = fork_urls;
917 self
918 }
919
920 #[must_use]
922 pub fn with_fork_block_number<U: Into<u64>>(self, fork_block_number: Option<U>) -> Self {
923 self.with_fork_choice(fork_block_number.map(Into::into))
924 }
925
926 #[must_use]
928 pub fn with_fork_transaction_hash<U: Into<TxHash>>(
929 self,
930 fork_transaction_hash: Option<U>,
931 ) -> Self {
932 self.with_fork_choice(fork_transaction_hash.map(Into::into))
933 }
934
935 #[must_use]
937 pub fn with_fork_choice<U: Into<ForkChoice>>(mut self, fork_choice: Option<U>) -> Self {
938 self.fork_choice = fork_choice.map(Into::into);
939 self
940 }
941
942 #[must_use]
944 pub const fn with_fork_chain_id(mut self, fork_chain_id: Option<U256>) -> Self {
945 self.fork_chain_id = fork_chain_id;
946 self
947 }
948
949 #[must_use]
951 pub fn with_fork_headers(mut self, headers: Vec<String>) -> Self {
952 self.fork_headers = headers;
953 self
954 }
955
956 #[must_use]
958 pub const fn fork_request_timeout(mut self, fork_request_timeout: Option<Duration>) -> Self {
959 if let Some(fork_request_timeout) = fork_request_timeout {
960 self.fork_request_timeout = fork_request_timeout;
961 }
962 self
963 }
964
965 #[must_use]
967 pub const fn fork_request_retries(mut self, fork_request_retries: Option<u32>) -> Self {
968 if let Some(fork_request_retries) = fork_request_retries {
969 self.fork_request_retries = fork_request_retries;
970 }
971 self
972 }
973
974 #[must_use]
976 pub const fn fork_retry_backoff(mut self, fork_retry_backoff: Option<Duration>) -> Self {
977 if let Some(fork_retry_backoff) = fork_retry_backoff {
978 self.fork_retry_backoff = fork_retry_backoff;
979 }
980 self
981 }
982
983 #[must_use]
987 pub const fn fork_compute_units_per_second(
988 mut self,
989 compute_units_per_second: Option<u64>,
990 ) -> Self {
991 if let Some(compute_units_per_second) = compute_units_per_second {
992 self.compute_units_per_second = compute_units_per_second;
993 }
994 self
995 }
996
997 #[must_use]
999 pub const fn with_tracing(mut self, enable_tracing: bool) -> Self {
1000 self.enable_tracing = enable_tracing;
1001 self
1002 }
1003
1004 #[must_use]
1006 pub const fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self {
1007 self.enable_steps_tracing = enable_steps_tracing;
1008 self
1009 }
1010
1011 #[must_use]
1013 pub const fn with_print_logs(mut self, print_logs: bool) -> Self {
1014 self.print_logs = print_logs;
1015 self
1016 }
1017
1018 #[must_use]
1020 pub const fn with_print_traces(mut self, print_traces: bool) -> Self {
1021 self.print_traces = print_traces;
1022 self
1023 }
1024
1025 #[must_use]
1027 pub const fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
1028 self.enable_auto_impersonate = enable_auto_impersonate;
1029 self
1030 }
1031
1032 #[must_use]
1033 pub fn with_server_config(mut self, config: ServerConfig) -> Self {
1034 self.server_config = config;
1035 self
1036 }
1037
1038 #[must_use]
1040 pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
1041 self.host = if host.is_empty() { vec![IpAddr::V4(Ipv4Addr::LOCALHOST)] } else { host };
1042 self
1043 }
1044
1045 #[must_use]
1046 pub const fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
1047 self.transaction_order = transaction_order;
1048 self
1049 }
1050
1051 pub fn get_ipc_path(&self) -> Option<String> {
1053 match &self.ipc_path {
1054 Some(path) => path.clone().or_else(|| Some(DEFAULT_IPC_ENDPOINT.to_string())),
1055 None => None,
1056 }
1057 }
1058
1059 pub fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
1061 if let Some(path) = &self.config_out {
1062 let value = self.as_json(fork);
1063 foundry_common::fs::write_json_file(path, &value).wrap_err("failed writing JSON")?;
1064 }
1065 if !self.silent {
1066 sh_println!("{}", self.as_string(fork))?;
1067 }
1068 Ok(())
1069 }
1070
1071 pub fn block_cache_path(&self, block: u64) -> Option<PathBuf> {
1075 if self.no_storage_caching || self.fork_urls.is_empty() {
1076 return None;
1077 }
1078 let chain_id = self.get_chain_id();
1079
1080 Config::foundry_block_cache_file(chain_id, block)
1081 }
1082
1083 #[must_use]
1085 pub const fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self {
1086 self.disable_default_create2_deployer = yes;
1087 self
1088 }
1089
1090 #[must_use]
1092 pub const fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self {
1093 self.disable_pool_balance_checks = yes;
1094 self
1095 }
1096
1097 #[must_use]
1099 pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self {
1100 self.precompile_factory = Some(Arc::new(factory));
1101 self
1102 }
1103
1104 #[must_use]
1106 pub const fn with_networks(mut self, networks: NetworkConfigs) -> Self {
1107 self.networks = networks;
1108 self
1109 }
1110
1111 #[must_use]
1113 pub fn with_tempo(mut self) -> Self {
1114 self.networks = NetworkConfigs::with_tempo();
1115 self
1116 }
1117
1118 #[cfg(feature = "optimism")]
1120 #[must_use]
1121 pub fn with_optimism(mut self) -> Self {
1122 self.networks = NetworkConfigs::with_optimism();
1123 self
1124 }
1125
1126 #[must_use]
1128 pub const fn silent(self) -> Self {
1129 self.set_silent(true)
1130 }
1131
1132 #[must_use]
1133 pub const fn set_silent(mut self, silent: bool) -> Self {
1134 self.silent = silent;
1135 self
1136 }
1137
1138 #[must_use]
1143 pub fn with_cache_path(mut self, cache_path: Option<PathBuf>) -> Self {
1144 self.cache_path = cache_path;
1145 self
1146 }
1147
1148 #[must_use]
1150 pub fn with_funded_accounts(mut self, accounts: HashMap<Address, U256>) -> Self {
1151 self.funded_accounts = accounts;
1152 self
1153 }
1154
1155 pub(crate) async fn setup<N>(&mut self) -> Result<mem::Backend<N>>
1160 where
1161 N: alloy_network::Network<
1162 TxEnvelope = foundry_primitives::FoundryTxEnvelope,
1163 ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope,
1164 >,
1165 {
1166 let mut cfg = CfgEnv::default();
1169 cfg.spec = self.get_hardfork().into();
1170
1171 cfg.chain_id = self.get_chain_id();
1172 cfg.limit_contract_code_size = self.code_size_limit;
1173 cfg.disable_eip3607 = true;
1177 cfg.disable_block_gas_limit = self.disable_block_gas_limit;
1178
1179 if !self.enable_tx_gas_limit {
1180 cfg.tx_gas_limit_cap = Some(u64::MAX);
1181 }
1182
1183 if let Some(value) = self.memory_limit {
1184 cfg.memory_limit = value;
1185 }
1186
1187 let spec_id = cfg.spec;
1188 let mut evm_env = EvmEnv::new(
1189 cfg,
1190 BlockEnv {
1191 gas_limit: self.gas_limit(),
1192 basefee: self.get_base_fee(),
1193 ..Default::default()
1194 },
1195 );
1196
1197 self.apply_tempo_fork_beneficiary_default(&mut evm_env);
1198
1199 let genesis_timestamp = self.get_genesis_timestamp();
1200 let base_fee_params: BaseFeeParams = self.networks.base_fee_params(genesis_timestamp);
1201
1202 let tempo_hardfork =
1204 self.networks.is_tempo().then(|| TempoHardfork::from(self.get_hardfork()));
1205
1206 let fees = FeeManager::new(
1207 spec_id,
1208 self.get_base_fee(),
1209 !self.disable_min_priority_fee,
1210 self.get_gas_price(),
1211 self.get_blob_excess_gas_and_price(),
1212 self.get_blob_params(),
1213 base_fee_params,
1214 tempo_hardfork,
1215 );
1216
1217 let (db, fork): (Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>) =
1218 if let Some(eth_rpc_url) = self.fork_urls.first().cloned() {
1219 self.setup_fork_db(eth_rpc_url, &mut evm_env, &fees).await?
1220 } else {
1221 let track_history = self.prune_history.is_state_history_supported();
1222 (Arc::new(TokioRwLock::new(Box::new(StateRootDb::new(track_history)))), None)
1223 };
1224
1225 if let Some(ref genesis) = self.genesis {
1227 if self.chain_id.is_none() {
1230 evm_env.cfg_env.chain_id = genesis.config.chain_id;
1231 }
1232 evm_env.block_env.timestamp = U256::from(genesis.timestamp);
1233 if let Some(base_fee) = genesis.base_fee_per_gas {
1234 evm_env.block_env.basefee = base_fee.try_into()?;
1235 }
1236 if let Some(number) = genesis.number {
1237 evm_env.block_env.number = U256::from(number);
1238 }
1239 evm_env.block_env.beneficiary = genesis.coinbase;
1240 }
1241
1242 let is_bsc = matches!(
1246 NamedChain::try_from(evm_env.cfg_env.chain_id),
1247 Ok(NamedChain::BinanceSmartChain | NamedChain::BinanceSmartChainTestnet)
1248 );
1249 if fork.is_none() && (self.genesis_timestamp.is_some() || is_bsc) {
1250 evm_env.block_env.timestamp = U256::from(genesis_timestamp);
1251 }
1252
1253 self.apply_tempo_fork_beneficiary_default(&mut evm_env);
1254
1255 let genesis = GenesisConfig {
1256 number: self.get_genesis_number(),
1257 timestamp: genesis_timestamp,
1258 balance: self.genesis_balance,
1259 accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(),
1260 genesis_init: self.genesis.clone(),
1261 };
1262
1263 let mut decoder_builder = CallTraceDecoderBuilder::new().with_tempo_hardfork(
1264 self.networks.is_tempo().then(|| TempoHardfork::from(self.get_hardfork())),
1265 );
1266 if self.print_traces {
1267 if let Ok(identifier) = SignaturesIdentifier::new(false) {
1269 debug!(target: "node", "using signature identifier");
1270 decoder_builder = decoder_builder.with_signature_identifier(identifier);
1271 }
1272 }
1273
1274 let backend = mem::Backend::with_genesis(
1276 db,
1277 Arc::new(RwLock::new(evm_env)),
1278 self.networks,
1279 genesis,
1280 fees,
1281 Arc::new(RwLock::new(fork)),
1282 self.enable_steps_tracing,
1283 self.print_logs,
1284 self.print_traces,
1285 Arc::new(decoder_builder.build()),
1286 self.prune_history,
1287 self.max_persisted_states,
1288 self.transaction_block_keeper,
1289 self.block_time,
1290 self.cache_path.clone(),
1291 Arc::new(TokioRwLock::new(self.clone())),
1292 )
1293 .await?;
1294
1295 if !self.disable_default_create2_deployer && self.fork_urls.is_empty() {
1298 backend
1299 .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER)
1300 .await
1301 .wrap_err("failed to create default create2 deployer")?;
1302 }
1303
1304 if !self.funded_accounts.is_empty() {
1305 for (address, balance) in &self.funded_accounts {
1306 backend
1307 .set_balance(*address, *balance)
1308 .await
1309 .wrap_err_with(|| format!("failed to fund account {address}"))?;
1310 }
1311 }
1312
1313 Ok(backend)
1314 }
1315
1316 pub async fn setup_fork_db(
1323 &mut self,
1324 eth_rpc_url: String,
1325 evm_env: &mut EvmEnv,
1326 fees: &FeeManager,
1327 ) -> Result<(Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>)> {
1328 let (db, config) = self.setup_fork_db_config(eth_rpc_url, evm_env, fees).await?;
1329 let db: Arc<TokioRwLock<Box<dyn Db>>> = Arc::new(TokioRwLock::new(Box::new(db)));
1330 let fork = ClientFork::new(config, Arc::clone(&db));
1331 Ok((db, Some(fork)))
1332 }
1333
1334 pub async fn setup_fork_db_config(
1340 &mut self,
1341 eth_rpc_url: String,
1342 evm_env: &mut EvmEnv,
1343 fees: &FeeManager,
1344 ) -> Result<(ForkedDatabase<AnyNetwork>, ClientForkConfig)> {
1345 debug!(target: "node", ?eth_rpc_url, "setting up fork db");
1346
1347 let provider = Arc::new(
1351 ProviderBuilder::new(ð_rpc_url)
1352 .timeout(self.fork_request_timeout)
1353 .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1354 .compute_units_per_second(self.compute_units_per_second)
1355 .max_retry(self.fork_request_retries)
1356 .headers(self.fork_headers.clone())
1357 .build()
1358 .wrap_err("failed to establish provider to fork url")?,
1359 );
1360
1361 let (fork_block_number, fork_chain_id, force_transactions) = if let Some(fork_choice) =
1362 &self.fork_choice
1363 {
1364 let (fork_block_number, force_transactions) =
1365 derive_block_and_transactions(fork_choice, &provider).await.wrap_err(
1366 "failed to derive fork block number and force transactions from fork choice",
1367 )?;
1368 let chain_id = if let Some(chain_id) = self.fork_chain_id {
1369 Some(chain_id)
1370 } else if self.hardfork.is_none() {
1371 let chain_id =
1372 provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?;
1373 Some(U256::from(chain_id))
1374 } else {
1375 None
1376 };
1377
1378 (fork_block_number, chain_id, force_transactions)
1379 } else {
1380 let bn = find_latest_fork_block(&provider)
1382 .await
1383 .wrap_err("failed to get fork block number")?;
1384 (bn, None, None)
1385 };
1386
1387 let block = provider
1388 .get_block(BlockNumberOrTag::Number(fork_block_number).into())
1389 .await
1390 .wrap_err("failed to get fork block")?;
1391
1392 let block = if let Some(block) = block {
1393 block
1394 } else {
1395 if let Ok(latest_block) = provider.get_block_number().await {
1396 let mut message = format!(
1397 "Failed to get block for block number: {fork_block_number}\n\
1398latest block number: {latest_block}"
1399 );
1400 if fork_block_number <= latest_block {
1404 message.push_str(&format!("\n{NON_ARCHIVE_NODE_WARNING}"));
1405 }
1406 eyre::bail!("{message}");
1407 }
1408 eyre::bail!("failed to get block for block number: {fork_block_number}");
1409 };
1410
1411 let gas_limit = self.fork_gas_limit(&block);
1412 self.gas_limit = Some(gas_limit);
1413
1414 let cache_block_env: BlockEnv = block_env_from_header(&block.header);
1417
1418 evm_env.block_env = BlockEnv {
1419 gas_limit,
1420 beneficiary: evm_env.block_env.beneficiary,
1422 basefee: evm_env.block_env.basefee,
1423 ..block_env_from_header(&block.header)
1424 };
1425
1426 let chain_id = if let Some(chain_id) = self.chain_id {
1428 chain_id
1429 } else {
1430 let chain_id = if let Some(fork_chain_id) = fork_chain_id {
1431 fork_chain_id.to()
1432 } else {
1433 provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?
1434 };
1435
1436 self.set_chain_id(Some(chain_id));
1438 evm_env.cfg_env.chain_id = chain_id;
1439 chain_id
1440 };
1441
1442 if self.hardfork.is_none()
1444 && let Some(hardfork) =
1445 FoundryHardfork::from_chain_and_timestamp(chain_id, block.header.timestamp())
1446 {
1447 evm_env.cfg_env.spec = SpecId::from(hardfork);
1448 self.hardfork = Some(hardfork);
1449 }
1450
1451 if self.networks.is_tempo() {
1454 fees.set_tempo_hardfork(Some(TempoHardfork::from(self.get_hardfork())));
1455 }
1456
1457 if self.base_fee.is_none()
1459 && let Some(base_fee) = block.header.base_fee_per_gas()
1460 {
1461 self.base_fee = Some(base_fee);
1462 evm_env.block_env.basefee = base_fee;
1463 let next_block_base_fee = fees.get_next_block_base_fee_per_gas(
1466 block.header.gas_used(),
1467 gas_limit,
1468 block.header.base_fee_per_gas().unwrap_or_default(),
1469 );
1470
1471 fees.set_base_fee(next_block_base_fee);
1473 }
1474
1475 if let (Some(blob_excess_gas), Some(blob_gas_used)) =
1476 (block.header.excess_blob_gas(), block.header.blob_gas_used())
1477 {
1478 let blob_params = get_blob_params(chain_id, block.header.timestamp());
1480
1481 evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
1482 blob_excess_gas,
1483 blob_params.update_fraction as u64,
1484 ));
1485
1486 fees.set_blob_params(blob_params);
1487
1488 let next_block_blob_excess_gas =
1489 fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used);
1490 fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
1491 next_block_blob_excess_gas,
1492 blob_params.update_fraction as u64,
1493 ));
1494 }
1495
1496 if self.gas_price.is_none()
1498 && let Ok(gas_price) = provider.get_gas_price().await
1499 {
1500 self.gas_price = Some(gas_price);
1501 fees.set_gas_price(gas_price);
1502 }
1503
1504 let block_hash = block.header.hash;
1505
1506 let override_chain_id = self.chain_id;
1507 apply_chain_and_block_specific_env_changes::<AnyNetwork, _, _>(
1509 evm_env,
1510 &block,
1511 self.networks,
1512 );
1513
1514 let meta = BlockchainDbMeta::new(cache_block_env, eth_rpc_url.clone());
1515 let block_chain_db = if self.fork_chain_id.is_some() {
1516 BlockchainDb::new_skip_check(meta, self.block_cache_path(fork_block_number))
1517 } else {
1518 BlockchainDb::new(meta, self.block_cache_path(fork_block_number))
1519 };
1520
1521 let provider = if self.fork_urls.len() > 1 {
1525 debug!(target: "node", urls=?self.fork_urls, "using multi-endpoint round-robin provider");
1526 Arc::new(
1527 ProviderBuilder::new(ð_rpc_url)
1528 .timeout(self.fork_request_timeout)
1529 .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1530 .compute_units_per_second(self.compute_units_per_second)
1531 .max_retry(self.fork_request_retries)
1532 .headers(self.fork_headers.clone())
1533 .build_fallback(self.fork_urls.clone())
1534 .wrap_err("failed to establish round-robin provider to fork urls")?,
1535 )
1536 } else {
1537 provider
1538 };
1539
1540 let backend = SharedBackend::spawn_backend(
1543 Arc::clone(&provider),
1544 block_chain_db.clone(),
1545 Some(fork_block_number.into()),
1546 )
1547 .await;
1548
1549 let config = ClientForkConfig {
1550 fork_urls: self.fork_urls.clone(),
1551 block_number: fork_block_number,
1552 block_hash,
1553 transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()),
1554 provider,
1555 chain_id,
1556 override_chain_id,
1557 hardfork: self.hardfork,
1558 timestamp: block.header.timestamp(),
1559 base_fee: block.header.base_fee_per_gas().map(|g| g as u128),
1560 timeout: self.fork_request_timeout,
1561 retries: self.fork_request_retries,
1562 backoff: self.fork_retry_backoff,
1563 compute_units_per_second: self.compute_units_per_second,
1564 headers: self.fork_headers.clone(),
1565 total_difficulty: block.header.total_difficulty.unwrap_or_default(),
1566 blob_gas_used: block.header.blob_gas_used().map(|g| g as u128),
1567 blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price,
1568 force_transactions,
1569 };
1570
1571 debug!(target: "node", fork_number=config.block_number, fork_hash=%config.block_hash, "set up fork db");
1572
1573 let mut db = ForkedDatabase::new(backend, block_chain_db);
1574
1575 db.insert_block_hash(U256::from(config.block_number), config.block_hash);
1577
1578 Ok((db, config))
1579 }
1580
1581 pub(crate) fn fork_gas_limit<B: BlockResponse<Header: BlockHeader>>(&self, block: &B) -> u64 {
1585 if !self.disable_block_gas_limit {
1586 if let Some(gas_limit) = self.gas_limit {
1587 return gas_limit;
1588 } else if block.header().gas_limit() > 0 {
1589 return block.header().gas_limit();
1590 }
1591 }
1592
1593 u64::MAX
1594 }
1595
1596 pub(crate) fn gas_limit(&self) -> u64 {
1600 if self.disable_block_gas_limit {
1601 return u64::MAX;
1602 }
1603
1604 self.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT)
1605 }
1606}
1607
1608pub(crate) const fn tempo_default_base_fee(hardfork: TempoHardfork) -> u64 {
1609 if hardfork.is_t1() { TEMPO_T1_BASE_FEE } else { TEMPO_T0_BASE_FEE }
1610}
1611
1612async fn derive_block_and_transactions(
1617 fork_choice: &ForkChoice,
1618 provider: &Arc<RetryProvider>,
1619) -> eyre::Result<(BlockNumber, Option<Vec<PoolTransaction<FoundryTxEnvelope>>>)> {
1620 match fork_choice {
1621 ForkChoice::Block(block_number) => {
1622 let block_number = *block_number;
1623 if block_number >= 0 {
1624 return Ok((block_number as u64, None));
1625 }
1626 let latest = provider.get_block_number().await?;
1628
1629 Ok((block_number.saturating_add(latest as i128) as u64, None))
1630 }
1631 ForkChoice::Transaction(transaction_hash) => {
1632 let transaction = provider
1634 .get_transaction_by_hash(transaction_hash.0.into())
1635 .await?
1636 .ok_or_else(|| eyre::eyre!("failed to get fork transaction by hash"))?;
1637 let transaction_block_number = transaction.block_number().ok_or_else(|| {
1638 eyre::eyre!("fork transaction is not mined yet (no block number)")
1639 })?;
1640
1641 let transaction_block = provider
1643 .get_block_by_number(transaction_block_number.into())
1644 .full()
1645 .await?
1646 .ok_or_else(|| eyre::eyre!("failed to get fork block by number"))?;
1647
1648 let filtered_transactions = transaction_block
1650 .transactions
1651 .as_transactions()
1652 .ok_or_else(|| eyre::eyre!("failed to get transactions from full fork block"))?
1653 .iter()
1654 .take_while_inclusive(|&transaction| transaction.tx_hash() != transaction_hash.0)
1655 .collect::<Vec<_>>();
1656
1657 let force_transactions = filtered_transactions
1659 .iter()
1660 .map(|&transaction| PoolTransaction::try_from(transaction.clone()))
1661 .collect::<Result<Vec<_>, _>>()
1662 .map_err(|e| eyre::eyre!("Err converting to pool transactions {e}"))?;
1663 Ok((transaction_block_number.saturating_sub(1), Some(force_transactions)))
1664 }
1665 }
1666}
1667
1668#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1670pub enum ForkChoice {
1671 Block(i128),
1675 Transaction(TxHash),
1677}
1678
1679impl ForkChoice {
1680 pub const fn block_number(&self) -> Option<i128> {
1682 match self {
1683 Self::Block(block_number) => Some(*block_number),
1684 Self::Transaction(_) => None,
1685 }
1686 }
1687
1688 pub const fn transaction_hash(&self) -> Option<TxHash> {
1690 match self {
1691 Self::Block(_) => None,
1692 Self::Transaction(transaction_hash) => Some(*transaction_hash),
1693 }
1694 }
1695}
1696
1697impl From<TxHash> for ForkChoice {
1699 fn from(tx_hash: TxHash) -> Self {
1700 Self::Transaction(tx_hash)
1701 }
1702}
1703
1704impl From<u64> for ForkChoice {
1706 fn from(block: u64) -> Self {
1707 Self::Block(block as i128)
1708 }
1709}
1710
1711#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1712pub struct PruneStateHistoryConfig {
1713 pub enabled: bool,
1714 pub max_memory_history: Option<usize>,
1715}
1716
1717impl PruneStateHistoryConfig {
1718 pub const fn is_state_history_supported(&self) -> bool {
1720 if !self.enabled {
1721 return true;
1722 }
1723
1724 match self.max_memory_history {
1725 Some(limit) => limit > 0,
1726 None => false,
1727 }
1728 }
1729
1730 pub const fn is_config_enabled(&self) -> bool {
1732 self.enabled
1733 }
1734
1735 pub fn from_args(val: Option<Option<usize>>) -> Self {
1736 val.map(|max_memory_history| Self {
1737 enabled: true,
1738 max_memory_history: max_memory_history.filter(|limit| *limit > 0),
1739 })
1740 .unwrap_or_default()
1741 }
1742}
1743
1744#[derive(Clone, Debug)]
1746pub struct AccountGenerator {
1747 chain_id: u64,
1748 amount: usize,
1749 phrase: String,
1750 derivation_path: Option<String>,
1751}
1752
1753impl AccountGenerator {
1754 pub fn new(amount: usize) -> Self {
1755 Self {
1756 chain_id: CHAIN_ID,
1757 amount,
1758 phrase: Mnemonic::<English>::new(&mut thread_rng()).to_phrase(),
1759 derivation_path: None,
1760 }
1761 }
1762
1763 #[must_use]
1764 pub fn phrase(mut self, phrase: impl Into<String>) -> Self {
1765 self.phrase = phrase.into();
1766 self
1767 }
1768
1769 fn get_phrase(&self) -> &str {
1770 &self.phrase
1771 }
1772
1773 #[must_use]
1774 pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
1775 self.chain_id = chain_id.into();
1776 self
1777 }
1778
1779 #[must_use]
1780 pub fn derivation_path(mut self, derivation_path: impl Into<String>) -> Self {
1781 let mut derivation_path = derivation_path.into();
1782 if !derivation_path.ends_with('/') {
1783 derivation_path.push('/');
1784 }
1785 self.derivation_path = Some(derivation_path);
1786 self
1787 }
1788
1789 fn get_derivation_path(&self) -> &str {
1790 self.derivation_path.as_deref().unwrap_or("m/44'/60'/0'/0/")
1791 }
1792}
1793
1794impl AccountGenerator {
1795 pub fn generate(&self) -> eyre::Result<Vec<PrivateKeySigner>> {
1796 let builder = MnemonicBuilder::<English>::default().phrase(self.phrase.as_str());
1797
1798 let derivation_path = self.get_derivation_path();
1800
1801 let mut wallets = Vec::with_capacity(self.amount);
1802 for idx in 0..self.amount {
1803 let builder =
1804 builder.clone().derivation_path(format!("{derivation_path}{idx}")).unwrap();
1805 let wallet = builder.build()?.with_chain_id(Some(self.chain_id));
1806 wallets.push(wallet)
1807 }
1808 Ok(wallets)
1809 }
1810}
1811
1812pub fn anvil_dir() -> Option<PathBuf> {
1814 Config::foundry_dir().map(|p| p.join("anvil"))
1815}
1816
1817pub fn anvil_tmp_dir() -> Option<PathBuf> {
1819 anvil_dir().map(|p| p.join("tmp"))
1820}
1821
1822async fn find_latest_fork_block<P: Provider<AnyNetwork>>(
1827 provider: P,
1828) -> Result<u64, TransportError> {
1829 let mut num = provider.get_block_number().await?;
1830
1831 for _ in 0..2 {
1834 if let Some(block) = provider.get_block(num.into()).await?
1835 && !block.header.hash.is_zero()
1836 {
1837 break;
1838 }
1839 num = num.saturating_sub(1)
1841 }
1842
1843 Ok(num)
1844}
1845
1846#[cfg(test)]
1847mod tests {
1848 use super::*;
1849
1850 #[test]
1851 fn test_prune_history() {
1852 let config = PruneStateHistoryConfig::default();
1853 assert!(config.is_state_history_supported());
1854 let config = PruneStateHistoryConfig::from_args(Some(None));
1855 assert!(!config.is_state_history_supported());
1856 let config = PruneStateHistoryConfig::from_args(Some(Some(0)));
1857 assert!(config.is_config_enabled());
1858 assert!(!config.is_state_history_supported());
1859 let config = PruneStateHistoryConfig::from_args(Some(Some(10)));
1860 assert!(config.is_state_history_supported());
1861 }
1862
1863 #[cfg(feature = "optimism")]
1864 #[test]
1865 fn set_chain_id_updates_network_config() {
1866 let mut config = NodeConfig::test();
1867 config.set_chain_id(Some(10u64));
1868
1869 assert!(config.networks.is_optimism());
1870 }
1871
1872 #[test]
1873 fn get_hardfork_on_tempo_never_returns_non_tempo_variant() {
1874 let shanghai_ts = 1_681_338_455u64;
1876
1877 let config = NodeConfig::test_tempo()
1878 .with_chain_id(Some(1u64))
1879 .with_genesis_timestamp(Some(shanghai_ts));
1880
1881 assert!(config.networks.is_tempo());
1882 assert!(matches!(config.get_hardfork(), FoundryHardfork::Tempo(_)));
1883 }
1884
1885 #[test]
1886 fn get_hardfork_on_local_tempo_defaults_to_latest_active() {
1887 let config = NodeConfig::test_tempo();
1888
1889 assert_eq!(config.get_hardfork(), FoundryHardfork::Tempo(latest_active_tempo_hardfork()));
1890 }
1891}