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