1use std::sync::Arc;
2
3use blockifier::blockifier_versioned_constants::VersionedConstants;
4use blockifier::state::cached_state::StateMaps;
5use blockifier::state::state_api::StateReader;
6use blockifier::transaction::account_transaction::ExecutionFlags;
7use blockifier::transaction::objects::TransactionExecutionInfo;
8use deploy_transaction::DeployTransaction;
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use starknet_api::block::{BlockNumber, GasPrice};
11use starknet_api::contract_class::{ClassInfo, EntryPointType};
12use starknet_api::core::calculate_contract_address;
13use starknet_api::data_availability::DataAvailabilityMode;
14use starknet_api::transaction::fields::{
15 AllResourceBounds, GasVectorComputationMode, Tip, ValidResourceBounds,
16};
17use starknet_api::transaction::{TransactionHasher, TransactionOptions, signed_tx_version};
18use starknet_rs_core::types::{
19 EventsPage, ExecutionResult, Felt, ResourceBounds, ResourceBoundsMapping,
20 TransactionExecutionStatus,
21};
22use starknet_rs_core::utils::parse_cairo_short_string;
23
24use self::broadcasted_declare_transaction_v3::BroadcastedDeclareTransactionV3;
25use self::broadcasted_deploy_account_transaction_v3::BroadcastedDeployAccountTransactionV3;
26use self::broadcasted_invoke_transaction_v3::BroadcastedInvokeTransactionV3;
27use self::declare_transaction_v3::DeclareTransactionV3;
28use self::deploy_account_transaction_v3::DeployAccountTransactionV3;
29use self::invoke_transaction_v3::InvokeTransactionV3;
30use self::l1_handler_transaction::L1HandlerTransaction;
31use super::block::BlockId;
32use super::estimate_message_fee::FeeEstimateWrapper;
33use super::felt::BlockHash;
34use super::messaging::{MessageToL1, OrderedMessageToL1};
35use super::state::ThinStateDiff;
36use super::transaction_receipt::{ExecutionResources, FeeInUnits, TransactionReceipt};
37use crate::constants::QUERY_VERSION_OFFSET;
38use crate::contract_address::ContractAddress;
39use crate::contract_class::{ContractClass, compute_sierra_class_hash};
40use crate::emitted_event::{Event, OrderedEvent};
41use crate::error::{ConversionError, DevnetResult};
42use crate::felt::{
43 Calldata, ClassHash, EntryPointSelector, Nonce, ProofFacts, TransactionHash,
44 TransactionSignature, TransactionVersion,
45};
46use crate::patricia_key::StorageKey;
47use crate::proof::Proof;
48use crate::rpc::transaction_receipt::CommonTransactionReceipt;
49use crate::{impl_wrapper_deserialize, impl_wrapper_serialize};
50
51pub mod broadcasted_declare_transaction_v3;
52pub mod broadcasted_deploy_account_transaction_v3;
53pub mod broadcasted_invoke_transaction_v3;
54
55pub mod declare_transaction_v3;
56pub mod deploy_account_transaction_v3;
57pub mod deploy_transaction;
58pub mod invoke_transaction_v3;
59
60pub mod l1_handler_transaction;
61
62#[derive(Debug, Clone, Serialize)]
63#[cfg_attr(feature = "testing", derive(Deserialize))]
64#[serde(untagged)]
65pub enum Transactions {
66 Hashes(Vec<TransactionHash>),
67 Full(Vec<TransactionWithHash>),
68 FullWithReceipts(Vec<TransactionWithReceipt>),
69}
70
71impl Transactions {
72 pub fn clone_without_proof_facts(&self) -> Self {
73 match &self {
74 Self::Hashes(hashes) => Self::Hashes(hashes.clone()),
75 Self::FullWithReceipts(txs) => Self::FullWithReceipts(
76 txs.iter().map(|tx| tx.clone_without_proof_facts()).collect(),
77 ),
78 Self::Full(txs) => {
79 Self::Full(txs.iter().map(|tx| tx.clone_without_proof_facts()).collect())
80 }
81 }
82 }
83
84 pub fn clone_with_proof_facts(&self) -> Self {
85 match &self {
86 Self::Hashes(hashes) => Self::Hashes(hashes.clone()),
87 Self::FullWithReceipts(txs) => {
88 Self::FullWithReceipts(txs.iter().map(|tx| tx.clone_with_proof_facts()).collect())
89 }
90 Self::Full(txs) => {
91 Self::Full(txs.iter().map(|tx| tx.clone_with_proof_facts()).collect())
92 }
93 }
94 }
95}
96
97#[derive(Debug, Copy, Clone, Serialize, Default)]
98#[cfg_attr(feature = "testing", derive(Deserialize))]
99pub enum TransactionType {
100 #[serde(rename(deserialize = "DECLARE", serialize = "DECLARE"))]
101 Declare,
102 #[serde(rename(deserialize = "DEPLOY", serialize = "DEPLOY"))]
103 Deploy,
104 #[serde(rename(deserialize = "DEPLOY_ACCOUNT", serialize = "DEPLOY_ACCOUNT"))]
105 DeployAccount,
106 #[serde(rename(deserialize = "INVOKE", serialize = "INVOKE"))]
107 #[default]
108 Invoke,
109 #[serde(rename(deserialize = "L1_HANDLER", serialize = "L1_HANDLER"))]
110 L1Handler,
111}
112
113#[derive(Debug, Clone, Serialize)]
114#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
115#[cfg_attr(feature = "testing", derive(Deserialize, PartialEq, Eq), serde(deny_unknown_fields))]
116pub enum Transaction {
117 Declare(DeclareTransaction),
118 DeployAccount(DeployAccountTransaction),
119 Deploy(DeployTransaction),
120 Invoke(InvokeTransaction),
121 L1Handler(L1HandlerTransaction),
122}
123
124impl Transaction {
125 pub fn gas_vector_computation_mode(&self) -> GasVectorComputationMode {
126 match self {
127 Transaction::Declare(DeclareTransaction::V3(v3)) => v3.get_resource_bounds().into(),
128 Transaction::DeployAccount(DeployAccountTransaction::V3(v3)) => {
129 v3.get_resource_bounds().into()
130 }
131 Transaction::Invoke(InvokeTransaction::V3(v3)) => v3.get_resource_bounds().into(),
132 _ => GasVectorComputationMode::NoL2Gas,
133 }
134 }
135
136 pub fn get_sender_address(&self) -> Option<ContractAddress> {
137 match self {
138 Self::Declare(tx) => Some(tx.get_sender_address()),
139 Self::DeployAccount(tx) => Some(*tx.get_contract_address()),
140 Self::Deploy(_) => None,
141 Self::Invoke(tx) => Some(tx.get_sender_address()),
142 Self::L1Handler(tx) => Some(tx.contract_address),
143 }
144 }
145}
146
147#[derive(Debug, Clone, Serialize)]
148#[cfg_attr(feature = "testing", derive(Deserialize, PartialEq, Eq))]
149pub struct TransactionWithHash {
150 transaction_hash: TransactionHash,
151 #[serde(flatten)]
152 pub transaction: Transaction,
153}
154
155impl TransactionWithHash {
156 pub fn new(transaction_hash: TransactionHash, transaction: Transaction) -> Self {
157 Self { transaction_hash, transaction }
158 }
159
160 pub fn get_transaction_hash(&self) -> &TransactionHash {
161 &self.transaction_hash
162 }
163
164 pub fn get_type(&self) -> TransactionType {
165 match self.transaction {
166 Transaction::Declare(_) => TransactionType::Declare,
167 Transaction::DeployAccount(_) => TransactionType::DeployAccount,
168 Transaction::Deploy(_) => TransactionType::Deploy,
169 Transaction::Invoke(_) => TransactionType::Invoke,
170 Transaction::L1Handler(_) => TransactionType::L1Handler,
171 }
172 }
173
174 pub fn get_signature(&self) -> TransactionSignature {
175 match &self.transaction {
176 Transaction::Declare(DeclareTransaction::V3(tx)) => tx.signature.clone(),
177 Transaction::DeployAccount(DeployAccountTransaction::V3(tx)) => tx.signature.clone(),
178 Transaction::Invoke(InvokeTransaction::V3(tx)) => tx.signature.clone(),
179 _ => TransactionSignature::default(),
180 }
181 }
182
183 pub fn clone_without_proof_facts(&self) -> Self {
184 match &self.transaction {
185 Transaction::Invoke(InvokeTransaction::V3(tx)) => Self {
186 transaction: Transaction::Invoke(InvokeTransaction::V3(
187 tx.clone_without_proof_facts(),
188 )),
189 transaction_hash: self.transaction_hash,
190 },
191 _ => self.clone(),
192 }
193 }
194
195 pub fn clone_with_proof_facts(&self) -> Self {
196 match &self.transaction {
197 Transaction::Invoke(InvokeTransaction::V3(tx)) => Self {
198 transaction: Transaction::Invoke(InvokeTransaction::V3(
199 tx.clone_with_proof_facts(),
200 )),
201 transaction_hash: self.transaction_hash,
202 },
203 _ => self.clone(),
204 }
205 }
206
207 #[allow(clippy::too_many_arguments)]
208 pub fn create_common_receipt(
209 &self,
210 transaction_events: &[Event],
211 transaction_messages_sent: &[MessageToL1],
212 block_hash: Option<&BlockHash>,
213 block_number: Option<BlockNumber>,
214 execution_result: &ExecutionResult,
215 finality_status: TransactionFinalityStatus,
216 actual_fee: FeeInUnits,
217 execution_info: &TransactionExecutionInfo,
218 ) -> CommonTransactionReceipt {
219 let r#type = self.get_type();
220 let execution_resources = ExecutionResources::from(execution_info);
221
222 CommonTransactionReceipt {
223 r#type,
224 transaction_hash: *self.get_transaction_hash(),
225 actual_fee,
226 messages_sent: transaction_messages_sent.to_vec(),
227 events: transaction_events.to_vec(),
228 execution_status: execution_result.clone(),
229 finality_status,
230 block_hash: block_hash.cloned(),
231 block_number,
232 execution_resources,
233 }
234 }
235
236 pub fn get_sender_address(&self) -> Option<ContractAddress> {
237 self.transaction.get_sender_address()
238 }
239}
240
241#[derive(Debug, Clone, Serialize)]
242#[cfg_attr(feature = "testing", derive(Deserialize), serde(deny_unknown_fields))]
243pub struct TransactionWithReceipt {
244 pub receipt: TransactionReceipt,
245 pub transaction: Transaction,
246}
247
248impl TransactionWithReceipt {
249 pub fn clone_without_proof_facts(&self) -> Self {
250 match &self.transaction {
251 Transaction::Invoke(InvokeTransaction::V3(tx)) => Self {
252 transaction: Transaction::Invoke(InvokeTransaction::V3(
253 tx.clone_without_proof_facts(),
254 )),
255 receipt: self.receipt.clone(),
256 },
257 _ => self.clone(),
258 }
259 }
260
261 pub fn clone_with_proof_facts(&self) -> Self {
262 match &self.transaction {
263 Transaction::Invoke(InvokeTransaction::V3(tx)) => Self {
264 transaction: Transaction::Invoke(InvokeTransaction::V3(
265 tx.clone_with_proof_facts(),
266 )),
267 receipt: self.receipt.clone(),
268 },
269 _ => self.clone(),
270 }
271 }
272}
273
274#[derive(Debug, Clone, Serialize)]
275#[cfg_attr(feature = "testing", derive(Deserialize, PartialEq, Eq))]
276#[serde(deny_unknown_fields)]
277pub struct TransactionStatus {
278 pub finality_status: TransactionFinalityStatus,
279 pub failure_reason: Option<String>,
280 pub execution_status: TransactionExecutionStatus,
281}
282
283#[derive(Debug, Clone, Serialize)]
284#[cfg_attr(feature = "testing", derive(Deserialize, PartialEq, Eq))]
285#[serde(untagged)]
286pub enum DeclareTransaction {
287 V3(DeclareTransactionV3),
288}
289
290impl DeclareTransaction {
291 pub fn get_sender_address(&self) -> ContractAddress {
292 match self {
293 DeclareTransaction::V3(tx) => tx.sender_address,
294 }
295 }
296}
297
298#[derive(Debug, Clone, Serialize)]
299#[cfg_attr(feature = "testing", derive(Deserialize, PartialEq, Eq))]
300#[serde(untagged)]
301pub enum InvokeTransaction {
302 V3(InvokeTransactionV3),
303}
304
305impl InvokeTransaction {
306 pub fn get_sender_address(&self) -> ContractAddress {
307 match self {
308 InvokeTransaction::V3(tx) => tx.sender_address,
309 }
310 }
311}
312
313#[derive(Debug, Clone, Serialize)]
314#[cfg_attr(feature = "testing", derive(Deserialize, PartialEq, Eq))]
315#[serde(untagged)]
316pub enum DeployAccountTransaction {
317 V3(Box<DeployAccountTransactionV3>),
318}
319
320impl DeployAccountTransaction {
321 pub fn get_contract_address(&self) -> &ContractAddress {
322 match self {
323 DeployAccountTransaction::V3(tx) => tx.get_contract_address(),
324 }
325 }
326}
327
328pub fn deserialize_paid_fee_on_l1<'de, D>(deserializer: D) -> Result<u128, D::Error>
329where
330 D: Deserializer<'de>,
331{
332 let buf = String::deserialize(deserializer)?;
333 let err_msg = format!("paid_fee_on_l1: expected 0x-prefixed hex string, got: {buf}");
334 if !buf.starts_with("0x") {
335 return Err(serde::de::Error::custom(err_msg));
336 }
337 u128::from_str_radix(&buf[2..], 16).map_err(|_| serde::de::Error::custom(err_msg))
338}
339
340fn serialize_paid_fee_on_l1<S>(paid_fee_on_l1: &u128, s: S) -> Result<S::Ok, S::Error>
341where
342 S: Serializer,
343{
344 s.serialize_str(&format!("{paid_fee_on_l1:#x}"))
345}
346
347fn deserialize_address<'de, D>(deserializer: D) -> Result<Option<Vec<ContractAddress>>, D::Error>
348where
349 D: Deserializer<'de>,
350{
351 #[derive(Deserialize)]
352 #[serde(untagged)]
353 enum AddressOrVec {
354 Single(ContractAddress),
355 Multiple(Vec<ContractAddress>),
356 }
357
358 let value = Option::<AddressOrVec>::deserialize(deserializer)?;
359 Ok(value.map(|v| match v {
360 AddressOrVec::Single(addr) => vec![addr],
361 AddressOrVec::Multiple(addrs) => addrs,
362 }))
363}
364
365#[derive(Debug, Clone, Deserialize)]
366pub struct EventFilter {
367 pub from_block: Option<BlockId>,
368 pub to_block: Option<BlockId>,
369 #[serde(default, deserialize_with = "deserialize_address")]
370 pub address: Option<Vec<ContractAddress>>,
371 pub keys: Option<Vec<Vec<Felt>>>,
372 pub continuation_token: Option<String>,
373 pub chunk_size: u64,
374}
375
376#[derive(Debug, Clone, Serialize)]
377#[cfg_attr(feature = "testing", derive(serde::Deserialize))]
378pub struct EventsChunk {
379 pub events: Vec<crate::emitted_event::EmittedEvent>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub continuation_token: Option<String>,
382}
383
384impl From<EventsPage> for EventsChunk {
385 fn from(events_page: EventsPage) -> Self {
386 Self {
387 events: events_page.events.into_iter().map(|e| e.into()).collect(),
388 continuation_token: events_page.continuation_token,
389 }
390 }
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
394#[cfg_attr(feature = "testing", derive(PartialEq, Eq), serde(deny_unknown_fields))]
395pub struct FunctionCall {
396 pub contract_address: ContractAddress,
397 pub entry_point_selector: EntryPointSelector,
398 pub calldata: Calldata,
399}
400
401fn is_only_query_common(version: &Felt) -> bool {
402 version >= &QUERY_VERSION_OFFSET
403}
404
405fn felt_to_sn_api_chain_id(f: &Felt) -> DevnetResult<starknet_api::core::ChainId> {
406 Ok(starknet_api::core::ChainId::Other(
407 parse_cairo_short_string(f).map_err(|e| ConversionError::OutOfRangeError(e.to_string()))?,
408 ))
409}
410
411#[derive(Debug, Clone, Deserialize, Serialize)]
413pub struct BroadcastedTransactionCommonV3 {
414 pub version: TransactionVersion,
415 pub signature: TransactionSignature,
416 pub nonce: Nonce,
417 pub resource_bounds: ResourceBoundsWrapper,
418 pub tip: Tip,
419 pub paymaster_data: Vec<Felt>,
420 pub nonce_data_availability_mode: DataAvailabilityMode,
421 pub fee_data_availability_mode: DataAvailabilityMode,
422}
423
424#[derive(Debug, Clone)]
425#[cfg_attr(feature = "testing", derive(PartialEq, Eq))]
426pub struct ResourceBoundsWrapper {
427 inner: ResourceBoundsMapping,
428}
429
430impl From<&ResourceBoundsWrapper> for GasVectorComputationMode {
431 fn from(val: &ResourceBoundsWrapper) -> GasVectorComputationMode {
432 let resource_bounds = starknet_api::transaction::fields::ValidResourceBounds::from(val);
433 match resource_bounds {
434 ValidResourceBounds::L1Gas(_) => GasVectorComputationMode::NoL2Gas,
435 ValidResourceBounds::AllResources(_) => GasVectorComputationMode::All,
436 }
437 }
438}
439
440impl_wrapper_serialize!(ResourceBoundsWrapper);
441impl_wrapper_deserialize!(ResourceBoundsWrapper, ResourceBoundsMapping);
442
443impl ResourceBoundsWrapper {
444 pub fn new(
445 l1_gas_max_amount: u64,
446 l1_gas_max_price_per_unit: u128,
447 l1_data_gas_max_amount: u64,
448 l1_data_gas_max_price_per_unit: u128,
449 l2_gas_max_amount: u64,
450 l2_gas_max_price_per_unit: u128,
451 ) -> Self {
452 ResourceBoundsWrapper {
453 inner: ResourceBoundsMapping {
454 l1_gas: ResourceBounds {
455 max_amount: l1_gas_max_amount,
456 max_price_per_unit: l1_gas_max_price_per_unit,
457 },
458 l1_data_gas: ResourceBounds {
459 max_amount: l1_data_gas_max_amount,
460 max_price_per_unit: l1_data_gas_max_price_per_unit,
461 },
462 l2_gas: ResourceBounds {
463 max_amount: l2_gas_max_amount,
464 max_price_per_unit: l2_gas_max_price_per_unit,
465 },
466 },
467 }
468 }
469}
470
471fn convert_resource_bounds_from_starknet_rs_to_starknet_api(
472 bounds: ResourceBounds,
473) -> starknet_api::transaction::fields::ResourceBounds {
474 starknet_api::transaction::fields::ResourceBounds {
475 max_amount: starknet_api::execution_resources::GasAmount(bounds.max_amount),
476 max_price_per_unit: GasPrice(bounds.max_price_per_unit),
477 }
478}
479
480impl From<&ResourceBoundsWrapper> for starknet_api::transaction::fields::ValidResourceBounds {
481 fn from(value: &ResourceBoundsWrapper) -> Self {
482 let l1_gas_max_amount = value.inner.l1_gas.max_amount;
483 let l2_gas_max_amount = value.inner.l2_gas.max_amount;
484 let l1_data_gas_max_amount = value.inner.l1_data_gas.max_amount;
485
486 match (l1_gas_max_amount, l2_gas_max_amount, l1_data_gas_max_amount) {
487 (0, 0, 0) => ValidResourceBounds::AllResources(AllResourceBounds::default()),
490 (_, 0, 0) => starknet_api::transaction::fields::ValidResourceBounds::L1Gas(
491 convert_resource_bounds_from_starknet_rs_to_starknet_api(
492 value.inner.l1_gas.clone(),
493 ),
494 ),
495 _ => starknet_api::transaction::fields::ValidResourceBounds::AllResources(
496 AllResourceBounds {
497 l1_gas: convert_resource_bounds_from_starknet_rs_to_starknet_api(
498 value.inner.l1_gas.clone(),
499 ),
500 l2_gas: convert_resource_bounds_from_starknet_rs_to_starknet_api(
501 value.inner.l2_gas.clone(),
502 ),
503 l1_data_gas: convert_resource_bounds_from_starknet_rs_to_starknet_api(
504 value.inner.l1_data_gas.clone(),
505 ),
506 },
507 ),
508 }
509 }
510}
511
512impl BroadcastedTransactionCommonV3 {
513 pub fn are_gas_bounds_valid(&self) -> bool {
516 let ResourceBoundsMapping { l1_gas, l2_gas, l1_data_gas } = &self.resource_bounds.inner;
517 let is_gt_zero = |gas: &ResourceBounds| gas.max_amount > 0 && gas.max_price_per_unit > 0;
518 match (l1_gas, l2_gas, l1_data_gas) {
522 (l1, l2, l1_data) if is_gt_zero(l1) && !is_gt_zero(l2) && !is_gt_zero(l1_data) => {
524 tracing::warn!(
525 "From version 0.5.0 V3 transactions that specify only L1 gas bounds are \
526 invalid. Please upgrade your client to provide values for the triplet \
527 (L1_GAS, L1_DATA_GAS, L2_GAS)."
528 );
529 true
530 }
531 (_, l2, l1_data) if is_gt_zero(l2) && is_gt_zero(l1_data) => true,
535 _ => false,
536 }
537 }
538
539 pub fn is_only_query(&self) -> bool {
540 is_only_query_common(&self.version)
541 }
542
543 pub fn get_gas_vector_computation_mode(&self) -> GasVectorComputationMode {
544 (&self.resource_bounds).into()
545 }
546}
547
548#[derive(Debug, Clone, Deserialize)]
549#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
550pub enum BroadcastedTransaction {
551 Invoke(BroadcastedInvokeTransaction),
552 Declare(BroadcastedDeclareTransaction),
553 DeployAccount(BroadcastedDeployAccountTransaction),
554}
555
556impl BroadcastedTransaction {
557 pub fn to_blockifier_account_transaction(
558 &self,
559 chain_id: &Felt,
560 execution_flags: ExecutionFlags,
561 ) -> DevnetResult<blockifier::transaction::account_transaction::AccountTransaction> {
562 let sn_api_tx = self.to_sn_api_account_transaction(chain_id)?;
563 Ok(blockifier::transaction::account_transaction::AccountTransaction {
564 tx: sn_api_tx,
565 execution_flags,
566 })
567 }
568
569 pub fn to_sn_api_account_transaction(
570 &self,
571 chain_id: &Felt,
572 ) -> DevnetResult<starknet_api::executable_transaction::AccountTransaction> {
573 let sn_api_tx = match self {
574 BroadcastedTransaction::Invoke(invoke_txn) => {
575 starknet_api::executable_transaction::AccountTransaction::Invoke(
576 invoke_txn.create_sn_api_invoke(chain_id)?,
577 )
578 }
579 BroadcastedTransaction::Declare(declare_txn) => {
580 starknet_api::executable_transaction::AccountTransaction::Declare(
581 declare_txn.create_sn_api_declare(chain_id)?,
582 )
583 }
584 BroadcastedTransaction::DeployAccount(deploy_account_txn) => {
585 starknet_api::executable_transaction::AccountTransaction::DeployAccount(
586 deploy_account_txn.create_sn_api_deploy_account(chain_id)?,
587 )
588 }
589 };
590
591 Ok(sn_api_tx)
592 }
593
594 pub fn get_type(&self) -> TransactionType {
595 match self {
596 BroadcastedTransaction::Invoke(_) => TransactionType::Invoke,
597 BroadcastedTransaction::Declare(_) => TransactionType::Declare,
598 BroadcastedTransaction::DeployAccount(_) => TransactionType::DeployAccount,
599 }
600 }
601
602 pub fn are_gas_bounds_valid(&self) -> bool {
603 match self {
604 BroadcastedTransaction::Invoke(broadcasted_invoke_transaction) => {
605 broadcasted_invoke_transaction.are_gas_bounds_valid()
606 }
607 BroadcastedTransaction::Declare(broadcasted_declare_transaction) => {
608 broadcasted_declare_transaction.are_gas_bounds_valid()
609 }
610 BroadcastedTransaction::DeployAccount(broadcasted_deploy_account_transaction) => {
611 broadcasted_deploy_account_transaction.are_gas_bounds_valid()
612 }
613 }
614 }
615
616 pub fn gas_vector_computation_mode(&self) -> GasVectorComputationMode {
617 match self {
618 BroadcastedTransaction::Declare(BroadcastedDeclareTransaction::V3(declare_v3)) => {
619 declare_v3.common.get_gas_vector_computation_mode()
620 }
621 BroadcastedTransaction::DeployAccount(BroadcastedDeployAccountTransaction::V3(
622 deploy_account_v3,
623 )) => deploy_account_v3.common.get_gas_vector_computation_mode(),
624 BroadcastedTransaction::Invoke(BroadcastedInvokeTransaction::V3(invoke_v3)) => {
625 invoke_v3.common.get_gas_vector_computation_mode()
626 }
627 }
628 }
629
630 pub fn requires_strict_nonce_check(&self, using_pre_confirmed_block: bool) -> bool {
631 match self {
632 BroadcastedTransaction::Invoke(tx) => {
633 tx.requires_strict_nonce_check(using_pre_confirmed_block)
634 }
635 BroadcastedTransaction::Declare(_) => true,
636 BroadcastedTransaction::DeployAccount(tx) => {
637 tx.requires_strict_nonce_check(using_pre_confirmed_block)
638 }
639 }
640 }
641}
642
643#[derive(Debug, Clone)]
644pub enum BroadcastedDeclareTransaction {
645 V3(Box<BroadcastedDeclareTransactionV3>),
646}
647
648impl BroadcastedDeclareTransaction {
649 pub fn are_gas_bounds_valid(&self) -> bool {
650 match self {
651 BroadcastedDeclareTransaction::V3(v3) => v3.common.are_gas_bounds_valid(),
652 }
653 }
654
655 pub fn is_only_query(&self) -> bool {
656 match self {
657 BroadcastedDeclareTransaction::V3(tx) => tx.common.is_only_query(),
658 }
659 }
660
661 pub fn create_sn_api_declare(
667 &self,
668 chain_id: &Felt,
669 ) -> DevnetResult<starknet_api::executable_transaction::DeclareTransaction> {
670 let sn_api_chain_id = felt_to_sn_api_chain_id(chain_id)?;
671
672 let (sn_api_transaction, tx_hash, class_info) = match self {
673 BroadcastedDeclareTransaction::V3(v3) => {
674 let sierra_class_hash = compute_sierra_class_hash(&v3.contract_class)?;
675
676 let sn_api_declare = starknet_api::transaction::DeclareTransaction::V3(
677 starknet_api::transaction::DeclareTransactionV3 {
678 resource_bounds: (&v3.common.resource_bounds).into(),
679 tip: v3.common.tip,
680 signature: starknet_api::transaction::fields::TransactionSignature(
681 Arc::new(v3.common.signature.clone()),
682 ),
683 nonce: starknet_api::core::Nonce(v3.common.nonce),
684 class_hash: starknet_api::core::ClassHash(sierra_class_hash),
685 compiled_class_hash: starknet_api::core::CompiledClassHash(
686 v3.compiled_class_hash,
687 ),
688 sender_address: v3.sender_address.into(),
689 nonce_data_availability_mode: v3.common.nonce_data_availability_mode,
690 fee_data_availability_mode: v3.common.fee_data_availability_mode,
691 paymaster_data: starknet_api::transaction::fields::PaymasterData(
692 v3.common.paymaster_data.clone(),
693 ),
694 account_deployment_data:
695 starknet_api::transaction::fields::AccountDeploymentData(
696 v3.account_deployment_data.clone(),
697 ),
698 },
699 );
700
701 let class_info: ClassInfo =
702 ContractClass::Cairo1(v3.contract_class.clone()).try_into()?;
703
704 let tx_version: starknet_api::transaction::TransactionVersion = signed_tx_version(
705 &sn_api_declare.version(),
706 &TransactionOptions { only_query: self.is_only_query() },
707 );
708
709 let tx_hash =
710 sn_api_declare.calculate_transaction_hash(&sn_api_chain_id, &tx_version)?;
711
712 (sn_api_declare, tx_hash, class_info)
713 }
714 };
715
716 Ok(starknet_api::executable_transaction::DeclareTransaction {
717 tx: sn_api_transaction,
718 tx_hash,
719 class_info,
720 })
721 }
722}
723
724#[derive(Debug, Clone)]
725pub enum BroadcastedDeployAccountTransaction {
726 V3(BroadcastedDeployAccountTransactionV3),
727}
728
729impl BroadcastedDeployAccountTransaction {
730 pub fn are_gas_bounds_valid(&self) -> bool {
731 match self {
732 BroadcastedDeployAccountTransaction::V3(v3) => v3.common.are_gas_bounds_valid(),
733 }
734 }
735
736 pub fn is_only_query(&self) -> bool {
737 match self {
738 BroadcastedDeployAccountTransaction::V3(tx) => tx.common.is_only_query(),
739 }
740 }
741
742 pub fn requires_strict_nonce_check(&self, using_pre_confirmed_block: bool) -> bool {
743 !using_pre_confirmed_block
744 }
745
746 pub fn create_sn_api_deploy_account(
752 &self,
753 chain_id: &Felt,
754 ) -> DevnetResult<starknet_api::executable_transaction::DeployAccountTransaction> {
755 let sn_api_transaction = match self {
756 BroadcastedDeployAccountTransaction::V3(v3) => {
757 let sn_api_transaction = starknet_api::transaction::DeployAccountTransactionV3 {
758 resource_bounds: (&v3.common.resource_bounds).into(),
759 tip: v3.common.tip,
760 signature: starknet_api::transaction::fields::TransactionSignature(Arc::new(
761 v3.common.signature.clone(),
762 )),
763 nonce: starknet_api::core::Nonce(v3.common.nonce),
764 class_hash: starknet_api::core::ClassHash(v3.class_hash),
765 nonce_data_availability_mode: v3.common.nonce_data_availability_mode,
766 fee_data_availability_mode: v3.common.fee_data_availability_mode,
767 paymaster_data: starknet_api::transaction::fields::PaymasterData(
768 v3.common.paymaster_data.clone(),
769 ),
770 contract_address_salt: starknet_api::transaction::fields::ContractAddressSalt(
771 v3.contract_address_salt,
772 ),
773 constructor_calldata: starknet_api::transaction::fields::Calldata(Arc::new(
774 v3.constructor_calldata.clone(),
775 )),
776 };
777
778 starknet_api::transaction::DeployAccountTransaction::V3(sn_api_transaction)
779 }
780 };
781
782 let chain_id = felt_to_sn_api_chain_id(chain_id)?;
783 let tx_version: starknet_api::transaction::TransactionVersion = signed_tx_version(
784 &sn_api_transaction.version(),
785 &TransactionOptions { only_query: self.is_only_query() },
786 );
787 let tx_hash = sn_api_transaction.calculate_transaction_hash(&chain_id, &tx_version)?;
788
789 let contract_address = calculate_contract_address(
791 sn_api_transaction.contract_address_salt(),
792 sn_api_transaction.class_hash(),
793 &sn_api_transaction.constructor_calldata(),
794 starknet_api::core::ContractAddress::default(),
795 )?;
796
797 Ok(starknet_api::executable_transaction::DeployAccountTransaction {
798 tx: sn_api_transaction,
799 tx_hash,
800 contract_address,
801 })
802 }
803}
804
805#[derive(Debug, Clone)]
806pub enum BroadcastedInvokeTransaction {
807 V3(BroadcastedInvokeTransactionV3),
808}
809
810impl BroadcastedInvokeTransaction {
811 pub fn are_gas_bounds_valid(&self) -> bool {
812 match self {
813 BroadcastedInvokeTransaction::V3(v3) => v3.common.are_gas_bounds_valid(),
814 }
815 }
816
817 pub fn is_only_query(&self) -> bool {
818 match self {
819 BroadcastedInvokeTransaction::V3(tx) => tx.common.is_only_query(),
820 }
821 }
822
823 pub fn requires_strict_nonce_check(&self, using_pre_confirmed_block: bool) -> bool {
824 !using_pre_confirmed_block
825 }
826
827 pub fn create_sn_api_invoke(
833 &self,
834 chain_id: &Felt,
835 ) -> DevnetResult<starknet_api::executable_transaction::InvokeTransaction> {
836 let sn_api_transaction = match self {
837 BroadcastedInvokeTransaction::V3(v3) => v3.create_sn_api_invoke()?,
838 };
839
840 let chain_id = felt_to_sn_api_chain_id(chain_id)?;
841 let tx_version: starknet_api::transaction::TransactionVersion = signed_tx_version(
842 &sn_api_transaction.version(),
843 &TransactionOptions { only_query: self.is_only_query() },
844 );
845 let tx_hash = sn_api_transaction.calculate_transaction_hash(&chain_id, &tx_version)?;
846
847 Ok(starknet_api::executable_transaction::InvokeTransaction {
848 tx: sn_api_transaction,
849 tx_hash,
850 })
851 }
852
853 pub fn get_proof(&self) -> Option<Proof> {
854 match self {
855 BroadcastedInvokeTransaction::V3(v3) => v3.proof.clone(),
856 }
857 }
858
859 pub fn get_proof_facts(&self) -> Option<ProofFacts> {
860 match self {
861 BroadcastedInvokeTransaction::V3(v3) => v3.proof_facts.clone(),
862 }
863 }
864}
865
866impl<'de> Deserialize<'de> for BroadcastedDeclareTransaction {
867 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
868 where
869 D: Deserializer<'de>,
870 {
871 let value = serde_json::Value::deserialize(deserializer)?;
872 let version_raw = value.get("version").ok_or(serde::de::Error::missing_field("version"))?;
873 match version_raw.as_str() {
874 Some(v) if ["0x3", "0x100000000000000000000000000000003"].contains(&v) => {
875 let unpacked = serde_json::from_value(value).map_err(|e| {
876 serde::de::Error::custom(format!("Invalid declare transaction v3: {e}"))
877 })?;
878 Ok(BroadcastedDeclareTransaction::V3(Box::new(unpacked)))
879 }
880 _ => Err(serde::de::Error::custom(format!(
881 "Invalid version of declare transaction: {version_raw}"
882 ))),
883 }
884 }
885}
886
887impl<'de> Deserialize<'de> for BroadcastedDeployAccountTransaction {
888 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
889 where
890 D: Deserializer<'de>,
891 {
892 let value = serde_json::Value::deserialize(deserializer)?;
893 let version_raw = value.get("version").ok_or(serde::de::Error::missing_field("version"))?;
894 match version_raw.as_str() {
895 Some(v) if ["0x3", "0x100000000000000000000000000000003"].contains(&v) => {
896 let unpacked = serde_json::from_value(value).map_err(|e| {
897 serde::de::Error::custom(format!("Invalid deploy account transaction v3: {e}"))
898 })?;
899 Ok(BroadcastedDeployAccountTransaction::V3(unpacked))
900 }
901 _ => Err(serde::de::Error::custom(format!(
902 "Invalid version of deploy account transaction: {version_raw}"
903 ))),
904 }
905 }
906}
907
908impl<'de> Deserialize<'de> for BroadcastedInvokeTransaction {
909 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
910 where
911 D: Deserializer<'de>,
912 {
913 let value = serde_json::Value::deserialize(deserializer)?;
914 let version_raw = value.get("version").ok_or(serde::de::Error::missing_field("version"))?;
915 match version_raw.as_str() {
916 Some(v) if ["0x3", "0x100000000000000000000000000000003"].contains(&v) => {
917 let unpacked = serde_json::from_value(value).map_err(|e| {
918 serde::de::Error::custom(format!("Invalid invoke transaction v3: {e}"))
919 })?;
920 Ok(BroadcastedInvokeTransaction::V3(unpacked))
921 }
922 _ => Err(serde::de::Error::custom(format!(
923 "Invalid version of invoke transaction: {version_raw}"
924 ))),
925 }
926 }
927}
928
929#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
934#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
935pub enum SimulationFlag {
936 SkipValidate,
937 SkipFeeCharge,
938 ReturnInitialReads,
939}
940
941#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
943#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
944pub enum TraceFlag {
945 ReturnInitialReads,
946}
947
948#[derive(Debug, Clone, Serialize)]
949#[cfg_attr(feature = "testing", derive(Deserialize))]
950#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
951pub enum CallType {
952 LibraryCall,
953 Call,
954 Delegate,
955}
956
957#[derive(Debug, Clone, Serialize)]
958#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
959pub struct FunctionInvocation {
960 contract_address: ContractAddress,
961 entry_point_selector: EntryPointSelector,
962 calldata: Calldata,
963 caller_address: ContractAddress,
964 class_hash: Felt,
965 entry_point_type: EntryPointType,
966 call_type: CallType,
967 result: Vec<Felt>,
968 calls: Vec<FunctionInvocation>,
969 events: Vec<OrderedEvent>,
970 messages: Vec<OrderedMessageToL1>,
971 execution_resources: InnerExecutionResources,
972 is_reverted: bool,
973}
974
975#[derive(Debug, Clone, Serialize)]
976#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
977pub struct InnerExecutionResources {
978 pub l1_gas: u64,
979 pub l2_gas: u64,
980}
981
982#[derive(Debug, Clone, Serialize)]
983#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
984#[cfg_attr(feature = "testing", derive(serde::Deserialize))]
985pub enum TransactionTrace {
986 Invoke(InvokeTransactionTrace),
987 Declare(DeclareTransactionTrace),
988 DeployAccount(DeployAccountTransactionTrace),
989 L1Handler(L1HandlerTransactionTrace),
990}
991
992#[derive(Debug, Clone, Serialize)]
993#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
994pub struct BlockTransactionTrace {
995 pub transaction_hash: Felt,
996 pub trace_root: TransactionTrace,
997}
998
999#[derive(Debug, Clone, Serialize)]
1000#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1001pub struct Reversion {
1002 pub revert_reason: String, }
1004
1005#[derive(Debug, Clone, Serialize)]
1006#[cfg_attr(feature = "testing", derive(serde::Deserialize))]
1007#[serde(untagged)]
1008#[allow(clippy::large_enum_variant)]
1009pub enum ExecutionInvocation {
1010 Succeeded(FunctionInvocation),
1011 Reverted(Reversion),
1012}
1013
1014#[derive(Debug, Clone, Serialize)]
1015#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1016pub struct InvokeTransactionTrace {
1017 #[serde(skip_serializing_if = "Option::is_none")]
1018 pub validate_invocation: Option<FunctionInvocation>,
1019 pub execute_invocation: ExecutionInvocation,
1020 #[serde(skip_serializing_if = "Option::is_none")]
1021 pub fee_transfer_invocation: Option<FunctionInvocation>,
1022 #[serde(skip_serializing_if = "Option::is_none")]
1023 pub state_diff: Option<ThinStateDiff>,
1024 pub execution_resources: ExecutionResources,
1025}
1026
1027#[derive(Debug, Clone, Serialize)]
1028#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1029pub struct DeclareTransactionTrace {
1030 #[serde(skip_serializing_if = "Option::is_none")]
1031 pub validate_invocation: Option<FunctionInvocation>,
1032 #[serde(skip_serializing_if = "Option::is_none")]
1033 pub fee_transfer_invocation: Option<FunctionInvocation>,
1034 #[serde(skip_serializing_if = "Option::is_none")]
1035 pub state_diff: Option<ThinStateDiff>,
1036 pub execution_resources: ExecutionResources,
1037}
1038
1039#[derive(Debug, Clone, Serialize)]
1040#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1041pub struct DeployAccountTransactionTrace {
1042 #[serde(skip_serializing_if = "Option::is_none")]
1043 pub validate_invocation: Option<FunctionInvocation>,
1044 #[serde(skip_serializing_if = "Option::is_none")]
1045 pub constructor_invocation: Option<FunctionInvocation>,
1046 #[serde(skip_serializing_if = "Option::is_none")]
1047 pub fee_transfer_invocation: Option<FunctionInvocation>,
1048 #[serde(skip_serializing_if = "Option::is_none")]
1049 pub state_diff: Option<ThinStateDiff>,
1050 pub execution_resources: ExecutionResources,
1051}
1052
1053#[derive(Debug, Clone, Serialize)]
1054#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1055pub struct L1HandlerTransactionTrace {
1056 pub function_invocation: ExecutionInvocation,
1057 #[serde(skip_serializing_if = "Option::is_none")]
1058 pub state_diff: Option<ThinStateDiff>,
1059 pub execution_resources: ExecutionResources,
1060}
1061
1062#[derive(Debug, Clone, Serialize)]
1063#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1064pub struct SimulatedTransaction {
1065 pub transaction_trace: TransactionTrace,
1066 pub fee_estimation: FeeEstimateWrapper,
1067}
1068
1069#[derive(Debug, Clone, Serialize)]
1070#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1071pub struct SimulatedTransactionsWithInitialReads {
1072 pub simulated_transactions: Vec<SimulatedTransaction>,
1073 pub initial_reads: InitialReads,
1074}
1075
1076#[derive(Debug, Clone)]
1077pub enum SimulationResult {
1078 SimulatedTransactions(Vec<SimulatedTransaction>),
1079 SimulatedTransactionsWithInitialReads(Box<SimulatedTransactionsWithInitialReads>),
1080}
1081
1082#[derive(Debug, Clone, Serialize)]
1083#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1084pub struct BlockTransactionTracesWithInitialReads {
1085 pub traces: Vec<BlockTransactionTrace>,
1086 pub initial_reads: InitialReads,
1087}
1088
1089#[derive(Debug, Clone)]
1090pub enum BlockTraceResult {
1091 Traces(Vec<BlockTransactionTrace>),
1092 TracesWithInitialReads(Box<BlockTransactionTracesWithInitialReads>),
1093}
1094
1095#[derive(Debug, Clone, Serialize)]
1096#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1097pub struct InitialReadsStorageEntry {
1098 pub contract_address: ContractAddress,
1099 pub key: StorageKey,
1100 pub value: Felt,
1101}
1102
1103#[derive(Debug, Clone, Serialize)]
1104#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1105pub struct InitialReadsNonceEntry {
1106 pub contract_address: ContractAddress,
1107 pub nonce: Felt,
1108}
1109
1110#[derive(Debug, Clone, Serialize)]
1111#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1112pub struct InitialReadsClassHashEntry {
1113 pub contract_address: ContractAddress,
1114 pub class_hash: ClassHash,
1115}
1116
1117#[derive(Debug, Clone, Serialize)]
1118#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1119pub struct InitialReadsDeclaredContractsEntry {
1120 pub class_hash: ClassHash,
1121 pub is_declared: bool,
1122}
1123
1124#[derive(Debug, Clone, Default, Serialize)]
1125#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
1126pub struct InitialReads {
1127 pub storage: Vec<InitialReadsStorageEntry>,
1128 pub nonces: Vec<InitialReadsNonceEntry>,
1129 pub class_hashes: Vec<InitialReadsClassHashEntry>,
1130 pub declared_contracts: Vec<InitialReadsDeclaredContractsEntry>,
1131}
1132
1133impl From<StateMaps> for InitialReads {
1134 fn from(value: StateMaps) -> Self {
1135 InitialReads {
1136 storage: value
1137 .storage
1138 .iter()
1139 .map(|s| InitialReadsStorageEntry {
1140 contract_address: s.0.0.into(),
1141 key: (*s.0.1).into(),
1142 value: *s.1,
1143 })
1144 .collect(),
1145 nonces: value
1146 .nonces
1147 .iter()
1148 .map(|n| InitialReadsNonceEntry { contract_address: (*n.0).into(), nonce: n.1.0 })
1149 .collect(),
1150 class_hashes: value
1151 .class_hashes
1152 .iter()
1153 .map(|c| InitialReadsClassHashEntry {
1154 contract_address: (*c.0).into(),
1155 class_hash: (*c.1).into(),
1156 })
1157 .collect(),
1158 declared_contracts: value
1159 .declared_contracts
1160 .iter()
1161 .map(|c| InitialReadsDeclaredContractsEntry {
1162 class_hash: (*c.0).into(),
1163 is_declared: *c.1,
1164 })
1165 .collect(),
1166 }
1167 }
1168}
1169
1170impl FunctionInvocation {
1171 pub fn try_from_call_info(
1172 call_info: &blockifier::execution::call_info::CallInfo,
1173 state_reader: &mut impl StateReader,
1174 versioned_constants: &VersionedConstants,
1175 gas_vector_computation_mode: &GasVectorComputationMode,
1176 ) -> DevnetResult<Self> {
1177 let mut internal_calls: Vec<FunctionInvocation> = vec![];
1178 for internal_call in &call_info.inner_calls {
1179 internal_calls.push(FunctionInvocation::try_from_call_info(
1180 internal_call,
1181 state_reader,
1182 versioned_constants,
1183 gas_vector_computation_mode,
1184 )?);
1185 }
1186
1187 let mut messages: Vec<OrderedMessageToL1> = vec![];
1188 for msg in call_info.execution.l2_to_l1_messages.iter() {
1189 messages.push(OrderedMessageToL1::new(msg, call_info.call.storage_address.into())?);
1193 }
1194 messages.sort_by_key(|msg| msg.order);
1195
1196 let mut events: Vec<OrderedEvent> =
1197 call_info.execution.events.iter().map(OrderedEvent::from).collect();
1198 let contract_address = call_info.call.storage_address;
1199 events.sort_by_key(|event| event.order);
1200
1201 let class_hash = if let Some(class_hash) = call_info.call.class_hash {
1204 class_hash
1205 } else {
1206 state_reader.get_class_hash_at(contract_address).map_err(|_| {
1207 ConversionError::InvalidInternalStructure(
1208 "class_hash is unexpectedly undefined".into(),
1209 )
1210 })?
1211 };
1212
1213 let gas_vector = blockifier::fee::fee_utils::get_extended_vm_resources_cost(
1214 versioned_constants,
1215 &call_info.resources,
1216 0,
1217 gas_vector_computation_mode,
1218 );
1219
1220 Ok(FunctionInvocation {
1221 contract_address: contract_address.into(),
1222 entry_point_selector: call_info.call.entry_point_selector.0,
1223 calldata: call_info.call.calldata.0.to_vec(),
1224 caller_address: call_info.call.caller_address.into(),
1225 class_hash: class_hash.0,
1226 entry_point_type: call_info.call.entry_point_type,
1227 call_type: match call_info.call.call_type {
1228 blockifier::execution::entry_point::CallType::Call => CallType::Call,
1229 blockifier::execution::entry_point::CallType::Delegate => CallType::Delegate,
1230 },
1231 result: call_info.execution.retdata.0.clone(),
1232 calls: internal_calls,
1233 events,
1234 messages,
1235 execution_resources: InnerExecutionResources {
1236 l1_gas: gas_vector.l1_gas.0,
1237 l2_gas: gas_vector.l2_gas.0,
1238 },
1239 is_reverted: call_info.execution.failed,
1240 })
1241 }
1242
1243 pub fn messages(&self) -> &[OrderedMessageToL1] {
1245 &self.messages
1246 }
1247
1248 pub fn all_messages(&self) -> Vec<OrderedMessageToL1> {
1250 let mut result = self.messages.clone();
1251 for call in &self.calls {
1252 result.extend(call.all_messages());
1253 }
1254 result.sort_by_key(|msg| msg.order);
1255 result
1256 }
1257}
1258
1259#[derive(Debug, Clone, Serialize, Deserialize)]
1260#[serde(deny_unknown_fields)]
1261pub struct L1HandlerTransactionStatus {
1262 pub transaction_hash: TransactionHash,
1263 pub finality_status: TransactionFinalityStatus,
1264 pub execution_status: TransactionExecutionStatus,
1265 pub failure_reason: Option<String>,
1266}
1267
1268#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Copy)]
1269#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1270pub enum TransactionFinalityStatus {
1271 Received,
1272 Candidate,
1273 PreConfirmed,
1274 AcceptedOnL2,
1275 AcceptedOnL1,
1276}