starknet_accounts/factory/
mod.rs

1use super::NotPreparedError;
2
3use async_trait::async_trait;
4use starknet_core::{
5    crypto::compute_hash_on_elements,
6    types::{
7        BlockId, BlockTag, BroadcastedDeployAccountTransactionV3, BroadcastedTransaction,
8        DataAvailabilityMode, DeployAccountTransactionResult, FeeEstimate, Felt, NonZeroFelt,
9        ResourceBounds, ResourceBoundsMapping, SimulatedTransaction, SimulationFlag,
10        SimulationFlagForEstimateFee, StarknetError,
11    },
12};
13use starknet_crypto::PoseidonHasher;
14use starknet_providers::{Provider, ProviderError};
15use std::error::Error;
16
17pub mod argent;
18pub mod open_zeppelin;
19
20/// Cairo string for `deploy_account`
21const PREFIX_DEPLOY_ACCOUNT: Felt = Felt::from_raw([
22    461298303000467581,
23    18446744073709551615,
24    18443211694809419988,
25    3350261884043292318,
26]);
27
28/// 2 ^ 128 + 3
29const QUERY_VERSION_THREE: Felt = Felt::from_raw([
30    576460752142432688,
31    18446744073709551584,
32    17407,
33    18446744073700081569,
34]);
35
36/// Cairo string for `STARKNET_CONTRACT_ADDRESS`
37const PREFIX_CONTRACT_ADDRESS: Felt = Felt::from_raw([
38    533439743893157637,
39    8635008616843941496,
40    17289941567720117366,
41    3829237882463328880,
42]);
43
44// 2 ** 251 - 256
45const ADDR_BOUND: NonZeroFelt = NonZeroFelt::from_raw([
46    576459263475590224,
47    18446744073709255680,
48    160989183,
49    18446743986131443745,
50]);
51
52/// Abstraction over different ways of deploying account contracts using the `DEPLOY_ACCOUNT`
53/// transaction type.
54#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
55#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
56pub trait AccountFactory: Sized {
57    /// The [`Provider`] type attached to this account factory.
58    type Provider: Provider + Sync;
59    /// Possible errors for signing transactions.
60    type SignError: Error + Send + Sync;
61
62    /// Gets the class hash of the account contract.
63    fn class_hash(&self) -> Felt;
64
65    /// Gets the constructor calldata for the deployment transaction.
66    fn calldata(&self) -> Vec<Felt>;
67
68    /// Gets the chain ID of the target network.
69    fn chain_id(&self) -> Felt;
70
71    /// Gets a reference to the attached [`Provider`] instance.
72    fn provider(&self) -> &Self::Provider;
73
74    /// Whether the underlying signer implementation is interactive, such as a hardware wallet.
75    /// Implementations should return `true` if the signing operation is very expensive, even if not
76    /// strictly "interactive" as in requiring human input.
77    ///
78    /// This affects how an account factory makes decision on whether to request a real signature
79    /// for estimation/simulation purposes.
80    fn is_signer_interactive(&self) -> bool;
81
82    /// Block ID to use when estimating fees.
83    fn block_id(&self) -> BlockId {
84        BlockId::Tag(BlockTag::Latest)
85    }
86
87    /// Signs an execution request to authorize an `DEPLOY_ACCOUNT` v3 transaction that pays
88    /// transaction fees in `STRK`.
89    ///
90    /// If `query_only` is `true`, the commitment must be constructed in a way that a real state-
91    /// changing transaction cannot be authenticated. This is to prevent replay attacks.
92    async fn sign_deployment_v3(
93        &self,
94        deployment: &RawAccountDeploymentV3,
95        query_only: bool,
96    ) -> Result<Vec<Felt>, Self::SignError>;
97
98    /// Generates an instance of [`AccountDeploymentV3`] for sending `DEPLOY_ACCOUNT` v3
99    /// transactions. Pays transaction fees in `STRK`.
100    fn deploy_v3(&self, salt: Felt) -> AccountDeploymentV3<'_, Self> {
101        AccountDeploymentV3::new(salt, self)
102    }
103
104    /// Generates an instance of [`AccountDeploymentV3`] for sending `DEPLOY_ACCOUNT` v3
105    /// transactions. Pays transaction fees in `STRK`.
106    #[deprecated = "transaction version used might change unexpectedly; use `deploy_v3` instead"]
107    fn deploy(&self, salt: Felt) -> AccountDeploymentV3<'_, Self> {
108        self.deploy_v3(salt)
109    }
110}
111
112/// Abstraction over `DEPLOY_ACCOUNT` transactions for account contract deployment. This struct uses
113/// v3 `DEPLOY_ACCOUNT` transactions under the hood, and hence pays transaction fees in STRK.
114///
115/// This is an intermediate type allowing users to optionally specify `nonce` and transaction fee
116/// options.
117#[must_use]
118#[derive(Debug)]
119pub struct AccountDeploymentV3<'f, F> {
120    factory: &'f F,
121    salt: Felt,
122    // We need to allow setting nonce here as `DeployAccount` transactions may have non-zero nonces
123    /// after failed transactions can be included in blocks.
124    nonce: Option<Felt>,
125    l1_gas: Option<u64>,
126    l1_gas_price: Option<u128>,
127    l2_gas: Option<u64>,
128    l2_gas_price: Option<u128>,
129    l1_data_gas: Option<u64>,
130    l1_data_gas_price: Option<u128>,
131    gas_estimate_multiplier: f64,
132    gas_price_estimate_multiplier: f64,
133    tip: Option<u64>,
134}
135
136/// [`AccountDeploymentV3`] but with `nonce` and other transaction fee options already determined.
137#[derive(Debug, Clone)]
138pub struct RawAccountDeploymentV3 {
139    salt: Felt,
140    nonce: Felt,
141    l1_gas: u64,
142    l1_gas_price: u128,
143    l2_gas: u64,
144    l2_gas_price: u128,
145    l1_data_gas: u64,
146    l1_data_gas_price: u128,
147    tip: u64,
148}
149
150/// [`RawAccountDeploymentV3`] but with a factory associated.
151#[derive(Debug)]
152pub struct PreparedAccountDeploymentV3<'f, F> {
153    factory: &'f F,
154    inner: RawAccountDeploymentV3,
155}
156
157/// Errors using Starknet account factories.
158#[derive(Debug, thiserror::Error)]
159pub enum AccountFactoryError<S> {
160    /// An error is encountered when signing a request.
161    #[error(transparent)]
162    Signing(S),
163    /// An error is encountered with communicating with the network.
164    #[error(transparent)]
165    Provider(ProviderError),
166    /// Transaction fee calculation overflow.
167    #[error("fee calculation overflow")]
168    FeeOutOfRange,
169}
170
171impl<'f, F> AccountDeploymentV3<'f, F> {
172    /// Constructs a new [`AccountDeploymentV3`].
173    ///
174    /// Users would typically use [`deploy_v3`](fn.deploy_v3) on an [`AccountFactory`] instead of
175    /// directly calling this method.
176    pub const fn new(salt: Felt, factory: &'f F) -> Self {
177        Self {
178            factory,
179            salt,
180            nonce: None,
181            l1_gas: None,
182            l1_gas_price: None,
183            l2_gas: None,
184            l2_gas_price: None,
185            l1_data_gas: None,
186            l1_data_gas_price: None,
187            gas_estimate_multiplier: 1.5,
188            gas_price_estimate_multiplier: 1.5,
189            tip: None,
190        }
191    }
192
193    /// Returns a new [`AccountDeploymentV3`] with the `nonce`.
194    pub const fn nonce(self, nonce: Felt) -> Self {
195        Self {
196            nonce: Some(nonce),
197            ..self
198        }
199    }
200
201    /// Returns a new [`AccountDeploymentV3`] with the `l1_gas`.
202    pub const fn l1_gas(self, l1_gas: u64) -> Self {
203        Self {
204            l1_gas: Some(l1_gas),
205            ..self
206        }
207    }
208
209    /// Returns a new [`AccountDeploymentV3`] with the `l1_gas_price`.
210    pub const fn l1_gas_price(self, l1_gas_price: u128) -> Self {
211        Self {
212            l1_gas_price: Some(l1_gas_price),
213            ..self
214        }
215    }
216
217    /// Returns a new [`AccountDeploymentV3`] with the `l2_gas`.
218    pub const fn l2_gas(self, l2_gas: u64) -> Self {
219        Self {
220            l2_gas: Some(l2_gas),
221            ..self
222        }
223    }
224
225    /// Returns a new [`AccountDeploymentV3`] with the `l2_gas_price`.
226    pub const fn l2_gas_price(self, l2_gas_price: u128) -> Self {
227        Self {
228            l2_gas_price: Some(l2_gas_price),
229            ..self
230        }
231    }
232
233    /// Returns a new [`AccountDeploymentV3`] with the `l1_data_gas`.
234    pub const fn l1_data_gas(self, l1_data_gas: u64) -> Self {
235        Self {
236            l1_data_gas: Some(l1_data_gas),
237            ..self
238        }
239    }
240
241    /// Returns a new [`AccountDeploymentV3`] with the `l1_data_gas_price`.
242    pub const fn l1_data_gas_price(self, l1_data_gas_price: u128) -> Self {
243        Self {
244            l1_data_gas_price: Some(l1_data_gas_price),
245            ..self
246        }
247    }
248
249    /// Returns a new [`AccountDeploymentV3`] with the gas amount estimate multiplier.  The
250    /// multiplier is used when the gas amount is not manually specified and must be fetched from a
251    /// [`Provider`] instead.
252    pub const fn gas_estimate_multiplier(self, gas_estimate_multiplier: f64) -> Self {
253        Self {
254            gas_estimate_multiplier,
255            ..self
256        }
257    }
258
259    /// Returns a new [`AccountDeploymentV3`] with the gas price estimate multiplier.  The
260    /// multiplier is used when the gas price is not manually specified and must be fetched from a
261    /// [`Provider`] instead.
262    pub const fn gas_price_estimate_multiplier(self, gas_price_estimate_multiplier: f64) -> Self {
263        Self {
264            gas_price_estimate_multiplier,
265            ..self
266        }
267    }
268
269    /// Returns a new [`AccountDeploymentV3`] with the `tip`.
270    pub const fn tip(self, tip: u64) -> Self {
271        Self {
272            tip: Some(tip),
273            ..self
274        }
275    }
276
277    /// Calling this function after manually specifying `nonce` and `max_fee` turns
278    /// [`AccountDeploymentV3`] into [`PreparedAccountDeploymentV3`]. Returns `Err` if either field is
279    /// `None`.
280    pub fn prepared(self) -> Result<PreparedAccountDeploymentV3<'f, F>, NotPreparedError> {
281        let nonce = self.nonce.ok_or(NotPreparedError)?;
282        let l1_gas = self.l1_gas.ok_or(NotPreparedError)?;
283        let l1_gas_price = self.l1_gas_price.ok_or(NotPreparedError)?;
284        let l2_gas = self.l2_gas.ok_or(NotPreparedError)?;
285        let l2_gas_price = self.l2_gas_price.ok_or(NotPreparedError)?;
286        let l1_data_gas = self.l1_data_gas.ok_or(NotPreparedError)?;
287        let l1_data_gas_price = self.l1_data_gas_price.ok_or(NotPreparedError)?;
288        let tip = self.tip.ok_or(NotPreparedError)?;
289
290        Ok(PreparedAccountDeploymentV3 {
291            factory: self.factory,
292            inner: RawAccountDeploymentV3 {
293                salt: self.salt,
294                nonce,
295                l1_gas,
296                l1_gas_price,
297                l2_gas,
298                l2_gas_price,
299                l1_data_gas,
300                l1_data_gas_price,
301                tip,
302            },
303        })
304    }
305}
306
307impl<'f, F> AccountDeploymentV3<'f, F>
308where
309    F: AccountFactory + Sync,
310{
311    /// Locally calculates the target deployment address.
312    pub fn address(&self) -> Felt {
313        calculate_contract_address(
314            self.salt,
315            self.factory.class_hash(),
316            &self.factory.calldata(),
317        )
318    }
319
320    /// Fetches the next available nonce from a [`Provider`]. In most cases this would be `0` but
321    /// it can also be non-zero if previous reverted deployment attempts exist.
322    pub async fn fetch_nonce(&self) -> Result<Felt, ProviderError> {
323        match self
324            .factory
325            .provider()
326            .get_nonce(self.factory.block_id(), self.address())
327            .await
328        {
329            Ok(nonce) => Ok(nonce),
330            Err(ProviderError::StarknetError(StarknetError::ContractNotFound)) => Ok(Felt::ZERO),
331            Err(err) => Err(err),
332        }
333    }
334
335    /// Estimates transaction fees from a [`Provider`].
336    pub async fn estimate_fee(&self) -> Result<FeeEstimate, AccountFactoryError<F::SignError>> {
337        // Resolves nonce
338        let nonce = match self.nonce {
339            Some(value) => value,
340            None => self
341                .fetch_nonce()
342                .await
343                .map_err(AccountFactoryError::Provider)?,
344        };
345
346        self.estimate_fee_with_nonce(nonce).await
347    }
348
349    /// Simulates the transaction from a [`Provider`]. Transaction validation and fee transfer can
350    /// be skipped.
351    pub async fn simulate(
352        &self,
353        skip_validate: bool,
354        skip_fee_charge: bool,
355    ) -> Result<SimulatedTransaction, AccountFactoryError<F::SignError>> {
356        // Resolves nonce
357        let nonce = match self.nonce {
358            Some(value) => value,
359            None => self
360                .fetch_nonce()
361                .await
362                .map_err(AccountFactoryError::Provider)?,
363        };
364
365        self.simulate_with_nonce(nonce, skip_validate, skip_fee_charge)
366            .await
367    }
368
369    /// Signs and broadcasts the transaction to the network.
370    pub async fn send(
371        &self,
372    ) -> Result<DeployAccountTransactionResult, AccountFactoryError<F::SignError>> {
373        self.prepare().await?.send().await
374    }
375
376    async fn prepare(
377        &self,
378    ) -> Result<PreparedAccountDeploymentV3<'f, F>, AccountFactoryError<F::SignError>> {
379        // Resolves nonce
380        let nonce = match self.nonce {
381            Some(value) => value,
382            None => self
383                .fetch_nonce()
384                .await
385                .map_err(AccountFactoryError::Provider)?,
386        };
387
388        // Resolves fee settings
389        let (
390            l1_gas,
391            l1_gas_price,
392            l2_gas,
393            l2_gas_price,
394            l1_data_gas,
395            l1_data_gas_price,
396            full_block,
397        ) = match (
398            self.l1_gas,
399            self.l1_gas_price,
400            self.l2_gas,
401            self.l2_gas_price,
402            self.l1_data_gas,
403            self.l1_data_gas_price,
404        ) {
405            (
406                Some(l1_gas),
407                Some(l1_gas_price),
408                Some(l2_gas),
409                Some(l2_gas_price),
410                Some(l1_data_gas),
411                Some(l1_data_gas_price),
412            ) => (
413                l1_gas,
414                l1_gas_price,
415                l2_gas,
416                l2_gas_price,
417                l1_data_gas,
418                l1_data_gas_price,
419                None,
420            ),
421            (Some(l1_gas), _, Some(l2_gas), _, Some(l1_data_gas), _) => {
422                // When all `gas` fields are specified, we only need the gas prices in FRI. By
423                // specifying all gas values, the user might be trying to avoid a full fee
424                // estimation (e.g. flaky dependencies), so it's inappropriate to call
425                // `estimate_fee` here.
426
427                let (block_l1_gas_price, block_l2_gas_price, block_l1_data_gas_price, full_block) =
428                    if self.tip.is_some() {
429                        // No need to estimate tip. Just fetch the lightest-weight block we can get.
430                        let block = self
431                            .factory
432                            .provider()
433                            .get_block_with_tx_hashes(self.factory.block_id())
434                            .await
435                            .map_err(AccountFactoryError::Provider)?;
436                        (
437                            block.l1_gas_price().price_in_fri,
438                            block.l2_gas_price().price_in_fri,
439                            block.l1_data_gas_price().price_in_fri,
440                            None,
441                        )
442                    } else {
443                        // We only need th block header here but still fetching the full block to be used
444                        // for tip estimation below.
445                        let block = self
446                            .factory
447                            .provider()
448                            .get_block_with_txs(self.factory.block_id())
449                            .await
450                            .map_err(AccountFactoryError::Provider)?;
451                        (
452                            block.l1_gas_price().price_in_fri,
453                            block.l2_gas_price().price_in_fri,
454                            block.l1_data_gas_price().price_in_fri,
455                            Some(block),
456                        )
457                    };
458
459                let adjusted_l1_gas_price = ((TryInto::<u64>::try_into(block_l1_gas_price)
460                    .map_err(|_| AccountFactoryError::FeeOutOfRange)?
461                    as f64)
462                    * self.gas_price_estimate_multiplier)
463                    as u128;
464                let adjusted_l2_gas_price = ((TryInto::<u64>::try_into(block_l2_gas_price)
465                    .map_err(|_| AccountFactoryError::FeeOutOfRange)?
466                    as f64)
467                    * self.gas_price_estimate_multiplier)
468                    as u128;
469                let adjusted_l1_data_gas_price = ((TryInto::<u64>::try_into(block_l1_data_gas_price)
470                    .map_err(|_| AccountFactoryError::FeeOutOfRange)?
471                    as f64)
472                    * self.gas_price_estimate_multiplier)
473                    as u128;
474
475                (
476                    l1_gas,
477                    adjusted_l1_gas_price,
478                    l2_gas,
479                    adjusted_l2_gas_price,
480                    l1_data_gas,
481                    adjusted_l1_data_gas_price,
482                    full_block,
483                )
484            }
485            // We have to perform fee estimation as long as gas is not specified
486            _ => {
487                let fee_estimate = self.estimate_fee_with_nonce(nonce).await?;
488
489                (
490                    ((fee_estimate.l1_gas_consumed as f64) * self.gas_estimate_multiplier) as u64,
491                    ((TryInto::<u64>::try_into(fee_estimate.l1_gas_price)
492                        .map_err(|_| AccountFactoryError::FeeOutOfRange)?
493                        as f64)
494                        * self.gas_price_estimate_multiplier) as u128,
495                    ((fee_estimate.l2_gas_consumed as f64) * self.gas_estimate_multiplier) as u64,
496                    ((TryInto::<u64>::try_into(fee_estimate.l2_gas_price)
497                        .map_err(|_| AccountFactoryError::FeeOutOfRange)?
498                        as f64)
499                        * self.gas_price_estimate_multiplier) as u128,
500                    ((fee_estimate.l1_data_gas_consumed as f64) * self.gas_estimate_multiplier)
501                        as u64,
502                    ((TryInto::<u64>::try_into(fee_estimate.l1_data_gas_price)
503                        .map_err(|_| AccountFactoryError::FeeOutOfRange)?
504                        as f64)
505                        * self.gas_price_estimate_multiplier) as u128,
506                    None,
507                )
508            }
509        };
510
511        let tip = match self.tip {
512            Some(tip) => tip,
513            None => {
514                // Need to estimate tip from median. Maybe a full block has already been fetched?
515                let block = match full_block {
516                    Some(block) => block,
517                    None => self
518                        .factory
519                        .provider()
520                        .get_block_with_txs(self.factory.block_id())
521                        .await
522                        .map_err(AccountFactoryError::Provider)?,
523                };
524                block.median_tip()
525            }
526        };
527
528        Ok(PreparedAccountDeploymentV3 {
529            factory: self.factory,
530            inner: RawAccountDeploymentV3 {
531                salt: self.salt,
532                nonce,
533                l1_gas,
534                l1_gas_price,
535                l2_gas,
536                l2_gas_price,
537                l1_data_gas,
538                l1_data_gas_price,
539                tip,
540            },
541        })
542    }
543
544    async fn estimate_fee_with_nonce(
545        &self,
546        nonce: Felt,
547    ) -> Result<FeeEstimate, AccountFactoryError<F::SignError>> {
548        let skip_signature = self.factory.is_signer_interactive();
549
550        let prepared = PreparedAccountDeploymentV3 {
551            factory: self.factory,
552            inner: RawAccountDeploymentV3 {
553                salt: self.salt,
554                nonce,
555                l1_gas: 0,
556                l1_gas_price: 0,
557                l2_gas: 0,
558                l2_gas_price: 0,
559                l1_data_gas: 0,
560                l1_data_gas_price: 0,
561                tip: 0,
562            },
563        };
564        let deploy = prepared
565            .get_deploy_request(true, skip_signature)
566            .await
567            .map_err(AccountFactoryError::Signing)?;
568
569        self.factory
570            .provider()
571            .estimate_fee_single(
572                BroadcastedTransaction::DeployAccount(deploy),
573                if skip_signature {
574                    // Validation would fail since real signature was not requested
575                    vec![SimulationFlagForEstimateFee::SkipValidate]
576                } else {
577                    // With the correct signature in place, run validation for accurate results
578                    vec![]
579                },
580                self.factory.block_id(),
581            )
582            .await
583            .map_err(AccountFactoryError::Provider)
584    }
585
586    async fn simulate_with_nonce(
587        &self,
588        nonce: Felt,
589        skip_validate: bool,
590        skip_fee_charge: bool,
591    ) -> Result<SimulatedTransaction, AccountFactoryError<F::SignError>> {
592        let skip_signature = if self.factory.is_signer_interactive() {
593            // If signer is interactive, we would try to minimize signing requests. However, if the
594            // caller has decided to not skip validation, it's best we still request a real
595            // signature, as otherwise the simulation would most likely fail.
596            skip_validate
597        } else {
598            // Signing with non-interactive signers is cheap so always request signatures.
599            false
600        };
601
602        let prepared = PreparedAccountDeploymentV3 {
603            factory: self.factory,
604            inner: RawAccountDeploymentV3 {
605                salt: self.salt,
606                nonce,
607                l1_gas: self.l1_gas.unwrap_or_default(),
608                l1_gas_price: self.l1_gas_price.unwrap_or_default(),
609                l2_gas: self.l2_gas.unwrap_or_default(),
610                l2_gas_price: self.l2_gas_price.unwrap_or_default(),
611                l1_data_gas: self.l1_data_gas.unwrap_or_default(),
612                l1_data_gas_price: self.l1_data_gas_price.unwrap_or_default(),
613                tip: self.tip.unwrap_or_default(),
614            },
615        };
616        let deploy = prepared
617            .get_deploy_request(true, skip_signature)
618            .await
619            .map_err(AccountFactoryError::Signing)?;
620
621        let mut flags = vec![];
622
623        if skip_validate {
624            flags.push(SimulationFlag::SkipValidate);
625        }
626        if skip_fee_charge {
627            flags.push(SimulationFlag::SkipFeeCharge);
628        }
629
630        self.factory
631            .provider()
632            .simulate_transaction(
633                self.factory.block_id(),
634                BroadcastedTransaction::DeployAccount(deploy),
635                &flags,
636            )
637            .await
638            .map_err(AccountFactoryError::Provider)
639    }
640}
641
642impl RawAccountDeploymentV3 {
643    /// Gets the `salt` of the deployment request.
644    pub const fn salt(&self) -> Felt {
645        self.salt
646    }
647
648    /// Gets the `nonce` of the deployment request.
649    pub const fn nonce(&self) -> Felt {
650        self.nonce
651    }
652
653    /// Gets the `l1_gas` of the deployment request.
654    pub const fn l1_gas(&self) -> u64 {
655        self.l1_gas
656    }
657
658    /// Gets the `l1_gas_price` of the deployment request.
659    pub const fn l1_gas_price(&self) -> u128 {
660        self.l1_gas_price
661    }
662
663    /// Gets the `l2_gas` of the deployment request.
664    pub const fn l2_gas(&self) -> u64 {
665        self.l2_gas
666    }
667
668    /// Gets the `l2_gas_price` of the deployment request.
669    pub const fn l2_gas_price(&self) -> u128 {
670        self.l2_gas_price
671    }
672
673    /// Gets the `l1_data_gas` of the deployment request.
674    pub const fn l1_data_gas(&self) -> u64 {
675        self.l1_data_gas
676    }
677
678    /// Gets the `l1_data_gas_price` of the deployment request.
679    pub const fn l1_data_gas_price(&self) -> u128 {
680        self.l1_data_gas_price
681    }
682
683    /// Gets the `tip` of the deployment request.
684    pub const fn tip(&self) -> u64 {
685        self.tip
686    }
687}
688
689impl<'f, F> PreparedAccountDeploymentV3<'f, F> {
690    /// Constructs [`PreparedAccountDeploymentV3`] by attaching a factory to
691    /// [`RawAccountDeploymentV3`].
692    pub const fn from_raw(raw_deployment: RawAccountDeploymentV3, factory: &'f F) -> Self {
693        Self {
694            factory,
695            inner: raw_deployment,
696        }
697    }
698}
699
700impl<F> PreparedAccountDeploymentV3<'_, F>
701where
702    F: AccountFactory,
703{
704    /// Locally calculates the target deployment address.
705    pub fn address(&self) -> Felt {
706        calculate_contract_address(
707            self.inner.salt,
708            self.factory.class_hash(),
709            &self.factory.calldata(),
710        )
711    }
712
713    /// Calculates transaction hash given `query_only`.
714    pub fn transaction_hash(&self, query_only: bool) -> Felt {
715        let mut hasher = PoseidonHasher::new();
716
717        hasher.update(PREFIX_DEPLOY_ACCOUNT);
718        hasher.update(if query_only {
719            QUERY_VERSION_THREE
720        } else {
721            Felt::THREE
722        });
723        hasher.update(self.address());
724
725        hasher.update({
726            let mut fee_hasher = PoseidonHasher::new();
727
728            fee_hasher.update(self.inner.tip.into());
729
730            let mut resource_buffer = [
731                0, 0, b'L', b'1', b'_', b'G', b'A', b'S', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
732                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
733            ];
734            resource_buffer[8..(8 + 8)].copy_from_slice(&self.inner.l1_gas.to_be_bytes());
735            resource_buffer[(8 + 8)..].copy_from_slice(&self.inner.l1_gas_price.to_be_bytes());
736            fee_hasher.update(Felt::from_bytes_be(&resource_buffer));
737
738            let mut resource_buffer = [
739                0, 0, b'L', b'2', b'_', b'G', b'A', b'S', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
740                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
741            ];
742            resource_buffer[8..(8 + 8)].copy_from_slice(&self.inner.l2_gas.to_be_bytes());
743            resource_buffer[(8 + 8)..].copy_from_slice(&self.inner.l2_gas_price.to_be_bytes());
744            fee_hasher.update(Felt::from_bytes_be(&resource_buffer));
745
746            let mut resource_buffer = [
747                0, b'L', b'1', b'_', b'D', b'A', b'T', b'A', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
748                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
749            ];
750            resource_buffer[8..(8 + 8)].copy_from_slice(&self.inner.l1_data_gas.to_be_bytes());
751            resource_buffer[(8 + 8)..].copy_from_slice(&self.inner.l1_data_gas_price.to_be_bytes());
752            fee_hasher.update(Felt::from_bytes_be(&resource_buffer));
753
754            fee_hasher.finalize()
755        });
756
757        // Hard-coded empty `paymaster_data`
758        hasher.update(PoseidonHasher::new().finalize());
759
760        hasher.update(self.factory.chain_id());
761        hasher.update(self.inner.nonce);
762
763        // Hard-coded L1 DA mode for nonce and fee
764        hasher.update(Felt::ZERO);
765
766        hasher.update({
767            let mut calldata_hasher = PoseidonHasher::new();
768
769            self.factory
770                .calldata()
771                .into_iter()
772                .for_each(|element| calldata_hasher.update(element));
773
774            calldata_hasher.finalize()
775        });
776
777        hasher.update(self.factory.class_hash());
778        hasher.update(self.inner.salt);
779
780        hasher.finalize()
781    }
782
783    /// Signs and broadcasts the transaction to the network.
784    pub async fn send(
785        &self,
786    ) -> Result<DeployAccountTransactionResult, AccountFactoryError<F::SignError>> {
787        let tx_request = self
788            .get_deploy_request(false, false)
789            .await
790            .map_err(AccountFactoryError::Signing)?;
791        self.factory
792            .provider()
793            .add_deploy_account_transaction(tx_request)
794            .await
795            .map_err(AccountFactoryError::Provider)
796    }
797
798    /// Get the broadcasted deploy account transaction request.
799    pub async fn get_deploy_request(
800        &self,
801        query_only: bool,
802        skip_signature: bool,
803    ) -> Result<BroadcastedDeployAccountTransactionV3, F::SignError> {
804        Ok(BroadcastedDeployAccountTransactionV3 {
805            signature: if skip_signature {
806                vec![]
807            } else {
808                self.factory
809                    .sign_deployment_v3(&self.inner, query_only)
810                    .await?
811            },
812            nonce: self.inner.nonce,
813            contract_address_salt: self.inner.salt,
814            constructor_calldata: self.factory.calldata(),
815            class_hash: self.factory.class_hash(),
816            resource_bounds: ResourceBoundsMapping {
817                l1_gas: ResourceBounds {
818                    max_amount: self.inner.l1_gas,
819                    max_price_per_unit: self.inner.l1_gas_price,
820                },
821                l1_data_gas: ResourceBounds {
822                    max_amount: self.inner.l1_data_gas,
823                    max_price_per_unit: self.inner.l1_data_gas_price,
824                },
825                l2_gas: ResourceBounds {
826                    max_amount: self.inner.l2_gas,
827                    max_price_per_unit: self.inner.l2_gas_price,
828                },
829            },
830            tip: self.inner.tip,
831            // Hard-coded empty `paymaster_data`
832            paymaster_data: vec![],
833            // Hard-coded L1 DA mode for nonce and fee
834            nonce_data_availability_mode: DataAvailabilityMode::L1,
835            fee_data_availability_mode: DataAvailabilityMode::L1,
836            is_query: query_only,
837        })
838    }
839}
840
841fn calculate_contract_address(salt: Felt, class_hash: Felt, constructor_calldata: &[Felt]) -> Felt {
842    compute_hash_on_elements(&[
843        PREFIX_CONTRACT_ADDRESS,
844        Felt::ZERO,
845        salt,
846        class_hash,
847        compute_hash_on_elements(constructor_calldata),
848    ])
849    .mod_floor(&ADDR_BOUND)
850}