starknet_devnet_core/
utils.rs

1use blockifier::blockifier_versioned_constants::VersionedConstants;
2use blockifier::bouncer::{BouncerConfig, BouncerWeights, BuiltinWeights};
3use blockifier::transaction::objects::TransactionExecutionInfo;
4use starknet_api::block::StarknetVersion;
5use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
6use starknet_rs_core::types::Felt;
7use starknet_types::patricia_key::{PatriciaKey, StorageKey};
8
9use crate::error::DevnetResult;
10
11pub mod random_number_generator {
12    use rand::{Rng, SeedableRng, rng};
13    use rand_mt::Mt64;
14
15    pub fn generate_u32_random_number() -> u32 {
16        rng().random()
17    }
18
19    pub(crate) fn generate_u128_random_numbers(seed: u32, random_numbers_count: u8) -> Vec<u128> {
20        let mut result: Vec<u128> = Vec::new();
21        let mut rng: Mt64 = SeedableRng::seed_from_u64(seed as u64);
22
23        for _ in 0..random_numbers_count {
24            result.push(rng.random());
25        }
26
27        result
28    }
29}
30
31/// Returns the storage address of a Starknet storage variable given its name and arguments.
32pub(crate) fn get_storage_var_address(
33    storage_var_name: &str,
34    args: &[Felt],
35) -> DevnetResult<StorageKey> {
36    let storage_var_address =
37        starknet_rs_core::utils::get_storage_var_address(storage_var_name, args)
38            .map_err(|err| crate::error::Error::UnexpectedInternalError { msg: err.to_string() })?;
39
40    Ok(PatriciaKey::new(storage_var_address)?)
41}
42
43/// This should be modified when updating to the version after 0.14.1
44pub(crate) fn get_versioned_constants() -> VersionedConstants {
45    #[allow(clippy::unwrap_used)]
46    VersionedConstants::get(&StarknetVersion::V0_14_1).unwrap().clone()
47}
48
49/// Values not present here: https://docs.starknet.io/tools/limits-and-triggers/
50/// Asked the blockifier team about the values, they provided them in these threads:
51/// https://spaceshard.slack.com/archives/C029F9AN8LX/p1721657837687799?thread_ts=1721400009.781699&cid=C029F9AN8LX
52/// https://spaceshard.slack.com/archives/C029F9AN8LX/p1739259794326519?thread_ts=1738840494.497479&cid=C029F9AN8LX
53/// https://spaceshard.slack.com/archives/C029F9AN8LX/p1750171661112469
54/// https://spaceshard.slack.com/archives/C029F9AN8LX/p1750237658621529
55pub(crate) fn custom_bouncer_config() -> BouncerConfig {
56    BouncerConfig {
57        block_max_capacity: BouncerWeights {
58            l1_gas: 4_950_000,
59            sierra_gas: starknet_api::execution_resources::GasAmount(2_000_000_000),
60            state_diff_size: 4_000,
61            n_events: 5_000,
62            ..BouncerWeights::max()
63        },
64        builtin_weights: BuiltinWeights::default(),
65        blake_weight: 5263, // from BouncerConfig::default
66    }
67}
68
69#[macro_export]
70macro_rules! nonzero_gas_price {
71    ($value:expr) => {{
72        let gas_price = starknet_api::block::GasPrice(($value).get());
73        starknet_api::block::NonzeroGasPrice::new(gas_price).unwrap()
74    }};
75}
76
77pub(crate) fn maybe_extract_failure_reason(
78    execution_info: &TransactionExecutionInfo,
79) -> Option<String> {
80    execution_info.revert_error.as_ref().map(|err| err.to_string())
81}
82
83#[cfg(test)]
84pub(crate) mod test_utils {
85    use cairo_lang_starknet_classes::contract_class::ContractClass as SierraContractClass;
86    use starknet_api::data_availability::DataAvailabilityMode;
87    use starknet_api::transaction::fields::Tip;
88    use starknet_rs_core::types::Felt;
89    use starknet_types::contract_address::ContractAddress;
90    use starknet_types::contract_class::deprecated::json_contract_class::Cairo0Json;
91    use starknet_types::contract_class::{Cairo0ContractClass, ContractClass};
92    use starknet_types::rpc::transactions::broadcasted_declare_transaction_v3::BroadcastedDeclareTransactionV3;
93    use starknet_types::rpc::transactions::broadcasted_invoke_transaction_v3::BroadcastedInvokeTransactionV3;
94    use starknet_types::rpc::transactions::declare_transaction_v3::DeclareTransactionV3;
95    use starknet_types::rpc::transactions::{
96        BroadcastedDeclareTransaction, BroadcastedInvokeTransaction,
97        BroadcastedTransactionCommonV3, DeclareTransaction, ResourceBoundsWrapper, Transaction,
98        TransactionWithHash,
99    };
100    use starknet_types::traits::HashProducer;
101
102    use crate::account::KeyPair;
103    use crate::constants::DEVNET_DEFAULT_CHAIN_ID;
104
105    pub(crate) fn dummy_felt() -> Felt {
106        Felt::from_hex_unchecked("0xDD10")
107    }
108
109    pub(crate) fn dummy_cairo_1_contract_class() -> SierraContractClass {
110        let json_str =
111            std::fs::read_to_string("../../contracts/test_artifacts/cairo1/cairo_1_test.sierra")
112                .unwrap();
113
114        ContractClass::cairo_1_from_sierra_json_str(&json_str).unwrap()
115    }
116
117    /// casm hash of dummy_cairo_1_contract_class
118    pub static DUMMY_CAIRO_1_COMPILED_CLASS_HASH: Felt = Felt::from_hex_unchecked(
119        "0x586026f886dd7ee74f6b32b12c9678f0bc090260c881a41d192103a17c110e8",
120    );
121
122    pub(crate) fn dummy_contract_address() -> ContractAddress {
123        ContractAddress::new(Felt::from_hex_unchecked("0xADD4E55")).unwrap()
124    }
125
126    /// Returns a gas bounds object with max amounts set to input params and prices set to 1
127    pub(crate) fn resource_bounds_with_price_1(
128        l1_gas: u64,
129        l1_data_gas: u64,
130        l2_gas: u64,
131    ) -> ResourceBoundsWrapper {
132        ResourceBoundsWrapper::new(l1_gas, 1, l1_data_gas, 1, l2_gas, 1)
133    }
134
135    /// Returns an unsigned tx representing a v3 declaration of a predefined dummy
136    /// class. The tx can be sent to Starknet.
137    pub(crate) fn broadcasted_declare_tx_v3_of_dummy_class(
138        sender_address: ContractAddress,
139        nonce: Felt,
140        resource_bounds: ResourceBoundsWrapper,
141    ) -> BroadcastedDeclareTransactionV3 {
142        broadcasted_declare_tx_v3(
143            sender_address,
144            nonce,
145            dummy_cairo_1_contract_class(),
146            DUMMY_CAIRO_1_COMPILED_CLASS_HASH,
147            resource_bounds,
148        )
149    }
150
151    /// Returns an unsigned tx representing a v3 declaration of the provided class. The
152    /// tx can be sent to Starknet.
153    pub(crate) fn broadcasted_declare_tx_v3(
154        sender_address: ContractAddress,
155        nonce: Felt,
156        contract_class: cairo_lang_starknet_classes::contract_class::ContractClass,
157        compiled_class_hash: Felt,
158        resource_bounds: ResourceBoundsWrapper,
159    ) -> BroadcastedDeclareTransactionV3 {
160        BroadcastedDeclareTransactionV3 {
161            common: BroadcastedTransactionCommonV3 {
162                version: Felt::THREE,
163                signature: vec![],
164                nonce,
165                resource_bounds,
166                tip: Tip(0),
167                paymaster_data: vec![],
168                nonce_data_availability_mode: DataAvailabilityMode::L1,
169                fee_data_availability_mode: DataAvailabilityMode::L1,
170            },
171            contract_class,
172            sender_address,
173            compiled_class_hash,
174            account_deployment_data: vec![],
175        }
176    }
177
178    pub(crate) fn dummy_declare_tx_v3_with_hash() -> TransactionWithHash {
179        let declare_txn = broadcasted_declare_tx_v3_of_dummy_class(
180            dummy_contract_address(),
181            Felt::ZERO,
182            ResourceBoundsWrapper::new(
183                1, 1, // l1_gas
184                0, 0, // l1_data_gas
185                0, 0, // l2_gas
186            ),
187        );
188        let sierra_hash =
189            ContractClass::Cairo1(declare_txn.contract_class.clone()).generate_hash().unwrap();
190
191        let tx_hash = BroadcastedDeclareTransaction::V3(Box::new(declare_txn.clone()))
192            .create_sn_api_declare(&DEVNET_DEFAULT_CHAIN_ID.to_felt())
193            .unwrap()
194            .tx_hash;
195
196        TransactionWithHash::new(
197            *tx_hash,
198            Transaction::Declare(DeclareTransaction::V3(DeclareTransactionV3::new(
199                &declare_txn,
200                sierra_hash,
201            ))),
202        )
203    }
204
205    pub(crate) fn cairo_0_account_without_validations() -> Cairo0ContractClass {
206        let account_json_path =
207            "../../contracts/test_artifacts/cairo0/account_without_validations/account.json";
208
209        Cairo0Json::raw_json_from_path(account_json_path).unwrap().into()
210    }
211
212    pub fn dummy_key_pair() -> KeyPair {
213        KeyPair { public_key: dummy_felt(), private_key: dummy_felt() }
214    }
215
216    pub fn test_invoke_transaction_v3(
217        account_address: ContractAddress,
218        contract_address: ContractAddress,
219        function_selector: Felt,
220        params: &[Felt],
221        nonce: u128,
222        resource_bounds: ResourceBoundsWrapper,
223    ) -> BroadcastedInvokeTransaction {
224        // Encode: [addr, selector, params len, *params]
225        let mut calldata = vec![Felt::from(contract_address), function_selector];
226        calldata.push(params.len().into());
227        calldata.extend_from_slice(params);
228
229        BroadcastedInvokeTransaction::V3(BroadcastedInvokeTransactionV3 {
230            common: BroadcastedTransactionCommonV3 {
231                version: Felt::THREE,
232                signature: vec![],
233                nonce: Felt::from(nonce),
234                resource_bounds,
235                tip: Tip(0),
236                paymaster_data: vec![],
237                nonce_data_availability_mode:
238                    starknet_api::data_availability::DataAvailabilityMode::L1,
239                fee_data_availability_mode:
240                    starknet_api::data_availability::DataAvailabilityMode::L1,
241            },
242            sender_address: account_address,
243            calldata,
244            account_deployment_data: vec![],
245        })
246    }
247}
248
249#[cfg(any(test, feature = "test_utils"))]
250#[allow(clippy::unwrap_used)]
251pub mod exported_test_utils {
252    use starknet_types::contract_class::Cairo0ContractClass;
253    use starknet_types::contract_class::deprecated::json_contract_class::Cairo0Json;
254
255    pub fn dummy_cairo_l1l2_contract() -> Cairo0ContractClass {
256        let json_str =
257            std::fs::read_to_string("../../contracts/test_artifacts/cairo0/l1l2.json").unwrap();
258
259        Cairo0Json::raw_json_from_json_str(&json_str).unwrap().into()
260    }
261
262    pub fn dummy_cairo_0_contract_class() -> Cairo0ContractClass {
263        let json_str =
264            std::fs::read_to_string("../../contracts/test_artifacts/cairo0/simple_contract.json")
265                .unwrap();
266
267        Cairo0Json::raw_json_from_json_str(&json_str).unwrap().into()
268    }
269}
270
271#[cfg(test)]
272mod tests {
273    use super::get_storage_var_address;
274    use super::test_utils::{self};
275
276    #[test]
277    fn correct_simple_storage_var_address_generated() {
278        let expected_storage_var_address =
279            starknet_api::abi::abi_utils::get_storage_var_address("simple", &[]);
280        let generated_storage_var_address = get_storage_var_address("simple", &[]).unwrap();
281
282        assert_eq!(
283            expected_storage_var_address.0.key().to_bytes_be(),
284            generated_storage_var_address.to_felt().to_bytes_be()
285        );
286    }
287
288    #[test]
289    fn correct_complex_storage_var_address_generated() {
290        let expected_storage_var_address = starknet_api::abi::abi_utils::get_storage_var_address(
291            "complex",
292            &[test_utils::dummy_felt()],
293        );
294
295        let generated_storage_var_address =
296            get_storage_var_address("complex", &[test_utils::dummy_felt()]).unwrap();
297
298        assert_eq!(
299            expected_storage_var_address.0.key().to_bytes_be(),
300            generated_storage_var_address.to_felt().to_bytes_be()
301        );
302    }
303}