pathfinder_common/
transaction.rs

1use fake::{Dummy, Fake, Faker};
2use pathfinder_crypto::hash::{HashChain as PedersenHasher, PoseidonHasher};
3use pathfinder_crypto::Felt;
4use primitive_types::H256;
5
6use crate::prelude::*;
7use crate::{
8    felt_bytes,
9    AccountDeploymentDataElem,
10    PaymasterDataElem,
11    ResourceAmount,
12    ResourcePricePerUnit,
13    Tip,
14};
15
16#[derive(Clone, Debug, PartialEq, Eq, Dummy)]
17pub struct Transaction {
18    pub hash: TransactionHash,
19    pub variant: TransactionVariant,
20}
21
22impl Transaction {
23    /// Verifies the transaction hash against the transaction data.
24    #[must_use = "Should act on verification result"]
25    pub fn verify_hash(&self, chain_id: ChainId) -> bool {
26        self.variant.verify_hash(chain_id, self.hash)
27    }
28
29    pub fn version(&self) -> TransactionVersion {
30        match &self.variant {
31            TransactionVariant::DeclareV0(_) => TransactionVersion::ZERO,
32            TransactionVariant::DeclareV1(_) => TransactionVersion::ONE,
33            TransactionVariant::DeclareV2(_) => TransactionVersion::TWO,
34            TransactionVariant::DeclareV3(_) => TransactionVersion::THREE,
35            TransactionVariant::DeployV0(_) => TransactionVersion::ZERO,
36            TransactionVariant::DeployV1(_) => TransactionVersion::ONE,
37            TransactionVariant::DeployAccountV1(_) => TransactionVersion::ONE,
38            TransactionVariant::DeployAccountV3(_) => TransactionVersion::THREE,
39            TransactionVariant::InvokeV0(_) => TransactionVersion::ZERO,
40            TransactionVariant::InvokeV1(_) => TransactionVersion::ONE,
41            TransactionVariant::InvokeV3(_) => TransactionVersion::THREE,
42            TransactionVariant::L1Handler(_) => TransactionVersion::ZERO,
43        }
44    }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, Dummy)]
48pub enum TransactionVariant {
49    DeclareV0(DeclareTransactionV0V1),
50    DeclareV1(DeclareTransactionV0V1),
51    DeclareV2(DeclareTransactionV2),
52    DeclareV3(DeclareTransactionV3),
53    DeployV0(DeployTransactionV0),
54    DeployV1(DeployTransactionV1),
55    DeployAccountV1(DeployAccountTransactionV1),
56    DeployAccountV3(DeployAccountTransactionV3),
57    InvokeV0(InvokeTransactionV0),
58    InvokeV1(InvokeTransactionV1),
59    InvokeV3(InvokeTransactionV3),
60    L1Handler(L1HandlerTransaction),
61}
62
63impl Default for TransactionVariant {
64    fn default() -> Self {
65        Self::DeclareV0(Default::default())
66    }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub enum TransactionKind {
71    Declare,
72    Deploy,
73    DeployAccount,
74    Invoke,
75    L1Handler,
76}
77
78impl TransactionVariant {
79    #[must_use = "Should act on verification result"]
80    fn verify_hash(&self, chain_id: ChainId, expected: TransactionHash) -> bool {
81        if expected == self.calculate_hash(chain_id, false) {
82            return true;
83        }
84
85        // Some transaction variants had a different hash calculation in ancient times.
86        if Some(expected) == self.calculate_legacy_hash(chain_id) {
87            return true;
88        }
89
90        // L1 Handlers had a specific hash calculation for Starknet v0.7 blocks.
91        if let Self::L1Handler(l1_handler) = self {
92            if expected == l1_handler.calculate_v07_hash(chain_id) {
93                return true;
94            }
95        }
96
97        false
98    }
99
100    pub fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
101        match self {
102            TransactionVariant::DeclareV0(tx) => tx.calculate_hash_v0(chain_id, query_only),
103            TransactionVariant::DeclareV1(tx) => tx.calculate_hash_v1(chain_id, query_only),
104            TransactionVariant::DeclareV2(tx) => tx.calculate_hash(chain_id, query_only),
105            TransactionVariant::DeclareV3(tx) => tx.calculate_hash(chain_id, query_only),
106            TransactionVariant::DeployV0(tx) => tx.calculate_hash(chain_id, query_only),
107            TransactionVariant::DeployV1(tx) => tx.calculate_hash(chain_id, query_only),
108            TransactionVariant::DeployAccountV1(tx) => tx.calculate_hash(chain_id, query_only),
109            TransactionVariant::DeployAccountV3(tx) => tx.calculate_hash(chain_id, query_only),
110            TransactionVariant::InvokeV0(tx) => tx.calculate_hash(chain_id, query_only),
111            TransactionVariant::InvokeV1(tx) => tx.calculate_hash(chain_id, query_only),
112            TransactionVariant::InvokeV3(tx) => tx.calculate_hash(chain_id, query_only),
113            TransactionVariant::L1Handler(tx) => tx.calculate_hash(chain_id),
114        }
115    }
116
117    pub fn kind(&self) -> TransactionKind {
118        match self {
119            TransactionVariant::DeclareV0(_) => TransactionKind::Declare,
120            TransactionVariant::DeclareV1(_) => TransactionKind::Declare,
121            TransactionVariant::DeclareV2(_) => TransactionKind::Declare,
122            TransactionVariant::DeclareV3(_) => TransactionKind::Declare,
123            TransactionVariant::DeployV0(_) => TransactionKind::Deploy,
124            TransactionVariant::DeployV1(_) => TransactionKind::Deploy,
125            TransactionVariant::DeployAccountV1(_) => TransactionKind::DeployAccount,
126            TransactionVariant::DeployAccountV3(_) => TransactionKind::DeployAccount,
127            TransactionVariant::InvokeV0(_) => TransactionKind::Invoke,
128            TransactionVariant::InvokeV1(_) => TransactionKind::Invoke,
129            TransactionVariant::InvokeV3(_) => TransactionKind::Invoke,
130            TransactionVariant::L1Handler(_) => TransactionKind::L1Handler,
131        }
132    }
133
134    /// Some variants had a different hash calculations for blocks around
135    /// Starknet v0.8 and earlier. The hash excluded the transaction version
136    /// and nonce.
137    fn calculate_legacy_hash(&self, chain_id: ChainId) -> Option<TransactionHash> {
138        let hash = match self {
139            TransactionVariant::DeployV0(tx) => tx.calculate_legacy_hash(chain_id),
140            TransactionVariant::DeployV1(tx) => tx.calculate_legacy_hash(chain_id),
141            TransactionVariant::InvokeV0(tx) => tx.calculate_legacy_hash(chain_id),
142            TransactionVariant::L1Handler(tx) => tx.calculate_legacy_hash(chain_id),
143            _ => return None,
144        };
145
146        Some(hash)
147    }
148
149    /// Compute contract address for deploy and deploy account transactions. Do
150    /// nothing in case of other transactions.
151    ///
152    /// ### Important
153    ///
154    /// This function should not be called in async context.
155    pub fn calculate_contract_address(&mut self) {
156        match self {
157            TransactionVariant::DeployV0(DeployTransactionV0 {
158                class_hash,
159                constructor_calldata,
160                contract_address,
161                contract_address_salt,
162            })
163            | TransactionVariant::DeployV1(DeployTransactionV1 {
164                class_hash,
165                constructor_calldata,
166                contract_address,
167                contract_address_salt,
168            }) => {
169                *contract_address = ContractAddress::deployed_contract_address(
170                    constructor_calldata.iter().map(|d| CallParam(d.0)),
171                    contract_address_salt,
172                    class_hash,
173                );
174            }
175            TransactionVariant::DeployAccountV1(DeployAccountTransactionV1 {
176                class_hash,
177                constructor_calldata,
178                contract_address,
179                contract_address_salt,
180                ..
181            })
182            | TransactionVariant::DeployAccountV3(DeployAccountTransactionV3 {
183                class_hash,
184                constructor_calldata,
185                contract_address,
186                contract_address_salt,
187                ..
188            }) => {
189                *contract_address = ContractAddress::deployed_contract_address(
190                    constructor_calldata.iter().copied(),
191                    contract_address_salt,
192                    class_hash,
193                );
194            }
195            _ => {}
196        }
197    }
198}
199
200impl From<DeclareTransactionV2> for TransactionVariant {
201    fn from(value: DeclareTransactionV2) -> Self {
202        Self::DeclareV2(value)
203    }
204}
205impl From<DeclareTransactionV3> for TransactionVariant {
206    fn from(value: DeclareTransactionV3) -> Self {
207        Self::DeclareV3(value)
208    }
209}
210impl From<DeployTransactionV0> for TransactionVariant {
211    fn from(value: DeployTransactionV0) -> Self {
212        Self::DeployV0(value)
213    }
214}
215impl From<DeployTransactionV1> for TransactionVariant {
216    fn from(value: DeployTransactionV1) -> Self {
217        Self::DeployV1(value)
218    }
219}
220impl From<DeployAccountTransactionV1> for TransactionVariant {
221    fn from(value: DeployAccountTransactionV1) -> Self {
222        Self::DeployAccountV1(value)
223    }
224}
225impl From<DeployAccountTransactionV3> for TransactionVariant {
226    fn from(value: DeployAccountTransactionV3) -> Self {
227        Self::DeployAccountV3(value)
228    }
229}
230impl From<InvokeTransactionV0> for TransactionVariant {
231    fn from(value: InvokeTransactionV0) -> Self {
232        Self::InvokeV0(value)
233    }
234}
235impl From<InvokeTransactionV1> for TransactionVariant {
236    fn from(value: InvokeTransactionV1) -> Self {
237        Self::InvokeV1(value)
238    }
239}
240impl From<InvokeTransactionV3> for TransactionVariant {
241    fn from(value: InvokeTransactionV3) -> Self {
242        Self::InvokeV3(value)
243    }
244}
245impl From<L1HandlerTransaction> for TransactionVariant {
246    fn from(value: L1HandlerTransaction) -> Self {
247        Self::L1Handler(value)
248    }
249}
250
251#[derive(Clone, Default, Debug, PartialEq, Eq)]
252pub struct DeclareTransactionV0V1 {
253    pub class_hash: ClassHash,
254    pub max_fee: Fee,
255    pub nonce: TransactionNonce,
256    pub signature: Vec<TransactionSignatureElem>,
257    pub sender_address: ContractAddress,
258}
259
260#[derive(Clone, Default, Debug, PartialEq, Eq, Dummy)]
261pub struct DeclareTransactionV2 {
262    pub class_hash: ClassHash,
263    pub max_fee: Fee,
264    pub nonce: TransactionNonce,
265    pub signature: Vec<TransactionSignatureElem>,
266    pub sender_address: ContractAddress,
267    pub compiled_class_hash: CasmHash,
268}
269
270#[derive(Clone, Default, Debug, PartialEq, Eq, Dummy)]
271pub struct DeclareTransactionV3 {
272    pub class_hash: ClassHash,
273    pub nonce: TransactionNonce,
274    pub nonce_data_availability_mode: DataAvailabilityMode,
275    pub fee_data_availability_mode: DataAvailabilityMode,
276    pub resource_bounds: ResourceBounds,
277    pub tip: Tip,
278    pub paymaster_data: Vec<PaymasterDataElem>,
279    pub signature: Vec<TransactionSignatureElem>,
280    pub account_deployment_data: Vec<AccountDeploymentDataElem>,
281    pub sender_address: ContractAddress,
282    pub compiled_class_hash: CasmHash,
283}
284
285#[derive(Clone, Default, Debug, PartialEq, Eq)]
286pub struct DeployTransactionV0 {
287    pub class_hash: ClassHash,
288    pub contract_address: ContractAddress,
289    pub contract_address_salt: ContractAddressSalt,
290    pub constructor_calldata: Vec<ConstructorParam>,
291}
292
293#[derive(Clone, Default, Debug, PartialEq, Eq)]
294pub struct DeployTransactionV1 {
295    pub class_hash: ClassHash,
296    pub contract_address: ContractAddress,
297    pub contract_address_salt: ContractAddressSalt,
298    pub constructor_calldata: Vec<ConstructorParam>,
299}
300
301#[derive(Clone, Default, Debug, PartialEq, Eq)]
302pub struct DeployAccountTransactionV1 {
303    pub contract_address: ContractAddress,
304    pub max_fee: Fee,
305    pub signature: Vec<TransactionSignatureElem>,
306    pub nonce: TransactionNonce,
307    pub contract_address_salt: ContractAddressSalt,
308    pub constructor_calldata: Vec<CallParam>,
309    pub class_hash: ClassHash,
310}
311
312#[derive(Clone, Default, Debug, PartialEq, Eq)]
313pub struct DeployAccountTransactionV3 {
314    pub contract_address: ContractAddress,
315    pub signature: Vec<TransactionSignatureElem>,
316    pub nonce: TransactionNonce,
317    pub nonce_data_availability_mode: DataAvailabilityMode,
318    pub fee_data_availability_mode: DataAvailabilityMode,
319    pub resource_bounds: ResourceBounds,
320    pub tip: Tip,
321    pub paymaster_data: Vec<PaymasterDataElem>,
322    pub contract_address_salt: ContractAddressSalt,
323    pub constructor_calldata: Vec<CallParam>,
324    pub class_hash: ClassHash,
325}
326
327#[derive(Clone, Default, Debug, PartialEq, Eq)]
328pub struct InvokeTransactionV0 {
329    pub calldata: Vec<CallParam>,
330    pub sender_address: ContractAddress,
331    pub entry_point_selector: EntryPoint,
332    pub entry_point_type: Option<EntryPointType>,
333    pub max_fee: Fee,
334    pub signature: Vec<TransactionSignatureElem>,
335}
336
337#[derive(Clone, Default, Debug, PartialEq, Eq, Dummy)]
338pub struct InvokeTransactionV1 {
339    pub calldata: Vec<CallParam>,
340    pub sender_address: ContractAddress,
341    pub max_fee: Fee,
342    pub signature: Vec<TransactionSignatureElem>,
343    pub nonce: TransactionNonce,
344}
345
346#[derive(Clone, Default, Debug, PartialEq, Eq, Dummy)]
347pub struct InvokeTransactionV3 {
348    pub signature: Vec<TransactionSignatureElem>,
349    pub nonce: TransactionNonce,
350    pub nonce_data_availability_mode: DataAvailabilityMode,
351    pub fee_data_availability_mode: DataAvailabilityMode,
352    pub resource_bounds: ResourceBounds,
353    pub tip: Tip,
354    pub paymaster_data: Vec<PaymasterDataElem>,
355    pub account_deployment_data: Vec<AccountDeploymentDataElem>,
356    pub calldata: Vec<CallParam>,
357    pub sender_address: ContractAddress,
358}
359
360#[derive(Clone, Default, Debug, PartialEq, Eq, Dummy)]
361pub struct L1HandlerTransaction {
362    pub contract_address: ContractAddress,
363    pub entry_point_selector: EntryPoint,
364    pub nonce: TransactionNonce,
365    pub calldata: Vec<CallParam>,
366}
367
368#[derive(Copy, Clone, Debug, PartialEq, Eq, Dummy)]
369pub enum EntryPointType {
370    External,
371    L1Handler,
372}
373
374#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Dummy)]
375pub struct ResourceBounds {
376    pub l1_gas: ResourceBound,
377    pub l2_gas: ResourceBound,
378    pub l1_data_gas: Option<ResourceBound>,
379}
380
381#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Dummy)]
382pub struct ResourceBound {
383    pub max_amount: ResourceAmount,
384    pub max_price_per_unit: ResourcePricePerUnit,
385}
386
387#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Dummy)]
388pub enum DataAvailabilityMode {
389    #[default]
390    L1,
391    L2,
392}
393
394impl From<DataAvailabilityMode> for u64 {
395    fn from(value: DataAvailabilityMode) -> Self {
396        match value {
397            DataAvailabilityMode::L1 => 0,
398            DataAvailabilityMode::L2 => 1,
399        }
400    }
401}
402
403impl DeclareTransactionV0V1 {
404    fn calculate_hash_v0(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
405        PreV3Hasher {
406            prefix: felt_bytes!(b"declare"),
407            version: TransactionVersion::ZERO.with_query_only(query_only),
408            address: self.sender_address,
409            data_hash: PedersenHasher::default().finalize(),
410            nonce_or_class: Some(self.class_hash.0),
411            ..Default::default()
412        }
413        .hash(chain_id)
414    }
415
416    fn calculate_hash_v1(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
417        PreV3Hasher {
418            prefix: felt_bytes!(b"declare"),
419            version: TransactionVersion::ONE.with_query_only(query_only),
420            address: self.sender_address,
421            data_hash: PedersenHasher::single(self.class_hash.0),
422            max_fee: self.max_fee,
423            nonce_or_class: Some(self.nonce.0),
424            ..Default::default()
425        }
426        .hash(chain_id)
427    }
428}
429
430impl DeclareTransactionV2 {
431    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
432        PreV3Hasher {
433            prefix: felt_bytes!(b"declare"),
434            version: TransactionVersion::TWO.with_query_only(query_only),
435            address: self.sender_address,
436            data_hash: PedersenHasher::single(self.class_hash.0),
437            max_fee: self.max_fee,
438            nonce_or_class: Some(self.nonce.0),
439            casm_hash: Some(self.compiled_class_hash),
440            ..Default::default()
441        }
442        .hash(chain_id)
443    }
444}
445
446impl DeployTransactionV0 {
447    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
448        PreV3Hasher {
449            prefix: felt_bytes!(b"deploy"),
450            version: TransactionVersion::ZERO.with_query_only(query_only),
451            address: self.contract_address,
452            entry_point: EntryPoint::CONSTRUCTOR,
453            data_hash: self.constructor_calldata_hash(),
454            ..Default::default()
455        }
456        .hash(chain_id)
457    }
458
459    fn calculate_legacy_hash(&self, chain_id: ChainId) -> TransactionHash {
460        LegacyHasher {
461            prefix: felt_bytes!(b"deploy"),
462            address: self.contract_address,
463            entry_point: EntryPoint::CONSTRUCTOR,
464            data_hash: self.constructor_calldata_hash(),
465            nonce: None,
466        }
467        .hash(chain_id)
468    }
469
470    fn constructor_calldata_hash(&self) -> Felt {
471        self.constructor_calldata
472            .iter()
473            .fold(PedersenHasher::default(), |hasher, data| {
474                hasher.chain_update(data.0)
475            })
476            .finalize()
477    }
478}
479
480impl DeployTransactionV1 {
481    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
482        PreV3Hasher {
483            prefix: felt_bytes!(b"deploy"),
484            version: TransactionVersion::ONE.with_query_only(query_only),
485            address: self.contract_address,
486            entry_point: EntryPoint::CONSTRUCTOR,
487            data_hash: self.constructor_calldata_hash(),
488            ..Default::default()
489        }
490        .hash(chain_id)
491    }
492
493    fn calculate_legacy_hash(&self, chain_id: ChainId) -> TransactionHash {
494        LegacyHasher {
495            prefix: felt_bytes!(b"deploy"),
496            address: self.contract_address,
497            entry_point: EntryPoint::CONSTRUCTOR,
498            data_hash: self.constructor_calldata_hash(),
499            nonce: None,
500        }
501        .hash(chain_id)
502    }
503
504    fn constructor_calldata_hash(&self) -> Felt {
505        self.constructor_calldata
506            .iter()
507            .fold(PedersenHasher::default(), |hasher, data| {
508                hasher.chain_update(data.0)
509            })
510            .finalize()
511    }
512}
513
514impl DeployAccountTransactionV1 {
515    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
516        let constructor_calldata_hash = std::iter::once(self.class_hash.0)
517            .chain(std::iter::once(self.contract_address_salt.0))
518            .chain(self.constructor_calldata.iter().map(|x| x.0))
519            .fold(PedersenHasher::default(), |hasher, data| {
520                hasher.chain_update(data)
521            })
522            .finalize();
523
524        PreV3Hasher {
525            prefix: felt_bytes!(b"deploy_account"),
526            version: TransactionVersion::ONE.with_query_only(query_only),
527            address: self.contract_address,
528            data_hash: constructor_calldata_hash,
529            max_fee: self.max_fee,
530            nonce_or_class: Some(self.nonce.0),
531            ..Default::default()
532        }
533        .hash(chain_id)
534    }
535}
536
537impl InvokeTransactionV0 {
538    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
539        PreV3Hasher {
540            prefix: felt_bytes!(b"invoke"),
541            version: TransactionVersion::ZERO.with_query_only(query_only),
542            address: self.sender_address,
543            entry_point: self.entry_point_selector,
544            data_hash: self.calldata_hash(),
545            max_fee: self.max_fee,
546            ..Default::default()
547        }
548        .hash(chain_id)
549    }
550
551    fn calculate_legacy_hash(&self, chain_id: ChainId) -> TransactionHash {
552        LegacyHasher {
553            prefix: felt_bytes!(b"invoke"),
554            address: self.sender_address,
555            entry_point: self.entry_point_selector,
556            data_hash: self.calldata_hash(),
557            nonce: None,
558        }
559        .hash(chain_id)
560    }
561
562    fn calldata_hash(&self) -> Felt {
563        self.calldata
564            .iter()
565            .fold(PedersenHasher::default(), |hasher, data| {
566                hasher.chain_update(data.0)
567            })
568            .finalize()
569    }
570}
571
572impl L1HandlerTransaction {
573    pub fn calculate_message_hash(&self) -> H256 {
574        use sha3::{Digest, Keccak256};
575
576        let Some((from_address, payload)) = self.calldata.split_first() else {
577            // This would indicate a pretty severe error in the L1 transaction.
578            // But since we haven't encoded this during serialization, this could in
579            // theory mess us up here.
580            //
581            // We should incorporate this into the deserialization instead. Returning an
582            // error here is unergonomic and far too late.
583            return H256::zero();
584        };
585
586        let mut hash = Keccak256::new();
587
588        // This is an ethereum address
589        hash.update(from_address.0.as_be_bytes());
590        hash.update(self.contract_address.0.as_be_bytes());
591        hash.update(self.nonce.0.as_be_bytes());
592        hash.update(self.entry_point_selector.0.as_be_bytes());
593
594        // Pad the u64 to 32 bytes to match a felt.
595        hash.update([0u8; 24]);
596        hash.update((payload.len() as u64).to_be_bytes());
597
598        for elem in payload {
599            hash.update(elem.0.as_be_bytes());
600        }
601
602        let hash = <[u8; 32]>::from(hash.finalize());
603
604        hash.into()
605    }
606
607    pub fn calculate_hash(&self, chain_id: ChainId) -> TransactionHash {
608        PreV3Hasher {
609            prefix: felt_bytes!(b"l1_handler"),
610            version: TransactionVersion::ZERO,
611            address: self.contract_address,
612            entry_point: self.entry_point_selector,
613            data_hash: self.calldata_hash(),
614            nonce_or_class: Some(self.nonce.0),
615            ..Default::default()
616        }
617        .hash(chain_id)
618    }
619
620    fn calculate_legacy_hash(&self, chain_id: ChainId) -> TransactionHash {
621        LegacyHasher {
622            // Old L1 handler's were actually invokes under the hood.
623            prefix: felt_bytes!(b"invoke"),
624            address: self.contract_address,
625            entry_point: self.entry_point_selector,
626            data_hash: self.calldata_hash(),
627            nonce: None,
628        }
629        .hash(chain_id)
630    }
631
632    // L1 handlers had a slightly different hash for Starknet v0.7.
633    fn calculate_v07_hash(&self, chain_id: ChainId) -> TransactionHash {
634        LegacyHasher {
635            prefix: felt_bytes!(b"l1_handler"),
636            address: self.contract_address,
637            entry_point: self.entry_point_selector,
638            data_hash: self.calldata_hash(),
639            nonce: Some(self.nonce),
640        }
641        .hash(chain_id)
642    }
643
644    fn calldata_hash(&self) -> Felt {
645        self.calldata
646            .iter()
647            .fold(PedersenHasher::default(), |hasher, data| {
648                hasher.chain_update(data.0)
649            })
650            .finalize()
651    }
652}
653
654impl DeclareTransactionV3 {
655    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
656        let deployment_hash = self
657            .account_deployment_data
658            .iter()
659            .fold(PoseidonHasher::default(), |hasher, data| {
660                hasher.chain(data.0.into())
661            })
662            .finish()
663            .into();
664
665        V3Hasher {
666            prefix: felt_bytes!(b"declare"),
667            sender_address: self.sender_address,
668            nonce: self.nonce,
669            data_hashes: &[
670                deployment_hash,
671                self.class_hash.0,
672                self.compiled_class_hash.0,
673            ],
674            tip: self.tip,
675            paymaster_data: &self.paymaster_data,
676            nonce_data_availability_mode: self.nonce_data_availability_mode,
677            fee_data_availability_mode: self.fee_data_availability_mode,
678            resource_bounds: self.resource_bounds,
679            query_only,
680        }
681        .hash(chain_id)
682    }
683}
684
685impl DeployAccountTransactionV3 {
686    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
687        let deployment_hash = self
688            .constructor_calldata
689            .iter()
690            .fold(PoseidonHasher::default(), |hasher, data| {
691                hasher.chain(data.0.into())
692            })
693            .finish()
694            .into();
695
696        V3Hasher {
697            prefix: felt_bytes!(b"deploy_account"),
698            sender_address: self.contract_address,
699            nonce: self.nonce,
700            data_hashes: &[
701                deployment_hash,
702                self.class_hash.0,
703                self.contract_address_salt.0,
704            ],
705            tip: self.tip,
706            paymaster_data: &self.paymaster_data,
707            nonce_data_availability_mode: self.nonce_data_availability_mode,
708            fee_data_availability_mode: self.fee_data_availability_mode,
709            resource_bounds: self.resource_bounds,
710            query_only,
711        }
712        .hash(chain_id)
713    }
714}
715
716impl InvokeTransactionV3 {
717    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
718        let deployment_hash = self
719            .account_deployment_data
720            .iter()
721            .fold(PoseidonHasher::default(), |hasher, data| {
722                hasher.chain(data.0.into())
723            })
724            .finish()
725            .into();
726        let calldata_hash = self
727            .calldata
728            .iter()
729            .fold(PoseidonHasher::default(), |hasher, data| {
730                hasher.chain(data.0.into())
731            })
732            .finish()
733            .into();
734
735        V3Hasher {
736            prefix: felt_bytes!(b"invoke"),
737            sender_address: self.sender_address,
738            nonce: self.nonce,
739            data_hashes: &[deployment_hash, calldata_hash],
740            tip: self.tip,
741            paymaster_data: &self.paymaster_data,
742            nonce_data_availability_mode: self.nonce_data_availability_mode,
743            fee_data_availability_mode: self.fee_data_availability_mode,
744            resource_bounds: self.resource_bounds,
745            query_only,
746        }
747        .hash(chain_id)
748    }
749}
750
751impl InvokeTransactionV1 {
752    fn calculate_hash(&self, chain_id: ChainId, query_only: bool) -> TransactionHash {
753        let list_hash = self
754            .calldata
755            .iter()
756            .fold(PedersenHasher::default(), |hasher, data| {
757                hasher.chain_update(data.0)
758            })
759            .finalize();
760
761        PreV3Hasher {
762            prefix: felt_bytes!(b"invoke"),
763            version: TransactionVersion::ONE.with_query_only(query_only),
764            address: self.sender_address,
765            data_hash: list_hash,
766            max_fee: self.max_fee,
767            nonce_or_class: Some(self.nonce.0),
768            ..Default::default()
769        }
770        .hash(chain_id)
771    }
772}
773
774#[derive(Default)]
775struct LegacyHasher {
776    pub prefix: Felt,
777    pub address: ContractAddress,
778    pub entry_point: EntryPoint,
779    pub data_hash: Felt,
780    pub nonce: Option<TransactionNonce>,
781}
782
783impl LegacyHasher {
784    fn hash(self, chain_id: ChainId) -> TransactionHash {
785        let mut hasher = PedersenHasher::default()
786            .chain_update(self.prefix)
787            .chain_update(*self.address.get())
788            .chain_update(self.entry_point.0)
789            .chain_update(self.data_hash)
790            .chain_update(chain_id.0);
791
792        if let Some(nonce) = self.nonce {
793            hasher.update(nonce.0);
794        }
795
796        TransactionHash(hasher.finalize())
797    }
798}
799
800#[derive(Default)]
801struct PreV3Hasher {
802    pub prefix: Felt,
803    pub version: TransactionVersion,
804    pub address: ContractAddress,
805    pub entry_point: EntryPoint,
806    pub data_hash: Felt,
807    pub max_fee: Fee,
808    pub nonce_or_class: Option<Felt>,
809    pub casm_hash: Option<CasmHash>,
810}
811
812impl PreV3Hasher {
813    fn hash(self, chain_id: ChainId) -> TransactionHash {
814        let mut hash = PedersenHasher::default()
815            .chain_update(self.prefix)
816            .chain_update(self.version.0)
817            .chain_update(self.address.0)
818            .chain_update(self.entry_point.0)
819            .chain_update(self.data_hash)
820            .chain_update(self.max_fee.0)
821            .chain_update(chain_id.0);
822
823        if let Some(felt) = self.nonce_or_class {
824            hash.update(felt);
825        }
826
827        if let Some(felt) = self.casm_hash {
828            hash.update(felt.0);
829        }
830
831        TransactionHash(hash.finalize())
832    }
833}
834
835/// Provides hashing for V3 transactions.
836struct V3Hasher<'a> {
837    pub prefix: Felt,
838    pub sender_address: ContractAddress,
839    pub nonce: TransactionNonce,
840    pub data_hashes: &'a [Felt],
841    pub tip: Tip,
842    pub paymaster_data: &'a [PaymasterDataElem],
843    pub nonce_data_availability_mode: DataAvailabilityMode,
844    pub fee_data_availability_mode: DataAvailabilityMode,
845    pub resource_bounds: ResourceBounds,
846    pub query_only: bool,
847}
848
849impl V3Hasher<'_> {
850    fn hash(self, chain_id: ChainId) -> TransactionHash {
851        let hasher = PoseidonHasher::default()
852            .chain(self.prefix.into())
853            .chain(
854                TransactionVersion::THREE
855                    .with_query_only(self.query_only)
856                    .0
857                    .into(),
858            )
859            .chain(self.sender_address.0.into())
860            .chain(self.hash_fee_fields().into())
861            .chain(self.hash_paymaster_data().into())
862            .chain(chain_id.0.into())
863            .chain(self.nonce.0.into())
864            .chain(self.pack_data_availability().into());
865
866        let hash = self
867            .data_hashes
868            .iter()
869            .fold(hasher, |hasher, &data| hasher.chain(data.into()))
870            .finish();
871
872        TransactionHash(hash.into())
873    }
874
875    fn pack_data_availability(&self) -> u64 {
876        let nonce = u64::from(self.nonce_data_availability_mode) << 32;
877        let fee = u64::from(self.fee_data_availability_mode);
878
879        nonce + fee
880    }
881
882    fn hash_paymaster_data(&self) -> Felt {
883        self.paymaster_data
884            .iter()
885            .fold(PoseidonHasher::default(), |hasher, data| {
886                hasher.chain(data.0.into())
887            })
888            .finish()
889            .into()
890    }
891
892    fn hash_fee_fields(&self) -> Felt {
893        let mut hasher = PoseidonHasher::default()
894            .chain(self.tip.0.into())
895            .chain(Self::pack_gas_bound(b"L1_GAS", &self.resource_bounds.l1_gas).into())
896            .chain(Self::pack_gas_bound(b"L2_GAS", &self.resource_bounds.l2_gas).into());
897
898        if let Some(l1_data_gas) = self.resource_bounds.l1_data_gas {
899            hasher = hasher.chain(Self::pack_gas_bound(b"L1_DATA", &l1_data_gas).into());
900        }
901
902        hasher.finish().into()
903    }
904
905    fn pack_gas_bound(name: &[u8], bound: &ResourceBound) -> Felt {
906        let mut buffer: [u8; 32] = Default::default();
907        let (remainder, max_price) = buffer.split_at_mut(128 / 8);
908        let (gas_kind, max_amount) = remainder.split_at_mut(64 / 8);
909
910        let padding = gas_kind.len() - name.len();
911        gas_kind[padding..].copy_from_slice(name);
912        max_amount.copy_from_slice(&bound.max_amount.0.to_be_bytes());
913        max_price.copy_from_slice(&bound.max_price_per_unit.0.to_be_bytes());
914
915        Felt::from_be_bytes(buffer).expect("Packed resource should fit into felt")
916    }
917}
918
919impl<T> Dummy<T> for DeclareTransactionV0V1 {
920    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
921        Self {
922            class_hash: Faker.fake_with_rng(rng),
923            max_fee: Faker.fake_with_rng(rng),
924            // This is to keep DeclareV0 p2p compliant
925            nonce: TransactionNonce::ZERO,
926            signature: Faker.fake_with_rng(rng),
927            sender_address: Faker.fake_with_rng(rng),
928        }
929    }
930}
931
932impl<T> Dummy<T> for DeployTransactionV0 {
933    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
934        let class_hash = Faker.fake_with_rng(rng);
935        let contract_address_salt = Faker.fake_with_rng(rng);
936        let constructor_calldata: Vec<ConstructorParam> = Faker.fake_with_rng(rng);
937        let contract_address = ContractAddress::deployed_contract_address(
938            constructor_calldata.iter().map(|d| CallParam(d.0)),
939            &contract_address_salt,
940            &class_hash,
941        );
942
943        Self {
944            class_hash,
945            contract_address,
946            contract_address_salt,
947            constructor_calldata,
948        }
949    }
950}
951
952impl<T> Dummy<T> for DeployTransactionV1 {
953    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
954        let class_hash = Faker.fake_with_rng(rng);
955        let contract_address_salt = Faker.fake_with_rng(rng);
956        let constructor_calldata: Vec<ConstructorParam> = Faker.fake_with_rng(rng);
957        let contract_address = ContractAddress::deployed_contract_address(
958            constructor_calldata.iter().map(|d| CallParam(d.0)),
959            &contract_address_salt,
960            &class_hash,
961        );
962
963        Self {
964            class_hash,
965            contract_address,
966            contract_address_salt,
967            constructor_calldata,
968        }
969    }
970}
971
972impl<T> Dummy<T> for DeployAccountTransactionV1 {
973    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
974        let class_hash = Faker.fake_with_rng(rng);
975        let contract_address_salt = Faker.fake_with_rng(rng);
976        let constructor_calldata: Vec<CallParam> = Faker.fake_with_rng(rng);
977        let contract_address = ContractAddress::deployed_contract_address(
978            constructor_calldata.iter().map(|d| CallParam(d.0)),
979            &contract_address_salt,
980            &class_hash,
981        );
982
983        Self {
984            contract_address,
985            max_fee: Faker.fake_with_rng(rng),
986            signature: Faker.fake_with_rng(rng),
987            nonce: Faker.fake_with_rng(rng),
988            contract_address_salt,
989            constructor_calldata,
990            class_hash,
991        }
992    }
993}
994
995impl<T> Dummy<T> for DeployAccountTransactionV3 {
996    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
997        let class_hash = Faker.fake_with_rng(rng);
998        let contract_address_salt = Faker.fake_with_rng(rng);
999        let constructor_calldata: Vec<CallParam> = Faker.fake_with_rng(rng);
1000        let contract_address = ContractAddress::deployed_contract_address(
1001            constructor_calldata.iter().map(|d| CallParam(d.0)),
1002            &contract_address_salt,
1003            &class_hash,
1004        );
1005        Self {
1006            contract_address,
1007            signature: Faker.fake_with_rng(rng),
1008            nonce: Faker.fake_with_rng(rng),
1009            nonce_data_availability_mode: Faker.fake_with_rng(rng),
1010            fee_data_availability_mode: Faker.fake_with_rng(rng),
1011            resource_bounds: Faker.fake_with_rng(rng),
1012            tip: Faker.fake_with_rng(rng),
1013            paymaster_data: Faker.fake_with_rng(rng),
1014            contract_address_salt,
1015            constructor_calldata,
1016            class_hash,
1017        }
1018    }
1019}
1020
1021impl<T> Dummy<T> for InvokeTransactionV0 {
1022    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
1023        Self {
1024            calldata: Faker.fake_with_rng(rng),
1025            sender_address: Faker.fake_with_rng(rng),
1026            entry_point_selector: Faker.fake_with_rng(rng),
1027            // This is a legacy field, not used in p2p
1028            entry_point_type: None,
1029            max_fee: Faker.fake_with_rng(rng),
1030            signature: Faker.fake_with_rng(rng),
1031        }
1032    }
1033}
1034
1035#[cfg(test)]
1036mod tests {
1037    use super::*;
1038    use crate::macro_prelude::*;
1039
1040    // Goerli support was removed, however some of the fixtures originally come from
1041    // Goerli.
1042    const GOERLI_TESTNET: ChainId = ChainId(match Felt::from_be_slice(b"SN_GOERLI") {
1043        Ok(chain_id) => chain_id,
1044        Err(_) => unreachable!(),
1045    });
1046
1047    #[rstest::rstest]
1048    #[test]
1049    #[case::declare_v0(declare_v0(), GOERLI_TESTNET)]
1050    #[case::declare_v1(declare_v1(), ChainId::SEPOLIA_TESTNET)]
1051    #[case::declare_v2(declare_v2(), ChainId::SEPOLIA_TESTNET)]
1052    #[case::declare_v3(declare_v3(), GOERLI_TESTNET)]
1053    #[case::deploy(deploy(), GOERLI_TESTNET)]
1054    #[case::deploy_legacy(deploy_legacy(), GOERLI_TESTNET)]
1055    #[case::deploy_account_v1(deploy_account_v1(), ChainId::MAINNET)]
1056    #[case::deploy_account_v3(deploy_account_v3(), GOERLI_TESTNET)]
1057    #[case::invoke_v0(invoke_v0(), GOERLI_TESTNET)]
1058    #[case::invoke_v0_legacy(invoke_v0_legacy(), GOERLI_TESTNET)]
1059    #[case::invoke_v1(invoke_v1(), ChainId::MAINNET)]
1060    #[case::invoke_v3(invoke_v3(), ChainId::SEPOLIA_TESTNET)]
1061    #[case::l1_handler(l1_handler(), ChainId::MAINNET)]
1062    #[case::l1_handler_v07(l1_handler_v07(), ChainId::MAINNET)]
1063    #[case::l1_handler_legacy(l1_handler_legacy(), GOERLI_TESTNET)]
1064    fn verify_hash(#[case] transaction: Transaction, #[case] chain_id: ChainId) {
1065        assert!(transaction.verify_hash(chain_id));
1066    }
1067
1068    fn declare_v0() -> Transaction {
1069        Transaction {
1070            hash: transaction_hash!(
1071                "0x6d346ba207eb124355960c19c737698ad37a3c920a588b741e0130ff5bd4d6d"
1072            ),
1073            variant: TransactionVariant::DeclareV0(DeclareTransactionV0V1 {
1074                class_hash: class_hash!(
1075                    "0x71e6ef53e53e6f5ca792fc4a5799a33e6f4118e4fd1d948dca3a371506f0cc7"
1076                ),
1077                sender_address: contract_address!("0x1"),
1078                ..Default::default()
1079            }),
1080        }
1081    }
1082
1083    fn declare_v1() -> Transaction {
1084        Transaction {
1085            hash: transaction_hash!(
1086                "0xb2d88f64d9655a7d47a5519d66b969168d02d0d33f6476f0d2539c51686329"
1087            ),
1088            variant: TransactionVariant::DeclareV1(DeclareTransactionV0V1 {
1089                class_hash: class_hash!(
1090                    "0x3131fa018d520a037686ce3efddeab8f28895662f019ca3ca18a626650f7d1e"
1091                ),
1092                max_fee: fee!("0x625e5879c08f4"),
1093                nonce: transaction_nonce!("0x7"),
1094                signature: vec![
1095                    transaction_signature_elem!(
1096                        "0x3609667964a8ed946bc507721ec35a851d97a097d159ef0ec2af8fab490223f"
1097                    ),
1098                    transaction_signature_elem!(
1099                        "0x68846bad9f0f010fac4eeaf39f9dd609b28765fd2336b70ce026e33e2421c15"
1100                    ),
1101                ],
1102                sender_address: contract_address!(
1103                    "0x68922eb87daed71fc3099031e178b6534fc39a570022342e8c166024da893f5"
1104                ),
1105            }),
1106        }
1107    }
1108
1109    fn declare_v2() -> Transaction {
1110        Transaction {
1111            hash: transaction_hash!(
1112                "0x4cacc2bbdd5ec77b20e908f311ab27d6495b69761e929bb24ba02632716944"
1113            ),
1114            variant: TransactionVariant::DeclareV2(DeclareTransactionV2 {
1115                class_hash: class_hash!(
1116                    "0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003"
1117                ),
1118                max_fee: fee!("0x92fa1ac712614"),
1119                nonce: transaction_nonce!("0x6"),
1120                signature: vec![
1121                    transaction_signature_elem!(
1122                        "0x4ab3e77908396c66b39326f52334b447fe878d1d899a287c9e3cf7bd09839ea"
1123                    ),
1124                    transaction_signature_elem!(
1125                        "0x79a56f9e61eb834f1ac524eb35da33cccf92ff3b01a7a8eaf68cbb64bebdba9"
1126                    ),
1127                ],
1128                sender_address: contract_address!(
1129                    "0x68922eb87daed71fc3099031e178b6534fc39a570022342e8c166024da893f5"
1130                ),
1131                compiled_class_hash: casm_hash!(
1132                    "0x29787a427a423ffc5986d43e630077a176e4391fcef3ebf36014b154069ae4"
1133                ),
1134            }),
1135        }
1136    }
1137
1138    fn declare_v3() -> Transaction {
1139        Transaction {
1140            hash: transaction_hash!(
1141                "0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3"
1142            ),
1143            variant: TransactionVariant::DeclareV3(DeclareTransactionV3 {
1144                signature: vec![
1145                    transaction_signature_elem!(
1146                        "0x29a49dff154fede73dd7b5ca5a0beadf40b4b069f3a850cd8428e54dc809ccc"
1147                    ),
1148                    transaction_signature_elem!(
1149                        "0x429d142a17223b4f2acde0f5ecb9ad453e188b245003c86fab5c109bad58fc3"
1150                    ),
1151                ],
1152                nonce: transaction_nonce!("0x1"),
1153                nonce_data_availability_mode: DataAvailabilityMode::L1,
1154                fee_data_availability_mode: DataAvailabilityMode::L1,
1155                resource_bounds: ResourceBounds {
1156                    l1_gas: ResourceBound {
1157                        max_amount: ResourceAmount(0x186a0),
1158                        max_price_per_unit: ResourcePricePerUnit(0x2540be400),
1159                    },
1160                    l2_gas: Default::default(),
1161                    l1_data_gas: Default::default(),
1162                },
1163                sender_address: contract_address!(
1164                    "0x2fab82e4aef1d8664874e1f194951856d48463c3e6bf9a8c68e234a629a6f50"
1165                ),
1166                class_hash: class_hash!(
1167                    "0x5ae9d09292a50ed48c5930904c880dab56e85b825022a7d689cfc9e65e01ee7"
1168                ),
1169                compiled_class_hash: casm_hash!(
1170                    "0x1add56d64bebf8140f3b8a38bdf102b7874437f0c861ab4ca7526ec33b4d0f8"
1171                ),
1172                ..Default::default()
1173            }),
1174        }
1175    }
1176
1177    fn deploy() -> Transaction {
1178        Transaction {
1179            hash: transaction_hash!(
1180                "0x3d7623443283d9a0cec946492db78b06d57642a551745ddfac8d3f1f4fcc2a8"
1181            ),
1182            variant: TransactionVariant::DeployV0(DeployTransactionV0 {
1183                contract_address: contract_address!(
1184                    "0x54c6883e459baeac4a9052ee109b86b9f81adbcdcb1f65a05dceec4c34d5cf9"
1185                ),
1186                contract_address_salt: contract_address_salt!(
1187                    "0x655a594122f68f5e821834e606e1243b249a88555fac2d548f7acbee7863f62"
1188                ),
1189                constructor_calldata: vec![
1190                    constructor_param!(
1191                        "0x734d2849eb47e10c59e5a433d425675849cb37338b1d7c4c4afb1e0ca42133"
1192                    ),
1193                    constructor_param!(
1194                        "0xffad0128dbd859ef97a246a2d2c00680dedc8d850ff9b6ebcc8b94ee9625bb"
1195                    ),
1196                ],
1197                class_hash: class_hash!(
1198                    "0x3523d31a077d891b4d888f9d3c7d33bdac2c0a06f89c08307a7f7b68f681c98"
1199                ),
1200            }),
1201        }
1202    }
1203
1204    fn deploy_legacy() -> Transaction {
1205        Transaction {
1206            hash: transaction_hash!(
1207                "0x45c61314be4da85f0e13df53d18062e002c04803218f08061e4b274d4b38537"
1208            ),
1209            variant: TransactionVariant::DeployV0(DeployTransactionV0 {
1210                contract_address: contract_address!(
1211                    "0x2f40faa63fdd5871415b2dcfb1a5e3e1ca06435b3dda6e2ba9df3f726fd3251"
1212                ),
1213                contract_address_salt: contract_address_salt!(
1214                    "0x7284a0367fdd636434f76da25532785690d5f27db40ba38b0cfcbc89a472507"
1215                ),
1216                constructor_calldata: vec![
1217                    constructor_param!(
1218                        "0x635b73abaa9efff71570cb08f3e5014424788470c3b972b952368fb3fc27cc3"
1219                    ),
1220                    constructor_param!(
1221                        "0x7e92479a573a24241ee6f3e4ade742ff37bae4a60bacef5be1caaff5e7e04f3"
1222                    ),
1223                ],
1224                class_hash: class_hash!(
1225                    "0x10455c752b86932ce552f2b0fe81a880746649b9aee7e0d842bf3f52378f9f8"
1226                ),
1227            }),
1228        }
1229    }
1230
1231    fn deploy_account_v1() -> Transaction {
1232        Transaction {
1233            hash: transaction_hash!(
1234                "0x63b72dba5a1b5cdd2585b0c7103242244860453f7013023c1a21f32e1863ec"
1235            ),
1236            variant: TransactionVariant::DeployAccountV1(DeployAccountTransactionV1 {
1237                contract_address: contract_address!(
1238                    "0x3faed8332496d9de9c546e7942b35ba3ea323a6af72d6033f746ea60ecc02ef"
1239                ),
1240                max_fee: fee!("0xb48040809d4b"),
1241                signature: vec![
1242                    transaction_signature_elem!(
1243                        "0x463d21c552a810c59be86c336c0cc68f28e3815eafbe1a2eaf9b3a6fe1c2b82"
1244                    ),
1245                    transaction_signature_elem!(
1246                        "0x2932cb2583da5d8d08f6f0179cc3d4aaae2b46123f02f00bfd544105671adfd"
1247                    ),
1248                ],
1249                nonce: transaction_nonce!("0x0"),
1250                contract_address_salt: contract_address_salt!(
1251                    "0x771b3077f205e2d77c06c9a3bd49d730a4fd8453941d031009fa40936912030"
1252                ),
1253                constructor_calldata: vec![
1254                    call_param!(
1255                        "0x771b3077f205e2d77c06c9a3bd49d730a4fd8453941d031009fa40936912030"
1256                    ),
1257                    call_param!("0x0"),
1258                ],
1259                class_hash: class_hash!(
1260                    "0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003"
1261                ),
1262            }),
1263        }
1264    }
1265
1266    fn deploy_account_v3() -> Transaction {
1267        Transaction {
1268            hash: transaction_hash!(
1269                "0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0"
1270            ),
1271            variant: TransactionVariant::DeployAccountV3(DeployAccountTransactionV3 {
1272                contract_address: contract_address!(
1273                    "0x2fab82e4aef1d8664874e1f194951856d48463c3e6bf9a8c68e234a629a6f50"
1274                ),
1275                resource_bounds: ResourceBounds {
1276                    l1_gas: ResourceBound {
1277                        max_amount: ResourceAmount(0x186a0),
1278                        max_price_per_unit: ResourcePricePerUnit(0x5af3107a4000),
1279                    },
1280                    l2_gas: Default::default(),
1281                    l1_data_gas: Default::default(),
1282                },
1283                constructor_calldata: vec![call_param!(
1284                    "0x5cd65f3d7daea6c63939d659b8473ea0c5cd81576035a4d34e52fb06840196c"
1285                )],
1286                class_hash: class_hash!(
1287                    "0x2338634f11772ea342365abd5be9d9dc8a6f44f159ad782fdebd3db5d969738"
1288                ),
1289                signature: vec![
1290                    transaction_signature_elem!(
1291                        "0x6d756e754793d828c6c1a89c13f7ec70dbd8837dfeea5028a673b80e0d6b4ec"
1292                    ),
1293                    transaction_signature_elem!(
1294                        "0x4daebba599f860daee8f6e100601d98873052e1c61530c630cc4375c6bd48e3"
1295                    ),
1296                ],
1297                ..Default::default()
1298            }),
1299        }
1300    }
1301
1302    fn invoke_v0() -> Transaction {
1303        Transaction {
1304            hash: transaction_hash!(
1305                "0x587d93f2339b7f2beda040187dbfcb9e076ce4a21eb8d15ae64819718817fbe"
1306            ),
1307            variant: TransactionVariant::InvokeV0(InvokeTransactionV0 {
1308                sender_address: contract_address!(
1309                    "0x7463cdd01f6e6a4f13084ea9eee170298b0bbe3faa17f46924c85bb284d4c98"
1310                ),
1311                max_fee: fee!("0x1ee7b2b881350"),
1312                signature: vec![
1313                    transaction_signature_elem!(
1314                        "0x6e82c6752bd13e29b68cf0c8b0d4eb9133b5a056336a842bff01756e514d04a"
1315                    ),
1316                    transaction_signature_elem!(
1317                        "0xa87f00c9e39fd0711aaea4edae0f00044384188a87f489170ac383e3ad087f"
1318                    ),
1319                ],
1320                calldata: vec![
1321                    call_param!("0x3"),
1322                    call_param!(
1323                        "0x72df4dc5b6c4df72e4288857317caf2ce9da166ab8719ab8306516a2fddfff7"
1324                    ),
1325                    call_param!(
1326                        "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c"
1327                    ),
1328                    call_param!("0x0"),
1329                    call_param!("0x3"),
1330                    call_param!(
1331                        "0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10"
1332                    ),
1333                    call_param!(
1334                        "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c"
1335                    ),
1336                    call_param!("0x3"),
1337                    call_param!("0x3"),
1338                    call_param!(
1339                        "0x4aec73f0611a9be0524e7ef21ab1679bdf9c97dc7d72614f15373d431226b6a"
1340                    ),
1341                    call_param!(
1342                        "0x3f35dbce7a07ce455b128890d383c554afbc1b07cf7390a13e2d602a38c1a0a"
1343                    ),
1344                    call_param!("0x6"),
1345                    call_param!("0xa"),
1346                    call_param!("0x10"),
1347                    call_param!(
1348                        "0x4aec73f0611a9be0524e7ef21ab1679bdf9c97dc7d72614f15373d431226b6a"
1349                    ),
1350                    call_param!("0x14934a76f"),
1351                    call_param!("0x0"),
1352                    call_param!(
1353                        "0x4aec73f0611a9be0524e7ef21ab1679bdf9c97dc7d72614f15373d431226b6a"
1354                    ),
1355                    call_param!("0x2613cd2f52b54fb440"),
1356                    call_param!("0x0"),
1357                    call_param!(
1358                        "0x72df4dc5b6c4df72e4288857317caf2ce9da166ab8719ab8306516a2fddfff7"
1359                    ),
1360                    call_param!(
1361                        "0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10"
1362                    ),
1363                    call_param!("0x14934a76f"),
1364                    call_param!("0x0"),
1365                    call_param!("0x2613cd2f52b54fb440"),
1366                    call_param!("0x0"),
1367                    call_param!("0x135740b18"),
1368                    call_param!("0x0"),
1369                    call_param!("0x23caeef429e7df66e0"),
1370                    call_param!("0x0"),
1371                    call_param!("0x17"),
1372                ],
1373                entry_point_selector: entry_point!(
1374                    "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad"
1375                ),
1376                ..Default::default()
1377            }),
1378        }
1379    }
1380
1381    fn invoke_v0_legacy() -> Transaction {
1382        Transaction {
1383            hash: transaction_hash!(
1384                "0x493d8fab73af67e972788e603aee18130facd3c7685f16084ecd98b07153e24"
1385            ),
1386            variant: TransactionVariant::InvokeV0(InvokeTransactionV0 {
1387                sender_address: contract_address!(
1388                    "0x639322e9822149638b70f6de65bc18f3563bd6fa16f0106e8162618eb72f7e"
1389                ),
1390                calldata: vec![
1391                    call_param!(
1392                        "0x49e2e40a0b61a4d6fe4c85cbbf61b5ba372427c852f88509350c4b1eeb88426"
1393                    ),
1394                    call_param!("0x2"),
1395                    call_param!(
1396                        "0x1576521d9ed09609f55b86740de4ae6abdb2837d5d960ae71083ccd39c715d2"
1397                    ),
1398                    call_param!(
1399                        "0x6897cf3003dc45dd016a34ee4309fc97f3bd471513553e64bc070b4eedf4eae"
1400                    ),
1401                ],
1402                entry_point_selector: entry_point!(
1403                    "0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"
1404                ),
1405                ..Default::default()
1406            }),
1407        }
1408    }
1409
1410    fn invoke_v1() -> Transaction {
1411        Transaction {
1412            hash: transaction_hash!(
1413                "0x53ee528f0572d6e43b3318ba59a02be15d51f66d8b5dc1f84af2ccbe606e769"
1414            ),
1415            variant: TransactionVariant::InvokeV1(InvokeTransactionV1 {
1416                sender_address: contract_address!(
1417                    "0x3b184c08ea47b80bbe024f42ca94210de552fe2096b0907b6a45809fee82779"
1418                ),
1419                max_fee: fee!("0x125c44c433000"),
1420                nonce: transaction_nonce!("0x1b"),
1421                signature: vec![
1422                    transaction_signature_elem!(
1423                        "0x50e7acc40dcdcad7bf5a758a85f6676620be6f76668913e07c58c4a8d4a45f8"
1424                    ),
1425                    transaction_signature_elem!(
1426                        "0x5eb8f2407a69ed0c19565267c0c67b588056f7201e471d687a3041be3732f35"
1427                    ),
1428                ],
1429                calldata: vec![
1430                    call_param!("0x1"),
1431                    call_param!(
1432                        "0x4c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05"
1433                    ),
1434                    call_param!(
1435                        "0x2f68935fe2620d447e6dee46fb77624aee380c157f7675e9e4220599f4a04bd"
1436                    ),
1437                    call_param!("0x0"),
1438                    call_param!("0x1"),
1439                    call_param!("0x1"),
1440                    call_param!(
1441                        "0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8"
1442                    ),
1443                ],
1444            }),
1445        }
1446    }
1447
1448    fn invoke_v3() -> Transaction {
1449        Transaction {
1450            hash: transaction_hash!(
1451                "0x22772429229cbca26cb062f6f6a0991a4e84d0f11f3b1bda1913613a5e609e0"
1452            ),
1453            variant: TransactionVariant::InvokeV3(InvokeTransactionV3 {
1454                signature: vec![
1455                    transaction_signature_elem!(
1456                        "0x389bca189562763f6a73da4aaab30d87d8bbc243571f4a353c48493a43a0634"
1457                    ),
1458                    transaction_signature_elem!(
1459                        "0x62d30041a0b1199b3ad93515066d5c7791211fa32f585956fafe630082270e9"
1460                    ),
1461                ],
1462                nonce: transaction_nonce!("0x1084b"),
1463                nonce_data_availability_mode: DataAvailabilityMode::L1,
1464                fee_data_availability_mode: DataAvailabilityMode::L1,
1465                resource_bounds: ResourceBounds {
1466                    l1_gas: ResourceBound {
1467                        max_amount: ResourceAmount(0x61a80),
1468                        max_price_per_unit: ResourcePricePerUnit(0x5af3107a4000),
1469                    },
1470                    l2_gas: Default::default(),
1471                    l1_data_gas: Default::default(),
1472                },
1473                sender_address: contract_address!(
1474                    "0x35acd6dd6c5045d18ca6d0192af46b335a5402c02d41f46e4e77ea2c951d9a3"
1475                ),
1476                calldata: vec![
1477                    call_param!("0x1"),
1478                    call_param!(
1479                        "0x47ad6a25df680763e5663bd0eba3d2bfd18b24b1e8f6bd36b71c37433c63ed0"
1480                    ),
1481                    call_param!(
1482                        "0x19a35a6e95cb7a3318dbb244f20975a1cd8587cc6b5259f15f61d7beb7ee43b"
1483                    ),
1484                    call_param!("0x2"),
1485                    call_param!(
1486                        "0x4d0b88ace5705bb7825f91ee95557d906600b7e7762f5615e6a4f407185a43a"
1487                    ),
1488                    call_param!(
1489                        "0x630ac7edd6c7c097e4f9774fe5855bed3a2b8886286c61f1f7afd601e124d60"
1490                    ),
1491                ],
1492                ..Default::default()
1493            }),
1494        }
1495    }
1496
1497    fn l1_handler() -> Transaction {
1498        Transaction {
1499            hash: transaction_hash!(
1500                "0x8d7d99f96167a01f2406ae25dd6bdeb4f903fd4ed433d96dcf2564b7ab0a8f"
1501            ),
1502            variant: TransactionVariant::L1Handler(L1HandlerTransaction {
1503                contract_address: contract_address!(
1504                    "0x73314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82"
1505                ),
1506                entry_point_selector: entry_point!(
1507                    "0x2d757788a8d8d6f21d1cd40bce38a8222d70654214e96ff95d8086e684fbee5"
1508                ),
1509                nonce: transaction_nonce!("0x18cb20"),
1510                calldata: vec![
1511                    call_param!("0xae0ee0a63a2ce6baeeffe56e7714fb4efe48d419"),
1512                    call_param!(
1513                        "0x13f55ae8d173a036cf8bdf0448f04b835a5d42cda5fe6b4678217ed92cabc94"
1514                    ),
1515                    call_param!("0xd7621dc58210000"),
1516                    call_param!("0x0"),
1517                ],
1518            }),
1519        }
1520    }
1521
1522    fn l1_handler_v07() -> Transaction {
1523        Transaction {
1524            hash: transaction_hash!(
1525                "0x61b518bb1f97c49244b8a7a1a984798b4c2876d42920eca2b6ba8dfb1bddc54"
1526            ),
1527            variant: TransactionVariant::L1Handler(L1HandlerTransaction {
1528                contract_address: contract_address!(
1529                    "0xda8054260ec00606197a4103eb2ef08d6c8af0b6a808b610152d1ce498f8c3"
1530                ),
1531                entry_point_selector: entry_point!(
1532                    "0xe3f5e9e1456ffa52a3fbc7e8c296631d4cc2120c0be1e2829301c0d8fa026b"
1533                ),
1534                nonce: transaction_nonce!("0x0"),
1535                calldata: vec![
1536                    call_param!("0x142273bcbfca76512b2a05aed21f134c4495208"),
1537                    call_param!("0xa0c316cb0bb0c9632315ddc8f49c7921f2c80daa"),
1538                    call_param!("0x2"),
1539                    call_param!(
1540                        "0x453b0310bcdfa50d3c2e7f757e284ac6cd4171933a4e67d1bdcfdbc7f3cbc93"
1541                    ),
1542                ],
1543            }),
1544        }
1545    }
1546
1547    fn l1_handler_legacy() -> Transaction {
1548        Transaction {
1549            hash: transaction_hash!(
1550                "0xfb118dc1d4a4141b7718da4b7fa98980b11caf5aa5d6e1e35e9b050aae788b"
1551            ),
1552            variant: TransactionVariant::L1Handler(L1HandlerTransaction {
1553                contract_address: contract_address!(
1554                    "0x55a46448decca3b138edf0104b7a47d41365b8293bdfd59b03b806c102b12b7"
1555                ),
1556                entry_point_selector: entry_point!(
1557                    "0xc73f681176fc7b3f9693986fd7b14581e8d540519e27400e88b8713932be01"
1558                ),
1559                calldata: vec![
1560                    call_param!("0x2db8c2615db39a5ed8750b87ac8f217485be11ec"),
1561                    call_param!("0xbc614e"),
1562                    call_param!("0x258"),
1563                ],
1564                ..Default::default()
1565            }),
1566        }
1567    }
1568
1569    #[test]
1570    fn invoke_v1_with_query_version() {
1571        let invoke = TransactionVariant::InvokeV1(InvokeTransactionV1 {
1572            sender_address: contract_address!(
1573                "0x05b53783880534bf39ba4224ffbf6cdbca5d9f8f018a21cf1fe870dff409b3ce"
1574            ),
1575            max_fee: fee!("0x0"),
1576            nonce: transaction_nonce!("0x3"),
1577            signature: vec![
1578                transaction_signature_elem!(
1579                    "0x29784653f04451ad9abb301d06320816756396b0bda4e598559eff4718fe6f9"
1580                ),
1581                transaction_signature_elem!(
1582                    "0x6c5288a10f44612ffdbfa8681af54f97e53339a5119713bdee36a05485abe60"
1583                ),
1584            ],
1585            calldata: vec![
1586                call_param!("0x1"),
1587                call_param!("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
1588                call_param!("0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e"),
1589                call_param!("0x0"),
1590                call_param!("0x3"),
1591                call_param!("0x3"),
1592                call_param!("0x5b53783880534bf39ba4224ffbf6cdbca5d9f8f018a21cf1fe870dff409b3ce"),
1593                call_param!("0x9184e72a000"),
1594                call_param!("0x0"),
1595            ],
1596        });
1597
1598        assert_eq!(
1599            transaction_hash!("0x00acd1213b669b094390c5b70a447cb2335ee40bbe21c4544db57450aa0e5c04"),
1600            invoke.calculate_hash(GOERLI_TESTNET, true)
1601        );
1602    }
1603}