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;
68
69pub const NODE_PORT: u16 = 8545;
71pub const CHAIN_ID: u64 = 31337;
73pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000;
75pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
77
78pub const DEFAULT_IPC_ENDPOINT: &str =
80 if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };
81
82const BANNER: &str = r"
83 _ _
84 (_) | |
85 __ _ _ __ __ __ _ | |
86 / _` | | '_ \ \ \ / / | | | |
87 | (_| | | | | | \ V / | | | |
88 \__,_| |_| |_| \_/ |_| |_|
89";
90
91#[derive(Clone, Debug)]
93pub struct NodeConfig {
94 pub chain_id: Option<u64>,
96 pub gas_limit: Option<u64>,
98 pub disable_block_gas_limit: bool,
100 pub gas_price: Option<u128>,
102 pub base_fee: Option<u64>,
104 pub disable_min_priority_fee: bool,
106 pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
108 pub hardfork: Option<ChainHardfork>,
110 pub genesis_accounts: Vec<PrivateKeySigner>,
112 pub genesis_balance: U256,
114 pub genesis_timestamp: Option<u64>,
116 pub genesis_block_number: Option<u64>,
118 pub signer_accounts: Vec<PrivateKeySigner>,
120 pub block_time: Option<Duration>,
122 pub no_mining: bool,
124 pub mixed_mining: bool,
126 pub port: u16,
128 pub max_transactions: usize,
130 pub eth_rpc_url: Option<String>,
132 pub fork_choice: Option<ForkChoice>,
134 pub fork_headers: Vec<String>,
136 pub fork_chain_id: Option<U256>,
138 pub account_generator: Option<AccountGenerator>,
140 pub enable_tracing: bool,
142 pub no_storage_caching: bool,
144 pub server_config: ServerConfig,
146 pub host: Vec<IpAddr>,
148 pub transaction_order: TransactionOrder,
150 pub config_out: Option<PathBuf>,
152 pub genesis: Option<Genesis>,
154 pub fork_request_timeout: Duration,
156 pub fork_request_retries: u32,
158 pub fork_retry_backoff: Duration,
160 pub compute_units_per_second: u64,
162 pub ipc_path: Option<Option<String>>,
164 pub enable_steps_tracing: bool,
166 pub print_logs: bool,
168 pub print_traces: bool,
170 pub enable_auto_impersonate: bool,
172 pub code_size_limit: Option<usize>,
174 pub prune_history: PruneStateHistoryConfig,
178 pub max_persisted_states: Option<usize>,
180 pub init_state: Option<SerializableState>,
182 pub transaction_block_keeper: Option<usize>,
184 pub disable_default_create2_deployer: bool,
186 pub enable_optimism: bool,
188 pub slots_in_an_epoch: u64,
190 pub memory_limit: Option<u64>,
192 pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
194 pub odyssey: bool,
196 pub silent: bool,
198 pub cache_path: Option<PathBuf>,
200}
201
202impl NodeConfig {
203 fn as_string(&self, fork: Option<&ClientFork>) -> String {
204 let mut s: String = String::new();
205 let _ = write!(s, "\n{}", BANNER.green());
206 let _ = write!(s, "\n {VERSION_MESSAGE}");
207 let _ = write!(s, "\n {}", "https://github.com/foundry-rs/foundry".green());
208
209 let _ = write!(
210 s,
211 r#"
212
213Available Accounts
214==================
215"#
216 );
217 let balance = alloy_primitives::utils::format_ether(self.genesis_balance);
218 for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
219 write!(s, "\n({idx}) {} ({balance} ETH)", wallet.address()).unwrap();
220 }
221
222 let _ = write!(
223 s,
224 r#"
225
226Private Keys
227==================
228"#
229 );
230
231 for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
232 let hex = hex::encode(wallet.credential().to_bytes());
233 let _ = write!(s, "\n({idx}) 0x{hex}");
234 }
235
236 if let Some(generator) = &self.account_generator {
237 let _ = write!(
238 s,
239 r#"
240
241Wallet
242==================
243Mnemonic: {}
244Derivation path: {}
245"#,
246 generator.phrase,
247 generator.get_derivation_path()
248 );
249 }
250
251 if let Some(fork) = fork {
252 let _ = write!(
253 s,
254 r#"
255
256Fork
257==================
258Endpoint: {}
259Block number: {}
260Block hash: {:?}
261Chain ID: {}
262"#,
263 fork.eth_rpc_url(),
264 fork.block_number(),
265 fork.block_hash(),
266 fork.chain_id()
267 );
268
269 if let Some(tx_hash) = fork.transaction_hash() {
270 let _ = writeln!(s, "Transaction hash: {tx_hash}");
271 }
272 } else {
273 let _ = write!(
274 s,
275 r#"
276
277Chain ID
278==================
279
280{}
281"#,
282 self.get_chain_id().green()
283 );
284 }
285
286 if (SpecId::from(self.get_hardfork()) as u8) < (SpecId::LONDON as u8) {
287 let _ = write!(
288 s,
289 r#"
290Gas Price
291==================
292
293{}
294"#,
295 self.get_gas_price().green()
296 );
297 } else {
298 let _ = write!(
299 s,
300 r#"
301Base Fee
302==================
303
304{}
305"#,
306 self.get_base_fee().green()
307 );
308 }
309
310 let _ = write!(
311 s,
312 r#"
313Gas Limit
314==================
315
316{}
317"#,
318 {
319 if self.disable_block_gas_limit {
320 "Disabled".to_string()
321 } else {
322 self.gas_limit.map(|l| l.to_string()).unwrap_or_else(|| {
323 if self.fork_choice.is_some() {
324 "Forked".to_string()
325 } else {
326 DEFAULT_GAS_LIMIT.to_string()
327 }
328 })
329 }
330 }
331 .green()
332 );
333
334 let _ = write!(
335 s,
336 r#"
337Genesis Timestamp
338==================
339
340{}
341"#,
342 self.get_genesis_timestamp().green()
343 );
344
345 let _ = write!(
346 s,
347 r#"
348Genesis Number
349==================
350
351{}
352"#,
353 self.get_genesis_number().green()
354 );
355
356 s
357 }
358
359 fn as_json(&self, fork: Option<&ClientFork>) -> Value {
360 let mut wallet_description = HashMap::new();
361 let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
362 let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
363
364 for wallet in &self.genesis_accounts {
365 available_accounts.push(format!("{:?}", wallet.address()));
366 private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
367 }
368
369 if let Some(generator) = &self.account_generator {
370 let phrase = generator.get_phrase().to_string();
371 let derivation_path = generator.get_derivation_path().to_string();
372
373 wallet_description.insert("derivation_path".to_string(), derivation_path);
374 wallet_description.insert("mnemonic".to_string(), phrase);
375 };
376
377 let gas_limit = match self.gas_limit {
378 Some(_) | None if self.disable_block_gas_limit => Some(u64::MAX.to_string()),
380 Some(limit) => Some(limit.to_string()),
381 _ => None,
382 };
383
384 if let Some(fork) = fork {
385 json!({
386 "available_accounts": available_accounts,
387 "private_keys": private_keys,
388 "endpoint": fork.eth_rpc_url(),
389 "block_number": fork.block_number(),
390 "block_hash": fork.block_hash(),
391 "chain_id": fork.chain_id(),
392 "wallet": wallet_description,
393 "base_fee": format!("{}", self.get_base_fee()),
394 "gas_price": format!("{}", self.get_gas_price()),
395 "gas_limit": gas_limit,
396 })
397 } else {
398 json!({
399 "available_accounts": available_accounts,
400 "private_keys": private_keys,
401 "wallet": wallet_description,
402 "base_fee": format!("{}", self.get_base_fee()),
403 "gas_price": format!("{}", self.get_gas_price()),
404 "gas_limit": gas_limit,
405 "genesis_timestamp": format!("{}", self.get_genesis_timestamp()),
406 })
407 }
408 }
409}
410
411impl NodeConfig {
412 #[doc(hidden)]
415 pub fn test() -> Self {
416 Self { enable_tracing: true, port: 0, silent: true, ..Default::default() }
417 }
418
419 pub fn empty_state() -> Self {
421 Self {
422 genesis_accounts: vec![],
423 signer_accounts: vec![],
424 disable_default_create2_deployer: true,
425 ..Default::default()
426 }
427 }
428}
429
430impl Default for NodeConfig {
431 fn default() -> Self {
432 let genesis_accounts = AccountGenerator::new(10)
434 .phrase(DEFAULT_MNEMONIC)
435 .generate()
436 .expect("Invalid mnemonic.");
437 Self {
438 chain_id: None,
439 gas_limit: None,
440 disable_block_gas_limit: false,
441 gas_price: None,
442 hardfork: None,
443 signer_accounts: genesis_accounts.clone(),
444 genesis_timestamp: None,
445 genesis_block_number: None,
446 genesis_accounts,
447 genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)),
449 block_time: None,
450 no_mining: false,
451 mixed_mining: false,
452 port: NODE_PORT,
453 max_transactions: 1_000,
455 eth_rpc_url: None,
456 fork_choice: None,
457 account_generator: None,
458 base_fee: None,
459 disable_min_priority_fee: false,
460 blob_excess_gas_and_price: None,
461 enable_tracing: true,
462 enable_steps_tracing: false,
463 print_logs: true,
464 print_traces: false,
465 enable_auto_impersonate: false,
466 no_storage_caching: false,
467 server_config: Default::default(),
468 host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
469 transaction_order: Default::default(),
470 config_out: None,
471 genesis: None,
472 fork_request_timeout: REQUEST_TIMEOUT,
473 fork_headers: vec![],
474 fork_request_retries: 5,
475 fork_retry_backoff: Duration::from_millis(1_000),
476 fork_chain_id: None,
477 compute_units_per_second: ALCHEMY_FREE_TIER_CUPS,
479 ipc_path: None,
480 code_size_limit: None,
481 prune_history: Default::default(),
482 max_persisted_states: None,
483 init_state: None,
484 transaction_block_keeper: None,
485 disable_default_create2_deployer: false,
486 enable_optimism: false,
487 slots_in_an_epoch: 32,
488 memory_limit: None,
489 precompile_factory: None,
490 odyssey: false,
491 silent: false,
492 cache_path: None,
493 }
494 }
495}
496
497impl NodeConfig {
498 #[must_use]
500 pub fn with_memory_limit(mut self, mems_value: Option<u64>) -> Self {
501 self.memory_limit = mems_value;
502 self
503 }
504 pub fn get_base_fee(&self) -> u64 {
506 self.base_fee
507 .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64)))
508 .unwrap_or(INITIAL_BASE_FEE)
509 }
510
511 pub fn get_gas_price(&self) -> u128 {
513 self.gas_price.unwrap_or(INITIAL_GAS_PRICE)
514 }
515
516 pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice {
517 if let Some(value) = self.blob_excess_gas_and_price {
518 value
519 } else {
520 let excess_blob_gas =
521 self.genesis.as_ref().and_then(|g| g.excess_blob_gas).unwrap_or(0);
522 BlobExcessGasAndPrice::new(
523 excess_blob_gas,
524 get_blob_base_fee_update_fraction(
525 self.chain_id.unwrap_or(Chain::mainnet().id()),
526 self.get_genesis_timestamp(),
527 ),
528 )
529 }
530 }
531
532 pub fn get_hardfork(&self) -> ChainHardfork {
534 if self.odyssey {
535 return ChainHardfork::Ethereum(EthereumHardfork::default());
536 }
537 if let Some(hardfork) = self.hardfork {
538 return hardfork;
539 }
540 if self.enable_optimism {
541 return OpHardfork::default().into();
542 }
543 EthereumHardfork::default().into()
544 }
545
546 #[must_use]
548 pub fn with_code_size_limit(mut self, code_size_limit: Option<usize>) -> Self {
549 self.code_size_limit = code_size_limit;
550 self
551 }
552 #[must_use]
554 pub fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self {
555 if disable_code_size_limit {
556 self.code_size_limit = Some(usize::MAX);
557 }
558 self
559 }
560
561 #[must_use]
563 pub fn with_init_state(mut self, init_state: Option<SerializableState>) -> Self {
564 self.init_state = init_state;
565 self
566 }
567
568 #[must_use]
570 #[cfg(feature = "cmd")]
571 pub fn with_init_state_path(mut self, path: impl AsRef<std::path::Path>) -> Self {
572 self.init_state = crate::cmd::StateFile::parse_path(path).ok().and_then(|file| file.state);
573 self
574 }
575
576 #[must_use]
578 pub fn with_chain_id<U: Into<u64>>(mut self, chain_id: Option<U>) -> Self {
579 self.set_chain_id(chain_id);
580 self
581 }
582
583 pub fn get_chain_id(&self) -> u64 {
585 self.chain_id
586 .or_else(|| self.genesis.as_ref().map(|g| g.config.chain_id))
587 .unwrap_or(CHAIN_ID)
588 }
589
590 pub fn set_chain_id(&mut self, chain_id: Option<impl Into<u64>>) {
592 self.chain_id = chain_id.map(Into::into);
593 let chain_id = self.get_chain_id();
594 self.genesis_accounts.iter_mut().for_each(|wallet| {
595 *wallet = wallet.clone().with_chain_id(Some(chain_id));
596 });
597 self.signer_accounts.iter_mut().for_each(|wallet| {
598 *wallet = wallet.clone().with_chain_id(Some(chain_id));
599 })
600 }
601
602 #[must_use]
604 pub fn with_gas_limit(mut self, gas_limit: Option<u64>) -> Self {
605 self.gas_limit = gas_limit;
606 self
607 }
608
609 #[must_use]
613 pub fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self {
614 self.disable_block_gas_limit = disable_block_gas_limit;
615 self
616 }
617
618 #[must_use]
620 pub fn with_gas_price(mut self, gas_price: Option<u128>) -> Self {
621 self.gas_price = gas_price;
622 self
623 }
624
625 #[must_use]
627 pub fn set_pruned_history(mut self, prune_history: Option<Option<usize>>) -> Self {
628 self.prune_history = PruneStateHistoryConfig::from_args(prune_history);
629 self
630 }
631
632 #[must_use]
634 pub fn with_max_persisted_states<U: Into<usize>>(
635 mut self,
636 max_persisted_states: Option<U>,
637 ) -> Self {
638 self.max_persisted_states = max_persisted_states.map(Into::into);
639 self
640 }
641
642 #[must_use]
644 pub fn with_transaction_block_keeper<U: Into<usize>>(
645 mut self,
646 transaction_block_keeper: Option<U>,
647 ) -> Self {
648 self.transaction_block_keeper = transaction_block_keeper.map(Into::into);
649 self
650 }
651
652 #[must_use]
654 pub fn with_base_fee(mut self, base_fee: Option<u64>) -> Self {
655 self.base_fee = base_fee;
656 self
657 }
658
659 #[must_use]
661 pub fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self {
662 self.disable_min_priority_fee = disable_min_priority_fee;
663 self
664 }
665
666 #[must_use]
668 pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
669 self.genesis = genesis;
670 self
671 }
672
673 pub fn get_genesis_timestamp(&self) -> u64 {
675 self.genesis_timestamp
676 .or_else(|| self.genesis.as_ref().map(|g| g.timestamp))
677 .unwrap_or_else(|| duration_since_unix_epoch().as_secs())
678 }
679
680 #[must_use]
682 pub fn with_genesis_timestamp<U: Into<u64>>(mut self, timestamp: Option<U>) -> Self {
683 if let Some(timestamp) = timestamp {
684 self.genesis_timestamp = Some(timestamp.into());
685 }
686 self
687 }
688
689 #[must_use]
691 pub fn with_genesis_block_number<U: Into<u64>>(mut self, number: Option<U>) -> Self {
692 if let Some(number) = number {
693 self.genesis_block_number = Some(number.into());
694 }
695 self
696 }
697
698 pub fn get_genesis_number(&self) -> u64 {
700 self.genesis_block_number
701 .or_else(|| self.genesis.as_ref().and_then(|g| g.number))
702 .unwrap_or(0)
703 }
704
705 #[must_use]
707 pub fn with_hardfork(mut self, hardfork: Option<ChainHardfork>) -> Self {
708 self.hardfork = hardfork;
709 self
710 }
711
712 #[must_use]
714 pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
715 self.genesis_accounts = accounts;
716 self
717 }
718
719 #[must_use]
721 pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
722 self.signer_accounts = accounts;
723 self
724 }
725
726 pub fn with_account_generator(mut self, generator: AccountGenerator) -> eyre::Result<Self> {
729 let accounts = generator.generate()?;
730 self.account_generator = Some(generator);
731 Ok(self.with_signer_accounts(accounts.clone()).with_genesis_accounts(accounts))
732 }
733
734 #[must_use]
736 pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
737 self.genesis_balance = balance.into();
738 self
739 }
740
741 #[must_use]
743 pub fn with_blocktime<D: Into<Duration>>(mut self, block_time: Option<D>) -> Self {
744 self.block_time = block_time.map(Into::into);
745 self
746 }
747
748 #[must_use]
749 pub fn with_mixed_mining<D: Into<Duration>>(
750 mut self,
751 mixed_mining: bool,
752 block_time: Option<D>,
753 ) -> Self {
754 self.block_time = block_time.map(Into::into);
755 self.mixed_mining = mixed_mining;
756 self
757 }
758
759 #[must_use]
761 pub fn with_no_mining(mut self, no_mining: bool) -> Self {
762 self.no_mining = no_mining;
763 self
764 }
765
766 #[must_use]
768 pub fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self {
769 self.slots_in_an_epoch = slots_in_an_epoch;
770 self
771 }
772
773 #[must_use]
775 pub fn with_port(mut self, port: u16) -> Self {
776 self.port = port;
777 self
778 }
779
780 #[must_use]
787 pub fn with_ipc(mut self, ipc_path: Option<Option<String>>) -> Self {
788 self.ipc_path = ipc_path;
789 self
790 }
791
792 #[must_use]
794 pub fn set_config_out(mut self, config_out: Option<PathBuf>) -> Self {
795 self.config_out = config_out;
796 self
797 }
798
799 #[must_use]
801 pub fn no_storage_caching(self) -> Self {
802 self.with_storage_caching(true)
803 }
804
805 #[must_use]
806 pub fn with_storage_caching(mut self, storage_caching: bool) -> Self {
807 self.no_storage_caching = storage_caching;
808 self
809 }
810
811 #[must_use]
813 pub fn with_eth_rpc_url<U: Into<String>>(mut self, eth_rpc_url: Option<U>) -> Self {
814 self.eth_rpc_url = eth_rpc_url.map(Into::into);
815 self
816 }
817
818 #[must_use]
820 pub fn with_fork_block_number<U: Into<u64>>(self, fork_block_number: Option<U>) -> Self {
821 self.with_fork_choice(fork_block_number.map(Into::into))
822 }
823
824 #[must_use]
826 pub fn with_fork_transaction_hash<U: Into<TxHash>>(
827 self,
828 fork_transaction_hash: Option<U>,
829 ) -> Self {
830 self.with_fork_choice(fork_transaction_hash.map(Into::into))
831 }
832
833 #[must_use]
835 pub fn with_fork_choice<U: Into<ForkChoice>>(mut self, fork_choice: Option<U>) -> Self {
836 self.fork_choice = fork_choice.map(Into::into);
837 self
838 }
839
840 #[must_use]
842 pub fn with_fork_chain_id(mut self, fork_chain_id: Option<U256>) -> Self {
843 self.fork_chain_id = fork_chain_id;
844 self
845 }
846
847 #[must_use]
849 pub fn with_fork_headers(mut self, headers: Vec<String>) -> Self {
850 self.fork_headers = headers;
851 self
852 }
853
854 #[must_use]
856 pub fn fork_request_timeout(mut self, fork_request_timeout: Option<Duration>) -> Self {
857 if let Some(fork_request_timeout) = fork_request_timeout {
858 self.fork_request_timeout = fork_request_timeout;
859 }
860 self
861 }
862
863 #[must_use]
865 pub fn fork_request_retries(mut self, fork_request_retries: Option<u32>) -> Self {
866 if let Some(fork_request_retries) = fork_request_retries {
867 self.fork_request_retries = fork_request_retries;
868 }
869 self
870 }
871
872 #[must_use]
874 pub fn fork_retry_backoff(mut self, fork_retry_backoff: Option<Duration>) -> Self {
875 if let Some(fork_retry_backoff) = fork_retry_backoff {
876 self.fork_retry_backoff = fork_retry_backoff;
877 }
878 self
879 }
880
881 #[must_use]
885 pub fn fork_compute_units_per_second(mut self, compute_units_per_second: Option<u64>) -> Self {
886 if let Some(compute_units_per_second) = compute_units_per_second {
887 self.compute_units_per_second = compute_units_per_second;
888 }
889 self
890 }
891
892 #[must_use]
894 pub fn with_tracing(mut self, enable_tracing: bool) -> Self {
895 self.enable_tracing = enable_tracing;
896 self
897 }
898
899 #[must_use]
901 pub fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self {
902 self.enable_steps_tracing = enable_steps_tracing;
903 self
904 }
905
906 #[must_use]
908 pub fn with_print_logs(mut self, print_logs: bool) -> Self {
909 self.print_logs = print_logs;
910 self
911 }
912
913 #[must_use]
915 pub fn with_print_traces(mut self, print_traces: bool) -> Self {
916 self.print_traces = print_traces;
917 self
918 }
919
920 #[must_use]
922 pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
923 self.enable_auto_impersonate = enable_auto_impersonate;
924 self
925 }
926
927 #[must_use]
928 pub fn with_server_config(mut self, config: ServerConfig) -> Self {
929 self.server_config = config;
930 self
931 }
932
933 #[must_use]
935 pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
936 self.host = if host.is_empty() { vec![IpAddr::V4(Ipv4Addr::LOCALHOST)] } else { host };
937 self
938 }
939
940 #[must_use]
941 pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
942 self.transaction_order = transaction_order;
943 self
944 }
945
946 pub fn get_ipc_path(&self) -> Option<String> {
948 match &self.ipc_path {
949 Some(path) => path.clone().or_else(|| Some(DEFAULT_IPC_ENDPOINT.to_string())),
950 None => None,
951 }
952 }
953
954 pub fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
956 if let Some(path) = &self.config_out {
957 let file = io::BufWriter::new(
958 File::create(path).wrap_err("unable to create anvil config description file")?,
959 );
960 let value = self.as_json(fork);
961 serde_json::to_writer(file, &value).wrap_err("failed writing JSON")?;
962 }
963 if !self.silent {
964 sh_println!("{}", self.as_string(fork))?;
965 }
966 Ok(())
967 }
968
969 pub fn block_cache_path(&self, block: u64) -> Option<PathBuf> {
973 if self.no_storage_caching || self.eth_rpc_url.is_none() {
974 return None;
975 }
976 let chain_id = self.get_chain_id();
977
978 Config::foundry_block_cache_file(chain_id, block)
979 }
980
981 #[must_use]
983 pub fn with_optimism(mut self, enable_optimism: bool) -> Self {
984 self.enable_optimism = enable_optimism;
985 self
986 }
987
988 #[must_use]
990 pub fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self {
991 self.disable_default_create2_deployer = yes;
992 self
993 }
994
995 #[must_use]
997 pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self {
998 self.precompile_factory = Some(Arc::new(factory));
999 self
1000 }
1001
1002 #[must_use]
1004 pub fn with_odyssey(mut self, odyssey: bool) -> Self {
1005 self.odyssey = odyssey;
1006 self
1007 }
1008
1009 #[must_use]
1011 pub fn silent(self) -> Self {
1012 self.set_silent(true)
1013 }
1014
1015 #[must_use]
1016 pub fn set_silent(mut self, silent: bool) -> Self {
1017 self.silent = silent;
1018 self
1019 }
1020
1021 #[must_use]
1023 pub fn with_cache_path(mut self, cache_path: Option<PathBuf>) -> Self {
1024 self.cache_path = cache_path;
1025 self
1026 }
1027
1028 pub(crate) async fn setup(&mut self) -> Result<mem::Backend> {
1033 let mut cfg = CfgEnv::default();
1036 cfg.spec = self.get_hardfork().into();
1037
1038 cfg.chain_id = self.get_chain_id();
1039 cfg.limit_contract_code_size = self.code_size_limit;
1040 cfg.disable_eip3607 = true;
1044 cfg.disable_block_gas_limit = self.disable_block_gas_limit;
1045
1046 if let Some(value) = self.memory_limit {
1047 cfg.memory_limit = value;
1048 }
1049
1050 let spec_id = cfg.spec;
1051 let mut env = Env::new(
1052 cfg,
1053 BlockEnv {
1054 gas_limit: self.gas_limit(),
1055 basefee: self.get_base_fee(),
1056 ..Default::default()
1057 },
1058 OpTransaction {
1059 base: TxEnv { chain_id: Some(self.get_chain_id()), ..Default::default() },
1060 ..Default::default()
1061 },
1062 self.enable_optimism,
1063 );
1064
1065 let fees = FeeManager::new(
1066 spec_id,
1067 self.get_base_fee(),
1068 !self.disable_min_priority_fee,
1069 self.get_gas_price(),
1070 self.get_blob_excess_gas_and_price(),
1071 );
1072
1073 let (db, fork): (Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>) =
1074 if let Some(eth_rpc_url) = self.eth_rpc_url.clone() {
1075 self.setup_fork_db(eth_rpc_url, &mut env, &fees).await?
1076 } else {
1077 (Arc::new(TokioRwLock::new(Box::<MemDb>::default())), None)
1078 };
1079
1080 if let Some(ref genesis) = self.genesis {
1082 if self.chain_id.is_none() {
1085 env.evm_env.cfg_env.chain_id = genesis.config.chain_id;
1086 }
1087 env.evm_env.block_env.timestamp = U256::from(genesis.timestamp);
1088 if let Some(base_fee) = genesis.base_fee_per_gas {
1089 env.evm_env.block_env.basefee = base_fee.try_into()?;
1090 }
1091 if let Some(number) = genesis.number {
1092 env.evm_env.block_env.number = U256::from(number);
1093 }
1094 env.evm_env.block_env.beneficiary = genesis.coinbase;
1095 }
1096
1097 let genesis = GenesisConfig {
1098 number: self.get_genesis_number(),
1099 timestamp: self.get_genesis_timestamp(),
1100 balance: self.genesis_balance,
1101 accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(),
1102 genesis_init: self.genesis.clone(),
1103 };
1104
1105 let backend = mem::Backend::with_genesis(
1107 db,
1108 Arc::new(RwLock::new(env)),
1109 genesis,
1110 fees,
1111 Arc::new(RwLock::new(fork)),
1112 self.enable_steps_tracing,
1113 self.print_logs,
1114 self.print_traces,
1115 self.odyssey,
1116 self.prune_history,
1117 self.max_persisted_states,
1118 self.transaction_block_keeper,
1119 self.block_time,
1120 self.cache_path.clone(),
1121 Arc::new(TokioRwLock::new(self.clone())),
1122 )
1123 .await?;
1124
1125 if !self.disable_default_create2_deployer && self.eth_rpc_url.is_none() {
1128 backend
1129 .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER)
1130 .await
1131 .wrap_err("failed to create default create2 deployer")?;
1132 }
1133
1134 if let Some(state) = self.init_state.clone() {
1135 backend.load_state(state).await.wrap_err("failed to load init state")?;
1136 }
1137
1138 Ok(backend)
1139 }
1140
1141 pub async fn setup_fork_db(
1148 &mut self,
1149 eth_rpc_url: String,
1150 env: &mut Env,
1151 fees: &FeeManager,
1152 ) -> Result<(Arc<TokioRwLock<Box<dyn Db>>>, Option<ClientFork>)> {
1153 let (db, config) = self.setup_fork_db_config(eth_rpc_url, env, fees).await?;
1154 let db: Arc<TokioRwLock<Box<dyn Db>>> = Arc::new(TokioRwLock::new(Box::new(db)));
1155 let fork = ClientFork::new(config, Arc::clone(&db));
1156 Ok((db, Some(fork)))
1157 }
1158
1159 pub async fn setup_fork_db_config(
1165 &mut self,
1166 eth_rpc_url: String,
1167 env: &mut Env,
1168 fees: &FeeManager,
1169 ) -> Result<(ForkedDatabase, ClientForkConfig)> {
1170 debug!(target: "node", ?eth_rpc_url, "setting up fork db");
1171 let provider = Arc::new(
1172 ProviderBuilder::new(ð_rpc_url)
1173 .timeout(self.fork_request_timeout)
1174 .initial_backoff(self.fork_retry_backoff.as_millis() as u64)
1175 .compute_units_per_second(self.compute_units_per_second)
1176 .max_retry(self.fork_request_retries)
1177 .initial_backoff(1000)
1178 .headers(self.fork_headers.clone())
1179 .build()
1180 .wrap_err("failed to establish provider to fork url")?,
1181 );
1182
1183 let (fork_block_number, fork_chain_id, force_transactions) = if let Some(fork_choice) =
1184 &self.fork_choice
1185 {
1186 let (fork_block_number, force_transactions) =
1187 derive_block_and_transactions(fork_choice, &provider).await.wrap_err(
1188 "failed to derive fork block number and force transactions from fork choice",
1189 )?;
1190 let chain_id = if let Some(chain_id) = self.fork_chain_id {
1191 Some(chain_id)
1192 } else if self.hardfork.is_none() {
1193 let chain_id =
1195 provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?;
1196 if alloy_chains::NamedChain::Mainnet == chain_id {
1197 let hardfork: EthereumHardfork =
1198 ethereum_hardfork_from_block_tag(fork_block_number);
1199
1200 env.evm_env.cfg_env.spec = spec_id_from_ethereum_hardfork(hardfork);
1201 self.hardfork = Some(ChainHardfork::Ethereum(hardfork));
1202 }
1203 Some(U256::from(chain_id))
1204 } else {
1205 None
1206 };
1207
1208 (fork_block_number, chain_id, force_transactions)
1209 } else {
1210 let bn = find_latest_fork_block(&provider)
1212 .await
1213 .wrap_err("failed to get fork block number")?;
1214 (bn, None, None)
1215 };
1216
1217 let block = provider
1218 .get_block(BlockNumberOrTag::Number(fork_block_number).into())
1219 .await
1220 .wrap_err("failed to get fork block")?;
1221
1222 let block = if let Some(block) = block {
1223 block
1224 } else {
1225 if let Ok(latest_block) = provider.get_block_number().await {
1226 let mut message = format!(
1227 "Failed to get block for block number: {fork_block_number}\n\
1228latest block number: {latest_block}"
1229 );
1230 if fork_block_number <= latest_block {
1234 message.push_str(&format!("\n{NON_ARCHIVE_NODE_WARNING}"));
1235 }
1236 eyre::bail!("{message}");
1237 }
1238 eyre::bail!("failed to get block for block number: {fork_block_number}")
1239 };
1240
1241 let gas_limit = self.fork_gas_limit(&block);
1242 self.gas_limit = Some(gas_limit);
1243
1244 env.evm_env.block_env = BlockEnv {
1245 number: U256::from(fork_block_number),
1246 timestamp: U256::from(block.header.timestamp),
1247 difficulty: block.header.difficulty,
1248 prevrandao: Some(block.header.mix_hash.unwrap_or_default()),
1250 gas_limit,
1251 beneficiary: env.evm_env.block_env.beneficiary,
1253 basefee: env.evm_env.block_env.basefee,
1254 ..Default::default()
1255 };
1256
1257 if self.base_fee.is_none() {
1259 if let Some(base_fee) = block.header.base_fee_per_gas {
1260 self.base_fee = Some(base_fee);
1261 env.evm_env.block_env.basefee = base_fee;
1262 let next_block_base_fee = fees.get_next_block_base_fee_per_gas(
1265 block.header.gas_used,
1266 gas_limit,
1267 block.header.base_fee_per_gas.unwrap_or_default(),
1268 );
1269
1270 fees.set_base_fee(next_block_base_fee);
1272 }
1273 if let (Some(blob_excess_gas), Some(blob_gas_used)) =
1274 (block.header.excess_blob_gas, block.header.blob_gas_used)
1275 {
1276 let blob_base_fee_update_fraction = get_blob_base_fee_update_fraction(
1277 fork_chain_id
1278 .unwrap_or_else(|| U256::from(Chain::mainnet().id()))
1279 .saturating_to(),
1280 block.header.timestamp,
1281 );
1282
1283 env.evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
1284 blob_excess_gas,
1285 blob_base_fee_update_fraction,
1286 ));
1287
1288 let next_block_blob_excess_gas =
1289 fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used);
1290
1291 fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
1292 next_block_blob_excess_gas,
1293 blob_base_fee_update_fraction,
1294 ));
1295 }
1296 }
1297
1298 if self.gas_price.is_none()
1300 && let Ok(gas_price) = provider.get_gas_price().await
1301 {
1302 self.gas_price = Some(gas_price);
1303 fees.set_gas_price(gas_price);
1304 }
1305
1306 let block_hash = block.header.hash;
1307
1308 let chain_id = if let Some(chain_id) = self.chain_id {
1309 chain_id
1310 } else {
1311 let chain_id = if let Some(fork_chain_id) = fork_chain_id {
1312 fork_chain_id.to()
1313 } else {
1314 provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?
1315 };
1316
1317 self.set_chain_id(Some(chain_id));
1319 env.evm_env.cfg_env.chain_id = chain_id;
1320 env.tx.base.chain_id = chain_id.into();
1321 chain_id
1322 };
1323 let override_chain_id = self.chain_id;
1324 apply_chain_and_block_specific_env_changes::<AnyNetwork>(env.as_env_mut(), &block);
1326
1327 let meta = BlockchainDbMeta::new(env.evm_env.block_env.clone(), eth_rpc_url.clone());
1328 let block_chain_db = if self.fork_chain_id.is_some() {
1329 BlockchainDb::new_skip_check(meta, self.block_cache_path(fork_block_number))
1330 } else {
1331 BlockchainDb::new(meta, self.block_cache_path(fork_block_number))
1332 };
1333
1334 let backend = SharedBackend::spawn_backend(
1337 Arc::clone(&provider),
1338 block_chain_db.clone(),
1339 Some(fork_block_number.into()),
1340 )
1341 .await;
1342
1343 let config = ClientForkConfig {
1344 eth_rpc_url,
1345 block_number: fork_block_number,
1346 block_hash,
1347 transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()),
1348 provider,
1349 chain_id,
1350 override_chain_id,
1351 timestamp: block.header.timestamp,
1352 base_fee: block.header.base_fee_per_gas.map(|g| g as u128),
1353 timeout: self.fork_request_timeout,
1354 retries: self.fork_request_retries,
1355 backoff: self.fork_retry_backoff,
1356 compute_units_per_second: self.compute_units_per_second,
1357 total_difficulty: block.header.total_difficulty.unwrap_or_default(),
1358 blob_gas_used: block.header.blob_gas_used.map(|g| g as u128),
1359 blob_excess_gas_and_price: env.evm_env.block_env.blob_excess_gas_and_price,
1360 force_transactions,
1361 };
1362
1363 debug!(target: "node", fork_number=config.block_number, fork_hash=%config.block_hash, "set up fork db");
1364
1365 let mut db = ForkedDatabase::new(backend, block_chain_db);
1366
1367 db.insert_block_hash(U256::from(config.block_number), config.block_hash);
1369
1370 Ok((db, config))
1371 }
1372
1373 pub(crate) fn fork_gas_limit<T: TransactionResponse, H: BlockHeader>(
1377 &self,
1378 block: &Block<T, H>,
1379 ) -> u64 {
1380 if !self.disable_block_gas_limit {
1381 if let Some(gas_limit) = self.gas_limit {
1382 return gas_limit;
1383 } else if block.header.gas_limit() > 0 {
1384 return block.header.gas_limit();
1385 }
1386 }
1387
1388 u64::MAX
1389 }
1390
1391 pub(crate) fn gas_limit(&self) -> u64 {
1395 if self.disable_block_gas_limit {
1396 return u64::MAX;
1397 }
1398
1399 self.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT)
1400 }
1401}
1402
1403async fn derive_block_and_transactions(
1408 fork_choice: &ForkChoice,
1409 provider: &Arc<RetryProvider>,
1410) -> eyre::Result<(BlockNumber, Option<Vec<PoolTransaction>>)> {
1411 match fork_choice {
1412 ForkChoice::Block(block_number) => {
1413 let block_number = *block_number;
1414 if block_number >= 0 {
1415 return Ok((block_number as u64, None));
1416 }
1417 let latest = provider.get_block_number().await?;
1419
1420 Ok((block_number.saturating_add(latest as i128) as u64, None))
1421 }
1422 ForkChoice::Transaction(transaction_hash) => {
1423 let transaction = provider
1425 .get_transaction_by_hash(transaction_hash.0.into())
1426 .await?
1427 .ok_or_else(|| eyre::eyre!("failed to get fork transaction by hash"))?;
1428 let transaction_block_number = transaction.block_number.unwrap();
1429
1430 let transaction_block = provider
1432 .get_block_by_number(transaction_block_number.into())
1433 .full()
1434 .await?
1435 .ok_or_else(|| eyre::eyre!("failed to get fork block by number"))?;
1436
1437 let filtered_transactions = transaction_block
1439 .transactions
1440 .as_transactions()
1441 .ok_or_else(|| eyre::eyre!("failed to get transactions from full fork block"))?
1442 .iter()
1443 .take_while_inclusive(|&transaction| transaction.tx_hash() != transaction_hash.0)
1444 .collect::<Vec<_>>();
1445
1446 let force_transactions = filtered_transactions
1448 .iter()
1449 .map(|&transaction| PoolTransaction::try_from(transaction.clone()))
1450 .collect::<Result<Vec<_>, _>>()
1451 .map_err(|e| eyre::eyre!("Err converting to pool transactions {e}"))?;
1452 Ok((transaction_block_number.saturating_sub(1), Some(force_transactions)))
1453 }
1454 }
1455}
1456
1457#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1459pub enum ForkChoice {
1460 Block(i128),
1464 Transaction(TxHash),
1466}
1467
1468impl ForkChoice {
1469 pub fn block_number(&self) -> Option<i128> {
1471 match self {
1472 Self::Block(block_number) => Some(*block_number),
1473 Self::Transaction(_) => None,
1474 }
1475 }
1476
1477 pub fn transaction_hash(&self) -> Option<TxHash> {
1479 match self {
1480 Self::Block(_) => None,
1481 Self::Transaction(transaction_hash) => Some(*transaction_hash),
1482 }
1483 }
1484}
1485
1486impl From<TxHash> for ForkChoice {
1488 fn from(tx_hash: TxHash) -> Self {
1489 Self::Transaction(tx_hash)
1490 }
1491}
1492
1493impl From<u64> for ForkChoice {
1495 fn from(block: u64) -> Self {
1496 Self::Block(block as i128)
1497 }
1498}
1499
1500#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1501pub struct PruneStateHistoryConfig {
1502 pub enabled: bool,
1503 pub max_memory_history: Option<usize>,
1504}
1505
1506impl PruneStateHistoryConfig {
1507 pub fn is_state_history_supported(&self) -> bool {
1509 !self.enabled || self.max_memory_history.is_some()
1510 }
1511
1512 pub fn is_config_enabled(&self) -> bool {
1514 self.enabled
1515 }
1516
1517 pub fn from_args(val: Option<Option<usize>>) -> Self {
1518 val.map(|max_memory_history| Self { enabled: true, max_memory_history }).unwrap_or_default()
1519 }
1520}
1521
1522#[derive(Clone, Debug)]
1524pub struct AccountGenerator {
1525 chain_id: u64,
1526 amount: usize,
1527 phrase: String,
1528 derivation_path: Option<String>,
1529}
1530
1531impl AccountGenerator {
1532 pub fn new(amount: usize) -> Self {
1533 Self {
1534 chain_id: CHAIN_ID,
1535 amount,
1536 phrase: Mnemonic::<English>::new(&mut thread_rng()).to_phrase(),
1537 derivation_path: None,
1538 }
1539 }
1540
1541 #[must_use]
1542 pub fn phrase(mut self, phrase: impl Into<String>) -> Self {
1543 self.phrase = phrase.into();
1544 self
1545 }
1546
1547 fn get_phrase(&self) -> &str {
1548 &self.phrase
1549 }
1550
1551 #[must_use]
1552 pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
1553 self.chain_id = chain_id.into();
1554 self
1555 }
1556
1557 #[must_use]
1558 pub fn derivation_path(mut self, derivation_path: impl Into<String>) -> Self {
1559 let mut derivation_path = derivation_path.into();
1560 if !derivation_path.ends_with('/') {
1561 derivation_path.push('/');
1562 }
1563 self.derivation_path = Some(derivation_path);
1564 self
1565 }
1566
1567 fn get_derivation_path(&self) -> &str {
1568 self.derivation_path.as_deref().unwrap_or("m/44'/60'/0'/0/")
1569 }
1570}
1571
1572impl AccountGenerator {
1573 pub fn generate(&self) -> eyre::Result<Vec<PrivateKeySigner>> {
1574 let builder = MnemonicBuilder::<English>::default().phrase(self.phrase.as_str());
1575
1576 let derivation_path = self.get_derivation_path();
1578
1579 let mut wallets = Vec::with_capacity(self.amount);
1580 for idx in 0..self.amount {
1581 let builder =
1582 builder.clone().derivation_path(format!("{derivation_path}{idx}")).unwrap();
1583 let wallet = builder.build()?.with_chain_id(Some(self.chain_id));
1584 wallets.push(wallet)
1585 }
1586 Ok(wallets)
1587 }
1588}
1589
1590pub fn anvil_dir() -> Option<PathBuf> {
1592 Config::foundry_dir().map(|p| p.join("anvil"))
1593}
1594
1595pub fn anvil_tmp_dir() -> Option<PathBuf> {
1597 anvil_dir().map(|p| p.join("tmp"))
1598}
1599
1600async fn find_latest_fork_block<P: Provider<AnyNetwork>>(
1605 provider: P,
1606) -> Result<u64, TransportError> {
1607 let mut num = provider.get_block_number().await?;
1608
1609 for _ in 0..2 {
1612 if let Some(block) = provider.get_block(num.into()).await?
1613 && !block.header.hash.is_zero()
1614 {
1615 break;
1616 }
1617 num = num.saturating_sub(1)
1619 }
1620
1621 Ok(num)
1622}
1623
1624#[cfg(test)]
1625mod tests {
1626 use super::*;
1627
1628 #[test]
1629 fn test_prune_history() {
1630 let config = PruneStateHistoryConfig::default();
1631 assert!(config.is_state_history_supported());
1632 let config = PruneStateHistoryConfig::from_args(Some(None));
1633 assert!(!config.is_state_history_supported());
1634 let config = PruneStateHistoryConfig::from_args(Some(Some(10)));
1635 assert!(config.is_state_history_supported());
1636 }
1637}