Skip to content

Commit

Permalink
feat: support account transaction simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
xJonathanLEI committed Oct 22, 2023
1 parent 26f5c55 commit f2cfd9d
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 6 deletions.
117 changes: 116 additions & 1 deletion starknet-accounts/src/account/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::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<
Expand Down Expand Up @@ -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<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::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> {
Expand Down Expand Up @@ -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<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::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<
Expand Down Expand Up @@ -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<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::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 {
Expand Down
63 changes: 60 additions & 3 deletions starknet-accounts/src/account/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::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,
Expand Down Expand Up @@ -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<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::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 {
Expand Down
66 changes: 65 additions & 1 deletion starknet-accounts/src/factory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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<F::SignError, <F::Provider as Provider>::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<
Expand Down Expand Up @@ -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<F::SignError, <F::Provider as Provider>::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> {
Expand Down
12 changes: 11 additions & 1 deletion starknet-contract/src/factory.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -130,6 +130,16 @@ where
execution.estimate_fee().await
}

pub async fn simulate(
&self,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
let execution: Execution<A> = self.into();
execution.simulate(skip_validate, skip_fee_charge).await
}

pub async fn send(
&self,
) -> Result<InvokeTransactionResult, AccountError<A::SignError, <A::Provider as Provider>::Error>>
Expand Down

0 comments on commit f2cfd9d

Please sign in to comment.