use async_trait::async_trait;
use auto_impl::auto_impl;
use starknet_core::types::{
contract::ComputeClassHashError, BlockId, BlockTag, Call, Felt, FlattenedSierraClass,
};
use starknet_providers::{Provider, ProviderError};
use starknet_signers::SignerInteractivityContext;
use std::{error::Error, sync::Arc};
mod declaration;
mod execution;
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait Account: ExecutionEncoder + Sized {
type SignError: Error + Send + Sync;
fn address(&self) -> Felt;
fn chain_id(&self) -> Felt;
async fn sign_execution_v3(
&self,
execution: &RawExecutionV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError>;
async fn sign_declaration_v3(
&self,
declaration: &RawDeclarationV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError>;
fn is_signer_interactive(&self, context: SignerInteractivityContext<'_>) -> bool;
fn execute_v3(&self, calls: Vec<Call>) -> ExecutionV3<'_, Self> {
ExecutionV3::new(calls, self)
}
#[deprecated = "transaction version used might change unexpectedly; use `execute_v3` instead"]
fn execute(&self, calls: Vec<Call>) -> ExecutionV3<'_, Self> {
self.execute_v3(calls)
}
fn declare_v3(
&self,
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: Felt,
) -> DeclarationV3<'_, Self> {
DeclarationV3::new(contract_class, compiled_class_hash, self)
}
#[deprecated = "transaction version used might change unexpectedly; use `declare_v3` instead"]
fn declare(
&self,
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: Felt,
) -> DeclarationV3<'_, Self> {
self.declare_v3(contract_class, compiled_class_hash)
}
}
#[auto_impl(&, Box, Arc)]
pub trait ExecutionEncoder {
fn encode_calls(&self, calls: &[Call]) -> Vec<Felt>;
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait ConnectedAccount: Account {
type Provider: Provider + Sync;
fn provider(&self) -> &Self::Provider;
fn block_id(&self) -> BlockId {
BlockId::Tag(BlockTag::Latest)
}
async fn get_nonce(&self) -> Result<Felt, ProviderError> {
self.provider()
.get_nonce(self.block_id(), self.address())
.await
}
}
#[must_use]
#[derive(Debug)]
pub struct ExecutionV3<'a, A> {
account: &'a A,
calls: Vec<Call>,
nonce: Option<Felt>,
l1_gas: Option<u64>,
l1_gas_price: Option<u128>,
l2_gas: Option<u64>,
l2_gas_price: Option<u128>,
l1_data_gas: Option<u64>,
l1_data_gas_price: Option<u128>,
gas_estimate_multiplier: f64,
gas_price_estimate_multiplier: f64,
tip: Option<u64>,
}
#[must_use]
#[derive(Debug)]
pub struct DeclarationV3<'a, A> {
account: &'a A,
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: Felt,
nonce: Option<Felt>,
l1_gas: Option<u64>,
l1_gas_price: Option<u128>,
l2_gas: Option<u64>,
l2_gas_price: Option<u128>,
l1_data_gas: Option<u64>,
l1_data_gas_price: Option<u128>,
gas_estimate_multiplier: f64,
gas_price_estimate_multiplier: f64,
tip: Option<u64>,
}
#[derive(Debug)]
pub struct RawExecutionV3 {
calls: Vec<Call>,
nonce: Felt,
l1_gas: u64,
l1_gas_price: u128,
l2_gas: u64,
l2_gas_price: u128,
l1_data_gas: u64,
l1_data_gas_price: u128,
tip: u64,
}
#[derive(Debug)]
pub struct RawDeclarationV3 {
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: Felt,
nonce: Felt,
l1_gas: u64,
l1_gas_price: u128,
l2_gas: u64,
l2_gas_price: u128,
l1_data_gas: u64,
l1_data_gas_price: u128,
tip: u64,
}
#[derive(Debug)]
pub struct PreparedExecutionV3<'a, A> {
account: &'a A,
inner: RawExecutionV3,
}
#[derive(Debug)]
pub struct PreparedDeclarationV3<'a, A> {
account: &'a A,
inner: RawDeclarationV3,
}
#[derive(Debug, thiserror::Error)]
pub enum AccountError<S> {
#[error(transparent)]
Signing(S),
#[error(transparent)]
Provider(ProviderError),
#[error(transparent)]
ClassHashCalculation(ComputeClassHashError),
#[error("fee calculation overflow")]
FeeOutOfRange,
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<A> Account for &A
where
A: Account + Sync,
{
type SignError = A::SignError;
fn address(&self) -> Felt {
(*self).address()
}
fn chain_id(&self) -> Felt {
(*self).chain_id()
}
async fn sign_execution_v3(
&self,
execution: &RawExecutionV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError> {
(*self).sign_execution_v3(execution, query_only).await
}
async fn sign_declaration_v3(
&self,
declaration: &RawDeclarationV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError> {
(*self).sign_declaration_v3(declaration, query_only).await
}
fn is_signer_interactive(&self, context: SignerInteractivityContext<'_>) -> bool {
(*self).is_signer_interactive(context)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<A> Account for Box<A>
where
A: Account + Sync + Send,
{
type SignError = A::SignError;
fn address(&self) -> Felt {
self.as_ref().address()
}
fn chain_id(&self) -> Felt {
self.as_ref().chain_id()
}
async fn sign_execution_v3(
&self,
execution: &RawExecutionV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError> {
self.as_ref().sign_execution_v3(execution, query_only).await
}
async fn sign_declaration_v3(
&self,
declaration: &RawDeclarationV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError> {
self.as_ref()
.sign_declaration_v3(declaration, query_only)
.await
}
fn is_signer_interactive(&self, context: SignerInteractivityContext<'_>) -> bool {
self.as_ref().is_signer_interactive(context)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<A> Account for Arc<A>
where
A: Account + Sync + Send,
{
type SignError = A::SignError;
fn address(&self) -> Felt {
self.as_ref().address()
}
fn chain_id(&self) -> Felt {
self.as_ref().chain_id()
}
async fn sign_execution_v3(
&self,
execution: &RawExecutionV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError> {
self.as_ref().sign_execution_v3(execution, query_only).await
}
async fn sign_declaration_v3(
&self,
declaration: &RawDeclarationV3,
query_only: bool,
) -> Result<Vec<Felt>, Self::SignError> {
self.as_ref()
.sign_declaration_v3(declaration, query_only)
.await
}
fn is_signer_interactive(&self, context: SignerInteractivityContext<'_>) -> bool {
self.as_ref().is_signer_interactive(context)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<A> ConnectedAccount for &A
where
A: ConnectedAccount + Sync,
{
type Provider = A::Provider;
fn provider(&self) -> &Self::Provider {
(*self).provider()
}
fn block_id(&self) -> BlockId {
(*self).block_id()
}
async fn get_nonce(&self) -> Result<Felt, ProviderError> {
(*self).get_nonce().await
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<A> ConnectedAccount for Box<A>
where
A: ConnectedAccount + Sync + Send,
{
type Provider = A::Provider;
fn provider(&self) -> &Self::Provider {
self.as_ref().provider()
}
fn block_id(&self) -> BlockId {
self.as_ref().block_id()
}
async fn get_nonce(&self) -> Result<Felt, ProviderError> {
self.as_ref().get_nonce().await
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<A> ConnectedAccount for Arc<A>
where
A: ConnectedAccount + Sync + Send,
{
type Provider = A::Provider;
fn provider(&self) -> &Self::Provider {
self.as_ref().provider()
}
fn block_id(&self) -> BlockId {
self.as_ref().block_id()
}
async fn get_nonce(&self) -> Result<Felt, ProviderError> {
self.as_ref().get_nonce().await
}
}