starknet_accounts/
single_owner.rs1use crate::{Account, ConnectedAccount, ExecutionEncoder, RawDeclarationV3, RawExecutionV3};
2
3use async_trait::async_trait;
4use starknet_core::types::{contract::ComputeClassHashError, BlockId, BlockTag, Call, Felt};
5use starknet_providers::Provider;
6use starknet_signers::{Signer, SignerInteractivityContext};
7
8#[derive(Debug, Clone)]
11pub struct SingleOwnerAccount<P, S>
12where
13 P: Provider + Send,
14 S: Signer + Send,
15{
16 provider: P,
17 signer: S,
18 address: Felt,
19 chain_id: Felt,
20 block_id: BlockId,
21 encoding: ExecutionEncoding,
22}
23
24#[derive(Debug, thiserror::Error)]
26pub enum SignError<S> {
27 #[error(transparent)]
29 Signer(S),
30 #[error(transparent)]
32 ClassHash(ComputeClassHashError),
33}
34
35#[derive(Debug, Copy, Clone, PartialEq, Eq)]
37pub enum ExecutionEncoding {
38 Legacy,
41 New,
43}
44
45impl<P, S> SingleOwnerAccount<P, S>
46where
47 P: Provider + Sync + Send,
48 S: Signer + Sync + Send,
49{
50 pub const fn new(
60 provider: P,
61 signer: S,
62 address: Felt,
63 chain_id: Felt,
64 encoding: ExecutionEncoding,
65 ) -> Self {
66 Self {
67 provider,
68 signer,
69 address,
70 chain_id,
71 block_id: BlockId::Tag(BlockTag::PreConfirmed),
72 encoding,
73 }
74 }
75
76 pub fn set_block_id(&mut self, block_id: BlockId) -> &Self {
78 self.block_id = block_id;
79 self
80 }
81}
82
83#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
84#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
85impl<P, S> Account for SingleOwnerAccount<P, S>
86where
87 P: Provider + Sync + Send,
88 S: Signer + Sync + Send,
89{
90 type SignError = SignError<S::SignError>;
91
92 fn address(&self) -> Felt {
93 self.address
94 }
95
96 fn chain_id(&self) -> Felt {
97 self.chain_id
98 }
99
100 async fn sign_execution_v3(
101 &self,
102 execution: &RawExecutionV3,
103 query_only: bool,
104 ) -> Result<Vec<Felt>, Self::SignError> {
105 let tx_hash = execution.transaction_hash(self.chain_id, self.address, query_only, self);
106 let signature = self
107 .signer
108 .sign_hash(&tx_hash)
109 .await
110 .map_err(SignError::Signer)?;
111
112 Ok(vec![signature.r, signature.s])
113 }
114
115 async fn sign_declaration_v3(
116 &self,
117 declaration: &RawDeclarationV3,
118 query_only: bool,
119 ) -> Result<Vec<Felt>, Self::SignError> {
120 let tx_hash = declaration.transaction_hash(self.chain_id, self.address, query_only);
121 let signature = self
122 .signer
123 .sign_hash(&tx_hash)
124 .await
125 .map_err(SignError::Signer)?;
126
127 Ok(vec![signature.r, signature.s])
128 }
129
130 fn is_signer_interactive(&self, context: SignerInteractivityContext<'_>) -> bool {
131 self.signer.is_interactive(context)
132 }
133}
134
135impl<P, S> ExecutionEncoder for SingleOwnerAccount<P, S>
136where
137 P: Provider + Send,
138 S: Signer + Send,
139{
140 fn encode_calls(&self, calls: &[Call]) -> Vec<Felt> {
141 let mut execute_calldata: Vec<Felt> = vec![calls.len().into()];
142
143 match self.encoding {
144 ExecutionEncoding::Legacy => {
145 let mut concated_calldata: Vec<Felt> = vec![];
146 for call in calls {
147 execute_calldata.push(call.to); execute_calldata.push(call.selector); execute_calldata.push(concated_calldata.len().into()); execute_calldata.push(call.calldata.len().into()); for item in &call.calldata {
153 concated_calldata.push(*item);
154 }
155 }
156
157 execute_calldata.push(concated_calldata.len().into()); execute_calldata.extend_from_slice(&concated_calldata);
159 }
160 ExecutionEncoding::New => {
161 for call in calls {
162 execute_calldata.push(call.to); execute_calldata.push(call.selector); execute_calldata.push(call.calldata.len().into()); execute_calldata.extend_from_slice(&call.calldata);
167 }
168 }
169 }
170
171 execute_calldata
172 }
173}
174
175impl<P, S> ConnectedAccount for SingleOwnerAccount<P, S>
176where
177 P: Provider + Sync + Send,
178 S: Signer + Sync + Send,
179{
180 type Provider = P;
181
182 fn provider(&self) -> &Self::Provider {
183 &self.provider
184 }
185
186 fn block_id(&self) -> BlockId {
187 self.block_id
188 }
189}