diff --git a/crates/starknet-devnet-core/src/account.rs b/crates/starknet-devnet-core/src/account.rs index 0ecf65fd1..57c308435 100644 --- a/crates/starknet-devnet-core/src/account.rs +++ b/crates/starknet-devnet-core/src/account.rs @@ -1,6 +1,10 @@ +use std::fmt::Display; use std::sync::Arc; +use blockifier::context::BlockContext; use blockifier::state::state_api::StateReader; +use blockifier::transaction::account_transaction::ExecutionFlags; +use blockifier::transaction::transactions::ExecutableTransaction; use starknet_api::core::calculate_contract_address; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_api::{felt, patricia_key}; @@ -11,15 +15,18 @@ use starknet_types::error::Error; use starknet_types::felt::{ClassHash, Key, felt_from_prefixed_hex, join_felts, split_biguint}; use starknet_types::num_bigint::BigUint; use starknet_types::rpc::state::Balance; +use starknet_types::rpc::transactions::broadcasted_deploy_account_transaction_v3::BroadcastedDeployAccountTransactionV3; +use starknet_types::rpc::transactions::{ + BroadcastedDeployAccountTransaction, BroadcastedTransactionCommonV3, ResourceBoundsWrapper, +}; use crate::constants::{ CHARGEABLE_ACCOUNT_ADDRESS, CHARGEABLE_ACCOUNT_PRIVATE_KEY, CHARGEABLE_ACCOUNT_PUBLIC_KEY, - ISRC6_ID_HEX, }; use crate::contract_class_choice::{AccountClassWrapper, AccountContractClassChoice}; use crate::error::DevnetResult; use crate::state::state_readers::DictState; -use crate::state::{CustomState, StarknetState}; +use crate::state::StarknetState; use crate::traits::{Accounted, Deployed}; use crate::utils::get_storage_var_address; @@ -32,6 +39,28 @@ pub enum FeeToken { STRK, } +#[derive(Clone, Debug, Default, Copy)] +pub enum AccountType { + OpenZeppelin0_5_1, + #[default] + OpenZeppelin0_20_0, + Argent0_4_0, + Custom, +} + +impl Display for AccountType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let account_version = match self { + AccountType::OpenZeppelin0_5_1 => "OpenZeppelin 0.5.1", + AccountType::OpenZeppelin0_20_0 => "OpenZeppelin 0.20.0", + AccountType::Argent0_4_0 => "Argent 0.4.0", + AccountType::Custom => "Custom", + }; + + f.write_str(account_version) + } +} + #[derive(Clone)] pub struct KeyPair { pub public_key: Key, @@ -44,18 +73,23 @@ pub struct Account { pub account_address: ContractAddress, pub initial_balance: Balance, pub class_hash: ClassHash, - pub class_metadata: &'static str, + pub class_metadata: String, pub(crate) contract_class: ContractClass, pub(crate) eth_fee_token_address: ContractAddress, pub(crate) strk_fee_token_address: ContractAddress, + block_context: BlockContext, + account_type: AccountType, + chain_id: Felt, } impl Account { pub(crate) fn new_chargeable( eth_fee_token_address: ContractAddress, strk_fee_token_address: ContractAddress, + block_context: BlockContext, + chain_id: Felt, ) -> DevnetResult { - let AccountClassWrapper { contract_class, class_hash, class_metadata } = + let AccountClassWrapper { contract_class, class_hash, account_type } = AccountContractClassChoice::Cairo1.get_class_wrapper()?; // very big number @@ -71,32 +105,41 @@ impl Account { )?)?, initial_balance, class_hash, - class_metadata, + class_metadata: account_type.to_string(), contract_class, eth_fee_token_address, strk_fee_token_address, + block_context, + account_type, + chain_id, }) } + #[allow(clippy::too_many_arguments)] pub(crate) fn new( initial_balance: Balance, keys: KeyPair, class_hash: ClassHash, - class_metadata: &'static str, contract_class: ContractClass, eth_fee_token_address: ContractAddress, strk_fee_token_address: ContractAddress, + block_context: BlockContext, + account_type: AccountType, + chain_id: Felt, ) -> DevnetResult { let account_address = Account::compute_account_address(&keys.public_key)?; Ok(Self { initial_balance, keys, class_hash, - class_metadata, + class_metadata: account_type.to_string(), contract_class, account_address, eth_fee_token_address, strk_fee_token_address, + block_context, + account_type, + chain_id, }) } @@ -114,27 +157,52 @@ impl Account { Ok(ContractAddress::from(account_address)) } - // simulate constructor logic (register interfaces and set public key), as done in - // https://github.com/OpenZeppelin/cairo-contracts/blob/89a450a88628ec3b86273f261b2d8d1ca9b1522b/src/account/account.cairo#L207-L211 - fn simulate_constructor(&self, state: &mut StarknetState) -> DevnetResult<()> { - let core_address = self.account_address.try_into()?; - - let interface_storage_var = get_storage_var_address( - "SRC5_supported_interfaces", - &[felt_from_prefixed_hex(ISRC6_ID_HEX)?], - )?; - state.state.state.set_storage_at( - core_address, - interface_storage_var.try_into()?, - Felt::ONE, - )?; + fn calldata(&self) -> Vec { + match self.account_type { + AccountType::OpenZeppelin0_5_1 | AccountType::OpenZeppelin0_20_0 => { + vec![self.keys.public_key] + } + AccountType::Argent0_4_0 => todo!(), + AccountType::Custom => vec![], + } + } - let public_key_storage_var = get_storage_var_address("Account_public_key", &[])?; - state.state.state.set_storage_at( - core_address, - public_key_storage_var.try_into()?, - self.keys.public_key, - )?; + /// Invoke constructor by executing BroadcastedDeployAccount transaction and change the state + fn deploy_via_transaction(&self, state: &mut StarknetState) -> DevnetResult<()> { + let core_address = self.account_address.try_into()?; + let mut deploy_account_txn = + BroadcastedDeployAccountTransaction::V3(BroadcastedDeployAccountTransactionV3 { + common: BroadcastedTransactionCommonV3 { + version: Felt::THREE, + signature: vec![], + nonce: Felt::ZERO, + resource_bounds: ResourceBoundsWrapper::new(0, 0, 0, 0, 0, 0), + tip: Default::default(), + paymaster_data: vec![], + nonce_data_availability_mode: + starknet_api::data_availability::DataAvailabilityMode::L1, + fee_data_availability_mode: + starknet_api::data_availability::DataAvailabilityMode::L1, + }, + contract_address_salt: Felt::ZERO, + constructor_calldata: self.calldata(), + class_hash: self.class_hash, + }) + .create_sn_api_deploy_account(&self.chain_id)?; + + deploy_account_txn.contract_address = core_address; + + blockifier::transaction::account_transaction::AccountTransaction { + tx: starknet_api::executable_transaction::AccountTransaction::DeployAccount( + deploy_account_txn, + ), + execution_flags: ExecutionFlags { + only_query: false, + charge_fee: false, + validate: false, + }, + } + .execute(&mut state.state, &self.block_context)?; Ok(()) } @@ -144,19 +212,13 @@ impl Deployed for Account { fn deploy(&self, state: &mut StarknetState) -> DevnetResult<()> { self.declare_if_undeclared(state, self.class_hash, &self.contract_class)?; - state.predeploy_contract(self.account_address, self.class_hash)?; - // set balance directly in the most underlying state self.set_initial_balance(&mut state.state.state)?; - self.simulate_constructor(state)?; + self.deploy_via_transaction(state)?; Ok(()) } - - fn get_address(&self) -> ContractAddress { - self.account_address - } } impl Accounted for Account { @@ -223,19 +285,26 @@ impl Accounted for Account { #[cfg(test)] mod tests { + use blockifier::context::{BlockContext, ChainInfo}; + + use starknet_api::block::BlockInfo; use starknet_rs_core::types::Felt; + use starknet_types::chain_id::ChainId; use starknet_types::contract_address::ContractAddress; use starknet_types::felt::felt_from_prefixed_hex; use starknet_types::rpc::state::Balance; use super::{Account, KeyPair}; use crate::account::FeeToken; - use crate::constants::CAIRO_1_ERC20_CONTRACT_CLASS_HASH; + use crate::constants::{CAIRO_1_ERC20_CONTRACT_CLASS_HASH, USE_KZG_DA}; + use crate::state::{CustomState, StarknetState}; use crate::traits::{Accounted, Deployed}; use crate::utils::test_utils::{ - dummy_cairo_1_contract_class, dummy_contract_address, dummy_felt, + cairo_0_account_without_validations, dummy_contract_address, + dummy_felt, }; + use crate::utils::{custom_bouncer_config, get_versioned_constants}; /// Testing if generated account address has the same value as the first account in /// https://github.com/0xSpaceShard/starknet-devnet-deprecated/blob/9d867e38e6d465e568e82a47e82e40608f6d220f/test/support/schemas/predeployed_accounts_fixed_seed.json @@ -311,13 +380,20 @@ mod tests { let (mut account, _) = setup(); let expected_address = ContractAddress::new(Felt::from(11111)).unwrap(); account.account_address = expected_address; - assert_eq!(expected_address, account.get_address()); + assert_eq!(expected_address, account.account_address); } fn setup() -> (Account, StarknetState) { let mut state = StarknetState::default(); let fee_token_address = dummy_contract_address(); - + let block_context = BlockContext::new( + BlockInfo::create_for_testing_with_kzg(USE_KZG_DA), + ChainInfo::default(), + get_versioned_constants(), + custom_bouncer_config(), + ); + + let account_contract_class = cairo_0_account_without_validations(); // deploy the erc20 contract state.predeploy_contract(fee_token_address, CAIRO_1_ERC20_CONTRACT_CLASS_HASH).unwrap(); @@ -326,10 +402,12 @@ mod tests { Balance::from(10_u8), KeyPair { public_key: Felt::from(13431515), private_key: Felt::from(11) }, dummy_felt(), - "Dummy account", - dummy_cairo_1_contract_class().into(), + account_contract_class.into(), fee_token_address, fee_token_address, + block_context, + super::AccountType::Custom, + ChainId::Testnet.to_felt(), ) .unwrap(), state, diff --git a/crates/starknet-devnet-core/src/contract_class_choice.rs b/crates/starknet-devnet-core/src/contract_class_choice.rs index 1e67e2ff0..7d49118ed 100644 --- a/crates/starknet-devnet-core/src/contract_class_choice.rs +++ b/crates/starknet-devnet-core/src/contract_class_choice.rs @@ -6,6 +6,7 @@ use starknet_types::contract_class::deprecated::json_contract_class::Cairo0Json; use starknet_types::contract_class::{Cairo0ContractClass, ContractClass}; use starknet_types::traits::HashProducer; +use crate::account::AccountType; use crate::constants::{CAIRO_0_ACCOUNT_CONTRACT, CAIRO_1_ACCOUNT_CONTRACT_SIERRA}; use crate::error::DevnetResult; @@ -25,7 +26,7 @@ impl AccountContractClassChoice { AccountClassWrapper { class_hash: contract_class.generate_hash()?, contract_class: ContractClass::Cairo0(contract_class), - class_metadata: "OpenZeppelin 0.5.1", + account_type: AccountType::OpenZeppelin0_5_1, } } AccountContractClassChoice::Cairo1 => { @@ -35,7 +36,7 @@ impl AccountContractClassChoice { AccountClassWrapper { class_hash: contract_class.generate_hash()?, contract_class, - class_metadata: "OpenZeppelin 0.20.0", + account_type: AccountType::OpenZeppelin0_20_0, } } }) @@ -45,7 +46,7 @@ impl AccountContractClassChoice { pub struct AccountClassWrapper { pub contract_class: ContractClass, pub class_hash: Felt, - pub class_metadata: &'static str, + pub account_type: AccountType, } impl FromStr for AccountClassWrapper { @@ -81,7 +82,7 @@ impl FromStr for AccountClassWrapper { // generate the hash and return let contract_class = ContractClass::Cairo1(contract_class); let class_hash = contract_class.generate_hash()?; - Ok(Self { contract_class, class_hash, class_metadata: "Custom" }) + Ok(Self { contract_class, class_hash, account_type: AccountType::Custom }) } } @@ -103,11 +104,11 @@ mod tests { #[test] fn all_methods_work_with_all_options() { for implementation in AccountContractClassChoice::value_variants().iter() { - let AccountClassWrapper { contract_class, class_hash, class_metadata } = + let AccountClassWrapper { contract_class, class_hash, account_type } = implementation.get_class_wrapper().unwrap(); let generated_hash = contract_class.generate_hash().unwrap(); assert_eq!(generated_hash, class_hash); - assert!(class_metadata.starts_with("OpenZeppelin")); + assert!(account_type.to_string().starts_with("OpenZeppelin")); } } @@ -127,16 +128,24 @@ mod tests { #[test] fn correct_metadata() { assert_eq!( - AccountContractClassChoice::Cairo0.get_class_wrapper().unwrap().class_metadata, + AccountContractClassChoice::Cairo0 + .get_class_wrapper() + .unwrap() + .account_type + .to_string(), "OpenZeppelin 0.5.1" ); assert_eq!( - AccountContractClassChoice::Cairo1.get_class_wrapper().unwrap().class_metadata, + AccountContractClassChoice::Cairo1 + .get_class_wrapper() + .unwrap() + .account_type + .to_string(), "OpenZeppelin 0.20.0" ); let custom_class = AccountClassWrapper::from_str(CAIRO_1_ACCOUNT_CONTRACT_SIERRA_PATH).unwrap(); - assert_eq!(custom_class.class_metadata, "Custom"); + assert_eq!(custom_class.account_type.to_string(), "Custom"); } } diff --git a/crates/starknet-devnet-core/src/predeployed_accounts.rs b/crates/starknet-devnet-core/src/predeployed_accounts.rs index 395fafe40..015dcfd12 100644 --- a/crates/starknet-devnet-core/src/predeployed_accounts.rs +++ b/crates/starknet-devnet-core/src/predeployed_accounts.rs @@ -1,10 +1,12 @@ +use blockifier::context::BlockContext; +use starknet_rs_core::types::Felt; use starknet_rs_signers::SigningKey; use starknet_types::contract_address::ContractAddress; use starknet_types::contract_class::ContractClass; use starknet_types::felt::{ClassHash, Key}; use starknet_types::rpc::state::Balance; -use crate::account::{Account, KeyPair}; +use crate::account::{Account, AccountType, KeyPair}; use crate::error::DevnetResult; use crate::traits::AccountGenerator; use crate::utils::random_number_generator::generate_u128_random_numbers; @@ -15,7 +17,9 @@ pub(crate) struct PredeployedAccounts { initial_balance: Balance, eth_fee_token_address: ContractAddress, strk_fee_token_address: ContractAddress, + account_type: AccountType, accounts: Vec, + chain_id: Felt, } impl PredeployedAccounts { @@ -24,6 +28,8 @@ impl PredeployedAccounts { initial_balance: Balance, eth_fee_token_address: ContractAddress, strk_fee_token_address: ContractAddress, + account_type: AccountType, + chain_id: Felt, ) -> Self { Self { seed, @@ -31,6 +37,8 @@ impl PredeployedAccounts { eth_fee_token_address, strk_fee_token_address, accounts: Vec::new(), + account_type, + chain_id, } } } @@ -52,12 +60,12 @@ impl PredeployedAccounts { impl AccountGenerator for PredeployedAccounts { type Acc = Account; - fn generate_accounts( &mut self, number_of_accounts: u8, class_hash: ClassHash, contract_class: &ContractClass, + block_context: BlockContext, ) -> DevnetResult<&Vec> { let private_keys = self.generate_private_keys(number_of_accounts); @@ -66,10 +74,12 @@ impl AccountGenerator for PredeployedAccounts { self.initial_balance.clone(), KeyPair { public_key: self.generate_public_key(&private_key), private_key }, class_hash, - "Custom", contract_class.clone(), self.eth_fee_token_address, self.strk_fee_token_address, + block_context.clone(), + self.account_type, + self.chain_id, )?; self.accounts.push(account); } @@ -81,6 +91,7 @@ impl AccountGenerator for PredeployedAccounts { #[cfg(test)] mod tests { use rand::{Rng, thread_rng}; + use starknet_rs_core::types::Felt; use starknet_types::rpc::state::Balance; use crate::predeployed_accounts::PredeployedAccounts; @@ -96,6 +107,8 @@ mod tests { Balance::from(1_u8), dummy_contract_address(), dummy_contract_address(), + crate::account::AccountType::Custom, + Felt::ZERO, ) .generate_private_keys(1)[0]; @@ -104,6 +117,8 @@ mod tests { Balance::from(1_u8), dummy_contract_address(), dummy_contract_address(), + crate::account::AccountType::Custom, + Felt::ZERO, ) .generate_private_keys(1)[0]; @@ -133,6 +148,8 @@ mod tests { Balance::from(1_u8), dummy_contract_address(), dummy_contract_address(), + crate::account::AccountType::Custom, + Felt::ZERO, ) .generate_private_keys(1)[0]; @@ -141,6 +158,8 @@ mod tests { Balance::from(1_u8), dummy_contract_address(), dummy_contract_address(), + crate::account::AccountType::Custom, + Felt::ZERO, ) .generate_private_keys(1)[0]; diff --git a/crates/starknet-devnet-core/src/starknet/add_declare_transaction.rs b/crates/starknet-devnet-core/src/starknet/add_declare_transaction.rs index 9ccbe4e7b..668ae11e4 100644 --- a/crates/starknet-devnet-core/src/starknet/add_declare_transaction.rs +++ b/crates/starknet-devnet-core/src/starknet/add_declare_transaction.rs @@ -219,7 +219,7 @@ mod tests { let declare_tx = broadcasted_declare_tx_v3_of_dummy_class( sender.account_address, - Felt::ZERO, + Felt::ONE, resource_bounds_with_price_1(0, 1000, 1e9 as u64), ); @@ -249,7 +249,7 @@ mod tests { let declare_tx = broadcasted_declare_tx_v3_of_dummy_class( sender.account_address, - Felt::ZERO, + Felt::ONE, resource_bounds_with_price_1(0, 1, 1), ); @@ -267,7 +267,7 @@ mod tests { let declare_tx = broadcasted_declare_tx_v3_of_dummy_class( sender.account_address, - Felt::ZERO, + Felt::ONE, resource_bounds_with_price_1(0, 1000, 1e9 as u64), ); @@ -285,7 +285,7 @@ mod tests { let declare_tx = broadcasted_declare_tx_v3_of_dummy_class( sender.account_address, - Felt::ZERO, + Felt::ONE, resource_bounds_with_price_1(0, 1000, 1e9 as u64), ); diff --git a/crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs b/crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs index 3c66a907f..051d9fa86 100644 --- a/crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs +++ b/crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs @@ -124,7 +124,7 @@ mod tests { #[test] fn invoke_transaction_v3_successful_execution_with_only_l1_gas() { let (mut starknet, account, contract_address, increase_balance_selector, _) = setup(); - let account_address = account.get_address(); + let account_address = account.account_address; let initial_balance = account.get_balance(&mut starknet.pending_state, FeeToken::STRK).unwrap(); @@ -133,7 +133,7 @@ mod tests { contract_address, increase_balance_selector, &[Felt::from(10)], - 0, // nonce + 1, // nonce resource_bounds_with_price_1(biguint_to_u64(&initial_balance), 0, 0), ); @@ -152,7 +152,7 @@ mod tests { #[test] fn invoke_transaction_v3_successful_execution_with_all_three_gas_bounds() { let (mut starknet, account, contract_address, increase_balance_selector, _) = setup(); - let account_address = account.get_address(); + let account_address = account.account_address; let initial_balance = account.get_balance(&mut starknet.pending_state, FeeToken::STRK).unwrap(); @@ -164,7 +164,7 @@ mod tests { contract_address, increase_balance_selector, &[Felt::from(10)], - 0, + 1, resource_bounds_with_price_1(gas_amount, gas_amount, gas_amount), ); @@ -183,7 +183,7 @@ mod tests { #[test] fn invoke_transaction_v3_with_invalid_gas_amounts() { let (mut starknet, account, contract_address, increase_balance_selector, _) = setup(); - let account_address = account.get_address(); + let account_address = account.account_address; let balance: u64 = account .get_balance(&mut starknet.pending_state, FeeToken::STRK) @@ -229,7 +229,7 @@ mod tests { let blockifier_address = contract_address.try_into().unwrap(); let storage_key = (*balance_var_storage_address.get_storage_key()).try_into().unwrap(); - let account_address = account.get_address(); + let account_address = account.account_address; let resource_bounds = resource_bounds_with_price_1(0, 1000, 1e6 as u64); let invoke_transaction = test_invoke_transaction_v3( @@ -237,7 +237,7 @@ mod tests { contract_address, increase_balance_selector, &[Felt::from(10)], - 0, // nonce + 1, // nonce resource_bounds.clone(), ); @@ -258,7 +258,7 @@ mod tests { contract_address, increase_balance_selector, &[Felt::from(15)], - 1, // nonce + 2, // nonce resource_bounds, ); @@ -298,9 +298,9 @@ mod tests { fn invoke_transaction_should_return_an_error_if_same_nonce_supplied() { let (mut starknet, account, contract_address, increase_balance_selector, _) = setup(); - let account_address = account.get_address(); + let account_address = account.account_address; - let nonce = 0; + let nonce = 1; let tx = test_invoke_transaction_v3( account_address, contract_address, @@ -314,7 +314,6 @@ mod tests { let transaction = starknet.transactions.get_by_hash_mut(&transaction_hash).unwrap(); assert_eq!(transaction.finality_status, TransactionFinalityStatus::AcceptedOnL2); assert_eq!(transaction.execution_result.status(), TransactionExecutionStatus::Succeeded); - match starknet.add_invoke_transaction(tx) { Err(Error::TransactionValidationError( TransactionValidationError::InvalidTransactionNonce, @@ -327,10 +326,10 @@ mod tests { fn nonce_should_be_incremented_if_invoke_reverted() { let (mut starknet, account, contract_address, increase_balance_selector, _) = setup(); - let account_address = account.get_address().try_into().unwrap(); + let account_address = account.account_address.try_into().unwrap(); let initial_nonce = starknet.pending_state.get_nonce_at(account_address).unwrap().0.try_into().unwrap(); - assert_eq!(initial_nonce, 0); + assert_eq!(initial_nonce, 1); let tx = test_invoke_transaction_v3( account_address.into(), @@ -348,7 +347,7 @@ mod tests { assert_eq!(transaction.execution_result.status(), TransactionExecutionStatus::Reverted); let nonce_after_reverted = starknet.pending_state.get_nonce_at(account_address).unwrap(); - assert_eq!(nonce_after_reverted, Nonce(Felt::ONE)); + assert_eq!(nonce_after_reverted, Nonce(Felt::TWO)); } /// Initialize starknet object with: erc20 contract, account contract and simple contract that @@ -375,15 +374,17 @@ mod tests { Balance::from(1000000000_u32), dummy_key_pair(), account_without_validations_class_hash, - "Custom", ContractClass::Cairo0(account_without_validations_contract_class), - eth_erc_20_contract.get_address(), - strk_erc_20_contract.get_address(), + eth_erc_20_contract.address, + strk_erc_20_contract.address, + starknet.block_context.clone(), + crate::account::AccountType::Custom, + starknet.chain_id().to_felt(), ) .unwrap(); account.deploy(&mut starknet.pending_state).unwrap(); - + starknet.pending_state.commit_diff(0).unwrap(); // dummy contract let dummy_contract = dummy_cairo_0_contract_class(); diff --git a/crates/starknet-devnet-core/src/starknet/add_l1_handler_transaction.rs b/crates/starknet-devnet-core/src/starknet/add_l1_handler_transaction.rs index 28510284c..61a30cb77 100644 --- a/crates/starknet-devnet-core/src/starknet/add_l1_handler_transaction.rs +++ b/crates/starknet-devnet-core/src/starknet/add_l1_handler_transaction.rs @@ -212,10 +212,12 @@ mod tests { Balance::from(10000_u32), dummy_key_pair(), account_without_validations_class_hash, - "Custom", ContractClass::Cairo0(account_without_validations_contract_class), - eth_erc_20_contract.get_address(), - strk_erc_20_contract.get_address(), + eth_erc_20_contract.address, + strk_erc_20_contract.address, + starknet.block_context.clone(), + crate::account::AccountType::Custom, + starknet.chain_id().to_felt(), ) .unwrap(); @@ -278,7 +280,7 @@ mod tests { ( starknet, - account.get_address(), + account.account_address, dummy_contract_address, deposit_selector, withdraw_selector, diff --git a/crates/starknet-devnet-core/src/starknet/get_class_impls.rs b/crates/starknet-devnet-core/src/starknet/get_class_impls.rs index d1319f58f..8955e9a8c 100644 --- a/crates/starknet-devnet-core/src/starknet/get_class_impls.rs +++ b/crates/starknet-devnet-core/src/starknet/get_class_impls.rs @@ -93,7 +93,7 @@ mod tests { let declare_txn = broadcasted_declare_tx_v3_of_dummy_class( account.account_address, - Felt::ZERO, + Felt::ONE, resource_bounds_with_price_1(0, 1000, 1e9 as u64), ); diff --git a/crates/starknet-devnet-core/src/starknet/mod.rs b/crates/starknet-devnet-core/src/starknet/mod.rs index 64bc0f042..18d0dff70 100644 --- a/crates/starknet-devnet-core/src/starknet/mod.rs +++ b/crates/starknet-devnet-core/src/starknet/mod.rs @@ -12,6 +12,7 @@ use blockifier::transaction::transactions::ExecutableTransaction; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use ethers::types::H256; use parking_lot::RwLock; +use predeployed::Predeployer; use starknet_api::block::{ BlockInfo, BlockNumber, BlockStatus, BlockTimestamp, FeeType, GasPrice, GasPricePerToken, GasPriceVector, GasPrices, @@ -59,27 +60,23 @@ use tracing::{error, info}; use self::cheats::Cheats; use self::defaulter::StarknetDefaulter; -use self::predeployed::initialize_erc20_at_address; use self::starknet_config::{StarknetConfig, StateArchiveCapacity}; use self::transaction_trace::create_trace; use crate::account::Account; use crate::blocks::{StarknetBlock, StarknetBlocks}; use crate::constants::{ - ARGENT_CONTRACT_CLASS_HASH, ARGENT_CONTRACT_SIERRA, ARGENT_MULTISIG_CONTRACT_CLASS_HASH, - ARGENT_MULTISIG_CONTRACT_SIERRA, CHARGEABLE_ACCOUNT_ADDRESS, CHARGEABLE_ACCOUNT_PRIVATE_KEY, + CHARGEABLE_ACCOUNT_ADDRESS, CHARGEABLE_ACCOUNT_PRIVATE_KEY, DEVNET_DEFAULT_CHAIN_ID, DEVNET_DEFAULT_L1_DATA_GAS_PRICE, DEVNET_DEFAULT_L1_GAS_PRICE, DEVNET_DEFAULT_L2_GAS_PRICE, DEVNET_DEFAULT_STARTING_BLOCK_NUMBER, - ENTRYPOINT_NOT_FOUND_ERROR_ENCODED, ETH_ERC20_CONTRACT_ADDRESS, ETH_ERC20_NAME, - ETH_ERC20_SYMBOL, STRK_ERC20_CONTRACT_ADDRESS, STRK_ERC20_NAME, STRK_ERC20_SYMBOL, USE_KZG_DA, + ENTRYPOINT_NOT_FOUND_ERROR_ENCODED, ETH_ERC20_CONTRACT_ADDRESS, STRK_ERC20_CONTRACT_ADDRESS, USE_KZG_DA, }; -use crate::contract_class_choice::AccountContractClassChoice; use crate::error::{ContractExecutionError, DevnetResult, Error, TransactionValidationError}; use crate::messaging::MessagingBroker; use crate::nonzero_gas_price; use crate::predeployed_accounts::PredeployedAccounts; use crate::state::state_diff::StateDiff; -use crate::state::{CommittedClassStorage, CustomState, CustomStateReader, StarknetState}; -use crate::traits::{AccountGenerator, Deployed, HashIdentified, HashIdentifiedMut}; +use crate::state::{CommittedClassStorage, CustomStateReader, StarknetState}; +use crate::traits::{HashIdentified, HashIdentifiedMut}; use crate::transactions::{StarknetTransaction, StarknetTransactions}; use crate::utils::{custom_bouncer_config, get_versioned_constants, maybe_extract_failure_reason}; @@ -103,7 +100,7 @@ pub struct Starknet { /// Contains the diff since the last block pending_state_diff: StateDiff, predeployed_accounts: PredeployedAccounts, - pub(in crate::starknet) block_context: BlockContext, + pub(crate) block_context: BlockContext, // To avoid repeating some logic related to blocks, // having `blocks` public allows to re-use functions like `get_blocks()`. pub(crate) blocks: StarknetBlocks, @@ -160,83 +157,7 @@ impl Starknet { pub fn new(config: &StarknetConfig) -> DevnetResult { let defaulter = StarknetDefaulter::new(config.fork_config.clone()); let rpc_contract_classes = Arc::new(RwLock::new(CommittedClassStorage::default())); - let mut state = StarknetState::new(defaulter, rpc_contract_classes.clone()); - - // predeclare account classes eligible for predeployment - for account_class_choice in - [AccountContractClassChoice::Cairo0, AccountContractClassChoice::Cairo1] - { - let class_wrapper = account_class_choice.get_class_wrapper()?; - state.predeclare_contract_class( - class_wrapper.class_hash, - class_wrapper.contract_class, - )?; - } - - // predeclare argent account classes (not predeployable) - if config.predeclare_argent { - for (class_hash, raw_sierra) in [ - (ARGENT_CONTRACT_CLASS_HASH, ARGENT_CONTRACT_SIERRA), - (ARGENT_MULTISIG_CONTRACT_CLASS_HASH, ARGENT_MULTISIG_CONTRACT_SIERRA), - ] { - let contract_class = - ContractClass::Cairo1(ContractClass::cairo_1_from_sierra_json_str(raw_sierra)?); - state.predeclare_contract_class(class_hash, contract_class)?; - } - } - - // deploy udc, eth erc20 and strk erc20 contracts - let eth_erc20_fee_contract = predeployed::create_erc20_at_address_extended( - ETH_ERC20_CONTRACT_ADDRESS, - config.eth_erc20_class_hash, - &config.eth_erc20_contract_class, - )?; - let strk_erc20_fee_contract = predeployed::create_erc20_at_address_extended( - STRK_ERC20_CONTRACT_ADDRESS, - config.strk_erc20_class_hash, - &config.strk_erc20_contract_class, - )?; - - let udc_contract = predeployed::create_udc()?; - udc_contract.deploy(&mut state)?; - - eth_erc20_fee_contract.deploy(&mut state)?; - initialize_erc20_at_address( - &mut state, - ETH_ERC20_CONTRACT_ADDRESS, - ETH_ERC20_NAME, - ETH_ERC20_SYMBOL, - )?; - - strk_erc20_fee_contract.deploy(&mut state)?; - initialize_erc20_at_address( - &mut state, - STRK_ERC20_CONTRACT_ADDRESS, - STRK_ERC20_NAME, - STRK_ERC20_SYMBOL, - )?; - - let mut predeployed_accounts = PredeployedAccounts::new( - config.seed, - config.predeployed_accounts_initial_balance.clone(), - eth_erc20_fee_contract.get_address(), - strk_erc20_fee_contract.get_address(), - ); - - let accounts = predeployed_accounts.generate_accounts( - config.total_accounts, - config.account_contract_class_hash, - &config.account_contract_class, - )?; - for account in accounts { - account.deploy(&mut state)?; - } - - let chargeable_account = Account::new_chargeable( - eth_erc20_fee_contract.get_address(), - strk_erc20_fee_contract.get_address(), - )?; - chargeable_account.deploy(&mut state)?; + let state = StarknetState::new(defaulter, rpc_contract_classes.clone()); // when forking, the number of the first new block to be mined is equal to the last origin // block (the one specified by the user) plus one. @@ -245,25 +166,35 @@ impl Starknet { config.fork_config.block_number.map_or(DEVNET_DEFAULT_STARTING_BLOCK_NUMBER, |n| n + 1); let last_block_hash = config.fork_config.block_hash; - let pending_state_diff = state.commit_diff(starting_block_number)?; + let block_context = Self::init_block_context( + config.gas_price_wei, + config.gas_price_fri, + config.data_gas_price_wei, + config.data_gas_price_fri, + config.l2_gas_price_wei, + config.l2_gas_price_fri, + ETH_ERC20_CONTRACT_ADDRESS, + STRK_ERC20_CONTRACT_ADDRESS, + config.chain_id, + starting_block_number, + ); + + let mut predeployer = Predeployer::new(block_context.clone(), config, state)?; + + predeployer + .deploy_eth_fee_token()? + .deploy_strk_fee_token()? + .deploy_udc()? + .deploy_accounts()?; + + let pending_state_diff = predeployer.state.commit_diff(starting_block_number)?; let mut this = Self { latest_state: Default::default(), // temporary - overwritten on genesis block creation - pending_state: state, + pending_state: predeployer.state, pending_state_diff, - predeployed_accounts, - block_context: Self::init_block_context( - config.gas_price_wei, - config.gas_price_fri, - config.data_gas_price_wei, - config.data_gas_price_fri, - config.l2_gas_price_wei, - config.l2_gas_price_fri, - ETH_ERC20_CONTRACT_ADDRESS, - STRK_ERC20_CONTRACT_ADDRESS, - config.chain_id, - starting_block_number, - ), + predeployed_accounts: predeployer.predeployed_accounts, + block_context, blocks: StarknetBlocks::new(starting_block_number, last_block_hash), transactions: StarknetTransactions::default(), config: config.clone(), @@ -1563,10 +1494,12 @@ mod tests { Balance::from(acc_balance), dummy_key_pair(), account_class.generate_hash().unwrap(), - "Custom", account_class.into(), starknet.block_context.chain_info().fee_token_addresses.eth_fee_token_address.into(), starknet.block_context.chain_info().fee_token_addresses.strk_fee_token_address.into(), + starknet.block_context.clone(), + crate::account::AccountType::Custom, + starknet.chain_id().to_felt(), ) .unwrap(); acc.deploy(&mut starknet.pending_state).unwrap(); diff --git a/crates/starknet-devnet-core/src/starknet/predeployed.rs b/crates/starknet-devnet-core/src/starknet/predeployed.rs index f2b61f189..f54397d86 100644 --- a/crates/starknet-devnet-core/src/starknet/predeployed.rs +++ b/crates/starknet-devnet-core/src/starknet/predeployed.rs @@ -1,17 +1,155 @@ +use blockifier::context::BlockContext; use blockifier::state::state_api::State; use starknet_rs_core::types::Felt; use starknet_rs_core::utils::cairo_short_string_to_felt; use starknet_types::contract_address::ContractAddress; +use starknet_types::contract_class::ContractClass; use starknet_types::felt::felt_from_prefixed_hex; +use super::starknet_config::StarknetConfig; +use crate::account::Account; use crate::constants::{ - CHARGEABLE_ACCOUNT_ADDRESS, UDC_CONTRACT, UDC_CONTRACT_ADDRESS, UDC_CONTRACT_CLASS_HASH, + ARGENT_CONTRACT_CLASS_HASH, ARGENT_CONTRACT_SIERRA, ARGENT_MULTISIG_CONTRACT_CLASS_HASH, + ARGENT_MULTISIG_CONTRACT_SIERRA, CHARGEABLE_ACCOUNT_ADDRESS, ETH_ERC20_CONTRACT_ADDRESS, + ETH_ERC20_NAME, ETH_ERC20_SYMBOL, STRK_ERC20_CONTRACT_ADDRESS, STRK_ERC20_NAME, + STRK_ERC20_SYMBOL, UDC_CONTRACT, UDC_CONTRACT_ADDRESS, UDC_CONTRACT_CLASS_HASH, }; +use crate::contract_class_choice::AccountContractClassChoice; use crate::error::{DevnetResult, Error}; -use crate::state::StarknetState; +use crate::predeployed_accounts::PredeployedAccounts; +use crate::state::{CustomState, StarknetState}; use crate::system_contract::SystemContract; +use crate::traits::{AccountGenerator, Deployed}; use crate::utils::get_storage_var_address; +pub(crate) struct Predeployer<'a> { + pub(crate) state: StarknetState, + pub(crate) predeployed_accounts: PredeployedAccounts, + block_context: BlockContext, + config: &'a StarknetConfig, + eth_fee_token_address: Felt, + strk_fee_token_address: Felt, + chain_id: Felt, +} +impl<'a> Predeployer<'a> { + pub(crate) fn new( + block_context: BlockContext, + config: &'a StarknetConfig, + state: StarknetState, + ) -> DevnetResult { + let chain_id = config.chain_id.to_felt(); + let predeployed_accounts = PredeployedAccounts::new( + config.seed, + config.predeployed_accounts_initial_balance.clone(), + ContractAddress::new(ETH_ERC20_CONTRACT_ADDRESS)?, + ContractAddress::new(STRK_ERC20_CONTRACT_ADDRESS)?, + config.account_type, + chain_id, + ); + + Ok(Self { + block_context, + config, + eth_fee_token_address: ETH_ERC20_CONTRACT_ADDRESS, + strk_fee_token_address: STRK_ERC20_CONTRACT_ADDRESS, + state, + predeployed_accounts, + chain_id, + }) + } + + pub(crate) fn deploy_eth_fee_token(&mut self) -> DevnetResult<&mut Self> { + let eth_erc20_fee_contract = create_erc20_at_address_extended( + self.eth_fee_token_address, + self.config.eth_erc20_class_hash, + &self.config.eth_erc20_contract_class, + )?; + + eth_erc20_fee_contract.deploy(&mut self.state)?; + + initialize_erc20_at_address( + &mut self.state, + ETH_ERC20_CONTRACT_ADDRESS, + ETH_ERC20_NAME, + ETH_ERC20_SYMBOL, + )?; + + Ok(self) + } + + pub(crate) fn deploy_strk_fee_token(&mut self) -> DevnetResult<&mut Self> { + let strk_erc20_fee_contract = create_erc20_at_address_extended( + self.strk_fee_token_address, + self.config.strk_erc20_class_hash, + &self.config.strk_erc20_contract_class, + )?; + + strk_erc20_fee_contract.deploy(&mut self.state)?; + + initialize_erc20_at_address( + &mut self.state, + STRK_ERC20_CONTRACT_ADDRESS, + STRK_ERC20_NAME, + STRK_ERC20_SYMBOL, + )?; + + Ok(self) + } + + pub(crate) fn deploy_udc(&mut self) -> DevnetResult<&mut Self> { + let udc_contract = create_udc()?; + udc_contract.deploy(&mut self.state)?; + + Ok(self) + } + + pub(crate) fn deploy_accounts(&mut self) -> DevnetResult<&mut Self> { + for account_class_choice in + [AccountContractClassChoice::Cairo0, AccountContractClassChoice::Cairo1] + { + let class_wrapper = account_class_choice.get_class_wrapper()?; + self.state.predeclare_contract_class( + class_wrapper.class_hash, + class_wrapper.contract_class, + )?; + } + + if self.config.predeclare_argent { + for (class_hash, raw_sierra) in [ + (ARGENT_CONTRACT_CLASS_HASH, ARGENT_CONTRACT_SIERRA), + (ARGENT_MULTISIG_CONTRACT_CLASS_HASH, ARGENT_MULTISIG_CONTRACT_SIERRA), + ] { + let contract_class = + ContractClass::Cairo1(ContractClass::cairo_1_from_sierra_json_str(raw_sierra)?); + self.state.predeclare_contract_class(class_hash, contract_class)?; + } + } + + let eth_fee_token_address = ContractAddress::new(self.eth_fee_token_address)?; + let strk_fee_token_address = ContractAddress::new(self.strk_fee_token_address)?; + + let accounts = self.predeployed_accounts.generate_accounts( + self.config.total_accounts, + self.config.account_contract_class_hash, + &self.config.account_contract_class, + self.block_context.clone(), + )?; + for account in accounts { + account.deploy(&mut self.state)?; + } + + let chargeable_account = Account::new_chargeable( + eth_fee_token_address, + strk_fee_token_address, + self.block_context.clone(), + self.chain_id, + )?; + chargeable_account.deploy(&mut self.state)?; + + Ok(self) + } +} + pub(crate) fn create_erc20_at_address_extended( contract_address: Felt, class_hash: Felt, diff --git a/crates/starknet-devnet-core/src/starknet/starknet_config.rs b/crates/starknet-devnet-core/src/starknet/starknet_config.rs index f67c6f244..ab8774f51 100644 --- a/crates/starknet-devnet-core/src/starknet/starknet_config.rs +++ b/crates/starknet-devnet-core/src/starknet/starknet_config.rs @@ -9,6 +9,7 @@ use starknet_types::rpc::state::Balance; use starknet_types::traits::HashProducer; use url::Url; +use crate::account::AccountType; use crate::constants::{ CAIRO_1_ACCOUNT_CONTRACT_SIERRA, CAIRO_1_ERC20_CONTRACT, CAIRO_1_ERC20_CONTRACT_CLASS_HASH, DEVNET_DEFAULT_CHAIN_ID, DEVNET_DEFAULT_INITIAL_BALANCE, DEVNET_DEFAULT_L1_DATA_GAS_PRICE, @@ -103,6 +104,8 @@ pub struct StarknetConfig { #[serde(skip_serializing)] pub account_contract_class: ContractClass, pub account_contract_class_hash: Felt, + #[serde(skip_serializing)] + pub account_type: AccountType, #[serde(serialize_with = "serialize_initial_balance")] pub predeployed_accounts_initial_balance: Balance, pub start_time: Option, @@ -173,6 +176,7 @@ impl Default for StarknetConfig { eth_erc20_contract_class: CAIRO_1_ERC20_CONTRACT.to_string(), strk_erc20_contract_class: CAIRO_1_ERC20_CONTRACT.to_string(), predeclare_argent: false, + account_type: AccountType::OpenZeppelin0_20_0, } } } diff --git a/crates/starknet-devnet-core/src/starknet/state_update.rs b/crates/starknet-devnet-core/src/starknet/state_update.rs index a843560f2..cdcd72797 100644 --- a/crates/starknet-devnet-core/src/starknet/state_update.rs +++ b/crates/starknet-devnet-core/src/starknet/state_update.rs @@ -42,7 +42,7 @@ mod tests { let declare_txn = broadcasted_declare_tx_v3( acc.account_address, - Felt::ZERO, + Felt::ONE, contract_class.clone(), compiled_class_hash, resource_bounds_with_price_1(0, 1000, 1e9 as u64), @@ -66,7 +66,7 @@ mod tests { class_hash: sierra_class_hash, compiled_class_hash, }], - nonces: vec![ContractNonce { contract_address: acc.account_address, nonce: Felt::ONE }], + nonces: vec![ContractNonce { contract_address: acc.account_address, nonce: Felt::TWO }], ..Default::default() }; diff --git a/crates/starknet-devnet-core/src/state/state_diff.rs b/crates/starknet-devnet-core/src/state/state_diff.rs index 40d08ea5f..56952b1a8 100644 --- a/crates/starknet-devnet-core/src/state/state_diff.rs +++ b/crates/starknet-devnet-core/src/state/state_diff.rs @@ -285,10 +285,12 @@ mod tests { Balance::from(u128::MAX), dummy_key_pair(), account_without_validations_class_hash, - "Custom", ContractClass::Cairo0(account_without_validations_contract_class), ContractAddress::new(ETH_ERC20_CONTRACT_ADDRESS).unwrap(), ContractAddress::new(STRK_ERC20_CONTRACT_ADDRESS).unwrap(), + starknet.block_context.clone(), + crate::account::AccountType::Custom, + starknet.chain_id().to_felt(), ) .unwrap(); @@ -310,7 +312,7 @@ mod tests { .unwrap(); for (contract_class, nonce) in - [(replaceable_contract.clone(), 0), (replacing_contract.clone(), 1)] + [(replaceable_contract.clone(), 1), (replacing_contract.clone(), 2)] { let compiled_class_hash = compile_sierra_contract(&contract_class).unwrap().compiled_class_hash(); @@ -346,7 +348,7 @@ mod tests { replaceable_contract_address, get_selector_from_name("test_replace_class").unwrap(), &[new_class_hash], - 2, // nonce + 3, // nonce resource_bounds_with_price_1(0, 1000, 1e7 as u64), ); diff --git a/crates/starknet-devnet-core/src/system_contract.rs b/crates/starknet-devnet-core/src/system_contract.rs index 3b0510056..2af614133 100644 --- a/crates/starknet-devnet-core/src/system_contract.rs +++ b/crates/starknet-devnet-core/src/system_contract.rs @@ -13,7 +13,7 @@ use crate::traits::{Accounted, Deployed}; pub(crate) struct SystemContract { class_hash: ClassHash, - address: ContractAddress, + pub(crate) address: ContractAddress, contract_class: ContractClass, } @@ -50,10 +50,6 @@ impl Deployed for SystemContract { state.predeploy_contract(self.address, self.class_hash)?; Ok(()) } - - fn get_address(&self) -> ContractAddress { - self.address - } } impl Accounted for SystemContract { diff --git a/crates/starknet-devnet-core/src/traits.rs b/crates/starknet-devnet-core/src/traits.rs index 4e2a6dd44..38d1ca45a 100644 --- a/crates/starknet-devnet-core/src/traits.rs +++ b/crates/starknet-devnet-core/src/traits.rs @@ -1,5 +1,5 @@ +use blockifier::context::BlockContext; use blockifier::state::state_api::StateReader; -use starknet_types::contract_address::ContractAddress; use starknet_types::contract_class::ContractClass; use starknet_types::felt::ClassHash; use starknet_types::rpc::state::Balance; @@ -27,7 +27,6 @@ pub trait HashIdentifiedMut { pub(crate) trait Deployed { fn deploy(&self, state: &mut StarknetState) -> DevnetResult<()>; - fn get_address(&self) -> ContractAddress; /// `class_hash` is sierra hash for cairo1 contracts fn declare_if_undeclared( &self, @@ -68,5 +67,6 @@ pub trait AccountGenerator { number_of_accounts: u8, class_hash: ClassHash, contract_class: &ContractClass, + block_context: BlockContext, ) -> DevnetResult<&Vec>; }