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