From f2cfd9dd58d12bee51e7154675d5639a3ea05954 Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Sat, 21 Oct 2023 08:02:18 +0000 Subject: [PATCH] feat: support account transaction simulation --- starknet-accounts/src/account/declaration.rs | 117 ++++++++++++++++++- starknet-accounts/src/account/execution.rs | 63 +++++++++- starknet-accounts/src/factory/mod.rs | 66 ++++++++++- starknet-contract/src/factory.rs | 12 +- 4 files changed, 252 insertions(+), 6 deletions(-) diff --git a/starknet-accounts/src/account/declaration.rs b/starknet-accounts/src/account/declaration.rs index 1d60bcd6..2af89a48 100644 --- a/starknet-accounts/src/account/declaration.rs +++ b/starknet-accounts/src/account/declaration.rs @@ -10,7 +10,7 @@ use starknet_core::{ contract::{legacy::LegacyContractClass, ComputeClassHashError}, BroadcastedDeclareTransaction, BroadcastedDeclareTransactionV1, BroadcastedDeclareTransactionV2, BroadcastedTransaction, DeclareTransactionResult, - FeeEstimate, FieldElement, FlattenedSierraClass, + FeeEstimate, FieldElement, FlattenedSierraClass, SimulatedTransaction, SimulationFlag, }, }; use starknet_providers::Provider; @@ -115,6 +115,26 @@ where self.estimate_fee_with_nonce(nonce).await } + pub async fn simulate( + &self, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + // Resolves nonce + let nonce = match self.nonce { + Some(value) => value, + None => self + .account + .get_nonce() + .await + .map_err(AccountError::Provider)?, + }; + + self.simulate_with_nonce(nonce, skip_validate, skip_fee_charge) + .await + } + pub async fn send( &self, ) -> Result< @@ -184,6 +204,44 @@ where .await .map_err(AccountError::Provider) } + + async fn simulate_with_nonce( + &self, + nonce: FieldElement, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + let prepared = PreparedDeclaration { + account: self.account, + inner: RawDeclaration { + contract_class: self.contract_class.clone(), + compiled_class_hash: self.compiled_class_hash, + nonce, + max_fee: self.max_fee.unwrap_or_default(), + }, + }; + let declare = prepared.get_declare_request(true).await?; + + let mut flags = vec![]; + + if skip_validate { + flags.push(SimulationFlag::SkipValidate); + } + if skip_fee_charge { + flags.push(SimulationFlag::SkipFeeCharge); + } + + self.account + .provider() + .simulate_transaction( + self.account.block_id(), + BroadcastedTransaction::Declare(BroadcastedDeclareTransaction::V2(declare)), + &flags, + ) + .await + .map_err(AccountError::Provider) + } } impl<'a, A> LegacyDeclaration<'a, A> { @@ -256,6 +314,26 @@ where self.estimate_fee_with_nonce(nonce).await } + pub async fn simulate( + &self, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + // Resolves nonce + let nonce = match self.nonce { + Some(value) => value, + None => self + .account + .get_nonce() + .await + .map_err(AccountError::Provider)?, + }; + + self.simulate_with_nonce(nonce, skip_validate, skip_fee_charge) + .await + } + pub async fn send( &self, ) -> Result< @@ -323,6 +401,43 @@ where .await .map_err(AccountError::Provider) } + + async fn simulate_with_nonce( + &self, + nonce: FieldElement, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + let prepared = PreparedLegacyDeclaration { + account: self.account, + inner: RawLegacyDeclaration { + contract_class: self.contract_class.clone(), + nonce, + max_fee: self.max_fee.unwrap_or_default(), + }, + }; + let declare = prepared.get_declare_request(true).await?; + + let mut flags = vec![]; + + if skip_validate { + flags.push(SimulationFlag::SkipValidate); + } + if skip_fee_charge { + flags.push(SimulationFlag::SkipFeeCharge); + } + + self.account + .provider() + .simulate_transaction( + self.account.block_id(), + BroadcastedTransaction::Declare(BroadcastedDeclareTransaction::V1(declare)), + &flags, + ) + .await + .map_err(AccountError::Provider) + } } impl RawDeclaration { diff --git a/starknet-accounts/src/account/execution.rs b/starknet-accounts/src/account/execution.rs index 736308c2..5a6abbda 100644 --- a/starknet-accounts/src/account/execution.rs +++ b/starknet-accounts/src/account/execution.rs @@ -8,7 +8,7 @@ use starknet_core::{ crypto::compute_hash_on_elements, types::{ BroadcastedInvokeTransaction, BroadcastedTransaction, FeeEstimate, FieldElement, - InvokeTransactionResult, + InvokeTransactionResult, SimulatedTransaction, SimulationFlag, }, }; use starknet_providers::Provider; @@ -98,8 +98,25 @@ where self.estimate_fee_with_nonce(nonce).await } - // The `simulate` function is temporarily removed until it's supported in [Provider] - // TODO: add `simulate` back once transaction simulation in supported + pub async fn simulate( + &self, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + // Resolves nonce + let nonce = match self.nonce { + Some(value) => value, + None => self + .account + .get_nonce() + .await + .map_err(AccountError::Provider)?, + }; + + self.simulate_with_nonce(nonce, skip_validate, skip_fee_charge) + .await + } pub async fn send( &self, @@ -169,6 +186,46 @@ where .await .map_err(AccountError::Provider) } + + async fn simulate_with_nonce( + &self, + nonce: FieldElement, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + let prepared = PreparedExecution { + account: self.account, + inner: RawExecution { + calls: self.calls.clone(), + nonce, + max_fee: self.max_fee.unwrap_or_default(), + }, + }; + let invoke = prepared + .get_invoke_request(true) + .await + .map_err(AccountError::Signing)?; + + let mut flags = vec![]; + + if skip_validate { + flags.push(SimulationFlag::SkipValidate); + } + if skip_fee_charge { + flags.push(SimulationFlag::SkipFeeCharge); + } + + self.account + .provider() + .simulate_transaction( + self.account.block_id(), + BroadcastedTransaction::Invoke(invoke), + &flags, + ) + .await + .map_err(AccountError::Provider) + } } impl RawExecution { diff --git a/starknet-accounts/src/factory/mod.rs b/starknet-accounts/src/factory/mod.rs index e86746f7..5561c655 100644 --- a/starknet-accounts/src/factory/mod.rs +++ b/starknet-accounts/src/factory/mod.rs @@ -5,7 +5,8 @@ use starknet_core::{ crypto::compute_hash_on_elements, types::{ BlockId, BlockTag, BroadcastedDeployAccountTransaction, BroadcastedTransaction, - DeployAccountTransactionResult, FeeEstimate, FieldElement, StarknetError, + DeployAccountTransactionResult, FeeEstimate, FieldElement, SimulatedTransaction, + SimulationFlag, StarknetError, }, }; use starknet_providers::{ @@ -203,6 +204,27 @@ where self.estimate_fee_with_nonce(nonce).await } + pub async fn simulate( + &self, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result< + SimulatedTransaction, + AccountFactoryError::Error>, + > { + // Resolves nonce + let nonce = match self.nonce { + Some(value) => value, + None => self + .fetch_nonce() + .await + .map_err(AccountFactoryError::Provider)?, + }; + + self.simulate_with_nonce(nonce, skip_validate, skip_fee_charge) + .await + } + pub async fn send( &self, ) -> Result< @@ -273,6 +295,48 @@ where .await .map_err(AccountFactoryError::Provider) } + + async fn simulate_with_nonce( + &self, + nonce: FieldElement, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result< + SimulatedTransaction, + AccountFactoryError::Error>, + > { + let prepared = PreparedAccountDeployment { + factory: self.factory, + inner: RawAccountDeployment { + salt: self.salt, + nonce, + max_fee: self.max_fee.unwrap_or_default(), + }, + }; + let deploy = prepared + .get_deploy_request() + .await + .map_err(AccountFactoryError::Signing)?; + + let mut flags = vec![]; + + if skip_validate { + flags.push(SimulationFlag::SkipValidate); + } + if skip_fee_charge { + flags.push(SimulationFlag::SkipFeeCharge); + } + + self.factory + .provider() + .simulate_transaction( + self.factory.block_id(), + BroadcastedTransaction::DeployAccount(deploy), + &flags, + ) + .await + .map_err(AccountFactoryError::Provider) + } } impl<'f, F> PreparedAccountDeployment<'f, F> { diff --git a/starknet-contract/src/factory.rs b/starknet-contract/src/factory.rs index 5b82f6f8..d57b1381 100644 --- a/starknet-contract/src/factory.rs +++ b/starknet-contract/src/factory.rs @@ -1,6 +1,6 @@ use starknet_accounts::{Account, AccountError, Call, ConnectedAccount, Execution}; use starknet_core::{ - types::{FeeEstimate, FieldElement, InvokeTransactionResult}, + types::{FeeEstimate, FieldElement, InvokeTransactionResult, SimulatedTransaction}, utils::{get_udc_deployed_address, UdcUniqueSettings, UdcUniqueness}, }; use starknet_providers::Provider; @@ -130,6 +130,16 @@ where execution.estimate_fee().await } + pub async fn simulate( + &self, + skip_validate: bool, + skip_fee_charge: bool, + ) -> Result::Error>> + { + let execution: Execution = self.into(); + execution.simulate(skip_validate, skip_fee_charge).await + } + pub async fn send( &self, ) -> Result::Error>>