diff --git a/starknet-accounts/src/account/declaration.rs b/starknet-accounts/src/account/declaration.rs index d8d2de4e..8efe9b59 100644 --- a/starknet-accounts/src/account/declaration.rs +++ b/starknet-accounts/src/account/declaration.rs @@ -52,6 +52,10 @@ const QUERY_VERSION_THREE: Felt = Felt::from_raw([ ]); impl<'a, A> DeclarationV2<'a, A> { + /// Constructs a new [`DeclarationV2`]. + /// + /// Users would typically use [`declare_v2`](fn.declare_v2) on an [`Account`] instead of + /// directly calling this method. pub const fn new( contract_class: Arc, compiled_class_hash: Felt, @@ -67,6 +71,7 @@ impl<'a, A> DeclarationV2<'a, A> { } } + /// Returns a new [`DeclarationV2`] with the `nonce`. pub fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -74,6 +79,7 @@ impl<'a, A> DeclarationV2<'a, A> { } } + /// Returns a new [`DeclarationV2`] with the `max_fee`. pub fn max_fee(self, max_fee: Felt) -> Self { Self { max_fee: Some(max_fee), @@ -81,6 +87,9 @@ impl<'a, A> DeclarationV2<'a, A> { } } + /// Returns a new [`DeclarationV2`] with the fee estimate multiplier. The multiplier is used + /// when transaction fee is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn fee_estimate_multiplier(self, fee_estimate_multiplier: f64) -> Self { Self { fee_estimate_multiplier, @@ -110,6 +119,7 @@ impl<'a, A> DeclarationV2<'a, A> where A: ConnectedAccount + Sync, { + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -124,6 +134,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -143,6 +155,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { self.prepare().await?.send().await } @@ -275,6 +288,10 @@ where } impl<'a, A> DeclarationV3<'a, A> { + /// Constructs a new [`DeclarationV3`]. + /// + /// Users would typically use [`declare_v3`](fn.declare_v3) on an [`Account`] instead of + /// directly calling this method. pub const fn new( contract_class: Arc, compiled_class_hash: Felt, @@ -292,6 +309,7 @@ impl<'a, A> DeclarationV3<'a, A> { } } + /// Returns a new [`DeclarationV3`] with the `nonce`. pub fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -299,6 +317,7 @@ impl<'a, A> DeclarationV3<'a, A> { } } + /// Returns a new [`DeclarationV3`] with the `gas`. pub fn gas(self, gas: u64) -> Self { Self { gas: Some(gas), @@ -306,6 +325,7 @@ impl<'a, A> DeclarationV3<'a, A> { } } + /// Returns a new [`DeclarationV3`] with the `gas_price`. pub fn gas_price(self, gas_price: u128) -> Self { Self { gas_price: Some(gas_price), @@ -313,6 +333,9 @@ impl<'a, A> DeclarationV3<'a, A> { } } + /// Returns a new [`DeclarationV3`] with the gas amount estimate multiplier. The multiplier is + /// used when the gas amount is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn gas_estimate_multiplier(self, gas_estimate_multiplier: f64) -> Self { Self { gas_estimate_multiplier, @@ -320,6 +343,9 @@ impl<'a, A> DeclarationV3<'a, A> { } } + /// Returns a new [`DeclarationV3`] with the gas price estimate multiplier. The multiplier is + /// used when the gas price is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn gas_price_estimate_multiplier(self, gas_price_estimate_multiplier: f64) -> Self { Self { gas_price_estimate_multiplier, @@ -351,6 +377,7 @@ impl<'a, A> DeclarationV3<'a, A> where A: ConnectedAccount + Sync, { + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -365,6 +392,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -384,6 +413,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { self.prepare().await?.send().await } @@ -570,6 +600,10 @@ where } impl<'a, A> LegacyDeclaration<'a, A> { + /// Constructs a new [`LegacyDeclaration`]. + /// + /// Users would typically use [`declare_legacy`](fn.declare_legacy) on an [`Account`] instead of + /// directly calling this method. pub const fn new(contract_class: Arc, account: &'a A) -> Self { Self { account, @@ -580,6 +614,7 @@ impl<'a, A> LegacyDeclaration<'a, A> { } } + /// Returns a new [`LegacyDeclaration`] with the `nonce`. pub fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -587,6 +622,7 @@ impl<'a, A> LegacyDeclaration<'a, A> { } } + /// Returns a new [`LegacyDeclaration`] with the `max_fee`. pub fn max_fee(self, max_fee: Felt) -> Self { Self { max_fee: Some(max_fee), @@ -594,6 +630,9 @@ impl<'a, A> LegacyDeclaration<'a, A> { } } + /// Returns a new [`LegacyDeclaration`] with the fee estimate multiplier. The multiplier is used + /// when transaction fee is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn fee_estimate_multiplier(self, fee_estimate_multiplier: f64) -> Self { Self { fee_estimate_multiplier, @@ -623,6 +662,7 @@ impl<'a, A> LegacyDeclaration<'a, A> where A: ConnectedAccount + Sync, { + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -637,6 +677,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -656,6 +698,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { self.prepare().await?.send().await } @@ -787,6 +830,7 @@ where } impl RawDeclarationV2 { + /// Calculates transaction hash given `chain_id`, `address`, and `query_only`. pub fn transaction_hash(&self, chain_id: Felt, address: Felt, query_only: bool) -> Felt { compute_hash_on_elements(&[ PREFIX_DECLARE, @@ -805,24 +849,29 @@ impl RawDeclarationV2 { ]) } + /// Gets a reference to the flattened Sierra (Cairo 1) class being declared. pub fn contract_class(&self) -> &FlattenedSierraClass { &self.contract_class } + /// Gets the CASM class hash corresponding to the Sierra class being declared. pub const fn compiled_class_hash(&self) -> Felt { self.compiled_class_hash } + /// Gets the `nonce` of the declaration request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `max_fee` of the declaration request. pub const fn max_fee(&self) -> Felt { self.max_fee } } impl RawDeclarationV3 { + /// Calculates transaction hash given `chain_id`, `address`, and `query_only`. pub fn transaction_hash(&self, chain_id: Felt, address: Felt, query_only: bool) -> Felt { let mut hasher = PoseidonHasher::new(); @@ -876,28 +925,34 @@ impl RawDeclarationV3 { hasher.finalize() } + /// Gets a reference to the flattened Sierra (Cairo 1) class being declared. pub fn contract_class(&self) -> &FlattenedSierraClass { &self.contract_class } + /// Gets the CASM class hash corresponding to the Sierra class being declared. pub const fn compiled_class_hash(&self) -> Felt { self.compiled_class_hash } + /// Gets the `nonce` of the declaration request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `gas` of the declaration request. pub const fn gas(&self) -> u64 { self.gas } + /// Gets the `gas_price` of the declaration request. pub const fn gas_price(&self) -> u128 { self.gas_price } } impl RawLegacyDeclaration { + /// Calculates transaction hash given `chain_id`, `address`, and `query_only`. pub fn transaction_hash( &self, chain_id: Felt, @@ -920,14 +975,17 @@ impl RawLegacyDeclaration { ])) } + /// Gets a reference to the legacy (Cairo 0) class being declared. pub fn contract_class(&self) -> &LegacyContractClass { &self.contract_class } + /// Gets the `nonce` of the declaration request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `max_fee` of the declaration request. pub const fn max_fee(&self) -> Felt { self.max_fee } @@ -949,6 +1007,7 @@ impl<'a, A> PreparedDeclarationV2<'a, A> where A: ConnectedAccount, { + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { let tx_request = self.get_declare_request(false, false).await?; self.account @@ -998,6 +1057,7 @@ impl<'a, A> PreparedDeclarationV3<'a, A> where A: ConnectedAccount, { + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { let tx_request = self.get_declare_request(false, false).await?; self.account @@ -1066,6 +1126,7 @@ impl<'a, A> PreparedLegacyDeclaration<'a, A> where A: ConnectedAccount, { + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { let tx_request = self.get_declare_request(false, false).await?; self.account diff --git a/starknet-accounts/src/account/execution.rs b/starknet-accounts/src/account/execution.rs index 3e529a45..b96ee505 100644 --- a/starknet-accounts/src/account/execution.rs +++ b/starknet-accounts/src/account/execution.rs @@ -41,6 +41,10 @@ const QUERY_VERSION_THREE: Felt = Felt::from_raw([ ]); impl<'a, A> ExecutionV1<'a, A> { + /// Constructs a new [`ExecutionV1`]. + /// + /// Users would typically use [`execute_v1`](fn.execute_v1) on an [`Account`] instead of + /// directly calling this method. pub const fn new(calls: Vec, account: &'a A) -> Self { Self { account, @@ -51,6 +55,7 @@ impl<'a, A> ExecutionV1<'a, A> { } } + /// Returns a new [`ExecutionV1`] with the `nonce`. pub fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -58,6 +63,7 @@ impl<'a, A> ExecutionV1<'a, A> { } } + /// Returns a new [`ExecutionV1`] with the `max_fee`. pub fn max_fee(self, max_fee: Felt) -> Self { Self { max_fee: Some(max_fee), @@ -65,6 +71,9 @@ impl<'a, A> ExecutionV1<'a, A> { } } + /// Returns a new [`ExecutionV1`] with the fee estimate multiplier. The multiplier is used + /// when transaction fee is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn fee_estimate_multiplier(self, fee_estimate_multiplier: f64) -> Self { Self { fee_estimate_multiplier, @@ -90,6 +99,10 @@ impl<'a, A> ExecutionV1<'a, A> { } impl<'a, A> ExecutionV3<'a, A> { + /// Constructs a new [`ExecutionV3`]. + /// + /// Users would typically use [`execute_v3`](fn.execute_v3) on an [`Account`] instead of + /// directly calling this method. pub const fn new(calls: Vec, account: &'a A) -> Self { Self { account, @@ -102,6 +115,7 @@ impl<'a, A> ExecutionV3<'a, A> { } } + /// Returns a new [`ExecutionV3`] with the `nonce`. pub fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -109,6 +123,7 @@ impl<'a, A> ExecutionV3<'a, A> { } } + /// Returns a new [`ExecutionV3`] with the `gas`. pub fn gas(self, gas: u64) -> Self { Self { gas: Some(gas), @@ -116,6 +131,7 @@ impl<'a, A> ExecutionV3<'a, A> { } } + /// Returns a new [`ExecutionV3`] with the `gas_price`. pub fn gas_price(self, gas_price: u128) -> Self { Self { gas_price: Some(gas_price), @@ -123,6 +139,9 @@ impl<'a, A> ExecutionV3<'a, A> { } } + /// Returns a new [`ExecutionV3`] with the gas amount estimate multiplier. The multiplier is + /// used when the gas amount is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn gas_estimate_multiplier(self, gas_estimate_multiplier: f64) -> Self { Self { gas_estimate_multiplier, @@ -130,6 +149,9 @@ impl<'a, A> ExecutionV3<'a, A> { } } + /// Returns a new [`ExecutionV3`] with the gas price estimate multiplier. The multiplier is + /// used when the gas price is not manually specified and must be fetched from a [`Provider`] + /// instead. pub fn gas_price_estimate_multiplier(self, gas_price_estimate_multiplier: f64) -> Self { Self { gas_price_estimate_multiplier, @@ -160,6 +182,7 @@ impl<'a, A> ExecutionV1<'a, A> where A: ConnectedAccount + Sync, { + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -174,6 +197,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -193,6 +218,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { self.prepare().await?.send().await } @@ -331,6 +357,7 @@ impl<'a, A> ExecutionV3<'a, A> where A: ConnectedAccount + Sync, { + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -345,6 +372,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -364,6 +393,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { self.prepare().await?.send().await } @@ -553,6 +583,7 @@ where } impl RawExecutionV1 { + /// Calculates transaction hash given `chain_id`, `address`, `query_only`, and `encoder`. pub fn transaction_hash( &self, chain_id: Felt, @@ -579,20 +610,24 @@ impl RawExecutionV1 { ]) } + /// Gets a reference to the list of contract calls included in the execution. pub fn calls(&self) -> &[Call] { &self.calls } + /// Gets the `nonce` of the execution request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `max_fee` of the execution request. pub const fn max_fee(&self) -> Felt { self.max_fee } } impl RawExecutionV3 { + /// Calculates transaction hash given `chain_id`, `address`, `query_only`, and `encoder`. pub fn transaction_hash( &self, chain_id: Felt, @@ -663,18 +698,22 @@ impl RawExecutionV3 { hasher.finalize() } + /// Gets a reference to the list of contract calls included in the execution. pub fn calls(&self) -> &[Call] { &self.calls } + /// Gets the `nonce` of the execution request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `gas` of the execution request. pub const fn gas(&self) -> u64 { self.gas } + /// Gets the `gas_price` of the execution request. pub const fn gas_price(&self) -> u128 { self.gas_price } @@ -716,6 +755,7 @@ impl<'a, A> PreparedExecutionV1<'a, A> where A: ConnectedAccount, { + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { let tx_request = self .get_invoke_request(false, false) @@ -757,6 +797,7 @@ impl<'a, A> PreparedExecutionV3<'a, A> where A: ConnectedAccount, { + /// Signs and broadcasts the transaction to the network. pub async fn send(&self) -> Result> { let tx_request = self .get_invoke_request(false, false) diff --git a/starknet-accounts/src/account/mod.rs b/starknet-accounts/src/account/mod.rs index 5bb2db3e..a3e19f54 100644 --- a/starknet-accounts/src/account/mod.rs +++ b/starknet-accounts/src/account/mod.rs @@ -19,36 +19,64 @@ mod execution; #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait Account: ExecutionEncoder + Sized { + /// Possible errors for signing transactions. type SignError: Error + Send + Sync; + /// Gets the account contract's address. fn address(&self) -> Felt; + /// Gets the chain ID of the network where the account contract was deployed. fn chain_id(&self) -> Felt; + /// Signs an execution request to authorize an `INVOKE` v1 transaction that pays transaction + /// fees in `ETH`. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_execution_v1( &self, execution: &RawExecutionV1, query_only: bool, ) -> Result, Self::SignError>; + /// Signs an execution request to authorize an `INVOKE` v3 transaction that pays transaction + /// fees in `STRK`. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_execution_v3( &self, execution: &RawExecutionV3, query_only: bool, ) -> Result, Self::SignError>; + /// Signs an execution request to authorize an `DECLARE` v2 transaction that pays transaction + /// fees in `ETH` for declaring Cairo 1 classes. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_declaration_v2( &self, declaration: &RawDeclarationV2, query_only: bool, ) -> Result, Self::SignError>; + /// Signs an execution request to authorize an `DECLARE` v3 transaction that pays transaction + /// fees in `STRK` for declaring Cairo 1 classes. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_declaration_v3( &self, declaration: &RawDeclarationV3, query_only: bool, ) -> Result, Self::SignError>; + /// Signs an execution request to authorize an `DECLARE` v1 transaction that pays transaction + /// fees in `ETH` for declaring Cairo 0 classes. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_legacy_declaration( &self, legacy_declaration: &RawLegacyDeclaration, @@ -63,19 +91,40 @@ pub trait Account: ExecutionEncoder + Sized { /// estimation/simulation purposes. fn is_signer_interactive(&self) -> bool; + /// Generates an instance of [`ExecutionV1`] for sending `INVOKE` v1 transactions. Pays + /// transaction fees in `ETH`. fn execute_v1(&self, calls: Vec) -> ExecutionV1<'_, Self> { ExecutionV1::new(calls, self) } + /// Generates an instance of [`ExecutionV3`] for sending `INVOKE` v3 transactions. Pays + /// transaction fees in `STRK`. fn execute_v3(&self, calls: Vec) -> ExecutionV3<'_, Self> { ExecutionV3::new(calls, self) } + /// Generates an instance of [`ExecutionV1`] for sending `INVOKE` v1 transactions. Pays + /// transaction fees in `ETH`. #[deprecated = "use version specific variants (`execute_v1` & `execute_v3`) instead"] fn execute(&self, calls: Vec) -> ExecutionV1<'_, Self> { self.execute_v1(calls) } + /// Generates an instance of [`DeclarationV2`] for sending `DECLARE` v2 transactions. Pays + /// transaction fees in `ETH`. + /// + /// To declare a Sierra (Cairo 1) class, a `compiled_class_hash` must be provided. This can be + /// obtained by compiling the Sierra class to obtain a CASM class, and then hashing it. + /// + /// The compilation of Sierra to CASM can either be done interactively via the + /// `starknet-sierra-compile` command from the Cairo toolchain, or programmatically through the + /// Cairo crates. + /// + /// Hashing the resulting CASM class is supported in the `starknet-core` crate. It can also be + /// done interactively via Starkli with its `starkli class-hash` command. + /// + /// This method is only used for declaring Sierra (Cairo 1) classes. To declare legacy (Cairo 0) + /// classes use [`declare_legacy`](fn.declare_legacy) instead. fn declare_v2( &self, contract_class: Arc, @@ -84,6 +133,21 @@ pub trait Account: ExecutionEncoder + Sized { DeclarationV2::new(contract_class, compiled_class_hash, self) } + /// Generates an instance of [`DeclarationV3`] for sending `DECLARE` v3 transactions. Pays + /// transaction fees in `STRK`. + /// + /// To declare a Sierra (Cairo 1) class, a `compiled_class_hash` must be provided. This can be + /// obtained by compiling the Sierra class to obtain a CASM class, and then hashing it. + /// + /// The compilation of Sierra to CASM can either be done interactively via the + /// `starknet-sierra-compile` command from the Cairo toolchain, or programmatically through the + /// Cairo crates. + /// + /// Hashing the resulting CASM class is supported in the `starknet-core` crate. It can also be + /// done interactively via Starkli with its `starkli class-hash` command. + /// + /// This method is only used for declaring Sierra (Cairo 1) classes. To declare legacy (Cairo 0) + /// classes use [`declare_legacy`](fn.declare_legacy) instead. fn declare_v3( &self, contract_class: Arc, @@ -92,6 +156,21 @@ pub trait Account: ExecutionEncoder + Sized { DeclarationV3::new(contract_class, compiled_class_hash, self) } + /// Generates an instance of [`DeclarationV2`] for sending `DECLARE` v2 transactions. Pays + /// transaction fees in `ETH`. + /// + /// To declare a Sierra (Cairo 1) class, a `compiled_class_hash` must be provided. This can be + /// obtained by compiling the Sierra class to obtain a CASM class, and then hashing it. + /// + /// The compilation of Sierra to CASM can either be done interactively via the + /// `starknet-sierra-compile` command from the Cairo toolchain, or programmatically through the + /// Cairo crates. + /// + /// Hashing the resulting CASM class is supported in the `starknet-core` crate. It can also be + /// done interactively via Starkli with its `starkli class-hash` command. + /// + /// This method is only used for declaring Sierra (Cairo 1) classes. To declare legacy (Cairo 0) + /// classes use [`declare_legacy`](fn.declare_legacy) instead. #[deprecated = "use version specific variants (`declare_v2` & `declare_v3`) instead"] fn declare( &self, @@ -101,6 +180,11 @@ pub trait Account: ExecutionEncoder + Sized { self.declare_v2(contract_class, compiled_class_hash) } + /// Generates an instance of [`LegacyDeclaration`] for sending `DECLARE` v1 transactions. Pays + /// transaction fees in `ETH`. + /// + /// This method is only used for declaring legacy (Cairo 0) classes. To declare Sierra (Cairo 1) + /// classes use [`declare_v2`](fn.declare_v2) or [`declare_v3`](fn.declare_v3) instead. fn declare_legacy( &self, contract_class: Arc, @@ -109,26 +193,35 @@ pub trait Account: ExecutionEncoder + Sized { } } +/// An abstraction over different ways to encode [`Vec`] into [`Vec`]. +/// +/// Standard Cairo 0 and Cairo 1 account contracts encodes calls differently. Custom account +/// contract implementations might also impose arbitrary encoding rules. #[auto_impl(&, Box, Arc)] pub trait ExecutionEncoder { + /// Encodes the list of [`Call`] into a list of [`Felt`] to be used as calldata to the account's + /// `__execute__` entrypoint. fn encode_calls(&self, calls: &[Call]) -> Vec; } -/// An [Account] implementation that also comes with a [Provider]. Functionalities that require a -/// connection to the sequencer or node are offloaded to this trait to keep the base [Account] +/// An [`Account`] implementation that also comes with a [`Provider`]. Functionalities that require +/// a connection to the sequencer or node are offloaded to this trait to keep the base [`Account`] /// clean and flexible. #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait ConnectedAccount: Account { + /// The [`Provider`] type attached to this account. type Provider: Provider + Sync; + /// Gets a reference to the attached [`Provider`] instance. fn provider(&self) -> &Self::Provider; - /// Block ID to use when checking nonce and estimating fees. + /// Gets block ID to use when checking nonce and estimating fees. fn block_id(&self) -> BlockId { BlockId::Tag(BlockTag::Latest) } + /// Gets the next available nonce to be used. async fn get_nonce(&self) -> Result { self.provider() .get_nonce(self.block_id(), self.address()) @@ -294,16 +387,22 @@ pub struct PreparedLegacyDeclaration<'a, A> { inner: RawLegacyDeclaration, } +/// Errors using Starknet accounts. #[derive(Debug, thiserror::Error)] pub enum AccountError { + /// An error is encountered when signing a request. #[error(transparent)] Signing(S), + /// An error is encountered with communicating with the network. #[error(transparent)] Provider(ProviderError), + /// Unable to calculate the class hash for declaration. #[error(transparent)] ClassHashCalculation(ComputeClassHashError), + /// Unable to compress the legacy (Cairo 0) class for declaration. #[error(transparent)] ClassCompression(CompressProgramError), + /// Transaction fee calculation overflow. #[error("fee calculation overflow")] FeeOutOfRange, } diff --git a/starknet-accounts/src/call.rs b/starknet-accounts/src/call.rs index 6d86ed53..0788e011 100644 --- a/starknet-accounts/src/call.rs +++ b/starknet-accounts/src/call.rs @@ -1,8 +1,12 @@ use starknet_core::types::Felt; +/// A contract call as part of a multi-call execution request. #[derive(Debug, Clone)] pub struct Call { + /// Address of the contract being invoked. pub to: Felt, + /// Entrypoint selector of the function being invoked. pub selector: Felt, + /// List of calldata to be sent for the call. pub calldata: Vec, } diff --git a/starknet-accounts/src/factory/argent.rs b/starknet-accounts/src/factory/argent.rs index a8cf6482..23bf3d7f 100644 --- a/starknet-accounts/src/factory/argent.rs +++ b/starknet-accounts/src/factory/argent.rs @@ -8,6 +8,7 @@ use starknet_core::types::{BlockId, BlockTag, Felt}; use starknet_providers::Provider; use starknet_signers::Signer; +/// [`AccountFactory`] implementation for deploying `Argent X` account contracts. #[derive(Debug)] pub struct ArgentAccountFactory { class_hash: Felt, @@ -23,6 +24,7 @@ impl ArgentAccountFactory where S: Signer, { + /// Constructs a new [`ArgentAccountFactory`]. pub async fn new( class_hash: Felt, chain_id: Felt, @@ -42,6 +44,7 @@ where }) } + /// Sets a new block ID to run queries against. pub fn set_block_id(&mut self, block_id: BlockId) -> &Self { self.block_id = block_id; self diff --git a/starknet-accounts/src/factory/mod.rs b/starknet-accounts/src/factory/mod.rs index 17f3d2a2..c72ab017 100644 --- a/starknet-accounts/src/factory/mod.rs +++ b/starknet-accounts/src/factory/mod.rs @@ -58,19 +58,26 @@ const ADDR_BOUND: NonZeroFelt = NonZeroFelt::from_raw([ 18446743986131443745, ]); -/// This trait enables deploying account contracts using the `DeployAccount` transaction type. +/// Abstraction over different ways of deploying account contracts using the `DEPLOY_ACCOUNT` +/// transaction type. #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait AccountFactory: Sized { + /// The [`Provider`] type attached to this account factory. type Provider: Provider + Sync; + /// Possible errors for signing transactions. type SignError: Error + Send + Sync; + /// Gets the class hash of the account contract. fn class_hash(&self) -> Felt; + /// Gets the constructor calldata for the deployment transaction. fn calldata(&self) -> Vec; + /// Gets the chain ID of the target network. fn chain_id(&self) -> Felt; + /// Gets a reference to the attached [`Provider`] instance. fn provider(&self) -> &Self::Provider; /// Whether the underlying signer implementation is interactive, such as a hardware wallet. @@ -86,26 +93,42 @@ pub trait AccountFactory: Sized { BlockId::Tag(BlockTag::Latest) } + /// Signs an execution request to authorize an `DEPLOY_ACCOUNT` v1 transaction that pays + /// transaction fees in `ETH`. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_deployment_v1( &self, deployment: &RawAccountDeploymentV1, query_only: bool, ) -> Result, Self::SignError>; + /// Signs an execution request to authorize an `DEPLOY_ACCOUNT` v3 transaction that pays + /// transaction fees in `STRK`. + /// + /// If `query_only` is `true`, the commitment must be constructed in a way that a real state- + /// changing transaction cannot be authenticated. This is to prevent replay attacks. async fn sign_deployment_v3( &self, deployment: &RawAccountDeploymentV3, query_only: bool, ) -> Result, Self::SignError>; + /// Generates an instance of [`AccountDeploymentV1`] for sending `DEPLOY_ACCOUNT` v1 + /// transactions. Pays transaction fees in `ETH`. fn deploy_v1(&self, salt: Felt) -> AccountDeploymentV1<'_, Self> { AccountDeploymentV1::new(salt, self) } + /// Generates an instance of [`AccountDeploymentV3`] for sending `DEPLOY_ACCOUNT` v3 + /// transactions. Pays transaction fees in `STRK`. fn deploy_v3(&self, salt: Felt) -> AccountDeploymentV3<'_, Self> { AccountDeploymentV3::new(salt, self) } + /// Generates an instance of [`AccountDeploymentV1`] for sending `DEPLOY_ACCOUNT` v1 + /// transactions. Pays transaction fees in `ETH`. #[deprecated = "use version specific variants (`deploy_v1` & `deploy_v3`) instead"] fn deploy(&self, salt: Felt) -> AccountDeploymentV1<'_, Self> { self.deploy_v1(salt) @@ -180,17 +203,25 @@ pub struct PreparedAccountDeploymentV3<'f, F> { inner: RawAccountDeploymentV3, } +/// Errors using Starknet account factories. #[derive(Debug, thiserror::Error)] pub enum AccountFactoryError { + /// An error is encountered when signing a request. #[error(transparent)] Signing(S), + /// An error is encountered with communicating with the network. #[error(transparent)] Provider(ProviderError), + /// Transaction fee calculation overflow. #[error("fee calculation overflow")] FeeOutOfRange, } impl<'f, F> AccountDeploymentV1<'f, F> { + /// Constructs a new [`AccountDeploymentV1`]. + /// + /// Users would typically use [`deploy_v1`](fn.deploy_v1) on an [`AccountFactory`] instead of + /// directly calling this method. pub const fn new(salt: Felt, factory: &'f F) -> Self { Self { factory, @@ -201,6 +232,7 @@ impl<'f, F> AccountDeploymentV1<'f, F> { } } + /// Returns a new [`AccountDeploymentV1`] with the `nonce`. pub const fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -208,6 +240,7 @@ impl<'f, F> AccountDeploymentV1<'f, F> { } } + /// Returns a new [`AccountDeploymentV1`] with the `max_fee`. pub const fn max_fee(self, max_fee: Felt) -> Self { Self { max_fee: Some(max_fee), @@ -215,6 +248,9 @@ impl<'f, F> AccountDeploymentV1<'f, F> { } } + /// Returns a new [`AccountDeploymentV1`] with the fee estimate multiplier. The multiplier is + /// used when transaction fee is not manually specified and must be fetched from a [`Provider`] + /// instead. pub const fn fee_estimate_multiplier(self, fee_estimate_multiplier: f64) -> Self { Self { fee_estimate_multiplier, @@ -241,6 +277,10 @@ impl<'f, F> AccountDeploymentV1<'f, F> { } impl<'f, F> AccountDeploymentV3<'f, F> { + /// Constructs a new [`AccountDeploymentV3`]. + /// + /// Users would typically use [`deploy_v3`](fn.deploy_v3) on an [`AccountFactory`] instead of + /// directly calling this method. pub const fn new(salt: Felt, factory: &'f F) -> Self { Self { factory, @@ -253,6 +293,7 @@ impl<'f, F> AccountDeploymentV3<'f, F> { } } + /// Returns a new [`AccountDeploymentV3`] with the `nonce`. pub const fn nonce(self, nonce: Felt) -> Self { Self { nonce: Some(nonce), @@ -260,6 +301,7 @@ impl<'f, F> AccountDeploymentV3<'f, F> { } } + /// Returns a new [`AccountDeploymentV3`] with the `gas`. pub const fn gas(self, gas: u64) -> Self { Self { gas: Some(gas), @@ -267,6 +309,7 @@ impl<'f, F> AccountDeploymentV3<'f, F> { } } + /// Returns a new [`AccountDeploymentV3`] with the `gas_price`. pub const fn gas_price(self, gas_price: u128) -> Self { Self { gas_price: Some(gas_price), @@ -274,6 +317,9 @@ impl<'f, F> AccountDeploymentV3<'f, F> { } } + /// Returns a new [`AccountDeploymentV3`] with the gas amount estimate multiplier. The + /// multiplier is used when the gas amount is not manually specified and must be fetched from a + /// [`Provider`] instead. pub const fn gas_estimate_multiplier(self, gas_estimate_multiplier: f64) -> Self { Self { gas_estimate_multiplier, @@ -281,6 +327,9 @@ impl<'f, F> AccountDeploymentV3<'f, F> { } } + /// Returns a new [`AccountDeploymentV3`] with the gas price estimate multiplier. The + /// multiplier is used when the gas price is not manually specified and must be fetched from a + /// [`Provider`] instead. pub const fn gas_price_estimate_multiplier(self, gas_price_estimate_multiplier: f64) -> Self { Self { gas_price_estimate_multiplier, @@ -321,6 +370,8 @@ where ) } + /// Fetches the next available nonce from a [`Provider`]. In most cases this would be `0` but + /// it can also be non-zero if previous reverted deployment attempts exist. pub async fn fetch_nonce(&self) -> Result { match self .factory @@ -334,6 +385,7 @@ where } } + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -347,6 +399,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -365,6 +419,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send( &self, ) -> Result> { @@ -520,6 +575,8 @@ where ) } + /// Fetches the next available nonce from a [`Provider`]. In most cases this would be `0` but + /// it can also be non-zero if previous reverted deployment attempts exist. pub async fn fetch_nonce(&self) -> Result { match self .factory @@ -533,6 +590,7 @@ where } } + /// Estimates transaction fees from a [`Provider`]. pub async fn estimate_fee(&self) -> Result> { // Resolves nonce let nonce = match self.nonce { @@ -546,6 +604,8 @@ where self.estimate_fee_with_nonce(nonce).await } + /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can + /// be skipped. pub async fn simulate( &self, skip_validate: bool, @@ -564,6 +624,7 @@ where .await } + /// Signs and broadcasts the transaction to the network. pub async fn send( &self, ) -> Result> { @@ -760,38 +821,47 @@ where } impl RawAccountDeploymentV1 { + /// Gets the `salt` of the deployment request. pub const fn salt(&self) -> Felt { self.salt } + /// Gets the `nonce` of the deployment request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `max_fee` of the deployment request. pub const fn max_fee(&self) -> Felt { self.max_fee } } impl RawAccountDeploymentV3 { + /// Gets the `salt` of the deployment request. pub const fn salt(&self) -> Felt { self.salt } + /// Gets the `nonce` of the deployment request. pub const fn nonce(&self) -> Felt { self.nonce } + /// Gets the `gas` of the deployment request. pub const fn gas(&self) -> u64 { self.gas } + /// Gets the `gas_price` of the deployment request. pub const fn gas_price(&self) -> u128 { self.gas_price } } impl<'f, F> PreparedAccountDeploymentV1<'f, F> { + /// Constructs [`PreparedAccountDeploymentV1`] by attaching a factory to + /// [`RawAccountDeploymentV1`]. pub const fn from_raw(raw_deployment: RawAccountDeploymentV1, factory: &'f F) -> Self { Self { factory, @@ -801,6 +871,8 @@ impl<'f, F> PreparedAccountDeploymentV1<'f, F> { } impl<'f, F> PreparedAccountDeploymentV3<'f, F> { + /// Constructs [`PreparedAccountDeploymentV3`] by attaching a factory to + /// [`RawAccountDeploymentV3`]. pub const fn from_raw(raw_deployment: RawAccountDeploymentV3, factory: &'f F) -> Self { Self { factory, @@ -822,6 +894,7 @@ where ) } + /// Calculates transaction hash given `query_only`. pub fn transaction_hash(&self, query_only: bool) -> Felt { let mut calldata_to_hash = vec![self.factory.class_hash(), self.inner.salt]; calldata_to_hash.append(&mut self.factory.calldata()); @@ -842,6 +915,7 @@ where ]) } + /// Signs and broadcasts the transaction to the network. pub async fn send( &self, ) -> Result> { @@ -892,6 +966,7 @@ where ) } + /// Calculates transaction hash given `query_only`. pub fn transaction_hash(&self, query_only: bool) -> Felt { let mut hasher = PoseidonHasher::new(); @@ -953,6 +1028,7 @@ where hasher.finalize() } + /// Signs and broadcasts the transaction to the network. pub async fn send( &self, ) -> Result> { diff --git a/starknet-accounts/src/factory/open_zeppelin.rs b/starknet-accounts/src/factory/open_zeppelin.rs index 8fa95022..90c32722 100644 --- a/starknet-accounts/src/factory/open_zeppelin.rs +++ b/starknet-accounts/src/factory/open_zeppelin.rs @@ -8,6 +8,7 @@ use starknet_core::types::{BlockId, BlockTag, Felt}; use starknet_providers::Provider; use starknet_signers::Signer; +/// [`AccountFactory`] implementation for deploying `OpenZeppelin` account contracts. #[derive(Debug)] pub struct OpenZeppelinAccountFactory { class_hash: Felt, @@ -22,6 +23,7 @@ impl OpenZeppelinAccountFactory where S: Signer, { + /// Constructs a new [`OpenZeppelinAccountFactory`]. pub async fn new( class_hash: Felt, chain_id: Felt, @@ -39,6 +41,7 @@ where }) } + /// Sets a new block ID to run queries against. pub fn set_block_id(&mut self, block_id: BlockId) -> &Self { self.block_id = block_id; self diff --git a/starknet-accounts/src/lib.rs b/starknet-accounts/src/lib.rs index b0f4483f..5da9d090 100644 --- a/starknet-accounts/src/lib.rs +++ b/starknet-accounts/src/lib.rs @@ -1,3 +1,7 @@ +//! Library for deploying and using Starknet account contracts. + +#![deny(missing_docs)] + mod account; pub use account::{ Account, AccountError, ConnectedAccount, DeclarationV2, DeclarationV3, ExecutionEncoder, @@ -16,9 +20,11 @@ pub use factory::{ PreparedAccountDeploymentV3, RawAccountDeploymentV1, RawAccountDeploymentV3, }; +/// Module containing types for using an account contract with only one signer. pub mod single_owner; pub use single_owner::{ExecutionEncoding, SingleOwnerAccount}; +/// Error when calling `prepared()` on a type when not all fields are populated. #[derive(Debug, thiserror::Error)] #[error("Not all fields are prepared")] pub struct NotPreparedError; diff --git a/starknet-accounts/src/single_owner.rs b/starknet-accounts/src/single_owner.rs index c615faf2..1c15bee8 100644 --- a/starknet-accounts/src/single_owner.rs +++ b/starknet-accounts/src/single_owner.rs @@ -8,6 +8,8 @@ use starknet_core::types::{contract::ComputeClassHashError, BlockId, BlockTag, F use starknet_providers::Provider; use starknet_signers::Signer; +/// A generic [`Account`] implementation for controlling account contracts that only have one signer +/// using ECDSA the STARK curve. #[derive(Debug, Clone)] pub struct SingleOwnerAccount where @@ -22,10 +24,13 @@ where encoding: ExecutionEncoding, } +/// Errors signing an execution/declaration request. #[derive(Debug, thiserror::Error)] pub enum SignError { + /// An error encountered by the signer implementation. #[error(transparent)] Signer(S), + /// Failure to compute the class hash of the class being declared. #[error(transparent)] ClassHash(ComputeClassHashError), } @@ -71,6 +76,7 @@ where } } + /// Sets a new block ID to run queries against. pub fn set_block_id(&mut self, block_id: BlockId) -> &Self { self.block_id = block_id; self