diff --git a/crates/sncast/src/helpers/fee.rs b/crates/sncast/src/helpers/fee.rs index cbfae691c0..ddf78fa1c8 100644 --- a/crates/sncast/src/helpers/fee.rs +++ b/crates/sncast/src/helpers/fee.rs @@ -169,6 +169,22 @@ pub enum FeeSettings { }, } +impl From for FeeSettings { + fn from(value: ScriptFeeSettings) -> Self { + match value { + ScriptFeeSettings::Eth { max_fee } => FeeSettings::Eth { max_fee }, + ScriptFeeSettings::Strk { + max_gas, + max_gas_unit_price, + .. + } => FeeSettings::Strk { + max_gas, + max_gas_unit_price, + }, + } + } +} + pub trait PayableTransaction { fn error_message(&self, token: &str, version: &str) -> String; fn validate(&self) -> Result<()>; diff --git a/crates/sncast/src/lib.rs b/crates/sncast/src/lib.rs index 1ef493b6ea..ded59fa225 100644 --- a/crates/sncast/src/lib.rs +++ b/crates/sncast/src/lib.rs @@ -266,6 +266,19 @@ pub async fn get_account<'a>( Ok(account) } +pub async fn get_contract_class( + class_hash: Felt, + provider: &JsonRpcClient, +) -> Result { + provider + .get_class(BlockId::Tag(BlockTag::Latest), class_hash) + .await + .map_err(handle_rpc_error) + .context(format!( + "Couldn't retrieve contract class with hash: {class_hash:#x}" + )) +} + async fn build_account( account_data: AccountData, chain_id: Felt, diff --git a/crates/sncast/src/main.rs b/crates/sncast/src/main.rs index b4a0554605..264ca5b00e 100644 --- a/crates/sncast/src/main.rs +++ b/crates/sncast/src/main.rs @@ -23,6 +23,7 @@ use sncast::{ chain_id_to_network_name, get_account, get_block_id, get_chain_id, get_default_state_file_name, NumbersFormat, ValidatedWaitParams, WaitForTx, }; +use starknet::accounts::ConnectedAccount; use starknet::core::utils::get_selector_from_name; use starknet::providers::Provider; use starknet_commands::account::list::print_account_list; @@ -221,9 +222,19 @@ async fn run_async_command( } Commands::Deploy(deploy) => { - let provider = deploy.rpc.get_provider(&config).await?; - deploy.validate()?; + + let fee_token = deploy.token_from_version(); + + let Deploy { + constructor_calldata, + fee_args, + rpc, + .. + } = deploy; + + let provider = rpc.get_provider(&config).await?; + let account = get_account( &config.account, &config.accounts_file, @@ -232,9 +243,24 @@ async fn run_async_command( ) .await?; - let result = starknet_commands::deploy::deploy(deploy, &account, wait_config) - .await - .map_err(handle_starknet_command_error); + let fee_settings = fee_args + .clone() + .fee_token(fee_token) + .try_into_fee_settings(&provider, account.block_id()) + .await?; + + let result = starknet_commands::deploy::deploy( + deploy.class_hash, + &constructor_calldata, + deploy.salt, + deploy.unique, + fee_settings, + deploy.nonce, + &account, + wait_config, + ) + .await + .map_err(handle_starknet_command_error); print_command_result("deploy", &result, numbers_format, output_format)?; print_block_explorer_link_if_allowed( @@ -247,16 +273,24 @@ async fn run_async_command( Ok(()) } - Commands::Call(call) => { - let provider = call.rpc.get_provider(&config).await?; + Commands::Call(Call { + contract_address, + function, + calldata, + block_id, + rpc, + }) => { + let provider = rpc.get_provider(&config).await?; + + let block_id = get_block_id(&block_id)?; - let block_id = get_block_id(&call.block_id)?; + let entry_point_selector = get_selector_from_name(&function) + .context("Failed to convert entry point selector to FieldElement")?; let result = starknet_commands::call::call( - call.contract_address, - get_selector_from_name(&call.function) - .context("Failed to convert entry point selector to FieldElement")?, - call.calldata, + contract_address, + entry_point_selector, + calldata, &provider, block_id.as_ref(), ) @@ -268,10 +302,22 @@ async fn run_async_command( } Commands::Invoke(invoke) => { - let provider = invoke.rpc.get_provider(&config).await?; - invoke.validate()?; + let fee_token = invoke.token_from_version(); + + let Invoke { + contract_address, + function, + calldata, + fee_args, + rpc, + nonce, + .. + } = invoke; + + let provider = rpc.get_provider(&config).await?; + let account = get_account( &config.account, &config.accounts_file, @@ -279,10 +325,18 @@ async fn run_async_command( config.keystore, ) .await?; + + let fee_args = fee_args.fee_token(fee_token); + + let selector = get_selector_from_name(&function) + .context("Failed to convert entry point selector to FieldElement")?; + let result = starknet_commands::invoke::invoke( - invoke.clone(), - get_selector_from_name(&invoke.function) - .context("Failed to convert entry point selector to FieldElement")?, + contract_address, + calldata, + nonce, + fee_args, + selector, &account, wait_config, ) diff --git a/crates/sncast/src/starknet_commands/account/import.rs b/crates/sncast/src/starknet_commands/account/import.rs index 66d4fd3e11..fb8bcb72d5 100644 --- a/crates/sncast/src/starknet_commands/account/import.rs +++ b/crates/sncast/src/starknet_commands/account/import.rs @@ -7,13 +7,16 @@ use camino::Utf8PathBuf; use clap::Args; use conversions::string::{TryFromDecStr, TryFromHexStr}; use regex::Regex; +use sncast::check_if_legacy_contract; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::RpcArgs; use sncast::response::structs::AccountImportResponse; -use sncast::{check_class_hash_exists, get_chain_id, AccountType as SNCastAccountType}; -use sncast::{check_if_legacy_contract, get_class_hash_by_address}; -use starknet::core::types::Felt; +use sncast::{ + check_class_hash_exists, get_chain_id, handle_rpc_error, AccountType as SNCastAccountType, +}; +use starknet::core::types::{BlockId, BlockTag, Felt, StarknetError}; use starknet::providers::jsonrpc::{HttpTransport, JsonRpcClient}; +use starknet::providers::{Provider, ProviderError}; use starknet::signers::SigningKey; use super::deploy::compute_account_address; @@ -77,7 +80,15 @@ pub async fn import( }; let private_key = &SigningKey::from_secret_scalar(*private_key); - let fetched_class_hash = get_class_hash_by_address(provider, import.address).await?; + let fetched_class_hash = match provider + .get_class_hash_at(BlockId::Tag(BlockTag::Pending), import.address) + .await + { + Ok(class_hash) => Ok(Some(class_hash)), + Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => Ok(None), + Err(err) => Err(handle_rpc_error(err)), + }?; + let deployed: bool = fetched_class_hash.is_some(); let class_hash = if let (Some(from_provider), Some(from_user)) = (fetched_class_hash, import.class_hash) diff --git a/crates/sncast/src/starknet_commands/deploy.rs b/crates/sncast/src/starknet_commands/deploy.rs index b2a5ab96ce..8b4e1dd370 100644 --- a/crates/sncast/src/starknet_commands/deploy.rs +++ b/crates/sncast/src/starknet_commands/deploy.rs @@ -61,29 +61,27 @@ impl_payable_transaction!(Deploy, token_not_supported_for_deployment, DeployVersion::V3 => FeeToken::Strk ); +#[allow(clippy::ptr_arg, clippy::too_many_arguments)] pub async fn deploy( - deploy: Deploy, + class_hash: Felt, + calldata: &Vec, + salt: Option, + unique: bool, + fee_settings: FeeSettings, + nonce: Option, account: &SingleOwnerAccount<&JsonRpcClient, LocalWallet>, wait_config: WaitForTx, ) -> Result { - let fee_settings = deploy - .fee_args - .clone() - .fee_token(deploy.token_from_version()) - .try_into_fee_settings(account.provider(), account.block_id()) - .await?; - - let salt = extract_or_generate_salt(deploy.salt); - let factory = ContractFactory::new(deploy.class_hash, account); + let salt = extract_or_generate_salt(salt); + let factory = ContractFactory::new(class_hash, account); let result = match fee_settings { FeeSettings::Eth { max_fee } => { - let execution = - factory.deploy_v1(deploy.constructor_calldata.clone(), salt, deploy.unique); + let execution = factory.deploy_v1(calldata.clone(), salt, unique); let execution = match max_fee { None => execution, Some(max_fee) => execution.max_fee(max_fee), }; - let execution = match deploy.nonce { + let execution = match nonce { None => execution, Some(nonce) => execution.nonce(nonce), }; @@ -93,8 +91,7 @@ pub async fn deploy( max_gas, max_gas_unit_price, } => { - let execution = - factory.deploy_v3(deploy.constructor_calldata.clone(), salt, deploy.unique); + let execution = factory.deploy_v3(calldata.clone(), salt, unique); let execution = match max_gas { None => execution, @@ -104,7 +101,7 @@ pub async fn deploy( None => execution, Some(max_gas_unit_price) => execution.gas_price(max_gas_unit_price), }; - let execution = match deploy.nonce { + let execution = match nonce { None => execution, Some(nonce) => execution.nonce(nonce), }; @@ -119,9 +116,9 @@ pub async fn deploy( DeployResponse { contract_address: get_udc_deployed_address( salt, - deploy.class_hash, - &udc_uniqueness(deploy.unique, account.address()), - &deploy.constructor_calldata, + class_hash, + &udc_uniqueness(unique, account.address()), + calldata, ), transaction_hash: result.transaction_hash, }, diff --git a/crates/sncast/src/starknet_commands/invoke.rs b/crates/sncast/src/starknet_commands/invoke.rs index 1a1742dc0b..7f0c18dce6 100644 --- a/crates/sncast/src/starknet_commands/invoke.rs +++ b/crates/sncast/src/starknet_commands/invoke.rs @@ -55,23 +55,21 @@ impl_payable_transaction!(Invoke, token_not_supported_for_invoke, ); pub async fn invoke( - invoke: Invoke, + contract_address: Felt, + calldata: Vec, + nonce: Option, + fee_args: FeeArgs, function_selector: Felt, account: &SingleOwnerAccount<&JsonRpcClient, LocalWallet>, wait_config: WaitForTx, ) -> Result { - let fee_args = invoke - .fee_args - .clone() - .fee_token(invoke.token_from_version()); - let call = Call { - to: invoke.contract_address, + to: contract_address, selector: function_selector, - calldata: invoke.calldata.clone(), + calldata, }; - execute_calls(account, vec![call], fee_args, invoke.nonce, wait_config).await + execute_calls(account, vec![call], fee_args, nonce, wait_config).await } pub async fn execute_calls( diff --git a/crates/sncast/src/starknet_commands/script/run.rs b/crates/sncast/src/starknet_commands/script/run.rs index ce7220afe3..b004c37139 100644 --- a/crates/sncast/src/starknet_commands/script/run.rs +++ b/crates/sncast/src/starknet_commands/script/run.rs @@ -1,6 +1,4 @@ use crate::starknet_commands::declare::Declare; -use crate::starknet_commands::deploy::Deploy; -use crate::starknet_commands::invoke::Invoke; use crate::starknet_commands::{call, declare, deploy, invoke, tx_status}; use crate::{get_account, WaitForTx}; use anyhow::{anyhow, Context, Result}; @@ -37,7 +35,7 @@ use shared::utils::build_readable_text; use sncast::get_nonce; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::SCRIPT_LIB_ARTIFACT_NAME; -use sncast::helpers::fee::ScriptFeeSettings; +use sncast::helpers::fee::{FeeSettings, ScriptFeeSettings}; use sncast::helpers::rpc::RpcArgs; use sncast::response::structs::ScriptRunResponse; use sncast::state::hashing::{ @@ -45,6 +43,7 @@ use sncast::state::hashing::{ }; use sncast::state::state_file::StateManager; use starknet::accounts::{Account, SingleOwnerAccount}; +use starknet::core::types::Felt; use starknet::core::types::{BlockId, BlockTag::Pending}; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; @@ -156,25 +155,14 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> { } "deploy" => { let class_hash = input_reader.read()?; - let constructor_calldata = input_reader.read()?; + let constructor_calldata = input_reader.read::>()?; let salt = input_reader.read()?; let unique = input_reader.read()?; - let fee_args = input_reader.read::()?.into(); + let fee_args: FeeSettings = input_reader.read::()?.into(); let nonce = input_reader.read()?; - let deploy = Deploy { - class_hash, - constructor_calldata, - salt, - unique, - fee_args, - nonce, - version: None, - rpc: RpcArgs::default(), - }; - let deploy_tx_id = - generate_deploy_tx_id(class_hash, &deploy.constructor_calldata, salt, unique); + generate_deploy_tx_id(class_hash, &constructor_calldata, salt, unique); if let Some(success_output) = self.state.get_output_if_success(deploy_tx_id.as_str()) @@ -183,7 +171,12 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> { } let deploy_result = self.tokio_runtime.block_on(deploy::deploy( - deploy, + class_hash, + &constructor_calldata, + salt, + unique, + fee_args, + nonce, self.account()?, WaitForTx { wait: true, @@ -206,16 +199,6 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> { let fee_args = input_reader.read::()?.into(); let nonce = input_reader.read()?; - let invoke = Invoke { - contract_address, - function: String::new(), - calldata: calldata.clone(), - fee_args, - nonce, - version: None, - rpc: RpcArgs::default(), - }; - let invoke_tx_id = generate_invoke_tx_id(contract_address, function_selector, &calldata); @@ -226,7 +209,10 @@ impl<'a> ExtensionLogic for CastScriptExtension<'a> { } let invoke_result = self.tokio_runtime.block_on(invoke::invoke( - invoke, + contract_address, + calldata, + nonce, + fee_args, function_selector, self.account()?, WaitForTx {