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 #[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 if Some(expected) == self.calculate_legacy_hash(chain_id) {
87 return true;
88 }
89
90 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 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 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 return H256::zero();
584 };
585
586 let mut hash = Keccak256::new();
587
588 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 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 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 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
835struct 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 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 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 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}