use crate::constants::*;
use crate::types::*;
use crate::utils::{format_eth, format_gwei};
use alloy::primitives::hex;
use alloy::signers::local::PrivateKeySigner;
use anvil_zksync_common::cache::{CacheConfig, DEFAULT_DISK_CACHE_DIR};
use anvil_zksync_common::sh_println;
use anvil_zksync_types::{
LogLevel, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails, TransactionOrder,
};
use colored::{Colorize, CustomColor};
use serde_json::{json, to_writer, Value};
use std::collections::HashMap;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr};
use std::path::PathBuf;
use std::time::Duration;
use zksync_types::fee_model::FeeModelConfigV2;
use zksync_types::{ProtocolVersionId, U256};
pub const VERSION_MESSAGE: &str = concat!(env!("CARGO_PKG_VERSION"));
pub const DEFAULT_PROTOCOL_VERSION: ProtocolVersionId = ProtocolVersionId::Version26;
const BANNER: &str = r#"
_ _ _____ _ __
__ _ _ __ __ __(_)| | |__ /| |/ / ___ _ _ _ __ ___
/ _` || '_ \ \ \ / /| || | _____ / / | ' / / __|| | | || '_ \ / __|
| (_| || | | | \ V / | || ||_____| / /_ | . \ \__ \| |_| || | | || (__
\__,_||_| |_| \_/ |_||_| /____||_|\_\|___/ \__, ||_| |_| \___|
|___/
"#;
pub struct ForkPrintInfo {
pub network_rpc: String,
pub l1_block: String,
pub l2_block: String,
pub block_timestamp: String,
pub fork_block_hash: String,
pub fee_model_config_v2: FeeModelConfigV2,
}
#[derive(Debug, Clone)]
pub struct TestNodeConfig {
pub config_out: Option<String>,
pub port: u16,
pub show_node_config: bool,
pub show_tx_summary: bool,
pub show_event_logs: bool,
pub disable_console_log: bool,
pub show_calls: ShowCalls,
pub show_outputs: bool,
pub show_storage_logs: ShowStorageLogs,
pub show_vm_details: ShowVMDetails,
pub show_gas_details: ShowGasDetails,
pub verbosity: u8,
pub resolve_hashes: bool,
pub silent: bool,
pub system_contracts_options: SystemContractsOptions,
pub protocol_version: Option<ProtocolVersionId>,
pub override_bytecodes_dir: Option<String>,
pub bytecode_compression: bool,
pub use_evm_emulator: bool,
pub use_zkos: bool,
pub chain_id: Option<u32>,
pub l1_gas_price: Option<u64>,
pub l2_gas_price: Option<u64>,
pub l1_pubdata_price: Option<u64>,
pub price_scale_factor: Option<f64>,
pub limit_scale_factor: Option<f32>,
pub log_level: LogLevel,
pub log_file_path: String,
pub cache_dir: String,
pub cache_config: CacheConfig,
pub genesis_accounts: Vec<PrivateKeySigner>,
pub genesis_balance: U256,
pub account_generator: Option<AccountGenerator>,
pub signer_accounts: Vec<PrivateKeySigner>,
pub genesis: Option<Genesis>,
pub genesis_timestamp: Option<u64>,
pub enable_auto_impersonate: bool,
pub offline: bool,
pub host: Vec<IpAddr>,
pub health_check_endpoint: bool,
pub block_time: Option<Duration>,
pub max_transactions: usize,
pub no_mining: bool,
pub allow_origin: String,
pub no_cors: bool,
pub transaction_order: TransactionOrder,
pub state: Option<PathBuf>,
pub dump_state: Option<PathBuf>,
pub state_interval: Option<u64>,
pub preserve_historical_states: bool,
pub load_state: Option<PathBuf>,
pub l1_config: Option<L1Config>,
pub auto_execute_l1: bool,
}
#[derive(Debug, Clone)]
pub enum L1Config {
Spawn {
port: u16,
},
External {
address: String,
},
}
impl Default for TestNodeConfig {
fn default() -> Self {
let genesis_accounts = AccountGenerator::new(10).phrase(DEFAULT_MNEMONIC).gen();
Self {
config_out: None,
port: NODE_PORT,
show_node_config: true,
show_tx_summary: true,
show_event_logs: false,
disable_console_log: false,
show_calls: Default::default(),
show_outputs: false,
show_storage_logs: Default::default(),
show_vm_details: Default::default(),
show_gas_details: Default::default(),
resolve_hashes: false,
verbosity: 0,
silent: false,
system_contracts_options: Default::default(),
protocol_version: None,
override_bytecodes_dir: None,
bytecode_compression: false,
use_evm_emulator: false,
use_zkos: false,
chain_id: None,
l1_gas_price: None,
l2_gas_price: None,
l1_pubdata_price: None,
price_scale_factor: None,
limit_scale_factor: None,
log_level: Default::default(),
log_file_path: String::from(DEFAULT_LOG_FILE_PATH),
cache_dir: String::from(DEFAULT_DISK_CACHE_DIR),
cache_config: Default::default(),
account_generator: None,
genesis_accounts: genesis_accounts.clone(),
signer_accounts: genesis_accounts,
enable_auto_impersonate: false,
genesis_balance: U256::from(100u128 * 10u128.pow(18)),
genesis_timestamp: Some(NON_FORK_FIRST_BLOCK_TIMESTAMP),
genesis: None,
offline: false,
host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)],
health_check_endpoint: false,
block_time: None,
no_mining: false,
max_transactions: 1000,
transaction_order: TransactionOrder::Fifo,
allow_origin: "*".to_string(),
no_cors: false,
state: None,
dump_state: None,
state_interval: None,
preserve_historical_states: false,
load_state: None,
l1_config: None,
auto_execute_l1: false,
}
}
}
impl TestNodeConfig {
pub fn protocol_version(&self) -> ProtocolVersionId {
match self.system_contracts_options {
SystemContractsOptions::BuiltIn => self
.protocol_version
.unwrap_or(DEFAULT_PROTOCOL_VERSION),
SystemContractsOptions::Local =>
self.protocol_version.expect("cannot deduce protocol version when using local contracts; please specify --protocol-version explicitly"),
SystemContractsOptions::BuiltInWithoutSecurity => self
.protocol_version
.unwrap_or(DEFAULT_PROTOCOL_VERSION),
}
}
}
impl TestNodeConfig {
pub fn print(&self, fork_details: Option<&ForkPrintInfo>) {
if let Some(config_out) = self.config_out.as_deref() {
let file = File::create(config_out)
.expect("Unable to create anvil-zksync config description file");
to_writer(&file, &self.as_json(fork_details)).expect("Failed writing json");
}
if self.silent || !self.show_node_config {
return;
}
let color = CustomColor::new(13, 71, 198);
sh_println!(
r#"
{}
Version: {}
Repository: {}
"#,
BANNER.custom_color(color),
VERSION_MESSAGE.green(),
"https://github.com/matter-labs/anvil-zksync".green()
);
let balance = format_eth(self.genesis_balance);
let mut rich_accounts = String::new();
for (idx, account) in self.genesis_accounts.iter().enumerate() {
rich_accounts.push_str(&format!("({}) {} ({})\n", idx, account.address(), balance));
}
sh_println!(
r#"
Rich Accounts
========================
{}
"#,
rich_accounts
);
let mut private_keys = String::new();
for (idx, account) in self.genesis_accounts.iter().enumerate() {
let private_key = hex::encode(account.credential().to_bytes());
private_keys.push_str(&format!("({}) 0x{}\n", idx, private_key));
}
sh_println!(
r#"
Private Keys
========================
{}
"#,
private_keys
);
if let Some(ref generator) = self.account_generator {
sh_println!(
r#"
Wallet
========================
Mnemonic: {}
Derivation path: {}
"#,
generator.get_phrase().green(),
generator.get_derivation_path().green()
);
}
if let Some(fd) = fork_details {
sh_println!(
r#"
Fork Details
========================
Network RPC: {}
Chain ID: {}
L1 Batch #: {}
L2 Block #: {}
Block Timestamp: {}
Fork Block Hash: {}
Compute Overhead Part: {}
Pubdata Overhead Part: {}
Batch Overhead L1 Gas: {}
Max Gas Per Batch: {}
Max Pubdata Per Batch: {}
"#,
fd.network_rpc.green(),
self.get_chain_id().to_string().green(),
fd.l1_block.green(),
fd.l2_block.green(),
fd.block_timestamp.to_string().green(),
format!("{:#}", fd.fork_block_hash).green(),
fd.fee_model_config_v2
.compute_overhead_part
.to_string()
.green(),
fd.fee_model_config_v2
.pubdata_overhead_part
.to_string()
.green(),
fd.fee_model_config_v2
.batch_overhead_l1_gas
.to_string()
.green(),
fd.fee_model_config_v2.max_gas_per_batch.to_string().green(),
fd.fee_model_config_v2
.max_pubdata_per_batch
.to_string()
.green()
);
} else {
sh_println!(
r#"
Network Configuration
========================
Chain ID: {}
"#,
self.chain_id
.unwrap_or(TEST_NODE_NETWORK_ID)
.to_string()
.green()
);
}
sh_println!(
r#"
Gas Configuration
========================
L1 Gas Price (gwei): {}
L2 Gas Price (gwei): {}
L1 Pubdata Price (gwei): {}
Estimated Gas Price Scale Factor: {}
Estimated Gas Limit Scale Factor: {}
"#,
format_gwei(self.get_l1_gas_price().into()).green(),
format_gwei(self.get_l2_gas_price().into()).green(),
format_gwei(self.get_l1_pubdata_price().into()).green(),
self.get_price_scale().to_string().green(),
self.get_gas_limit_scale().to_string().green()
);
sh_println!(
r#"
Genesis Timestamp
========================
{}
"#,
self.get_genesis_timestamp().to_string().green()
);
sh_println!(
r#"
Node Configuration
========================
Port: {}
EVM Emulator: {}
Health Check Endpoint: {}
ZK OS: {}
L1: {}
"#,
self.port,
if self.use_evm_emulator {
"Enabled".green()
} else {
"Disabled".red()
},
if self.health_check_endpoint {
"Enabled".green()
} else {
"Disabled".red()
},
if self.use_zkos {
"Enabled".green()
} else {
"Disabled".red()
},
if self.l1_config.is_some() {
"Enabled".green()
} else {
"Disabled".red()
}
);
match self.l1_config.as_ref() {
Some(L1Config::Spawn { port }) => {
sh_println!(
r#"
L1 Configuration (Spawned)
========================
Port: {port}
"#
);
}
Some(L1Config::External { address }) => {
sh_println!(
r#"
L1 Configuration (External)
========================
Address: {address}
"#
);
}
None => {}
}
let mut listening = String::new();
listening.push_str("\n========================================\n");
for host in &self.host {
listening.push_str(&format!(
" Listening on {}:{}\n",
host.to_string().green(),
self.port.to_string().green()
));
}
listening.push_str("========================================\n");
sh_println!("{}", listening);
}
fn as_json(&self, fork: Option<&ForkPrintInfo>) -> Value {
let mut wallet_description = HashMap::new();
let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());
for wallet in &self.genesis_accounts {
available_accounts.push(format!("{:?}", wallet.address()));
private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes())));
}
if let Some(ref gen) = self.account_generator {
let phrase = gen.get_phrase().to_string();
let derivation_path = gen.get_derivation_path().to_string();
wallet_description.insert("derivation_path".to_string(), derivation_path);
wallet_description.insert("mnemonic".to_string(), phrase);
};
if let Some(fork) = fork {
json!({
"available_accounts": available_accounts,
"private_keys": private_keys,
"endpoint": fork.network_rpc,
"l1_block": fork.l1_block,
"l2_block": fork.l2_block,
"block_hash": fork.fork_block_hash,
"chain_id": self.get_chain_id(),
"wallet": wallet_description,
"l1_gas_price": format!("{}", self.get_l1_gas_price()),
"l2_gas_price": format!("{}", self.get_l2_gas_price()),
"l1_pubdata_price": format!("{}", self.get_l1_pubdata_price()),
"price_scale_factor": format!("{}", self.get_price_scale()),
"limit_scale_factor": format!("{}", self.get_gas_limit_scale()),
"fee_model_config_v2": fork.fee_model_config_v2,
})
} else {
json!({
"available_accounts": available_accounts,
"private_keys": private_keys,
"wallet": wallet_description,
"chain_id": self.get_chain_id(),
"l1_gas_price": format!("{}", self.get_l1_gas_price()),
"l2_gas_price": format!("{}", self.get_l2_gas_price()),
"l1_pubdata_price": format!("{}", self.get_l1_pubdata_price()),
"price_scale_factor": format!("{}", self.get_price_scale()),
"limit_scale_factor": format!("{}", self.get_gas_limit_scale()),
})
}
}
#[must_use]
pub fn set_config_out(mut self, config_out: Option<String>) -> Self {
self.config_out = config_out;
self
}
#[must_use]
pub fn with_port(mut self, port: Option<u16>) -> Self {
if let Some(port) = port {
self.port = port;
}
self
}
pub fn get_port(&self) -> u16 {
self.port
}
#[must_use]
pub fn with_chain_id(mut self, chain_id: Option<u32>) -> Self {
if let Some(chain_id) = chain_id {
self.chain_id = Some(chain_id);
}
self
}
pub fn get_chain_id(&self) -> u32 {
self.chain_id.unwrap_or(TEST_NODE_NETWORK_ID)
}
pub fn update_chain_id(&mut self, chain_id: Option<u32>) -> &mut Self {
self.chain_id = chain_id;
self
}
#[must_use]
pub fn with_system_contracts(mut self, option: Option<SystemContractsOptions>) -> Self {
if let Some(option) = option {
self.system_contracts_options = option;
}
self
}
#[must_use]
pub fn with_protocol_version(mut self, protocol_version: Option<ProtocolVersionId>) -> Self {
self.protocol_version = protocol_version;
self
}
pub fn get_system_contracts(&self) -> SystemContractsOptions {
self.system_contracts_options
}
#[must_use]
pub fn with_override_bytecodes_dir(mut self, dir: Option<String>) -> Self {
if let Some(dir) = dir {
self.override_bytecodes_dir = Some(dir);
}
self
}
pub fn get_override_bytecodes_dir(&self) -> Option<&String> {
self.override_bytecodes_dir.as_ref()
}
#[must_use]
pub fn with_enforce_bytecode_compression(mut self, enforce: Option<bool>) -> Self {
if let Some(enforce) = enforce {
self.bytecode_compression = enforce;
}
self
}
pub fn is_bytecode_compression_enforced(&self) -> bool {
self.bytecode_compression
}
#[must_use]
pub fn with_evm_emulator(mut self, enable: Option<bool>) -> Self {
if let Some(enable) = enable {
self.use_evm_emulator = enable;
}
self
}
pub fn is_evm_emulator_enabled(&self) -> bool {
self.use_evm_emulator
}
#[must_use]
pub fn with_l1_gas_price(mut self, price: Option<u64>) -> Self {
if let Some(price) = price {
self.l1_gas_price = Some(price);
}
self
}
pub fn get_l1_gas_price(&self) -> u64 {
self.l1_gas_price.unwrap_or(DEFAULT_L1_GAS_PRICE)
}
pub fn update_l1_gas_price(&mut self, price: Option<u64>) -> &mut Self {
self.l1_gas_price = price;
self
}
#[must_use]
pub fn with_l2_gas_price(mut self, price: Option<u64>) -> Self {
if let Some(price) = price {
self.l2_gas_price = Some(price);
}
self
}
pub fn get_l2_gas_price(&self) -> u64 {
self.l2_gas_price.unwrap_or(DEFAULT_L2_GAS_PRICE)
}
pub fn update_l2_gas_price(&mut self, price: Option<u64>) -> &mut Self {
self.l2_gas_price = price;
self
}
#[must_use]
pub fn with_l1_pubdata_price(mut self, price: Option<u64>) -> Self {
self.l1_pubdata_price = price;
self
}
pub fn get_l1_pubdata_price(&self) -> u64 {
self.l1_pubdata_price.unwrap_or(DEFAULT_FAIR_PUBDATA_PRICE)
}
pub fn update_l1_pubdata_price(&mut self, price: Option<u64>) -> &mut Self {
self.l1_pubdata_price = price;
self
}
#[must_use]
pub fn with_log_level(mut self, level: Option<LogLevel>) -> Self {
if let Some(level) = level {
self.log_level = level;
}
self
}
pub fn get_log_level(&self) -> LogLevel {
self.log_level
}
pub fn get_cache_dir(&self) -> &str {
&self.cache_dir
}
#[must_use]
pub fn with_cache_dir(mut self, dir: Option<String>) -> Self {
if let Some(dir) = dir {
self.cache_dir = dir;
}
self
}
#[must_use]
pub fn with_cache_config(mut self, config: Option<CacheConfig>) -> Self {
if let Some(config) = config {
self.cache_config = config;
}
self
}
pub fn get_cache_config(&self) -> &CacheConfig {
&self.cache_config
}
#[must_use]
pub fn with_log_file_path(mut self, path: Option<String>) -> Self {
if let Some(path) = path {
self.log_file_path = path;
}
self
}
pub fn get_log_file_path(&self) -> &str {
&self.log_file_path
}
#[must_use]
pub fn with_debug_mode(mut self) -> Self {
self.show_calls = ShowCalls::User;
self.resolve_hashes = true;
self.show_gas_details = ShowGasDetails::All;
self
}
#[must_use]
pub fn with_show_calls(mut self, show_calls: Option<ShowCalls>) -> Self {
if let Some(show_calls) = show_calls {
self.show_calls = show_calls;
}
self
}
pub fn get_show_calls(&self) -> ShowCalls {
self.show_calls
}
#[must_use]
pub fn with_resolve_hashes(mut self, resolve: Option<bool>) -> Self {
if let Some(resolve) = resolve {
self.resolve_hashes = resolve;
}
self
}
#[must_use]
pub fn with_verbosity_level(mut self, verbosity: u8) -> Self {
self.verbosity = verbosity;
self
}
pub fn get_verbosity_level(&self) -> u8 {
self.verbosity
}
#[must_use]
pub fn with_silent(mut self, silent: Option<bool>) -> Self {
if let Some(silent) = silent {
self.silent = silent;
}
self
}
#[must_use]
pub fn with_show_node_config(mut self, show_node_config: Option<bool>) -> Self {
if let Some(show_node_config) = show_node_config {
self.show_node_config = show_node_config;
}
self
}
#[must_use]
pub fn with_show_tx_summary(mut self, show_tx_summary: Option<bool>) -> Self {
if let Some(show_tx_summary) = show_tx_summary {
self.show_tx_summary = show_tx_summary;
}
self
}
#[must_use]
pub fn with_show_event_logs(mut self, show_event_logs: Option<bool>) -> Self {
if let Some(show_event_logs) = show_event_logs {
self.show_event_logs = show_event_logs;
}
self
}
pub fn get_show_event_logs(&self) -> bool {
self.show_event_logs
}
#[must_use]
pub fn with_disable_console_log(mut self, disable_console_log: Option<bool>) -> Self {
if let Some(disable_console_log) = disable_console_log {
self.disable_console_log = disable_console_log;
}
self
}
pub fn is_resolve_hashes_enabled(&self) -> bool {
self.resolve_hashes
}
#[must_use]
pub fn with_show_storage_logs(mut self, show_storage_logs: Option<ShowStorageLogs>) -> Self {
if let Some(show_storage_logs) = show_storage_logs {
self.show_storage_logs = show_storage_logs;
}
self
}
pub fn get_show_storage_logs(&self) -> ShowStorageLogs {
self.show_storage_logs
}
#[must_use]
pub fn with_vm_log_detail(mut self, detail: Option<ShowVMDetails>) -> Self {
if let Some(detail) = detail {
self.show_vm_details = detail;
}
self
}
pub fn get_vm_log_detail(&self) -> ShowVMDetails {
self.show_vm_details
}
#[must_use]
pub fn with_show_gas_details(mut self, show_gas_details: Option<ShowGasDetails>) -> Self {
if let Some(show_gas_details) = show_gas_details {
self.show_gas_details = show_gas_details;
}
self
}
pub fn get_show_gas_details(&self) -> ShowGasDetails {
self.show_gas_details
}
#[must_use]
pub fn with_show_outputs(mut self, show_outputs: Option<bool>) -> Self {
if let Some(show_outputs) = show_outputs {
self.show_outputs = show_outputs;
}
self
}
pub fn get_show_outputs(&self) -> bool {
self.show_outputs
}
#[must_use]
pub fn with_gas_limit_scale(mut self, scale: Option<f32>) -> Self {
if let Some(scale) = scale {
self.limit_scale_factor = Some(scale);
}
self
}
pub fn get_gas_limit_scale(&self) -> f32 {
self.limit_scale_factor
.unwrap_or(DEFAULT_ESTIMATE_GAS_SCALE_FACTOR)
}
pub fn update_gas_limit_scale(&mut self, scale: Option<f32>) -> &mut Self {
self.limit_scale_factor = scale;
self
}
#[must_use]
pub fn with_price_scale(mut self, scale: Option<f64>) -> Self {
if let Some(scale) = scale {
self.price_scale_factor = Some(scale);
}
self
}
pub fn get_price_scale(&self) -> f64 {
self.price_scale_factor
.unwrap_or(DEFAULT_ESTIMATE_GAS_PRICE_SCALE_FACTOR)
}
pub fn update_price_scale(&mut self, scale: Option<f64>) -> &mut Self {
self.price_scale_factor = scale;
self
}
#[must_use]
pub fn with_genesis_balance<U: Into<U256>>(mut self, balance: U) -> Self {
self.genesis_balance = balance.into();
self
}
#[must_use]
pub fn with_genesis_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
self.genesis_accounts = accounts;
self
}
#[must_use]
pub fn with_signer_accounts(mut self, accounts: Vec<PrivateKeySigner>) -> Self {
self.signer_accounts = accounts;
self
}
#[must_use]
pub fn with_account_generator(mut self, generator: AccountGenerator) -> Self {
let accounts = generator.gen();
self.account_generator = Some(generator);
self.with_signer_accounts(accounts.clone())
.with_genesis_accounts(accounts)
}
#[must_use]
pub fn with_genesis_timestamp(mut self, timestamp: Option<u64>) -> Self {
self.genesis_timestamp = timestamp;
self
}
pub fn get_genesis_timestamp(&self) -> u64 {
self.genesis_timestamp
.unwrap_or(NON_FORK_FIRST_BLOCK_TIMESTAMP)
}
#[must_use]
pub fn with_genesis(mut self, genesis: Option<Genesis>) -> Self {
self.genesis = genesis;
self
}
#[must_use]
pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
self.enable_auto_impersonate = enable_auto_impersonate;
self
}
#[must_use]
pub fn with_offline(mut self, offline: Option<bool>) -> Self {
if let Some(offline) = offline {
self.offline = offline;
}
self
}
pub fn is_offline(&self) -> bool {
self.offline
}
#[must_use]
pub fn with_host(mut self, host: Vec<IpAddr>) -> Self {
self.host = if host.is_empty() {
vec![IpAddr::V4(Ipv4Addr::LOCALHOST)]
} else {
host
};
self
}
#[must_use]
pub fn with_health_check_endpoint(mut self, health_check_endpoint: Option<bool>) -> Self {
if let Some(health_check_endpoint) = health_check_endpoint {
self.health_check_endpoint = health_check_endpoint;
}
self
}
pub fn is_health_check_endpoint_endpoint_enabled(&self) -> bool {
self.health_check_endpoint
}
#[must_use]
pub fn with_block_time(mut self, block_time: Option<Duration>) -> Self {
self.block_time = block_time;
self
}
#[must_use]
pub fn with_no_mining(mut self, no_mining: bool) -> Self {
self.no_mining = no_mining;
self
}
#[must_use]
pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self {
self.transaction_order = transaction_order;
self
}
#[must_use]
pub fn with_allow_origin(mut self, allow_origin: String) -> Self {
self.allow_origin = allow_origin;
self
}
#[must_use]
pub fn with_no_cors(mut self, no_cors: bool) -> Self {
self.no_cors = no_cors;
self
}
#[must_use]
pub fn with_state(mut self, state: Option<PathBuf>) -> Self {
self.state = state;
self
}
#[must_use]
pub fn with_dump_state(mut self, dump_state: Option<PathBuf>) -> Self {
self.dump_state = dump_state;
self
}
#[must_use]
pub fn with_state_interval(mut self, state_interval: Option<u64>) -> Self {
self.state_interval = state_interval;
self
}
#[must_use]
pub fn with_preserve_historical_states(mut self, preserve_historical_states: bool) -> Self {
self.preserve_historical_states = preserve_historical_states;
self
}
#[must_use]
pub fn with_load_state(mut self, load_state: Option<PathBuf>) -> Self {
self.load_state = load_state;
self
}
#[must_use]
pub fn with_l1_config(mut self, l1_config: Option<L1Config>) -> Self {
self.l1_config = l1_config;
self
}
#[must_use]
pub fn with_auto_execute_l1(mut self, auto_execute_l1: Option<bool>) -> Self {
self.auto_execute_l1 = auto_execute_l1.unwrap_or(false);
self
}
}