anvil_zksync_config/
config.rs

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::{Value, json, to_writer};
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::{H256, ProtocolVersionId, U256};
20
21pub const VERSION_MESSAGE: &str = concat!(env!("CARGO_PKG_VERSION"));
22
23/// Protocol version that is used in anvil-zksync by default. Should match what is currently
24/// deployed to mainnet.
25pub const DEFAULT_PROTOCOL_VERSION: ProtocolVersionId = ProtocolVersionId::Version29;
26
27const BANNER: &str = r#"
28                      _  _         _____ _  __
29  __ _  _ __  __   __(_)| |       |__  /| |/ / ___  _   _  _ __    ___
30 / _` || '_ \ \ \ / /| || | _____   / / | ' / / __|| | | || '_ \  / __|
31| (_| || | | | \ V / | || ||_____| / /_ | . \ \__ \| |_| || | | || (__
32 \__,_||_| |_|  \_/  |_||_|       /____||_|\_\|___/ \__, ||_| |_| \___|
33                                                    |___/
34"#;
35/// Struct to hold the details of the fork for display purposes
36pub 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)]
46pub struct DebugTraceConfig {
47    pub fork_url: String,
48    pub tx: H256,
49    pub only_top: bool,
50}
51
52/// Defines the configuration parameters for the [InMemoryNode].
53#[derive(Debug, Clone)]
54pub struct TestNodeConfig {
55    /// Filename to write anvil-zksync output as json
56    pub config_out: Option<String>,
57    /// Port the node will listen on
58    pub port: u16,
59    /// Print node config on startup if true
60    pub show_node_config: bool,
61    /// Level of detail for storage logs
62    pub show_storage_logs: ShowStorageLogs,
63    /// Level of detail for VM execution logs
64    pub show_vm_details: ShowVMDetails,
65    /// Level of detail for gas usage logs
66    pub show_gas_details: ShowGasDetails,
67    /// Numeric verbosity derived from repeated `-v` flags (e.g. -v = 1, -vv = 2, etc.).
68    pub verbosity: u8,
69    /// Don’t print anything on startup if true
70    pub silent: bool,
71    /// Configuration for system contracts
72    pub system_contracts_options: SystemContractsOptions,
73    /// Path to the system contracts directory
74    pub system_contracts_path: Option<PathBuf>,
75    /// Protocol version to use for new blocks. Also affects revision of built-in contracts that
76    /// will get deployed (if applicable)
77    pub protocol_version: Option<ProtocolVersionId>,
78    /// Directory to override bytecodes
79    pub override_bytecodes_dir: Option<String>,
80    /// Enable bytecode compression
81    pub bytecode_compression: bool,
82    /// Enables EVM interpreter mode
83    pub use_evm_interpreter: bool,
84    /// Enables ZKsyncOS mode (experimental)
85    pub zksync_os: ZKsyncOsConfig,
86    /// Optional chain ID for the node
87    pub chain_id: Option<u32>,
88    /// L1 gas price (optional override)
89    pub l1_gas_price: Option<u64>,
90    /// L2 gas price (optional override)
91    pub l2_gas_price: Option<u64>,
92    /// Price for pubdata on L1
93    pub l1_pubdata_price: Option<u64>,
94    /// L1 gas price scale factor for gas estimation
95    pub price_scale_factor: Option<f64>,
96    /// The factor by which to scale the gasLimit
97    pub limit_scale_factor: Option<f32>,
98    /// Logging verbosity level
99    pub log_level: LogLevel,
100    /// Path to the log file
101    pub log_file_path: String,
102    /// Directory to store cache files (defaults to `./cache`)
103    pub cache_dir: String,
104    /// Cache configuration for the test node
105    pub cache_config: CacheConfig,
106    /// Signer accounts that will be initialized with `genesis_balance` in the genesis block.
107    pub genesis_accounts: Vec<PrivateKeySigner>,
108    /// Native token balance of every genesis account in the genesis block
109    pub genesis_balance: U256,
110    /// The generator used to generate the dev accounts
111    pub account_generator: Option<AccountGenerator>,
112    /// Signer accounts that can sign messages/transactions
113    pub signer_accounts: Vec<PrivateKeySigner>,
114    /// The genesis to use to initialize the node
115    pub genesis: Option<Genesis>,
116    /// Genesis block timestamp
117    pub genesis_timestamp: Option<u64>,
118    /// Enable auto impersonation of accounts on startup
119    pub enable_auto_impersonate: bool,
120    /// Whether the node operates in offline mode
121    pub offline: bool,
122    /// The host the server will listen on
123    pub host: Vec<IpAddr>,
124    /// Whether we need to enable the health check endpoint.
125    pub health_check_endpoint: bool,
126    /// Block time in seconds for interval sealing.
127    /// If unset, node seals a new block as soon as there is at least one transaction.
128    pub block_time: Option<Duration>,
129    /// Maximum number of transactions per block
130    pub max_transactions: usize,
131    /// Disable automatic sealing mode and use `BlockSealer::Noop` instead
132    pub no_mining: bool,
133    /// The cors `allow_origin` header
134    pub allow_origin: String,
135    /// Disable CORS if true
136    pub no_cors: bool,
137    /// How transactions are sorted in the mempool
138    pub transaction_order: TransactionOrder,
139    /// Path to load/dump the state from
140    pub state: Option<PathBuf>,
141    /// Path to dump the state to
142    pub dump_state: Option<PathBuf>,
143    /// Interval to dump the state
144    pub state_interval: Option<u64>,
145    /// Preserve historical states
146    pub preserve_historical_states: bool,
147    /// State to load
148    pub load_state: Option<PathBuf>,
149    /// L1 configuration, disabled if `None`
150    pub l1_config: Option<L1Config>,
151    /// Whether to automatically execute L1 batches
152    pub auto_execute_l1: bool,
153    /// Base token configuration
154    pub base_token_config: BaseTokenConfig,
155    /// Configuration for debug tracing
156    pub debug_trace: Option<DebugTraceConfig>,
157}
158
159#[derive(Debug, Clone)]
160pub enum L1Config {
161    /// Spawn a separate `anvil` process and initialize it to use as L1.
162    Spawn {
163        /// Port the spawned L1 anvil node will listen on
164        port: u16,
165    },
166    /// Use externally set up L1.
167    External {
168        /// Address of L1 node's JSON-RPC endpoint
169        address: String,
170    },
171}
172
173#[derive(Debug, Clone)]
174pub struct BaseTokenConfig {
175    /// Base token symbol to use instead of 'ETH'.
176    pub symbol: String,
177    /// Base token conversion ratio (e.g., '40000', '628/17').
178    pub ratio: BaseTokenConversionRatio,
179}
180
181impl Default for BaseTokenConfig {
182    fn default() -> Self {
183        Self {
184            symbol: "ETH".to_string(),
185            ratio: BaseTokenConversionRatio::default(),
186        }
187    }
188}
189
190impl Default for TestNodeConfig {
191    fn default() -> Self {
192        // generate some random wallets
193        let genesis_accounts = AccountGenerator::new(10)
194            .phrase(DEFAULT_MNEMONIC)
195            .generate();
196        Self {
197            // Node configuration defaults
198            config_out: None,
199            port: NODE_PORT,
200            show_node_config: true,
201            show_storage_logs: Default::default(),
202            show_vm_details: Default::default(),
203            show_gas_details: Default::default(),
204            verbosity: 0,
205            silent: false,
206            system_contracts_options: Default::default(),
207            system_contracts_path: None,
208            protocol_version: None,
209            override_bytecodes_dir: None,
210            bytecode_compression: false,
211            use_evm_interpreter: false,
212            zksync_os: Default::default(),
213            chain_id: None,
214
215            // Gas configuration defaults
216            l1_gas_price: None,
217            l2_gas_price: None,
218            l1_pubdata_price: None,
219            price_scale_factor: None,
220            limit_scale_factor: None,
221
222            // Log configuration defaults
223            log_level: Default::default(),
224            log_file_path: String::from(DEFAULT_LOG_FILE_PATH),
225
226            // Cache configuration default
227            cache_dir: String::from(DEFAULT_DISK_CACHE_DIR),
228            cache_config: Default::default(),
229
230            // Account generator
231            account_generator: None,
232            genesis_accounts: genesis_accounts.clone(),
233            signer_accounts: genesis_accounts,
234            enable_auto_impersonate: false,
235            // 100ETH default balance
236            genesis_balance: U256::from(100u128 * 10u128.pow(18)),
237            genesis_timestamp: Some(NON_FORK_FIRST_BLOCK_TIMESTAMP),
238            genesis: None,
239
240            // Offline mode disabled by default
241            offline: false,
242            host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
243            health_check_endpoint: false,
244
245            // Block sealing configuration default
246            block_time: None,
247            no_mining: false,
248
249            max_transactions: 1000,
250            transaction_order: TransactionOrder::Fifo,
251
252            // Server configuration
253            allow_origin: "*".to_string(),
254            no_cors: false,
255
256            // state configuration
257            state: None,
258            dump_state: None,
259            state_interval: None,
260            preserve_historical_states: false,
261            load_state: None,
262            l1_config: None,
263            auto_execute_l1: false,
264            base_token_config: BaseTokenConfig::default(),
265
266            // Debug trace configuration
267            debug_trace: None,
268        }
269    }
270}
271
272impl TestNodeConfig {
273    pub fn protocol_version(&self) -> ProtocolVersionId {
274        match self.system_contracts_options {
275            SystemContractsOptions::BuiltIn => self
276                .protocol_version
277                .unwrap_or(DEFAULT_PROTOCOL_VERSION),
278            SystemContractsOptions::Local =>
279                self.protocol_version.expect("cannot deduce protocol version when using local contracts; please specify --protocol-version explicitly"),
280            SystemContractsOptions::BuiltInWithoutSecurity => self
281                .protocol_version
282                .unwrap_or(DEFAULT_PROTOCOL_VERSION),
283        }
284    }
285}
286
287impl TestNodeConfig {
288    pub fn print(&self, fork_details: Option<&ForkPrintInfo>) {
289        if let Some(config_out) = self.config_out.as_deref() {
290            let file = File::create(config_out)
291                .expect("Unable to create anvil-zksync config description file");
292            to_writer(&file, &self.as_json(fork_details)).expect("Failed writing json");
293        }
294
295        if self.silent || !self.show_node_config {
296            return;
297        }
298
299        let color = CustomColor::new(13, 71, 198);
300
301        // Banner, version and repository section.
302        sh_println!(
303            r#"
304{}
305Version:        {}
306Repository:     {}
307
308"#,
309            BANNER.custom_color(color),
310            VERSION_MESSAGE.green(),
311            "https://github.com/matter-labs/anvil-zksync".green()
312        );
313
314        // Rich Accounts.
315        let balance = format_eth(self.genesis_balance);
316        let mut rich_accounts = String::new();
317        for (idx, account) in self.genesis_accounts.iter().enumerate() {
318            rich_accounts.push_str(&format!("({}) {} ({})\n", idx, account.address(), balance));
319        }
320        sh_println!(
321            r#"
322Rich Accounts
323========================
324{}
325"#,
326            rich_accounts
327        );
328
329        // Private Keys.
330        let mut private_keys = String::new();
331        for (idx, account) in self.genesis_accounts.iter().enumerate() {
332            let private_key = hex::encode(account.credential().to_bytes());
333            private_keys.push_str(&format!("({idx}) 0x{private_key}\n"));
334        }
335        sh_println!(
336            r#"
337Private Keys
338========================
339{}
340"#,
341            private_keys
342        );
343
344        // Wallet configuration.
345        if let Some(ref generator) = self.account_generator {
346            sh_println!(
347                r#"
348Wallet
349========================
350Mnemonic:            {}
351Derivation path:     {}
352"#,
353                generator.get_phrase().green(),
354                generator.get_derivation_path().green()
355            );
356        }
357
358        // Either print Fork Details (if provided) or the Network Configuration.
359        if let Some(fd) = fork_details {
360            sh_println!(
361                r#"
362Fork Details
363========================
364Network RPC:               {}
365Chain ID:                  {}
366L1 Batch #:                {}
367L2 Block #:                {}
368Block Timestamp:           {}
369Fork Block Hash:           {}
370Compute Overhead Part:     {}
371Pubdata Overhead Part:     {}
372Batch Overhead L1 Gas:     {}
373Max Gas Per Batch:         {}
374Max Pubdata Per Batch:     {}
375"#,
376                fd.network_rpc.green(),
377                self.get_chain_id().to_string().green(),
378                fd.l1_block.green(),
379                fd.l2_block.green(),
380                fd.block_timestamp.to_string().green(),
381                format!("{:#}", fd.fork_block_hash).green(),
382                fd.fee_model_config_v2
383                    .compute_overhead_part
384                    .to_string()
385                    .green(),
386                fd.fee_model_config_v2
387                    .pubdata_overhead_part
388                    .to_string()
389                    .green(),
390                fd.fee_model_config_v2
391                    .batch_overhead_l1_gas
392                    .to_string()
393                    .green(),
394                fd.fee_model_config_v2.max_gas_per_batch.to_string().green(),
395                fd.fee_model_config_v2
396                    .max_pubdata_per_batch
397                    .to_string()
398                    .green()
399            );
400        } else {
401            sh_println!(
402                r#"
403Network Configuration
404========================
405Chain ID: {}
406"#,
407                self.chain_id
408                    .unwrap_or(TEST_NODE_NETWORK_ID)
409                    .to_string()
410                    .green()
411            );
412        }
413
414        // Gas Configuration.
415        sh_println!(
416            r#"
417Gas Configuration
418========================
419L1 Gas Price (gwei):               {}
420L2 Gas Price (gwei):               {}
421L1 Pubdata Price (gwei):           {}
422Estimated Gas Price Scale Factor:  {}
423Estimated Gas Limit Scale Factor:  {}
424"#,
425            format_gwei(self.get_l1_gas_price().into()).green(),
426            format_gwei(self.get_l2_gas_price().into()).green(),
427            format_gwei(self.get_l1_pubdata_price().into()).green(),
428            self.get_price_scale().to_string().green(),
429            self.get_gas_limit_scale().to_string().green()
430        );
431
432        // Genesis Timestamp.
433        sh_println!(
434            r#"
435Genesis Timestamp
436========================
437{}
438"#,
439            self.get_genesis_timestamp().to_string().green()
440        );
441
442        // Node Configuration.
443        sh_println!(
444            r#"
445Node Configuration
446========================
447Port:                  {}
448EVM Interpreter:       {}
449Health Check Endpoint: {}
450ZKsync OS:             {}
451L1:                    {}
452"#,
453            self.port,
454            if self.use_evm_interpreter {
455                "Enabled".green()
456            } else {
457                "Disabled".red()
458            },
459            if self.health_check_endpoint {
460                "Enabled".green()
461            } else {
462                "Disabled".red()
463            },
464            if self.zksync_os.zksync_os {
465                "Enabled".green()
466            } else {
467                "Disabled".red()
468            },
469            if self.l1_config.is_some() {
470                "Enabled".green()
471            } else {
472                "Disabled".red()
473            }
474        );
475
476        // L1 Configuration
477        match self.l1_config.as_ref() {
478            Some(L1Config::Spawn { port }) => {
479                sh_println!(
480                    r#"
481L1 Configuration (Spawned)
482========================
483Port: {port}
484"#
485                );
486            }
487            Some(L1Config::External { address }) => {
488                sh_println!(
489                    r#"
490L1 Configuration (External)
491========================
492Address: {address}
493"#
494                );
495            }
496            None => {}
497        }
498
499        // Listening addresses.
500        let mut listening = String::new();
501        listening.push_str("\n========================================\n");
502        for host in &self.host {
503            listening.push_str(&format!(
504                "  Listening on {}:{}\n",
505                host.to_string().green(),
506                self.port.to_string().green()
507            ));
508        }
509        listening.push_str("========================================\n");
510        sh_println!("{}", listening);
511    }
512
513    fn as_json(&self, fork: Option<&ForkPrintInfo>) -> Value {
514        let mut wallet_description = HashMap::new();
515        let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
516        let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
517
518        for wallet in &self.genesis_accounts {
519            available_accounts.push(format!("{:?}", wallet.address()));
520            private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
521        }
522
523        if let Some(generator) = &self.account_generator {
524            let phrase = generator.get_phrase().to_string();
525            let derivation_path = generator.get_derivation_path().to_string();
526
527            wallet_description.insert("derivation_path".to_string(), derivation_path);
528            wallet_description.insert("mnemonic".to_string(), phrase);
529        };
530
531        if let Some(fork) = fork {
532            json!({
533              "available_accounts": available_accounts,
534              "private_keys": private_keys,
535              "endpoint": fork.network_rpc,
536              "l1_block": fork.l1_block,
537              "l2_block": fork.l2_block,
538              "block_hash": fork.fork_block_hash,
539              "chain_id": self.get_chain_id(),
540              "wallet": wallet_description,
541              "l1_gas_price": format!("{}", self.get_l1_gas_price()),
542              "l2_gas_price": format!("{}", self.get_l2_gas_price()),
543              "l1_pubdata_price": format!("{}", self.get_l1_pubdata_price()),
544              "price_scale_factor": format!("{}", self.get_price_scale()),
545              "limit_scale_factor": format!("{}", self.get_gas_limit_scale()),
546              "fee_model_config_v2": fork.fee_model_config_v2,
547            })
548        } else {
549            json!({
550              "available_accounts": available_accounts,
551              "private_keys": private_keys,
552              "wallet": wallet_description,
553              "chain_id": self.get_chain_id(),
554              "l1_gas_price": format!("{}", self.get_l1_gas_price()),
555              "l2_gas_price": format!("{}", self.get_l2_gas_price()),
556              "l1_pubdata_price": format!("{}", self.get_l1_pubdata_price()),
557              "price_scale_factor": format!("{}", self.get_price_scale()),
558              "limit_scale_factor": format!("{}", self.get_gas_limit_scale()),
559            })
560        }
561    }
562
563    /// Sets the file path to write the anvil-zksync config info to.
564    #[must_use]
565    pub fn set_config_out(mut self, config_out: Option<String>) -> Self {
566        self.config_out = config_out;
567        self
568    }
569
570    /// Set the port for the test node
571    #[must_use]
572    pub fn with_port(mut self, port: Option<u16>) -> Self {
573        if let Some(port) = port {
574            self.port = port;
575        }
576        self
577    }
578
579    /// Get the port for the test node
580    pub fn get_port(&self) -> u16 {
581        self.port
582    }
583
584    /// Set the chain ID for the test node
585    #[must_use]
586    pub fn with_chain_id(mut self, chain_id: Option<u32>) -> Self {
587        if let Some(chain_id) = chain_id {
588            self.chain_id = Some(chain_id);
589        }
590        self
591    }
592
593    /// Get the chain ID for the test node
594    pub fn get_chain_id(&self) -> u32 {
595        self.chain_id.unwrap_or(TEST_NODE_NETWORK_ID)
596    }
597
598    /// Update the chain ID
599    pub fn update_chain_id(&mut self, chain_id: Option<u32>) -> &mut Self {
600        self.chain_id = chain_id;
601        self
602    }
603
604    /// Set the system contracts configuration option
605    #[must_use]
606    pub fn with_system_contracts(mut self, option: Option<SystemContractsOptions>) -> Self {
607        if let Some(option) = option {
608            self.system_contracts_options = option;
609        }
610        self
611    }
612
613    /// Set the system contracts path
614    #[must_use]
615    pub fn with_system_contracts_path(mut self, path: Option<PathBuf>) -> Self {
616        if let Some(path) = path {
617            self.system_contracts_path = Some(path);
618        }
619        self
620    }
621
622    /// Set the protocol version configuration option
623    #[must_use]
624    pub fn with_protocol_version(mut self, protocol_version: Option<ProtocolVersionId>) -> Self {
625        self.protocol_version = protocol_version;
626        self
627    }
628
629    /// Get the system contracts configuration option
630    pub fn get_system_contracts(&self) -> SystemContractsOptions {
631        self.system_contracts_options
632    }
633
634    /// Set the override bytecodes directory
635    #[must_use]
636    pub fn with_override_bytecodes_dir(mut self, dir: Option<String>) -> Self {
637        if let Some(dir) = dir {
638            self.override_bytecodes_dir = Some(dir);
639        }
640        self
641    }
642
643    /// Get the override bytecodes directory
644    pub fn get_override_bytecodes_dir(&self) -> Option<&String> {
645        self.override_bytecodes_dir.as_ref()
646    }
647
648    /// Set whether bytecode compression is enforced
649    #[must_use]
650    pub fn with_enforce_bytecode_compression(mut self, enforce: Option<bool>) -> Self {
651        if let Some(enforce) = enforce {
652            self.bytecode_compression = enforce;
653        }
654        self
655    }
656
657    /// Check if bytecode compression enforcement is enabled
658    pub fn is_bytecode_compression_enforced(&self) -> bool {
659        self.bytecode_compression
660    }
661
662    /// Enable or disable EVM emulation
663    #[must_use]
664    pub fn with_evm_interpreter(mut self, enable: Option<bool>) -> Self {
665        if let Some(enable) = enable {
666            self.use_evm_interpreter = enable;
667        }
668        self
669    }
670
671    /// Enable or disable ZKsync OS
672    #[must_use]
673    pub fn with_zksync_os(mut self, zksync_os: ZKsyncOsConfig) -> Self {
674        self.zksync_os = zksync_os;
675        self
676    }
677
678    /// Get the EVM interpreter status
679    pub fn is_evm_interpreter_enabled(&self) -> bool {
680        self.use_evm_interpreter
681    }
682
683    /// Set the L1 gas price
684    #[must_use]
685    pub fn with_l1_gas_price(mut self, price: Option<u64>) -> Self {
686        if let Some(price) = price {
687            self.l1_gas_price = Some(price);
688        }
689        self
690    }
691
692    /// Get the L1 gas price
693    pub fn get_l1_gas_price(&self) -> u64 {
694        self.l1_gas_price.unwrap_or(DEFAULT_L1_GAS_PRICE)
695    }
696
697    /// Update the L1 gas price
698    pub fn update_l1_gas_price(&mut self, price: Option<u64>) -> &mut Self {
699        self.l1_gas_price = price;
700        self
701    }
702
703    /// Set the L2 gas price
704    #[must_use]
705    pub fn with_l2_gas_price(mut self, price: Option<u64>) -> Self {
706        if let Some(price) = price {
707            self.l2_gas_price = Some(price);
708        }
709        self
710    }
711
712    /// Get the L2 gas price
713    pub fn get_l2_gas_price(&self) -> u64 {
714        self.l2_gas_price.unwrap_or(DEFAULT_L2_GAS_PRICE)
715    }
716
717    /// Update the L2 gas price
718    pub fn update_l2_gas_price(&mut self, price: Option<u64>) -> &mut Self {
719        self.l2_gas_price = price;
720        self
721    }
722
723    /// Set the L1 pubdata price
724    #[must_use]
725    pub fn with_l1_pubdata_price(mut self, price: Option<u64>) -> Self {
726        self.l1_pubdata_price = price;
727        self
728    }
729
730    /// Get the L1 pubdata price
731    pub fn get_l1_pubdata_price(&self) -> u64 {
732        self.l1_pubdata_price.unwrap_or(DEFAULT_FAIR_PUBDATA_PRICE)
733    }
734
735    /// Update the L1 pubdata price
736    pub fn update_l1_pubdata_price(&mut self, price: Option<u64>) -> &mut Self {
737        self.l1_pubdata_price = price;
738        self
739    }
740
741    /// Set the log level
742    #[must_use]
743    pub fn with_log_level(mut self, level: Option<LogLevel>) -> Self {
744        if let Some(level) = level {
745            self.log_level = level;
746        }
747        self
748    }
749
750    /// Get the log level
751    pub fn get_log_level(&self) -> LogLevel {
752        self.log_level
753    }
754
755    /// Gets the cache directory
756    pub fn get_cache_dir(&self) -> &str {
757        &self.cache_dir
758    }
759
760    /// Set the cache directory
761    #[must_use]
762    pub fn with_cache_dir(mut self, dir: Option<String>) -> Self {
763        if let Some(dir) = dir {
764            self.cache_dir = dir;
765        }
766        self
767    }
768
769    /// Set the cache configuration
770    #[must_use]
771    pub fn with_cache_config(mut self, config: Option<CacheConfig>) -> Self {
772        if let Some(config) = config {
773            self.cache_config = config;
774        }
775        self
776    }
777
778    /// Get the cache configuration
779    pub fn get_cache_config(&self) -> &CacheConfig {
780        &self.cache_config
781    }
782
783    /// Set the log file path
784    #[must_use]
785    pub fn with_log_file_path(mut self, path: Option<String>) -> Self {
786        if let Some(path) = path {
787            self.log_file_path = path;
788        }
789        self
790    }
791
792    /// Get the log file path
793    pub fn get_log_file_path(&self) -> &str {
794        &self.log_file_path
795    }
796
797    /// Sets the numeric verbosity derived from repeated `-v` flags
798    #[must_use]
799    pub fn with_verbosity_level(mut self, verbosity: u8) -> Self {
800        self.verbosity = verbosity;
801        self
802    }
803
804    /// Get the numeric verbosity derived from repeated `-v` flags
805    pub fn get_verbosity_level(&self) -> u8 {
806        self.verbosity
807    }
808
809    /// Enable or disable silent mode
810    #[must_use]
811    pub fn with_silent(mut self, silent: Option<bool>) -> Self {
812        if let Some(silent) = silent {
813            self.silent = silent;
814        }
815        self
816    }
817
818    /// Enable or disable printing node config on startup
819    #[must_use]
820    pub fn with_show_node_config(mut self, show_node_config: Option<bool>) -> Self {
821        if let Some(show_node_config) = show_node_config {
822            self.show_node_config = show_node_config;
823        }
824        self
825    }
826
827    /// Set the visibility of storage logs
828    #[must_use]
829    pub fn with_show_storage_logs(mut self, show_storage_logs: Option<ShowStorageLogs>) -> Self {
830        if let Some(show_storage_logs) = show_storage_logs {
831            self.show_storage_logs = show_storage_logs;
832        }
833        self
834    }
835
836    /// Get the visibility of storage logs
837    pub fn get_show_storage_logs(&self) -> ShowStorageLogs {
838        self.show_storage_logs
839    }
840
841    /// Set the detail level of VM execution logs
842    #[must_use]
843    pub fn with_vm_log_detail(mut self, detail: Option<ShowVMDetails>) -> Self {
844        if let Some(detail) = detail {
845            self.show_vm_details = detail;
846        }
847        self
848    }
849
850    /// Get the detail level of VM execution logs
851    pub fn get_vm_log_detail(&self) -> ShowVMDetails {
852        self.show_vm_details
853    }
854
855    /// Set the visibility of gas usage logs
856    #[must_use]
857    pub fn with_show_gas_details(mut self, show_gas_details: Option<ShowGasDetails>) -> Self {
858        if let Some(show_gas_details) = show_gas_details {
859            self.show_gas_details = show_gas_details;
860        }
861        self
862    }
863
864    /// Get the visibility of gas usage logs
865    pub fn get_show_gas_details(&self) -> ShowGasDetails {
866        self.show_gas_details
867    }
868
869    /// Set the gas limit scale factor
870    #[must_use]
871    pub fn with_gas_limit_scale(mut self, scale: Option<f32>) -> Self {
872        if let Some(scale) = scale {
873            self.limit_scale_factor = Some(scale);
874        }
875        self
876    }
877
878    /// Get the gas limit scale factor
879    pub fn get_gas_limit_scale(&self) -> f32 {
880        self.limit_scale_factor
881            .unwrap_or(DEFAULT_ESTIMATE_GAS_SCALE_FACTOR)
882    }
883
884    /// Update the gas limit scale factor
885    pub fn update_gas_limit_scale(&mut self, scale: Option<f32>) -> &mut Self {
886        self.limit_scale_factor = scale;
887        self
888    }
889
890    /// Set the price scale factor
891    #[must_use]
892    pub fn with_price_scale(mut self, scale: Option<f64>) -> Self {
893        if let Some(scale) = scale {
894            self.price_scale_factor = Some(scale);
895        }
896        self
897    }
898
899    /// Get the price scale factor
900    pub fn get_price_scale(&self) -> f64 {
901        self.price_scale_factor
902            .unwrap_or(DEFAULT_ESTIMATE_GAS_PRICE_SCALE_FACTOR)
903    }
904
905    /// Updates the price scale factor
906    pub fn update_price_scale(&mut self, scale: Option<f64>) -> &mut Self {
907        self.price_scale_factor = scale;
908        self
909    }
910
911    /// Sets the balance of the genesis accounts in the genesis block
912    #[must_use]
913    pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
914        self.genesis_balance = balance.into();
915        self
916    }
917
918    /// Sets the genesis accounts.
919    #[must_use]
920    pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
921        self.genesis_accounts = accounts;
922        self
923    }
924
925    /// Sets the signer accounts
926    #[must_use]
927    pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
928        self.signer_accounts = accounts;
929        self
930    }
931
932    /// Sets both the genesis accounts and the signer accounts
933    /// so that `genesis_accounts == accounts`
934    #[must_use]
935    pub fn with_account_generator(mut self, generator: AccountGenerator) -> Self {
936        let accounts = generator.generate();
937        self.account_generator = Some(generator);
938        self.with_signer_accounts(accounts.clone())
939            .with_genesis_accounts(accounts)
940    }
941
942    /// Sets the genesis timestamp
943    #[must_use]
944    pub fn with_genesis_timestamp(mut self, timestamp: Option<u64>) -> Self {
945        self.genesis_timestamp = timestamp;
946        self
947    }
948
949    /// Returns the genesis timestamp to use
950    pub fn get_genesis_timestamp(&self) -> u64 {
951        self.genesis_timestamp
952            .unwrap_or(NON_FORK_FIRST_BLOCK_TIMESTAMP)
953    }
954
955    /// Sets the init genesis (genesis.json)
956    #[must_use]
957    pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
958        self.genesis = genesis;
959        self
960    }
961
962    /// Sets whether to enable autoImpersonate
963    #[must_use]
964    pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
965        self.enable_auto_impersonate = enable_auto_impersonate;
966        self
967    }
968
969    /// Set the offline mode
970    #[must_use]
971    pub fn with_offline(mut self, offline: Option<bool>) -> Self {
972        if let Some(offline) = offline {
973            self.offline = offline;
974        }
975        self
976    }
977
978    /// Get the offline mode status
979    pub fn is_offline(&self) -> bool {
980        self.offline
981    }
982
983    /// Sets the host the server will listen on
984    #[must_use]
985    pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
986        self.host = if host.is_empty() {
987            vec![IpAddr::V4(Ipv4Addr::LOCALHOST)]
988        } else {
989            host
990        };
991        self
992    }
993    /// Set the health check endpoint mode
994    #[must_use]
995    pub fn with_health_check_endpoint(mut self, health_check_endpoint: Option<bool>) -> Self {
996        if let Some(health_check_endpoint) = health_check_endpoint {
997            self.health_check_endpoint = health_check_endpoint;
998        }
999        self
1000    }
1001
1002    /// Get the health check endpoint mode status
1003    pub fn is_health_check_endpoint_endpoint_enabled(&self) -> bool {
1004        self.health_check_endpoint
1005    }
1006
1007    /// Set the block time
1008    #[must_use]
1009    pub fn with_block_time(mut self, block_time: Option<Duration>) -> Self {
1010        self.block_time = block_time;
1011        self
1012    }
1013
1014    /// If set to `true` auto sealing will be disabled
1015    #[must_use]
1016    pub fn with_no_mining(mut self, no_mining: bool) -> Self {
1017        self.no_mining = no_mining;
1018        self
1019    }
1020
1021    // Set transactions order in the mempool
1022    #[must_use]
1023    pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
1024        self.transaction_order = transaction_order;
1025        self
1026    }
1027
1028    /// Set allow_origin CORS header
1029    #[must_use]
1030    pub fn with_allow_origin(mut self, allow_origin: String) -> Self {
1031        self.allow_origin = allow_origin;
1032        self
1033    }
1034
1035    /// Enable or disable CORS
1036    #[must_use]
1037    pub fn with_no_cors(mut self, no_cors: bool) -> Self {
1038        self.no_cors = no_cors;
1039        self
1040    }
1041
1042    /// Set the state
1043    #[must_use]
1044    pub fn with_state(mut self, state: Option<PathBuf>) -> Self {
1045        self.state = state;
1046        self
1047    }
1048
1049    /// Set the state dump path
1050    #[must_use]
1051    pub fn with_dump_state(mut self, dump_state: Option<PathBuf>) -> Self {
1052        self.dump_state = dump_state;
1053        self
1054    }
1055
1056    /// Set the state dump interval
1057    #[must_use]
1058    pub fn with_state_interval(mut self, state_interval: Option<u64>) -> Self {
1059        self.state_interval = state_interval;
1060        self
1061    }
1062
1063    /// Set preserve historical states
1064    #[must_use]
1065    pub fn with_preserve_historical_states(mut self, preserve_historical_states: bool) -> Self {
1066        self.preserve_historical_states = preserve_historical_states;
1067        self
1068    }
1069
1070    /// Set the state to load
1071    #[must_use]
1072    pub fn with_load_state(mut self, load_state: Option<PathBuf>) -> Self {
1073        self.load_state = load_state;
1074        self
1075    }
1076
1077    /// Set the L1 config
1078    #[must_use]
1079    pub fn with_l1_config(mut self, l1_config: Option<L1Config>) -> Self {
1080        self.l1_config = l1_config;
1081        self
1082    }
1083
1084    /// Set the auto L1 execution
1085    #[must_use]
1086    pub fn with_auto_execute_l1(mut self, auto_execute_l1: Option<bool>) -> Self {
1087        self.auto_execute_l1 = auto_execute_l1.unwrap_or(false);
1088        self
1089    }
1090
1091    /// Set the base token config
1092    #[must_use]
1093    pub fn with_base_token_config(mut self, base_token_config: BaseTokenConfig) -> Self {
1094        self.base_token_config = base_token_config;
1095        self
1096    }
1097
1098    #[must_use]
1099    pub fn with_debug_trace(mut self, debug: Option<DebugTraceConfig>) -> Self {
1100        self.debug_trace = debug;
1101        self
1102    }
1103
1104    /// Returns the debug-trace config if present.
1105    pub fn get_debug_trace(&self) -> Option<&DebugTraceConfig> {
1106        self.debug_trace.as_ref()
1107    }
1108}