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