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 #[case::declare_v0(declare_v0(), GOERLI_TESTNET)]
1087 #[case::declare_v1(declare_v1(), ChainId::SEPOLIA_TESTNET)]
1088 #[case::declare_v2(declare_v2(), ChainId::SEPOLIA_TESTNET)]
1089 #[case::declare_v3(declare_v3(), GOERLI_TESTNET)]
1090 #[case::deploy(deploy(), GOERLI_TESTNET)]
1091 #[case::deploy_legacy(deploy_legacy(), GOERLI_TESTNET)]
1092 #[case::deploy_account_v1(deploy_account_v1(), ChainId::MAINNET)]
1093 #[case::deploy_account_v3(deploy_account_v3(), GOERLI_TESTNET)]
1094 #[case::invoke_v0(invoke_v0(), GOERLI_TESTNET)]
1095 #[case::invoke_v0_legacy(invoke_v0_legacy(), GOERLI_TESTNET)]
1096 #[case::invoke_v1(invoke_v1(), ChainId::MAINNET)]
1097 #[case::invoke_v3(invoke_v3(), ChainId::SEPOLIA_TESTNET)]
1098 #[case::invoke_v3_with_proof_facts(invoke_v3_with_proof_facts(), ChainId::MAINNET)]
1099 #[case::l1_handler(l1_handler(), ChainId::MAINNET)]
1100 #[case::l1_handler_v07(l1_handler_v07(), ChainId::MAINNET)]
1101 #[case::l1_handler_legacy(l1_handler_legacy(), GOERLI_TESTNET)]
1102 fn verify_hash(#[case] transaction: Transaction, #[case] chain_id: ChainId) {
1103 assert!(transaction.verify_hash(chain_id));
1104 }
1105
1106 fn declare_v0() -> Transaction {
1107 Transaction {
1108 hash: transaction_hash!(
1109 "0x6d346ba207eb124355960c19c737698ad37a3c920a588b741e0130ff5bd4d6d"
1110 ),
1111 variant: TransactionVariant::DeclareV0(DeclareTransactionV0V1 {
1112 class_hash: class_hash!(
1113 "0x71e6ef53e53e6f5ca792fc4a5799a33e6f4118e4fd1d948dca3a371506f0cc7"
1114 ),
1115 sender_address: contract_address!("0x1"),
1116 ..Default::default()
1117 }),
1118 }
1119 }
1120
1121 fn declare_v1() -> Transaction {
1122 Transaction {
1123 hash: transaction_hash!(
1124 "0xb2d88f64d9655a7d47a5519d66b969168d02d0d33f6476f0d2539c51686329"
1125 ),
1126 variant: TransactionVariant::DeclareV1(DeclareTransactionV0V1 {
1127 class_hash: class_hash!(
1128 "0x3131fa018d520a037686ce3efddeab8f28895662f019ca3ca18a626650f7d1e"
1129 ),
1130 max_fee: fee!("0x625e5879c08f4"),
1131 nonce: transaction_nonce!("0x7"),
1132 signature: vec![
1133 transaction_signature_elem!(
1134 "0x3609667964a8ed946bc507721ec35a851d97a097d159ef0ec2af8fab490223f"
1135 ),
1136 transaction_signature_elem!(
1137 "0x68846bad9f0f010fac4eeaf39f9dd609b28765fd2336b70ce026e33e2421c15"
1138 ),
1139 ],
1140 sender_address: contract_address!(
1141 "0x68922eb87daed71fc3099031e178b6534fc39a570022342e8c166024da893f5"
1142 ),
1143 }),
1144 }
1145 }
1146
1147 fn declare_v2() -> Transaction {
1148 Transaction {
1149 hash: transaction_hash!(
1150 "0x4cacc2bbdd5ec77b20e908f311ab27d6495b69761e929bb24ba02632716944"
1151 ),
1152 variant: TransactionVariant::DeclareV2(DeclareTransactionV2 {
1153 class_hash: class_hash!(
1154 "0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003"
1155 ),
1156 max_fee: fee!("0x92fa1ac712614"),
1157 nonce: transaction_nonce!("0x6"),
1158 signature: vec![
1159 transaction_signature_elem!(
1160 "0x4ab3e77908396c66b39326f52334b447fe878d1d899a287c9e3cf7bd09839ea"
1161 ),
1162 transaction_signature_elem!(
1163 "0x79a56f9e61eb834f1ac524eb35da33cccf92ff3b01a7a8eaf68cbb64bebdba9"
1164 ),
1165 ],
1166 sender_address: contract_address!(
1167 "0x68922eb87daed71fc3099031e178b6534fc39a570022342e8c166024da893f5"
1168 ),
1169 compiled_class_hash: casm_hash!(
1170 "0x29787a427a423ffc5986d43e630077a176e4391fcef3ebf36014b154069ae4"
1171 ),
1172 }),
1173 }
1174 }
1175
1176 fn declare_v3() -> Transaction {
1177 Transaction {
1178 hash: transaction_hash!(
1179 "0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3"
1180 ),
1181 variant: TransactionVariant::DeclareV3(DeclareTransactionV3 {
1182 signature: vec![
1183 transaction_signature_elem!(
1184 "0x29a49dff154fede73dd7b5ca5a0beadf40b4b069f3a850cd8428e54dc809ccc"
1185 ),
1186 transaction_signature_elem!(
1187 "0x429d142a17223b4f2acde0f5ecb9ad453e188b245003c86fab5c109bad58fc3"
1188 ),
1189 ],
1190 nonce: transaction_nonce!("0x1"),
1191 nonce_data_availability_mode: DataAvailabilityMode::L1,
1192 fee_data_availability_mode: DataAvailabilityMode::L1,
1193 resource_bounds: ResourceBounds {
1194 l1_gas: ResourceBound {
1195 max_amount: ResourceAmount(0x186a0),
1196 max_price_per_unit: ResourcePricePerUnit(0x2540be400),
1197 },
1198 l2_gas: Default::default(),
1199 l1_data_gas: Default::default(),
1200 },
1201 sender_address: contract_address!(
1202 "0x2fab82e4aef1d8664874e1f194951856d48463c3e6bf9a8c68e234a629a6f50"
1203 ),
1204 class_hash: class_hash!(
1205 "0x5ae9d09292a50ed48c5930904c880dab56e85b825022a7d689cfc9e65e01ee7"
1206 ),
1207 compiled_class_hash: casm_hash!(
1208 "0x1add56d64bebf8140f3b8a38bdf102b7874437f0c861ab4ca7526ec33b4d0f8"
1209 ),
1210 ..Default::default()
1211 }),
1212 }
1213 }
1214
1215 fn deploy() -> Transaction {
1216 Transaction {
1217 hash: transaction_hash!(
1218 "0x3d7623443283d9a0cec946492db78b06d57642a551745ddfac8d3f1f4fcc2a8"
1219 ),
1220 variant: TransactionVariant::DeployV0(DeployTransactionV0 {
1221 contract_address: contract_address!(
1222 "0x54c6883e459baeac4a9052ee109b86b9f81adbcdcb1f65a05dceec4c34d5cf9"
1223 ),
1224 contract_address_salt: contract_address_salt!(
1225 "0x655a594122f68f5e821834e606e1243b249a88555fac2d548f7acbee7863f62"
1226 ),
1227 constructor_calldata: vec![
1228 constructor_param!(
1229 "0x734d2849eb47e10c59e5a433d425675849cb37338b1d7c4c4afb1e0ca42133"
1230 ),
1231 constructor_param!(
1232 "0xffad0128dbd859ef97a246a2d2c00680dedc8d850ff9b6ebcc8b94ee9625bb"
1233 ),
1234 ],
1235 class_hash: class_hash!(
1236 "0x3523d31a077d891b4d888f9d3c7d33bdac2c0a06f89c08307a7f7b68f681c98"
1237 ),
1238 }),
1239 }
1240 }
1241
1242 fn deploy_legacy() -> Transaction {
1243 Transaction {
1244 hash: transaction_hash!(
1245 "0x45c61314be4da85f0e13df53d18062e002c04803218f08061e4b274d4b38537"
1246 ),
1247 variant: TransactionVariant::DeployV0(DeployTransactionV0 {
1248 contract_address: contract_address!(
1249 "0x2f40faa63fdd5871415b2dcfb1a5e3e1ca06435b3dda6e2ba9df3f726fd3251"
1250 ),
1251 contract_address_salt: contract_address_salt!(
1252 "0x7284a0367fdd636434f76da25532785690d5f27db40ba38b0cfcbc89a472507"
1253 ),
1254 constructor_calldata: vec![
1255 constructor_param!(
1256 "0x635b73abaa9efff71570cb08f3e5014424788470c3b972b952368fb3fc27cc3"
1257 ),
1258 constructor_param!(
1259 "0x7e92479a573a24241ee6f3e4ade742ff37bae4a60bacef5be1caaff5e7e04f3"
1260 ),
1261 ],
1262 class_hash: class_hash!(
1263 "0x10455c752b86932ce552f2b0fe81a880746649b9aee7e0d842bf3f52378f9f8"
1264 ),
1265 }),
1266 }
1267 }
1268
1269 fn deploy_account_v1() -> Transaction {
1270 Transaction {
1271 hash: transaction_hash!(
1272 "0x63b72dba5a1b5cdd2585b0c7103242244860453f7013023c1a21f32e1863ec"
1273 ),
1274 variant: TransactionVariant::DeployAccountV1(DeployAccountTransactionV1 {
1275 contract_address: contract_address!(
1276 "0x3faed8332496d9de9c546e7942b35ba3ea323a6af72d6033f746ea60ecc02ef"
1277 ),
1278 max_fee: fee!("0xb48040809d4b"),
1279 signature: vec![
1280 transaction_signature_elem!(
1281 "0x463d21c552a810c59be86c336c0cc68f28e3815eafbe1a2eaf9b3a6fe1c2b82"
1282 ),
1283 transaction_signature_elem!(
1284 "0x2932cb2583da5d8d08f6f0179cc3d4aaae2b46123f02f00bfd544105671adfd"
1285 ),
1286 ],
1287 nonce: transaction_nonce!("0x0"),
1288 contract_address_salt: contract_address_salt!(
1289 "0x771b3077f205e2d77c06c9a3bd49d730a4fd8453941d031009fa40936912030"
1290 ),
1291 constructor_calldata: vec![
1292 call_param!(
1293 "0x771b3077f205e2d77c06c9a3bd49d730a4fd8453941d031009fa40936912030"
1294 ),
1295 call_param!("0x0"),
1296 ],
1297 class_hash: class_hash!(
1298 "0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003"
1299 ),
1300 }),
1301 }
1302 }
1303
1304 fn deploy_account_v3() -> Transaction {
1305 Transaction {
1306 hash: transaction_hash!(
1307 "0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0"
1308 ),
1309 variant: TransactionVariant::DeployAccountV3(DeployAccountTransactionV3 {
1310 contract_address: contract_address!(
1311 "0x2fab82e4aef1d8664874e1f194951856d48463c3e6bf9a8c68e234a629a6f50"
1312 ),
1313 resource_bounds: ResourceBounds {
1314 l1_gas: ResourceBound {
1315 max_amount: ResourceAmount(0x186a0),
1316 max_price_per_unit: ResourcePricePerUnit(0x5af3107a4000),
1317 },
1318 l2_gas: Default::default(),
1319 l1_data_gas: Default::default(),
1320 },
1321 constructor_calldata: vec![call_param!(
1322 "0x5cd65f3d7daea6c63939d659b8473ea0c5cd81576035a4d34e52fb06840196c"
1323 )],
1324 class_hash: class_hash!(
1325 "0x2338634f11772ea342365abd5be9d9dc8a6f44f159ad782fdebd3db5d969738"
1326 ),
1327 signature: vec![
1328 transaction_signature_elem!(
1329 "0x6d756e754793d828c6c1a89c13f7ec70dbd8837dfeea5028a673b80e0d6b4ec"
1330 ),
1331 transaction_signature_elem!(
1332 "0x4daebba599f860daee8f6e100601d98873052e1c61530c630cc4375c6bd48e3"
1333 ),
1334 ],
1335 ..Default::default()
1336 }),
1337 }
1338 }
1339
1340 fn invoke_v0() -> Transaction {
1341 Transaction {
1342 hash: transaction_hash!(
1343 "0x587d93f2339b7f2beda040187dbfcb9e076ce4a21eb8d15ae64819718817fbe"
1344 ),
1345 variant: TransactionVariant::InvokeV0(InvokeTransactionV0 {
1346 sender_address: contract_address!(
1347 "0x7463cdd01f6e6a4f13084ea9eee170298b0bbe3faa17f46924c85bb284d4c98"
1348 ),
1349 max_fee: fee!("0x1ee7b2b881350"),
1350 signature: vec![
1351 transaction_signature_elem!(
1352 "0x6e82c6752bd13e29b68cf0c8b0d4eb9133b5a056336a842bff01756e514d04a"
1353 ),
1354 transaction_signature_elem!(
1355 "0xa87f00c9e39fd0711aaea4edae0f00044384188a87f489170ac383e3ad087f"
1356 ),
1357 ],
1358 calldata: vec![
1359 call_param!("0x3"),
1360 call_param!(
1361 "0x72df4dc5b6c4df72e4288857317caf2ce9da166ab8719ab8306516a2fddfff7"
1362 ),
1363 call_param!(
1364 "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c"
1365 ),
1366 call_param!("0x0"),
1367 call_param!("0x3"),
1368 call_param!(
1369 "0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10"
1370 ),
1371 call_param!(
1372 "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c"
1373 ),
1374 call_param!("0x3"),
1375 call_param!("0x3"),
1376 call_param!(
1377 "0x4aec73f0611a9be0524e7ef21ab1679bdf9c97dc7d72614f15373d431226b6a"
1378 ),
1379 call_param!(
1380 "0x3f35dbce7a07ce455b128890d383c554afbc1b07cf7390a13e2d602a38c1a0a"
1381 ),
1382 call_param!("0x6"),
1383 call_param!("0xa"),
1384 call_param!("0x10"),
1385 call_param!(
1386 "0x4aec73f0611a9be0524e7ef21ab1679bdf9c97dc7d72614f15373d431226b6a"
1387 ),
1388 call_param!("0x14934a76f"),
1389 call_param!("0x0"),
1390 call_param!(
1391 "0x4aec73f0611a9be0524e7ef21ab1679bdf9c97dc7d72614f15373d431226b6a"
1392 ),
1393 call_param!("0x2613cd2f52b54fb440"),
1394 call_param!("0x0"),
1395 call_param!(
1396 "0x72df4dc5b6c4df72e4288857317caf2ce9da166ab8719ab8306516a2fddfff7"
1397 ),
1398 call_param!(
1399 "0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10"
1400 ),
1401 call_param!("0x14934a76f"),
1402 call_param!("0x0"),
1403 call_param!("0x2613cd2f52b54fb440"),
1404 call_param!("0x0"),
1405 call_param!("0x135740b18"),
1406 call_param!("0x0"),
1407 call_param!("0x23caeef429e7df66e0"),
1408 call_param!("0x0"),
1409 call_param!("0x17"),
1410 ],
1411 entry_point_selector: entry_point!(
1412 "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad"
1413 ),
1414 ..Default::default()
1415 }),
1416 }
1417 }
1418
1419 fn invoke_v0_legacy() -> Transaction {
1420 Transaction {
1421 hash: transaction_hash!(
1422 "0x493d8fab73af67e972788e603aee18130facd3c7685f16084ecd98b07153e24"
1423 ),
1424 variant: TransactionVariant::InvokeV0(InvokeTransactionV0 {
1425 sender_address: contract_address!(
1426 "0x639322e9822149638b70f6de65bc18f3563bd6fa16f0106e8162618eb72f7e"
1427 ),
1428 calldata: vec![
1429 call_param!(
1430 "0x49e2e40a0b61a4d6fe4c85cbbf61b5ba372427c852f88509350c4b1eeb88426"
1431 ),
1432 call_param!("0x2"),
1433 call_param!(
1434 "0x1576521d9ed09609f55b86740de4ae6abdb2837d5d960ae71083ccd39c715d2"
1435 ),
1436 call_param!(
1437 "0x6897cf3003dc45dd016a34ee4309fc97f3bd471513553e64bc070b4eedf4eae"
1438 ),
1439 ],
1440 entry_point_selector: entry_point!(
1441 "0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"
1442 ),
1443 ..Default::default()
1444 }),
1445 }
1446 }
1447
1448 fn invoke_v1() -> Transaction {
1449 Transaction {
1450 hash: transaction_hash!(
1451 "0x53ee528f0572d6e43b3318ba59a02be15d51f66d8b5dc1f84af2ccbe606e769"
1452 ),
1453 variant: TransactionVariant::InvokeV1(InvokeTransactionV1 {
1454 sender_address: contract_address!(
1455 "0x3b184c08ea47b80bbe024f42ca94210de552fe2096b0907b6a45809fee82779"
1456 ),
1457 max_fee: fee!("0x125c44c433000"),
1458 nonce: transaction_nonce!("0x1b"),
1459 signature: vec![
1460 transaction_signature_elem!(
1461 "0x50e7acc40dcdcad7bf5a758a85f6676620be6f76668913e07c58c4a8d4a45f8"
1462 ),
1463 transaction_signature_elem!(
1464 "0x5eb8f2407a69ed0c19565267c0c67b588056f7201e471d687a3041be3732f35"
1465 ),
1466 ],
1467 calldata: vec![
1468 call_param!("0x1"),
1469 call_param!(
1470 "0x4c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05"
1471 ),
1472 call_param!(
1473 "0x2f68935fe2620d447e6dee46fb77624aee380c157f7675e9e4220599f4a04bd"
1474 ),
1475 call_param!("0x0"),
1476 call_param!("0x1"),
1477 call_param!("0x1"),
1478 call_param!(
1479 "0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8"
1480 ),
1481 ],
1482 }),
1483 }
1484 }
1485
1486 fn invoke_v3() -> Transaction {
1487 Transaction {
1488 hash: transaction_hash!(
1489 "0x22772429229cbca26cb062f6f6a0991a4e84d0f11f3b1bda1913613a5e609e0"
1490 ),
1491 variant: TransactionVariant::InvokeV3(InvokeTransactionV3 {
1492 signature: vec![
1493 transaction_signature_elem!(
1494 "0x389bca189562763f6a73da4aaab30d87d8bbc243571f4a353c48493a43a0634"
1495 ),
1496 transaction_signature_elem!(
1497 "0x62d30041a0b1199b3ad93515066d5c7791211fa32f585956fafe630082270e9"
1498 ),
1499 ],
1500 nonce: transaction_nonce!("0x1084b"),
1501 nonce_data_availability_mode: DataAvailabilityMode::L1,
1502 fee_data_availability_mode: DataAvailabilityMode::L1,
1503 resource_bounds: ResourceBounds {
1504 l1_gas: ResourceBound {
1505 max_amount: ResourceAmount(0x61a80),
1506 max_price_per_unit: ResourcePricePerUnit(0x5af3107a4000),
1507 },
1508 l2_gas: Default::default(),
1509 l1_data_gas: Default::default(),
1510 },
1511 sender_address: contract_address!(
1512 "0x35acd6dd6c5045d18ca6d0192af46b335a5402c02d41f46e4e77ea2c951d9a3"
1513 ),
1514 calldata: vec![
1515 call_param!("0x1"),
1516 call_param!(
1517 "0x47ad6a25df680763e5663bd0eba3d2bfd18b24b1e8f6bd36b71c37433c63ed0"
1518 ),
1519 call_param!(
1520 "0x19a35a6e95cb7a3318dbb244f20975a1cd8587cc6b5259f15f61d7beb7ee43b"
1521 ),
1522 call_param!("0x2"),
1523 call_param!(
1524 "0x4d0b88ace5705bb7825f91ee95557d906600b7e7762f5615e6a4f407185a43a"
1525 ),
1526 call_param!(
1527 "0x630ac7edd6c7c097e4f9774fe5855bed3a2b8886286c61f1f7afd601e124d60"
1528 ),
1529 ],
1530 ..Default::default()
1531 }),
1532 }
1533 }
1534
1535 fn invoke_v3_with_proof_facts() -> Transaction {
1538 Transaction {
1539 hash: transaction_hash!(
1540 "0x6d885b1a2b7cb7946480c63aa1697888a33e9ccd0b1516f41c41731a1628726"
1541 ),
1542 variant: TransactionVariant::InvokeV3(InvokeTransactionV3 {
1543 signature: vec![
1544 transaction_signature_elem!(
1545 "0x389bca189562763f6a73da4aaab30d87d8bbc243571f4a353c48493a43a0634"
1546 ),
1547 transaction_signature_elem!(
1548 "0x62d30041a0b1199b3ad93515066d5c7791211fa32f585956fafe630082270e9"
1549 ),
1550 ],
1551 nonce: transaction_nonce!("0x9d"),
1552 nonce_data_availability_mode: DataAvailabilityMode::L1,
1553 fee_data_availability_mode: DataAvailabilityMode::L1,
1554 resource_bounds: ResourceBounds {
1555 l1_gas: ResourceBound {
1556 max_amount: ResourceAmount(0xa9e),
1557 max_price_per_unit: ResourcePricePerUnit(0x7f2a1ad4f2f1),
1558 },
1559 l2_gas: Default::default(),
1560 l1_data_gas: Default::default(),
1561 },
1562 sender_address: contract_address!(
1563 "0x69c0f9bcd79697bdceaf7748e3ff8f34aa39e4063ce44896af664c0c96f6c10"
1564 ),
1565 calldata: vec![
1566 call_param!("0x1"),
1567 call_param!(
1568 "0x4c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05"
1569 ),
1570 call_param!(
1571 "0x3943907ef0ef6f9d2e2408b05e520a66daaf74293dbf665e5a20b117676170e"
1572 ),
1573 call_param!("0x2"),
1574 call_param!(
1575 "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"
1576 ),
1577 call_param!("0x16345785d8a0000"),
1578 ],
1579 proof_facts: vec![
1580 proof_fact_elem!("0x1"),
1581 proof_fact_elem!("0x2"),
1582 proof_fact_elem!("0x3"),
1583 ],
1584 ..Default::default()
1585 }),
1586 }
1587 }
1588
1589 fn l1_handler() -> Transaction {
1590 Transaction {
1591 hash: transaction_hash!(
1592 "0x8d7d99f96167a01f2406ae25dd6bdeb4f903fd4ed433d96dcf2564b7ab0a8f"
1593 ),
1594 variant: TransactionVariant::L1Handler(L1HandlerTransaction {
1595 contract_address: contract_address!(
1596 "0x73314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82"
1597 ),
1598 entry_point_selector: entry_point!(
1599 "0x2d757788a8d8d6f21d1cd40bce38a8222d70654214e96ff95d8086e684fbee5"
1600 ),
1601 nonce: transaction_nonce!("0x18cb20"),
1602 calldata: vec![
1603 call_param!("0xae0ee0a63a2ce6baeeffe56e7714fb4efe48d419"),
1604 call_param!(
1605 "0x13f55ae8d173a036cf8bdf0448f04b835a5d42cda5fe6b4678217ed92cabc94"
1606 ),
1607 call_param!("0xd7621dc58210000"),
1608 call_param!("0x0"),
1609 ],
1610 }),
1611 }
1612 }
1613
1614 fn l1_handler_v07() -> Transaction {
1615 Transaction {
1616 hash: transaction_hash!(
1617 "0x61b518bb1f97c49244b8a7a1a984798b4c2876d42920eca2b6ba8dfb1bddc54"
1618 ),
1619 variant: TransactionVariant::L1Handler(L1HandlerTransaction {
1620 contract_address: contract_address!(
1621 "0xda8054260ec00606197a4103eb2ef08d6c8af0b6a808b610152d1ce498f8c3"
1622 ),
1623 entry_point_selector: entry_point!(
1624 "0xe3f5e9e1456ffa52a3fbc7e8c296631d4cc2120c0be1e2829301c0d8fa026b"
1625 ),
1626 nonce: transaction_nonce!("0x0"),
1627 calldata: vec![
1628 call_param!("0x142273bcbfca76512b2a05aed21f134c4495208"),
1629 call_param!("0xa0c316cb0bb0c9632315ddc8f49c7921f2c80daa"),
1630 call_param!("0x2"),
1631 call_param!(
1632 "0x453b0310bcdfa50d3c2e7f757e284ac6cd4171933a4e67d1bdcfdbc7f3cbc93"
1633 ),
1634 ],
1635 }),
1636 }
1637 }
1638
1639 fn l1_handler_legacy() -> Transaction {
1640 Transaction {
1641 hash: transaction_hash!(
1642 "0xfb118dc1d4a4141b7718da4b7fa98980b11caf5aa5d6e1e35e9b050aae788b"
1643 ),
1644 variant: TransactionVariant::L1Handler(L1HandlerTransaction {
1645 contract_address: contract_address!(
1646 "0x55a46448decca3b138edf0104b7a47d41365b8293bdfd59b03b806c102b12b7"
1647 ),
1648 entry_point_selector: entry_point!(
1649 "0xc73f681176fc7b3f9693986fd7b14581e8d540519e27400e88b8713932be01"
1650 ),
1651 calldata: vec![
1652 call_param!("0x2db8c2615db39a5ed8750b87ac8f217485be11ec"),
1653 call_param!("0xbc614e"),
1654 call_param!("0x258"),
1655 ],
1656 ..Default::default()
1657 }),
1658 }
1659 }
1660
1661 #[test]
1662 fn invoke_v1_with_query_version() {
1663 let invoke = TransactionVariant::InvokeV1(InvokeTransactionV1 {
1664 sender_address: contract_address!(
1665 "0x05b53783880534bf39ba4224ffbf6cdbca5d9f8f018a21cf1fe870dff409b3ce"
1666 ),
1667 max_fee: fee!("0x0"),
1668 nonce: transaction_nonce!("0x3"),
1669 signature: vec![
1670 transaction_signature_elem!(
1671 "0x29784653f04451ad9abb301d06320816756396b0bda4e598559eff4718fe6f9"
1672 ),
1673 transaction_signature_elem!(
1674 "0x6c5288a10f44612ffdbfa8681af54f97e53339a5119713bdee36a05485abe60"
1675 ),
1676 ],
1677 calldata: vec![
1678 call_param!("0x1"),
1679 call_param!("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
1680 call_param!("0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e"),
1681 call_param!("0x0"),
1682 call_param!("0x3"),
1683 call_param!("0x3"),
1684 call_param!("0x5b53783880534bf39ba4224ffbf6cdbca5d9f8f018a21cf1fe870dff409b3ce"),
1685 call_param!("0x9184e72a000"),
1686 call_param!("0x0"),
1687 ],
1688 });
1689
1690 assert_eq!(
1691 transaction_hash!("0x00acd1213b669b094390c5b70a447cb2335ee40bbe21c4544db57450aa0e5c04"),
1692 invoke.calculate_hash(GOERLI_TESTNET, true)
1693 );
1694 }
1695}