use crate::Call;
use async_trait::async_trait;
use auto_impl::auto_impl;
use starknet_core::types::{
contract::{legacy::LegacyContractClass, CompressProgramError, ComputeClassHashError},
BlockId, BlockTag, FieldElement, FlattenedSierraClass,
};
use starknet_providers::{Provider, ProviderError};
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) -> FieldElement;
fn chain_id(&self) -> FieldElement;
async fn sign_execution(
&self,
execution: &RawExecution,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError>;
async fn sign_declaration(
&self,
declaration: &RawDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError>;
async fn sign_legacy_declaration(
&self,
legacy_declaration: &RawLegacyDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError>;
fn execute(&self, calls: Vec<Call>) -> Execution<Self> {
Execution::new(calls, self)
}
fn declare(
&self,
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: FieldElement,
) -> Declaration<Self> {
Declaration::new(contract_class, compiled_class_hash, self)
}
fn declare_legacy(&self, contract_class: Arc<LegacyContractClass>) -> LegacyDeclaration<Self> {
LegacyDeclaration::new(contract_class, self)
}
}
#[auto_impl(&, Box, Arc)]
pub trait ExecutionEncoder {
fn encode_calls(&self, calls: &[Call]) -> Vec<FieldElement>;
}
#[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<FieldElement, ProviderError> {
self.provider()
.get_nonce(self.block_id(), self.address())
.await
}
}
#[must_use]
#[derive(Debug)]
pub struct Execution<'a, A> {
account: &'a A,
calls: Vec<Call>,
nonce: Option<FieldElement>,
max_fee: Option<FieldElement>,
fee_estimate_multiplier: f64,
}
#[must_use]
#[derive(Debug)]
pub struct Declaration<'a, A> {
account: &'a A,
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: FieldElement,
nonce: Option<FieldElement>,
max_fee: Option<FieldElement>,
fee_estimate_multiplier: f64,
}
#[must_use]
#[derive(Debug)]
pub struct LegacyDeclaration<'a, A> {
account: &'a A,
contract_class: Arc<LegacyContractClass>,
nonce: Option<FieldElement>,
max_fee: Option<FieldElement>,
fee_estimate_multiplier: f64,
}
#[derive(Debug)]
pub struct RawExecution {
calls: Vec<Call>,
nonce: FieldElement,
max_fee: FieldElement,
}
#[derive(Debug)]
pub struct RawDeclaration {
contract_class: Arc<FlattenedSierraClass>,
compiled_class_hash: FieldElement,
nonce: FieldElement,
max_fee: FieldElement,
}
#[derive(Debug)]
pub struct RawLegacyDeclaration {
contract_class: Arc<LegacyContractClass>,
nonce: FieldElement,
max_fee: FieldElement,
}
#[derive(Debug)]
pub struct PreparedExecution<'a, A> {
account: &'a A,
inner: RawExecution,
}
#[derive(Debug)]
pub struct PreparedDeclaration<'a, A> {
account: &'a A,
inner: RawDeclaration,
}
#[derive(Debug)]
pub struct PreparedLegacyDeclaration<'a, A> {
account: &'a A,
inner: RawLegacyDeclaration,
}
#[derive(Debug, thiserror::Error)]
pub enum AccountError<S> {
#[error(transparent)]
Signing(S),
#[error(transparent)]
Provider(ProviderError),
#[error(transparent)]
ClassHashCalculation(ComputeClassHashError),
#[error(transparent)]
ClassCompression(CompressProgramError),
#[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) -> FieldElement {
(*self).address()
}
fn chain_id(&self) -> FieldElement {
(*self).chain_id()
}
async fn sign_execution(
&self,
execution: &RawExecution,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
(*self).sign_execution(execution, query_only).await
}
async fn sign_declaration(
&self,
declaration: &RawDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
(*self).sign_declaration(declaration, query_only).await
}
async fn sign_legacy_declaration(
&self,
legacy_declaration: &RawLegacyDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
(*self)
.sign_legacy_declaration(legacy_declaration, query_only)
.await
}
}
#[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) -> FieldElement {
self.as_ref().address()
}
fn chain_id(&self) -> FieldElement {
self.as_ref().chain_id()
}
async fn sign_execution(
&self,
execution: &RawExecution,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
self.as_ref().sign_execution(execution, query_only).await
}
async fn sign_declaration(
&self,
declaration: &RawDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
self.as_ref()
.sign_declaration(declaration, query_only)
.await
}
async fn sign_legacy_declaration(
&self,
legacy_declaration: &RawLegacyDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
self.as_ref()
.sign_legacy_declaration(legacy_declaration, query_only)
.await
}
}
#[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) -> FieldElement {
self.as_ref().address()
}
fn chain_id(&self) -> FieldElement {
self.as_ref().chain_id()
}
async fn sign_execution(
&self,
execution: &RawExecution,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
self.as_ref().sign_execution(execution, query_only).await
}
async fn sign_declaration(
&self,
declaration: &RawDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
self.as_ref()
.sign_declaration(declaration, query_only)
.await
}
async fn sign_legacy_declaration(
&self,
legacy_declaration: &RawLegacyDeclaration,
query_only: bool,
) -> Result<Vec<FieldElement>, Self::SignError> {
self.as_ref()
.sign_legacy_declaration(legacy_declaration, query_only)
.await
}
}
#[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<FieldElement, 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<FieldElement, 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<FieldElement, ProviderError> {
self.as_ref().get_nonce().await
}
}