starknet_devnet_core/starknet/
starknet_config.rs

1use std::num::NonZeroU128;
2
3use clap::Error;
4use serde::{Serialize, Serializer};
5use starknet_rs_core::types::Felt;
6use starknet_types::chain_id::ChainId;
7use starknet_types::contract_class::ContractClass;
8use starknet_types::rpc::state::Balance;
9use starknet_types::traits::HashProducer;
10use url::Url;
11
12use crate::constants::{
13    CAIRO_1_ACCOUNT_CONTRACT_SIERRA, DEVNET_DEFAULT_CHAIN_ID, DEVNET_DEFAULT_INITIAL_BALANCE,
14    DEVNET_DEFAULT_L1_DATA_GAS_PRICE, DEVNET_DEFAULT_L1_GAS_PRICE, DEVNET_DEFAULT_L2_GAS_PRICE,
15    DEVNET_DEFAULT_TEST_SEED, DEVNET_DEFAULT_TOTAL_ACCOUNTS, ETH_ERC20_CONTRACT_CLASS,
16    ETH_ERC20_CONTRACT_CLASS_HASH, MAXIMUM_CONTRACT_BYTECODE_SIZE, MAXIMUM_CONTRACT_CLASS_SIZE,
17    MAXIMUM_SIERRA_LENGTH, STRK_ERC20_CONTRACT_CLASS, STRK_ERC20_CONTRACT_CLASS_HASH,
18};
19
20#[derive(Copy, Clone, Debug, clap::ValueEnum, Serialize)]
21#[serde(rename_all = "snake_case")]
22pub enum DumpOn {
23    Exit,
24    Block,
25    Request,
26}
27
28#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, clap::ValueEnum, Serialize)]
29#[serde(rename_all = "snake_case")]
30#[clap(rename_all = "snake_case")]
31pub enum StateArchiveCapacity {
32    #[default]
33    None,
34    Full,
35}
36
37#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
38#[serde(rename_all = "snake_case")]
39pub enum BlockGenerationOn {
40    Transaction,
41    Demand,
42    Interval(u64),
43}
44
45#[derive(Clone, Debug, Serialize)]
46pub struct ClassSizeConfig {
47    pub maximum_contract_class_size: u64,
48    pub maximum_contract_bytecode_size: u64,
49    pub maximum_sierra_length: u64,
50}
51
52impl Default for ClassSizeConfig {
53    fn default() -> Self {
54        Self {
55            maximum_contract_class_size: MAXIMUM_CONTRACT_CLASS_SIZE,
56            maximum_contract_bytecode_size: MAXIMUM_CONTRACT_BYTECODE_SIZE,
57            maximum_sierra_length: MAXIMUM_SIERRA_LENGTH,
58        }
59    }
60}
61
62impl std::str::FromStr for BlockGenerationOn {
63    type Err = Error;
64
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        match s {
67            "transaction" => Ok(BlockGenerationOn::Transaction),
68            "demand" => Ok(BlockGenerationOn::Demand),
69            value => {
70                let interval_value = value
71                    .parse::<u64>()
72                    .map_err(|_| Error::new(clap::error::ErrorKind::InvalidValue))?;
73
74                if interval_value > 0 {
75                    Ok(BlockGenerationOn::Interval(interval_value))
76                } else {
77                    Err(Error::new(clap::error::ErrorKind::InvalidValue))
78                }
79            }
80        }
81    }
82}
83
84#[derive(Debug, Clone, Default, Serialize)]
85pub struct ForkConfig {
86    #[serde(serialize_with = "serialize_config_url")]
87    pub url: Option<Url>,
88    pub block_number: Option<u64>,
89    #[serde(skip)]
90    pub block_hash: Option<Felt>,
91}
92
93pub fn serialize_config_url<S>(url: &Option<Url>, serializer: S) -> Result<S::Ok, S::Error>
94where
95    S: Serializer,
96{
97    match url {
98        Some(url) => serializer.serialize_str(url.as_ref()),
99        None => serializer.serialize_none(),
100    }
101}
102
103pub fn serialize_initial_balance<S>(balance: &Balance, serializer: S) -> Result<S::Ok, S::Error>
104where
105    S: Serializer,
106{
107    serializer.serialize_str(&balance.to_str_radix(10))
108}
109
110pub fn serialize_chain_id<S>(chain_id: &ChainId, serializer: S) -> Result<S::Ok, S::Error>
111where
112    S: Serializer,
113{
114    serializer.serialize_str(&format!("{chain_id}"))
115}
116
117#[derive(Clone, Debug, Serialize)]
118pub struct StarknetConfig {
119    pub seed: u32,
120    pub total_accounts: u8,
121    #[serde(skip_serializing)]
122    pub account_contract_class: ContractClass,
123    pub account_contract_class_hash: Felt,
124    #[serde(serialize_with = "serialize_initial_balance")]
125    pub predeployed_accounts_initial_balance: Balance,
126    pub start_time: Option<u64>,
127    pub gas_price_wei: NonZeroU128,
128    pub gas_price_fri: NonZeroU128,
129    pub data_gas_price_wei: NonZeroU128,
130    pub data_gas_price_fri: NonZeroU128,
131    pub l2_gas_price_wei: NonZeroU128,
132    pub l2_gas_price_fri: NonZeroU128,
133    #[serde(serialize_with = "serialize_chain_id")]
134    pub chain_id: ChainId,
135    pub dump_on: Option<DumpOn>,
136    pub dump_path: Option<String>,
137    pub block_generation_on: BlockGenerationOn,
138    pub lite_mode: bool,
139    pub state_archive: StateArchiveCapacity,
140    pub fork_config: ForkConfig,
141    pub eth_erc20_class_hash: Felt,
142    pub strk_erc20_class_hash: Felt,
143    #[serde(skip_serializing)]
144    pub eth_erc20_contract_class: String,
145    #[serde(skip_serializing)]
146    pub strk_erc20_contract_class: String,
147    #[serde(skip_serializing)]
148    pub predeclare_argent: bool,
149    pub class_size_config: ClassSizeConfig,
150}
151
152impl StarknetConfig {
153    pub fn uses_pre_confirmed_block(&self) -> bool {
154        match self.block_generation_on {
155            BlockGenerationOn::Transaction => false,
156            BlockGenerationOn::Demand | BlockGenerationOn::Interval(_) => true,
157        }
158    }
159}
160
161#[allow(clippy::unwrap_used)]
162impl Default for StarknetConfig {
163    fn default() -> Self {
164        // unwrapping is safe here because the contract is hardcoded and we know it wont fail
165        let account_contract_class: ContractClass =
166            ContractClass::cairo_1_from_sierra_json_str(CAIRO_1_ACCOUNT_CONTRACT_SIERRA)
167                .unwrap()
168                .into();
169        StarknetConfig {
170            seed: DEVNET_DEFAULT_TEST_SEED,
171            total_accounts: DEVNET_DEFAULT_TOTAL_ACCOUNTS,
172            // same here
173            account_contract_class_hash: account_contract_class.generate_hash().unwrap(),
174            account_contract_class,
175            predeployed_accounts_initial_balance: DEVNET_DEFAULT_INITIAL_BALANCE.into(),
176            start_time: None,
177            gas_price_wei: DEVNET_DEFAULT_L1_GAS_PRICE.get().try_into().unwrap(),
178            gas_price_fri: DEVNET_DEFAULT_L1_GAS_PRICE.get().try_into().unwrap(),
179            data_gas_price_wei: DEVNET_DEFAULT_L1_DATA_GAS_PRICE.get().try_into().unwrap(),
180            data_gas_price_fri: DEVNET_DEFAULT_L1_DATA_GAS_PRICE.get().try_into().unwrap(),
181            l2_gas_price_wei: DEVNET_DEFAULT_L2_GAS_PRICE.get().try_into().unwrap(),
182            l2_gas_price_fri: DEVNET_DEFAULT_L2_GAS_PRICE.get().try_into().unwrap(),
183            chain_id: DEVNET_DEFAULT_CHAIN_ID,
184            dump_on: None,
185            dump_path: None,
186            block_generation_on: BlockGenerationOn::Transaction,
187            lite_mode: false,
188            state_archive: StateArchiveCapacity::default(),
189            fork_config: ForkConfig::default(),
190            eth_erc20_class_hash: ETH_ERC20_CONTRACT_CLASS_HASH,
191            strk_erc20_class_hash: STRK_ERC20_CONTRACT_CLASS_HASH,
192            eth_erc20_contract_class: ETH_ERC20_CONTRACT_CLASS.to_string(),
193            strk_erc20_contract_class: STRK_ERC20_CONTRACT_CLASS.to_string(),
194            predeclare_argent: false,
195            class_size_config: ClassSizeConfig::default(),
196        }
197    }
198}