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