1use crate::constants::*;
2use crate::types::*;
3use alloy::primitives::hex;
4use alloy::signers::local::PrivateKeySigner;
5use anvil_zksync_common::cache::{CacheConfig, DEFAULT_DISK_CACHE_DIR};
6use anvil_zksync_common::sh_println;
7use anvil_zksync_common::utils::cost::{format_eth, format_gwei};
8use anvil_zksync_types::{
9 LogLevel, ShowGasDetails, ShowStorageLogs, ShowVMDetails, TransactionOrder,
10};
11use colored::{Colorize, CustomColor};
12use serde_json::{json, to_writer, Value};
13use std::collections::HashMap;
14use std::fs::File;
15use std::net::{IpAddr, Ipv4Addr};
16use std::path::PathBuf;
17use std::time::Duration;
18use zksync_types::fee_model::{BaseTokenConversionRatio, FeeModelConfigV2};
19use zksync_types::{ProtocolVersionId, U256};
20
21pub const VERSION_MESSAGE: &str = concat!(env!("CARGO_PKG_VERSION"));
22
23pub const DEFAULT_PROTOCOL_VERSION: ProtocolVersionId = ProtocolVersionId::Version27;
26
27const BANNER: &str = r#"
28 _ _ _____ _ __
29 __ _ _ __ __ __(_)| | |__ /| |/ / ___ _ _ _ __ ___
30 / _` || '_ \ \ \ / /| || | _____ / / | ' / / __|| | | || '_ \ / __|
31| (_| || | | | \ V / | || ||_____| / /_ | . \ \__ \| |_| || | | || (__
32 \__,_||_| |_| \_/ |_||_| /____||_|\_\|___/ \__, ||_| |_| \___|
33 |___/
34"#;
35pub struct ForkPrintInfo {
37 pub network_rpc: String,
38 pub l1_block: String,
39 pub l2_block: String,
40 pub block_timestamp: String,
41 pub fork_block_hash: String,
42 pub fee_model_config_v2: FeeModelConfigV2,
43}
44
45#[derive(Debug, Clone)]
47pub struct TestNodeConfig {
48 pub config_out: Option<String>,
50 pub port: u16,
52 pub show_node_config: bool,
54 pub show_storage_logs: ShowStorageLogs,
56 pub show_vm_details: ShowVMDetails,
58 pub show_gas_details: ShowGasDetails,
60 pub verbosity: u8,
62 pub silent: bool,
64 pub system_contracts_options: SystemContractsOptions,
66 pub system_contracts_path: Option<PathBuf>,
68 pub protocol_version: Option<ProtocolVersionId>,
71 pub override_bytecodes_dir: Option<String>,
73 pub bytecode_compression: bool,
75 pub use_evm_interpreter: bool,
77 pub boojum: BoojumConfig,
79 pub chain_id: Option<u32>,
81 pub l1_gas_price: Option<u64>,
83 pub l2_gas_price: Option<u64>,
85 pub l1_pubdata_price: Option<u64>,
87 pub price_scale_factor: Option<f64>,
89 pub limit_scale_factor: Option<f32>,
91 pub log_level: LogLevel,
93 pub log_file_path: String,
95 pub cache_dir: String,
97 pub cache_config: CacheConfig,
99 pub genesis_accounts: Vec<PrivateKeySigner>,
101 pub genesis_balance: U256,
103 pub account_generator: Option<AccountGenerator>,
105 pub signer_accounts: Vec<PrivateKeySigner>,
107 pub genesis: Option<Genesis>,
109 pub genesis_timestamp: Option<u64>,
111 pub enable_auto_impersonate: bool,
113 pub offline: bool,
115 pub host: Vec<IpAddr>,
117 pub health_check_endpoint: bool,
119 pub block_time: Option<Duration>,
122 pub max_transactions: usize,
124 pub no_mining: bool,
126 pub allow_origin: String,
128 pub no_cors: bool,
130 pub transaction_order: TransactionOrder,
132 pub state: Option<PathBuf>,
134 pub dump_state: Option<PathBuf>,
136 pub state_interval: Option<u64>,
138 pub preserve_historical_states: bool,
140 pub load_state: Option<PathBuf>,
142 pub l1_config: Option<L1Config>,
144 pub auto_execute_l1: bool,
146 pub base_token_config: BaseTokenConfig,
148}
149
150#[derive(Debug, Clone)]
151pub enum L1Config {
152 Spawn {
154 port: u16,
156 },
157 External {
159 address: String,
161 },
162}
163
164#[derive(Debug, Clone)]
165pub struct BaseTokenConfig {
166 pub symbol: String,
168 pub ratio: BaseTokenConversionRatio,
170}
171
172impl Default for BaseTokenConfig {
173 fn default() -> Self {
174 Self {
175 symbol: "ETH".to_string(),
176 ratio: BaseTokenConversionRatio::default(),
177 }
178 }
179}
180
181impl Default for TestNodeConfig {
182 fn default() -> Self {
183 let genesis_accounts = AccountGenerator::new(10).phrase(DEFAULT_MNEMONIC).gen();
185 Self {
186 config_out: None,
188 port: NODE_PORT,
189 show_node_config: true,
190 show_storage_logs: Default::default(),
191 show_vm_details: Default::default(),
192 show_gas_details: Default::default(),
193 verbosity: 0,
194 silent: false,
195 system_contracts_options: Default::default(),
196 system_contracts_path: None,
197 protocol_version: None,
198 override_bytecodes_dir: None,
199 bytecode_compression: false,
200 use_evm_interpreter: false,
201 boojum: Default::default(),
202 chain_id: None,
203
204 l1_gas_price: None,
206 l2_gas_price: None,
207 l1_pubdata_price: None,
208 price_scale_factor: None,
209 limit_scale_factor: None,
210
211 log_level: Default::default(),
213 log_file_path: String::from(DEFAULT_LOG_FILE_PATH),
214
215 cache_dir: String::from(DEFAULT_DISK_CACHE_DIR),
217 cache_config: Default::default(),
218
219 account_generator: None,
221 genesis_accounts: genesis_accounts.clone(),
222 signer_accounts: genesis_accounts,
223 enable_auto_impersonate: false,
224 genesis_balance: U256::from(100u128 * 10u128.pow(18)),
226 genesis_timestamp: Some(NON_FORK_FIRST_BLOCK_TIMESTAMP),
227 genesis: None,
228
229 offline: false,
231 host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
232 health_check_endpoint: false,
233
234 block_time: None,
236 no_mining: false,
237
238 max_transactions: 1000,
239 transaction_order: TransactionOrder::Fifo,
240
241 allow_origin: "*".to_string(),
243 no_cors: false,
244
245 state: None,
247 dump_state: None,
248 state_interval: None,
249 preserve_historical_states: false,
250 load_state: None,
251 l1_config: None,
252 auto_execute_l1: false,
253 base_token_config: BaseTokenConfig::default(),
254 }
255 }
256}
257
258impl TestNodeConfig {
259 pub fn protocol_version(&self) -> ProtocolVersionId {
260 match self.system_contracts_options {
261 SystemContractsOptions::BuiltIn => self
262 .protocol_version
263 .unwrap_or(DEFAULT_PROTOCOL_VERSION),
264 SystemContractsOptions::Local =>
265 self.protocol_version.expect("cannot deduce protocol version when using local contracts; please specify --protocol-version explicitly"),
266 SystemContractsOptions::BuiltInWithoutSecurity => self
267 .protocol_version
268 .unwrap_or(DEFAULT_PROTOCOL_VERSION),
269 }
270 }
271}
272
273impl TestNodeConfig {
274 pub fn print(&self, fork_details: Option<&ForkPrintInfo>) {
275 if let Some(config_out) = self.config_out.as_deref() {
276 let file = File::create(config_out)
277 .expect("Unable to create anvil-zksync config description file");
278 to_writer(&file, &self.as_json(fork_details)).expect("Failed writing json");
279 }
280
281 if self.silent || !self.show_node_config {
282 return;
283 }
284
285 let color = CustomColor::new(13, 71, 198);
286
287 sh_println!(
289 r#"
290{}
291Version: {}
292Repository: {}
293
294"#,
295 BANNER.custom_color(color),
296 VERSION_MESSAGE.green(),
297 "https://github.com/matter-labs/anvil-zksync".green()
298 );
299
300 let balance = format_eth(self.genesis_balance);
302 let mut rich_accounts = String::new();
303 for (idx, account) in self.genesis_accounts.iter().enumerate() {
304 rich_accounts.push_str(&format!("({}) {} ({})\n", idx, account.address(), balance));
305 }
306 sh_println!(
307 r#"
308Rich Accounts
309========================
310{}
311"#,
312 rich_accounts
313 );
314
315 let mut private_keys = String::new();
317 for (idx, account) in self.genesis_accounts.iter().enumerate() {
318 let private_key = hex::encode(account.credential().to_bytes());
319 private_keys.push_str(&format!("({}) 0x{}\n", idx, private_key));
320 }
321 sh_println!(
322 r#"
323Private Keys
324========================
325{}
326"#,
327 private_keys
328 );
329
330 if let Some(ref generator) = self.account_generator {
332 sh_println!(
333 r#"
334Wallet
335========================
336Mnemonic: {}
337Derivation path: {}
338"#,
339 generator.get_phrase().green(),
340 generator.get_derivation_path().green()
341 );
342 }
343
344 if let Some(fd) = fork_details {
346 sh_println!(
347 r#"
348Fork Details
349========================
350Network RPC: {}
351Chain ID: {}
352L1 Batch #: {}
353L2 Block #: {}
354Block Timestamp: {}
355Fork Block Hash: {}
356Compute Overhead Part: {}
357Pubdata Overhead Part: {}
358Batch Overhead L1 Gas: {}
359Max Gas Per Batch: {}
360Max Pubdata Per Batch: {}
361"#,
362 fd.network_rpc.green(),
363 self.get_chain_id().to_string().green(),
364 fd.l1_block.green(),
365 fd.l2_block.green(),
366 fd.block_timestamp.to_string().green(),
367 format!("{:#}", fd.fork_block_hash).green(),
368 fd.fee_model_config_v2
369 .compute_overhead_part
370 .to_string()
371 .green(),
372 fd.fee_model_config_v2
373 .pubdata_overhead_part
374 .to_string()
375 .green(),
376 fd.fee_model_config_v2
377 .batch_overhead_l1_gas
378 .to_string()
379 .green(),
380 fd.fee_model_config_v2.max_gas_per_batch.to_string().green(),
381 fd.fee_model_config_v2
382 .max_pubdata_per_batch
383 .to_string()
384 .green()
385 );
386 } else {
387 sh_println!(
388 r#"
389Network Configuration
390========================
391Chain ID: {}
392"#,
393 self.chain_id
394 .unwrap_or(TEST_NODE_NETWORK_ID)
395 .to_string()
396 .green()
397 );
398 }
399
400 sh_println!(
402 r#"
403Gas Configuration
404========================
405L1 Gas Price (gwei): {}
406L2 Gas Price (gwei): {}
407L1 Pubdata Price (gwei): {}
408Estimated Gas Price Scale Factor: {}
409Estimated Gas Limit Scale Factor: {}
410"#,
411 format_gwei(self.get_l1_gas_price().into()).green(),
412 format_gwei(self.get_l2_gas_price().into()).green(),
413 format_gwei(self.get_l1_pubdata_price().into()).green(),
414 self.get_price_scale().to_string().green(),
415 self.get_gas_limit_scale().to_string().green()
416 );
417
418 sh_println!(
420 r#"
421Genesis Timestamp
422========================
423{}
424"#,
425 self.get_genesis_timestamp().to_string().green()
426 );
427
428 sh_println!(
430 r#"
431Node Configuration
432========================
433Port: {}
434EVM Interpreter: {}
435Health Check Endpoint: {}
436BoojumOS: {}
437L1: {}
438"#,
439 self.port,
440 if self.use_evm_interpreter {
441 "Enabled".green()
442 } else {
443 "Disabled".red()
444 },
445 if self.health_check_endpoint {
446 "Enabled".green()
447 } else {
448 "Disabled".red()
449 },
450 if self.boojum.use_boojum {
451 "Enabled".green()
452 } else {
453 "Disabled".red()
454 },
455 if self.l1_config.is_some() {
456 "Enabled".green()
457 } else {
458 "Disabled".red()
459 }
460 );
461
462 match self.l1_config.as_ref() {
464 Some(L1Config::Spawn { port }) => {
465 sh_println!(
466 r#"
467L1 Configuration (Spawned)
468========================
469Port: {port}
470"#
471 );
472 }
473 Some(L1Config::External { address }) => {
474 sh_println!(
475 r#"
476L1 Configuration (External)
477========================
478Address: {address}
479"#
480 );
481 }
482 None => {}
483 }
484
485 let mut listening = String::new();
487 listening.push_str("\n========================================\n");
488 for host in &self.host {
489 listening.push_str(&format!(
490 " Listening on {}:{}\n",
491 host.to_string().green(),
492 self.port.to_string().green()
493 ));
494 }
495 listening.push_str("========================================\n");
496 sh_println!("{}", listening);
497 }
498
499 fn as_json(&self, fork: Option<&ForkPrintInfo>) -> Value {
500 let mut wallet_description = HashMap::new();
501 let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
502 let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
503
504 for wallet in &self.genesis_accounts {
505 available_accounts.push(format!("{:?}", wallet.address()));
506 private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
507 }
508
509 if let Some(ref gen) = self.account_generator {
510 let phrase = gen.get_phrase().to_string();
511 let derivation_path = gen.get_derivation_path().to_string();
512
513 wallet_description.insert("derivation_path".to_string(), derivation_path);
514 wallet_description.insert("mnemonic".to_string(), phrase);
515 };
516
517 if let Some(fork) = fork {
518 json!({
519 "available_accounts": available_accounts,
520 "private_keys": private_keys,
521 "endpoint": fork.network_rpc,
522 "l1_block": fork.l1_block,
523 "l2_block": fork.l2_block,
524 "block_hash": fork.fork_block_hash,
525 "chain_id": self.get_chain_id(),
526 "wallet": wallet_description,
527 "l1_gas_price": format!("{}", self.get_l1_gas_price()),
528 "l2_gas_price": format!("{}", self.get_l2_gas_price()),
529 "l1_pubdata_price": format!("{}", self.get_l1_pubdata_price()),
530 "price_scale_factor": format!("{}", self.get_price_scale()),
531 "limit_scale_factor": format!("{}", self.get_gas_limit_scale()),
532 "fee_model_config_v2": fork.fee_model_config_v2,
533 })
534 } else {
535 json!({
536 "available_accounts": available_accounts,
537 "private_keys": private_keys,
538 "wallet": wallet_description,
539 "chain_id": self.get_chain_id(),
540 "l1_gas_price": format!("{}", self.get_l1_gas_price()),
541 "l2_gas_price": format!("{}", self.get_l2_gas_price()),
542 "l1_pubdata_price": format!("{}", self.get_l1_pubdata_price()),
543 "price_scale_factor": format!("{}", self.get_price_scale()),
544 "limit_scale_factor": format!("{}", self.get_gas_limit_scale()),
545 })
546 }
547 }
548
549 #[must_use]
551 pub fn set_config_out(mut self, config_out: Option<String>) -> Self {
552 self.config_out = config_out;
553 self
554 }
555
556 #[must_use]
558 pub fn with_port(mut self, port: Option<u16>) -> Self {
559 if let Some(port) = port {
560 self.port = port;
561 }
562 self
563 }
564
565 pub fn get_port(&self) -> u16 {
567 self.port
568 }
569
570 #[must_use]
572 pub fn with_chain_id(mut self, chain_id: Option<u32>) -> Self {
573 if let Some(chain_id) = chain_id {
574 self.chain_id = Some(chain_id);
575 }
576 self
577 }
578
579 pub fn get_chain_id(&self) -> u32 {
581 self.chain_id.unwrap_or(TEST_NODE_NETWORK_ID)
582 }
583
584 pub fn update_chain_id(&mut self, chain_id: Option<u32>) -> &mut Self {
586 self.chain_id = chain_id;
587 self
588 }
589
590 #[must_use]
592 pub fn with_system_contracts(mut self, option: Option<SystemContractsOptions>) -> Self {
593 if let Some(option) = option {
594 self.system_contracts_options = option;
595 }
596 self
597 }
598
599 #[must_use]
601 pub fn with_system_contracts_path(mut self, path: Option<PathBuf>) -> Self {
602 if let Some(path) = path {
603 self.system_contracts_path = Some(path);
604 }
605 self
606 }
607
608 #[must_use]
610 pub fn with_protocol_version(mut self, protocol_version: Option<ProtocolVersionId>) -> Self {
611 self.protocol_version = protocol_version;
612 self
613 }
614
615 pub fn get_system_contracts(&self) -> SystemContractsOptions {
617 self.system_contracts_options
618 }
619
620 #[must_use]
622 pub fn with_override_bytecodes_dir(mut self, dir: Option<String>) -> Self {
623 if let Some(dir) = dir {
624 self.override_bytecodes_dir = Some(dir);
625 }
626 self
627 }
628
629 pub fn get_override_bytecodes_dir(&self) -> Option<&String> {
631 self.override_bytecodes_dir.as_ref()
632 }
633
634 #[must_use]
636 pub fn with_enforce_bytecode_compression(mut self, enforce: Option<bool>) -> Self {
637 if let Some(enforce) = enforce {
638 self.bytecode_compression = enforce;
639 }
640 self
641 }
642
643 pub fn is_bytecode_compression_enforced(&self) -> bool {
645 self.bytecode_compression
646 }
647
648 #[must_use]
650 pub fn with_evm_interpreter(mut self, enable: Option<bool>) -> Self {
651 if let Some(enable) = enable {
652 self.use_evm_interpreter = enable;
653 }
654 self
655 }
656
657 #[must_use]
659 pub fn with_boojum(mut self, boojum: BoojumConfig) -> Self {
660 self.boojum = boojum;
661 self
662 }
663
664 pub fn is_evm_interpreter_enabled(&self) -> bool {
666 self.use_evm_interpreter
667 }
668
669 #[must_use]
671 pub fn with_l1_gas_price(mut self, price: Option<u64>) -> Self {
672 if let Some(price) = price {
673 self.l1_gas_price = Some(price);
674 }
675 self
676 }
677
678 pub fn get_l1_gas_price(&self) -> u64 {
680 self.l1_gas_price.unwrap_or(DEFAULT_L1_GAS_PRICE)
681 }
682
683 pub fn update_l1_gas_price(&mut self, price: Option<u64>) -> &mut Self {
685 self.l1_gas_price = price;
686 self
687 }
688
689 #[must_use]
691 pub fn with_l2_gas_price(mut self, price: Option<u64>) -> Self {
692 if let Some(price) = price {
693 self.l2_gas_price = Some(price);
694 }
695 self
696 }
697
698 pub fn get_l2_gas_price(&self) -> u64 {
700 self.l2_gas_price.unwrap_or(DEFAULT_L2_GAS_PRICE)
701 }
702
703 pub fn update_l2_gas_price(&mut self, price: Option<u64>) -> &mut Self {
705 self.l2_gas_price = price;
706 self
707 }
708
709 #[must_use]
711 pub fn with_l1_pubdata_price(mut self, price: Option<u64>) -> Self {
712 self.l1_pubdata_price = price;
713 self
714 }
715
716 pub fn get_l1_pubdata_price(&self) -> u64 {
718 self.l1_pubdata_price.unwrap_or(DEFAULT_FAIR_PUBDATA_PRICE)
719 }
720
721 pub fn update_l1_pubdata_price(&mut self, price: Option<u64>) -> &mut Self {
723 self.l1_pubdata_price = price;
724 self
725 }
726
727 #[must_use]
729 pub fn with_log_level(mut self, level: Option<LogLevel>) -> Self {
730 if let Some(level) = level {
731 self.log_level = level;
732 }
733 self
734 }
735
736 pub fn get_log_level(&self) -> LogLevel {
738 self.log_level
739 }
740
741 pub fn get_cache_dir(&self) -> &str {
743 &self.cache_dir
744 }
745
746 #[must_use]
748 pub fn with_cache_dir(mut self, dir: Option<String>) -> Self {
749 if let Some(dir) = dir {
750 self.cache_dir = dir;
751 }
752 self
753 }
754
755 #[must_use]
757 pub fn with_cache_config(mut self, config: Option<CacheConfig>) -> Self {
758 if let Some(config) = config {
759 self.cache_config = config;
760 }
761 self
762 }
763
764 pub fn get_cache_config(&self) -> &CacheConfig {
766 &self.cache_config
767 }
768
769 #[must_use]
771 pub fn with_log_file_path(mut self, path: Option<String>) -> Self {
772 if let Some(path) = path {
773 self.log_file_path = path;
774 }
775 self
776 }
777
778 pub fn get_log_file_path(&self) -> &str {
780 &self.log_file_path
781 }
782
783 #[must_use]
785 pub fn with_verbosity_level(mut self, verbosity: u8) -> Self {
786 self.verbosity = verbosity;
787 self
788 }
789
790 pub fn get_verbosity_level(&self) -> u8 {
792 self.verbosity
793 }
794
795 #[must_use]
797 pub fn with_silent(mut self, silent: Option<bool>) -> Self {
798 if let Some(silent) = silent {
799 self.silent = silent;
800 }
801 self
802 }
803
804 #[must_use]
806 pub fn with_show_node_config(mut self, show_node_config: Option<bool>) -> Self {
807 if let Some(show_node_config) = show_node_config {
808 self.show_node_config = show_node_config;
809 }
810 self
811 }
812
813 #[must_use]
815 pub fn with_show_storage_logs(mut self, show_storage_logs: Option<ShowStorageLogs>) -> Self {
816 if let Some(show_storage_logs) = show_storage_logs {
817 self.show_storage_logs = show_storage_logs;
818 }
819 self
820 }
821
822 pub fn get_show_storage_logs(&self) -> ShowStorageLogs {
824 self.show_storage_logs
825 }
826
827 #[must_use]
829 pub fn with_vm_log_detail(mut self, detail: Option<ShowVMDetails>) -> Self {
830 if let Some(detail) = detail {
831 self.show_vm_details = detail;
832 }
833 self
834 }
835
836 pub fn get_vm_log_detail(&self) -> ShowVMDetails {
838 self.show_vm_details
839 }
840
841 #[must_use]
843 pub fn with_show_gas_details(mut self, show_gas_details: Option<ShowGasDetails>) -> Self {
844 if let Some(show_gas_details) = show_gas_details {
845 self.show_gas_details = show_gas_details;
846 }
847 self
848 }
849
850 pub fn get_show_gas_details(&self) -> ShowGasDetails {
852 self.show_gas_details
853 }
854
855 #[must_use]
857 pub fn with_gas_limit_scale(mut self, scale: Option<f32>) -> Self {
858 if let Some(scale) = scale {
859 self.limit_scale_factor = Some(scale);
860 }
861 self
862 }
863
864 pub fn get_gas_limit_scale(&self) -> f32 {
866 self.limit_scale_factor
867 .unwrap_or(DEFAULT_ESTIMATE_GAS_SCALE_FACTOR)
868 }
869
870 pub fn update_gas_limit_scale(&mut self, scale: Option<f32>) -> &mut Self {
872 self.limit_scale_factor = scale;
873 self
874 }
875
876 #[must_use]
878 pub fn with_price_scale(mut self, scale: Option<f64>) -> Self {
879 if let Some(scale) = scale {
880 self.price_scale_factor = Some(scale);
881 }
882 self
883 }
884
885 pub fn get_price_scale(&self) -> f64 {
887 self.price_scale_factor
888 .unwrap_or(DEFAULT_ESTIMATE_GAS_PRICE_SCALE_FACTOR)
889 }
890
891 pub fn update_price_scale(&mut self, scale: Option<f64>) -> &mut Self {
893 self.price_scale_factor = scale;
894 self
895 }
896
897 #[must_use]
899 pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
900 self.genesis_balance = balance.into();
901 self
902 }
903
904 #[must_use]
906 pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
907 self.genesis_accounts = accounts;
908 self
909 }
910
911 #[must_use]
913 pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
914 self.signer_accounts = accounts;
915 self
916 }
917
918 #[must_use]
921 pub fn with_account_generator(mut self, generator: AccountGenerator) -> Self {
922 let accounts = generator.gen();
923 self.account_generator = Some(generator);
924 self.with_signer_accounts(accounts.clone())
925 .with_genesis_accounts(accounts)
926 }
927
928 #[must_use]
930 pub fn with_genesis_timestamp(mut self, timestamp: Option<u64>) -> Self {
931 self.genesis_timestamp = timestamp;
932 self
933 }
934
935 pub fn get_genesis_timestamp(&self) -> u64 {
937 self.genesis_timestamp
938 .unwrap_or(NON_FORK_FIRST_BLOCK_TIMESTAMP)
939 }
940
941 #[must_use]
943 pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
944 self.genesis = genesis;
945 self
946 }
947
948 #[must_use]
950 pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
951 self.enable_auto_impersonate = enable_auto_impersonate;
952 self
953 }
954
955 #[must_use]
957 pub fn with_offline(mut self, offline: Option<bool>) -> Self {
958 if let Some(offline) = offline {
959 self.offline = offline;
960 }
961 self
962 }
963
964 pub fn is_offline(&self) -> bool {
966 self.offline
967 }
968
969 #[must_use]
971 pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
972 self.host = if host.is_empty() {
973 vec![IpAddr::V4(Ipv4Addr::LOCALHOST)]
974 } else {
975 host
976 };
977 self
978 }
979 #[must_use]
981 pub fn with_health_check_endpoint(mut self, health_check_endpoint: Option<bool>) -> Self {
982 if let Some(health_check_endpoint) = health_check_endpoint {
983 self.health_check_endpoint = health_check_endpoint;
984 }
985 self
986 }
987
988 pub fn is_health_check_endpoint_endpoint_enabled(&self) -> bool {
990 self.health_check_endpoint
991 }
992
993 #[must_use]
995 pub fn with_block_time(mut self, block_time: Option<Duration>) -> Self {
996 self.block_time = block_time;
997 self
998 }
999
1000 #[must_use]
1002 pub fn with_no_mining(mut self, no_mining: bool) -> Self {
1003 self.no_mining = no_mining;
1004 self
1005 }
1006
1007 #[must_use]
1009 pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
1010 self.transaction_order = transaction_order;
1011 self
1012 }
1013
1014 #[must_use]
1016 pub fn with_allow_origin(mut self, allow_origin: String) -> Self {
1017 self.allow_origin = allow_origin;
1018 self
1019 }
1020
1021 #[must_use]
1023 pub fn with_no_cors(mut self, no_cors: bool) -> Self {
1024 self.no_cors = no_cors;
1025 self
1026 }
1027
1028 #[must_use]
1030 pub fn with_state(mut self, state: Option<PathBuf>) -> Self {
1031 self.state = state;
1032 self
1033 }
1034
1035 #[must_use]
1037 pub fn with_dump_state(mut self, dump_state: Option<PathBuf>) -> Self {
1038 self.dump_state = dump_state;
1039 self
1040 }
1041
1042 #[must_use]
1044 pub fn with_state_interval(mut self, state_interval: Option<u64>) -> Self {
1045 self.state_interval = state_interval;
1046 self
1047 }
1048
1049 #[must_use]
1051 pub fn with_preserve_historical_states(mut self, preserve_historical_states: bool) -> Self {
1052 self.preserve_historical_states = preserve_historical_states;
1053 self
1054 }
1055
1056 #[must_use]
1058 pub fn with_load_state(mut self, load_state: Option<PathBuf>) -> Self {
1059 self.load_state = load_state;
1060 self
1061 }
1062
1063 #[must_use]
1065 pub fn with_l1_config(mut self, l1_config: Option<L1Config>) -> Self {
1066 self.l1_config = l1_config;
1067 self
1068 }
1069
1070 #[must_use]
1072 pub fn with_auto_execute_l1(mut self, auto_execute_l1: Option<bool>) -> Self {
1073 self.auto_execute_l1 = auto_execute_l1.unwrap_or(false);
1074 self
1075 }
1076
1077 #[must_use]
1079 pub fn with_base_token_config(mut self, base_token_config: BaseTokenConfig) -> Self {
1080 self.base_token_config = base_token_config;
1081 self
1082 }
1083}