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