starknet_rust_providers/sequencer/models/
conversions.rs

1use std::sync::Arc;
2
3use starknet_rust_core::types::{self as core, contract::legacy as contract_legacy, Felt};
4
5use super::{
6    state_update::{DeclaredContract, DeployedContract, StateDiff, StorageDiff},
7    transaction::{DataAvailabilityMode, ResourceBounds, ResourceBoundsMapping},
8    *,
9};
10
11#[derive(Debug, thiserror::Error)]
12#[error("unable to convert type")]
13pub struct ConversionError;
14
15pub(crate) struct ConfirmedReceiptWithContext {
16    pub receipt: ConfirmedTransactionReceipt,
17    pub transaction: TransactionType,
18    pub finality: BlockStatus,
19}
20
21impl TryFrom<core::BlockId> for BlockId {
22    type Error = ConversionError;
23
24    fn try_from(value: core::BlockId) -> Result<Self, Self::Error> {
25        match value {
26            core::BlockId::Hash(hash) => Ok(Self::Hash(hash)),
27            core::BlockId::Number(num) => Ok(Self::Number(num)),
28            core::BlockId::Tag(core::BlockTag::Latest) => Ok(Self::Latest),
29            core::BlockId::Tag(core::BlockTag::PreConfirmed) => Ok(Self::Pending),
30            core::BlockId::Tag(core::BlockTag::L1Accepted) => Err(ConversionError),
31        }
32    }
33}
34
35impl TryFrom<Block> for core::MaybePreConfirmedBlockWithTxHashes {
36    type Error = ConversionError;
37
38    fn try_from(value: Block) -> Result<Self, Self::Error> {
39        match (value.block_hash, value.block_number, value.state_root) {
40            // Confirmed block
41            (Some(block_hash), Some(block_number), Some(state_root)) => {
42                Ok(Self::Block(core::BlockWithTxHashes {
43                    status: value.status.try_into()?,
44                    block_hash,
45                    parent_hash: value.parent_block_hash,
46                    block_number,
47                    new_root: state_root,
48                    timestamp: value.timestamp,
49                    sequencer_address: value.sequencer_address,
50                    l1_gas_price: value.l1_gas_price,
51                    l2_gas_price: value.l2_gas_price,
52                    l1_data_gas_price: value.l1_data_gas_price,
53                    l1_da_mode: value.l1_da_mode,
54                    starknet_version: value.starknet_version,
55                    event_commitment: value.event_commitment.unwrap_or_default(),
56                    transaction_commitment: value.transaction_commitment.unwrap_or_default(),
57                    receipt_commitment: value.receipt_commitment.unwrap_or_default(),
58                    state_diff_commitment: value.state_diff_commitment.unwrap_or_default(),
59                    event_count: value.event_count,
60                    transaction_count: value.transaction_count,
61                    state_diff_length: value.state_diff_length.unwrap_or_default(),
62                    transactions: value
63                        .transactions
64                        .iter()
65                        .map(|tx| tx.transaction_hash())
66                        .collect(),
67                }))
68            }
69            // Pending block
70            (None, None, None) => {
71                // We're almost able to map this into a pre-confirmed block, but can't do it as
72                // pending blocks from the sequencer doesn't contain block number.
73                //
74                // Technically the block number can be found by looking up the parent block, but we
75                // don't have a choice but to error here.
76                Err(ConversionError)
77            }
78            // Unknown combination
79            _ => Err(ConversionError),
80        }
81    }
82}
83
84impl TryFrom<Block> for core::MaybePreConfirmedBlockWithTxs {
85    type Error = ConversionError;
86
87    fn try_from(value: Block) -> Result<Self, Self::Error> {
88        match (value.block_hash, value.block_number, value.state_root) {
89            // Confirmed block
90            (Some(block_hash), Some(block_number), Some(state_root)) => {
91                Ok(Self::Block(core::BlockWithTxs {
92                    status: value.status.try_into()?,
93                    block_hash,
94                    parent_hash: value.parent_block_hash,
95                    block_number,
96                    new_root: state_root,
97                    timestamp: value.timestamp,
98                    sequencer_address: value.sequencer_address,
99                    l1_gas_price: value.l1_gas_price,
100                    l2_gas_price: value.l2_gas_price,
101                    l1_data_gas_price: value.l1_data_gas_price,
102                    l1_da_mode: value.l1_da_mode,
103                    starknet_version: value.starknet_version,
104                    event_commitment: value.event_commitment.unwrap_or_default(),
105                    transaction_commitment: value.transaction_commitment.unwrap_or_default(),
106                    receipt_commitment: value.receipt_commitment.unwrap_or_default(),
107                    state_diff_commitment: value.state_diff_commitment.unwrap_or_default(),
108                    event_count: value.event_count,
109                    transaction_count: value.transaction_count,
110                    state_diff_length: value.state_diff_length.unwrap_or_default(),
111                    transactions: value
112                        .transactions
113                        .into_iter()
114                        .map(|tx| tx.try_into())
115                        .collect::<Result<_, _>>()?,
116                }))
117            }
118            // Pending block
119            (None, None, None) => {
120                // We're almost able to map this into a pre-confirmed block, but can't do it as
121                // pending blocks from the sequencer doesn't contain block number.
122                //
123                // Technically the block number can be found by looking up the parent block, but we
124                // don't have a choice but to error here.
125                Err(ConversionError)
126            }
127            // Unknown combination
128            _ => Err(ConversionError),
129        }
130    }
131}
132
133impl TryFrom<Block> for core::MaybePreConfirmedBlockWithReceipts {
134    type Error = ConversionError;
135
136    fn try_from(value: Block) -> Result<Self, Self::Error> {
137        if value.transactions.len() != value.transaction_receipts.len() {
138            return Err(ConversionError);
139        }
140
141        let mut transactions = vec![];
142
143        for (tx, receipt) in value
144            .transactions
145            .into_iter()
146            .zip(value.transaction_receipts.into_iter())
147        {
148            let core_tx = tx.clone().try_into()?;
149
150            let tx_with_receipt = ConfirmedReceiptWithContext {
151                receipt,
152                transaction: tx,
153                finality: value.status,
154            };
155
156            transactions.push(core::TransactionWithReceipt {
157                transaction: core_tx,
158                receipt: tx_with_receipt.try_into()?,
159            });
160        }
161
162        match (value.block_hash, value.block_number, value.state_root) {
163            // Confirmed block
164            (Some(block_hash), Some(block_number), Some(state_root)) => {
165                Ok(Self::Block(core::BlockWithReceipts {
166                    status: value.status.try_into()?,
167                    block_hash,
168                    parent_hash: value.parent_block_hash,
169                    block_number,
170                    new_root: state_root,
171                    timestamp: value.timestamp,
172                    sequencer_address: value.sequencer_address,
173                    l1_gas_price: value.l1_gas_price,
174                    l2_gas_price: value.l2_gas_price,
175                    l1_data_gas_price: value.l1_data_gas_price,
176                    l1_da_mode: value.l1_da_mode,
177                    starknet_version: value.starknet_version,
178                    event_commitment: value.event_commitment.unwrap_or_default(),
179                    transaction_commitment: value.transaction_commitment.unwrap_or_default(),
180                    receipt_commitment: value.receipt_commitment.unwrap_or_default(),
181                    state_diff_commitment: value.state_diff_commitment.unwrap_or_default(),
182                    event_count: value.event_count,
183                    transaction_count: value.transaction_count,
184                    state_diff_length: value.state_diff_length.unwrap_or_default(),
185                    transactions,
186                }))
187            }
188            // Pending block
189            (None, None, None) => {
190                // We're almost able to map this into a pre-confirmed block, but can't do it as
191                // pending blocks from the sequencer doesn't contain block number.
192                //
193                // Technically the block number can be found by looking up the parent block, but we
194                // don't have a choice but to error here.
195                Err(ConversionError)
196            }
197            // Unknown combination
198            _ => Err(ConversionError),
199        }
200    }
201}
202
203impl TryFrom<BlockStatus> for core::BlockStatus {
204    type Error = ConversionError;
205
206    fn try_from(value: BlockStatus) -> Result<Self, Self::Error> {
207        match value {
208            BlockStatus::Pending => Ok(Self::PreConfirmed),
209            BlockStatus::Aborted | BlockStatus::Reverted => Err(ConversionError),
210            BlockStatus::AcceptedOnL2 => Ok(Self::AcceptedOnL2),
211            BlockStatus::AcceptedOnL1 => Ok(Self::AcceptedOnL1),
212        }
213    }
214}
215
216impl TryFrom<TransactionType> for core::Transaction {
217    type Error = ConversionError;
218
219    fn try_from(value: TransactionType) -> Result<Self, Self::Error> {
220        match value {
221            TransactionType::Declare(inner) => Ok(Self::Declare(inner.try_into()?)),
222            TransactionType::Deploy(inner) => Ok(Self::Deploy(inner.try_into()?)),
223            TransactionType::DeployAccount(inner) => Ok(Self::DeployAccount(inner.try_into()?)),
224            TransactionType::InvokeFunction(inner) => Ok(Self::Invoke(inner.try_into()?)),
225            TransactionType::L1Handler(inner) => Ok(Self::L1Handler(inner.try_into()?)),
226        }
227    }
228}
229
230impl TryFrom<TransactionType> for core::TransactionContent {
231    type Error = ConversionError;
232
233    fn try_from(value: TransactionType) -> Result<Self, Self::Error> {
234        let tx: core::Transaction = value.try_into()?;
235        Ok(tx.into())
236    }
237}
238
239impl TryFrom<DeclareTransaction> for core::DeclareTransaction {
240    type Error = ConversionError;
241
242    fn try_from(value: DeclareTransaction) -> Result<Self, Self::Error> {
243        if value.version == Felt::ZERO {
244            Ok(Self::V0(core::DeclareTransactionV0 {
245                transaction_hash: value.transaction_hash,
246                max_fee: value.max_fee.ok_or(ConversionError)?,
247                signature: value.signature,
248                class_hash: value.class_hash,
249                sender_address: value.sender_address,
250            }))
251        } else if value.version == Felt::ONE {
252            Ok(Self::V1(core::DeclareTransactionV1 {
253                transaction_hash: value.transaction_hash,
254                max_fee: value.max_fee.ok_or(ConversionError)?,
255                signature: value.signature,
256                nonce: value.nonce,
257                class_hash: value.class_hash,
258                sender_address: value.sender_address,
259            }))
260        } else if value.version == Felt::TWO {
261            Ok(Self::V2(core::DeclareTransactionV2 {
262                transaction_hash: value.transaction_hash,
263                max_fee: value.max_fee.ok_or(ConversionError)?,
264                signature: value.signature,
265                nonce: value.nonce,
266                class_hash: value.class_hash,
267                compiled_class_hash: value.compiled_class_hash.ok_or(ConversionError)?,
268                sender_address: value.sender_address,
269            }))
270        } else if value.version == Felt::THREE {
271            Ok(Self::V3(core::DeclareTransactionV3 {
272                transaction_hash: value.transaction_hash,
273                sender_address: value.sender_address,
274                compiled_class_hash: value.compiled_class_hash.ok_or(ConversionError)?,
275                signature: value.signature,
276                nonce: value.nonce,
277                class_hash: value.class_hash,
278                resource_bounds: value.resource_bounds.ok_or(ConversionError)?.into(),
279                tip: value.tip.ok_or(ConversionError)?,
280                paymaster_data: value.paymaster_data.ok_or(ConversionError)?,
281                account_deployment_data: value.account_deployment_data.ok_or(ConversionError)?,
282                nonce_data_availability_mode: value
283                    .nonce_data_availability_mode
284                    .ok_or(ConversionError)?
285                    .into(),
286                fee_data_availability_mode: value
287                    .fee_data_availability_mode
288                    .ok_or(ConversionError)?
289                    .into(),
290            }))
291        } else {
292            Err(ConversionError)
293        }
294    }
295}
296
297impl TryFrom<DeployTransaction> for core::DeployTransaction {
298    type Error = ConversionError;
299
300    fn try_from(value: DeployTransaction) -> Result<Self, Self::Error> {
301        Ok(Self {
302            transaction_hash: value.transaction_hash,
303            class_hash: value.class_hash,
304            version: value.version,
305            contract_address_salt: value.contract_address_salt,
306            constructor_calldata: value.constructor_calldata,
307        })
308    }
309}
310
311impl TryFrom<DeployAccountTransaction> for core::DeployAccountTransaction {
312    type Error = ConversionError;
313
314    fn try_from(value: DeployAccountTransaction) -> Result<Self, Self::Error> {
315        if value.version == Felt::ONE {
316            Ok(Self::V1(core::DeployAccountTransactionV1 {
317                transaction_hash: value.transaction_hash,
318                max_fee: value.max_fee.ok_or(ConversionError)?,
319                signature: value.signature,
320                nonce: value.nonce,
321                contract_address_salt: value.contract_address_salt,
322                constructor_calldata: value.constructor_calldata,
323                class_hash: value.class_hash,
324            }))
325        } else if value.version == Felt::THREE {
326            Ok(Self::V3(core::DeployAccountTransactionV3 {
327                transaction_hash: value.transaction_hash,
328                signature: value.signature,
329                nonce: value.nonce,
330                contract_address_salt: value.contract_address_salt,
331                constructor_calldata: value.constructor_calldata,
332                class_hash: value.class_hash,
333                resource_bounds: value.resource_bounds.ok_or(ConversionError)?.into(),
334                tip: value.tip.ok_or(ConversionError)?,
335                paymaster_data: value.paymaster_data.ok_or(ConversionError)?,
336                nonce_data_availability_mode: value
337                    .nonce_data_availability_mode
338                    .ok_or(ConversionError)?
339                    .into(),
340                fee_data_availability_mode: value
341                    .fee_data_availability_mode
342                    .ok_or(ConversionError)?
343                    .into(),
344            }))
345        } else {
346            Err(ConversionError)
347        }
348    }
349}
350
351impl TryFrom<InvokeFunctionTransaction> for core::InvokeTransaction {
352    type Error = ConversionError;
353
354    fn try_from(value: InvokeFunctionTransaction) -> Result<Self, Self::Error> {
355        if value.version == Felt::ZERO {
356            Ok(Self::V0(core::InvokeTransactionV0 {
357                transaction_hash: value.transaction_hash,
358                max_fee: value.max_fee.ok_or(ConversionError)?,
359                signature: value.signature,
360                contract_address: value.sender_address,
361                entry_point_selector: value.entry_point_selector.ok_or(ConversionError)?,
362                calldata: value.calldata,
363            }))
364        } else if value.version == Felt::ONE {
365            Ok(Self::V1(core::InvokeTransactionV1 {
366                transaction_hash: value.transaction_hash,
367                max_fee: value.max_fee.ok_or(ConversionError)?,
368                signature: value.signature,
369                nonce: value.nonce.ok_or(ConversionError)?,
370                sender_address: value.sender_address,
371                calldata: value.calldata,
372            }))
373        } else if value.version == Felt::THREE {
374            Ok(Self::V3(core::InvokeTransactionV3 {
375                transaction_hash: value.transaction_hash,
376                sender_address: value.sender_address,
377                calldata: value.calldata,
378                signature: value.signature,
379                nonce: value.nonce.ok_or(ConversionError)?,
380                resource_bounds: value.resource_bounds.ok_or(ConversionError)?.into(),
381                tip: value.tip.ok_or(ConversionError)?,
382                paymaster_data: value.paymaster_data.ok_or(ConversionError)?,
383                account_deployment_data: value.account_deployment_data.ok_or(ConversionError)?,
384                nonce_data_availability_mode: value
385                    .nonce_data_availability_mode
386                    .ok_or(ConversionError)?
387                    .into(),
388                fee_data_availability_mode: value
389                    .fee_data_availability_mode
390                    .ok_or(ConversionError)?
391                    .into(),
392            }))
393        } else {
394            Err(ConversionError)
395        }
396    }
397}
398
399impl TryFrom<L1HandlerTransaction> for core::L1HandlerTransaction {
400    type Error = ConversionError;
401
402    fn try_from(value: L1HandlerTransaction) -> Result<Self, Self::Error> {
403        Ok(Self {
404            transaction_hash: value.transaction_hash,
405            version: value.version,
406            nonce: {
407                // TODO: remove this when a proper u64 conversion is implemented for `Felt`
408                let nonce_bytes = value.nonce.unwrap_or_default().to_bytes_le();
409                if nonce_bytes.iter().skip(8).any(|&x| x != 0) {
410                    return Err(ConversionError);
411                }
412                u64::from_le_bytes(nonce_bytes[..8].try_into().unwrap())
413            },
414            contract_address: value.contract_address,
415            entry_point_selector: value.entry_point_selector,
416            calldata: value.calldata,
417        })
418    }
419}
420
421impl From<ResourceBoundsMapping> for core::ResourceBoundsMapping {
422    fn from(value: ResourceBoundsMapping) -> Self {
423        Self {
424            l1_gas: value.l1_gas.into(),
425            l1_data_gas: value.l1_data_gas.into(),
426            l2_gas: value.l2_gas.into(),
427        }
428    }
429}
430
431impl From<core::ResourceBoundsMapping> for ResourceBoundsMapping {
432    fn from(value: core::ResourceBoundsMapping) -> Self {
433        Self {
434            l1_gas: value.l1_gas.into(),
435            l1_data_gas: value.l1_data_gas.into(),
436            l2_gas: value.l2_gas.into(),
437        }
438    }
439}
440
441impl From<ResourceBounds> for core::ResourceBounds {
442    fn from(value: ResourceBounds) -> Self {
443        Self {
444            max_amount: value.max_amount,
445            max_price_per_unit: value.max_price_per_unit,
446        }
447    }
448}
449
450impl From<core::ResourceBounds> for ResourceBounds {
451    fn from(value: core::ResourceBounds) -> Self {
452        Self {
453            max_amount: value.max_amount,
454            max_price_per_unit: value.max_price_per_unit,
455        }
456    }
457}
458
459impl From<DataAvailabilityMode> for core::DataAvailabilityMode {
460    fn from(value: DataAvailabilityMode) -> Self {
461        match value {
462            DataAvailabilityMode::L1 => Self::L1,
463            DataAvailabilityMode::L2 => Self::L2,
464        }
465    }
466}
467
468impl From<core::DataAvailabilityMode> for DataAvailabilityMode {
469    fn from(value: core::DataAvailabilityMode) -> Self {
470        match value {
471            core::DataAvailabilityMode::L1 => Self::L1,
472            core::DataAvailabilityMode::L2 => Self::L2,
473        }
474    }
475}
476
477impl TryFrom<StateUpdate> for core::MaybePreConfirmedStateUpdate {
478    type Error = ConversionError;
479
480    fn try_from(value: StateUpdate) -> Result<Self, Self::Error> {
481        match (value.block_hash, value.new_root, value.old_root) {
482            (Some(block_hash), Some(new_root), Some(old_root)) => {
483                Ok(Self::Update(core::StateUpdate {
484                    block_hash,
485                    new_root,
486                    old_root,
487                    state_diff: value.state_diff.into(),
488                }))
489            }
490            (None, None, old_root) => Ok(Self::PreConfirmedUpdate(core::PreConfirmedStateUpdate {
491                old_root,
492                state_diff: value.state_diff.into(),
493            })),
494            _ => Err(ConversionError),
495        }
496    }
497}
498
499impl From<StateDiff> for core::StateDiff {
500    fn from(value: StateDiff) -> Self {
501        Self {
502            storage_diffs: value
503                .storage_diffs
504                .into_iter()
505                .map(|(key, value)| core::ContractStorageDiffItem {
506                    address: key,
507                    storage_entries: value.into_iter().map(|item| item.into()).collect(),
508                })
509                .collect(),
510            deprecated_declared_classes: value.old_declared_contracts,
511            declared_classes: value
512                .declared_classes
513                .into_iter()
514                .map(|item| item.into())
515                .collect(),
516            migrated_compiled_classes: value
517                .migrated_compiled_classes
518                .map(|classes| classes.into_iter().map(|item| item.into()).collect()),
519            deployed_contracts: value
520                .deployed_contracts
521                .into_iter()
522                .map(|item| item.into())
523                .collect(),
524            replaced_classes: value
525                .replaced_classes
526                .into_iter()
527                .map(|item| item.into())
528                .collect(),
529            nonces: value
530                .nonces
531                .into_iter()
532                .map(|(key, value)| core::NonceUpdate {
533                    contract_address: key,
534                    nonce: value,
535                })
536                .collect(),
537        }
538    }
539}
540
541impl From<StorageDiff> for core::StorageEntry {
542    fn from(value: StorageDiff) -> Self {
543        Self {
544            key: value.key,
545            value: value.value,
546        }
547    }
548}
549
550impl From<DeclaredContract> for core::DeclaredClassItem {
551    fn from(value: DeclaredContract) -> Self {
552        Self {
553            class_hash: value.class_hash,
554            compiled_class_hash: value.compiled_class_hash,
555        }
556    }
557}
558
559impl From<DeclaredContract> for core::MigratedCompiledClassItem {
560    fn from(value: DeclaredContract) -> Self {
561        Self {
562            class_hash: value.class_hash,
563            compiled_class_hash: value.compiled_class_hash,
564        }
565    }
566}
567
568impl From<DeployedContract> for core::DeployedContractItem {
569    fn from(value: DeployedContract) -> Self {
570        Self {
571            address: value.address,
572            class_hash: value.class_hash,
573        }
574    }
575}
576
577impl From<DeployedContract> for core::ReplacedClassItem {
578    fn from(value: DeployedContract) -> Self {
579        Self {
580            contract_address: value.address,
581            class_hash: value.class_hash,
582        }
583    }
584}
585
586impl TryFrom<TransactionInfo> for core::Transaction {
587    type Error = ConversionError;
588
589    fn try_from(value: TransactionInfo) -> Result<Self, Self::Error> {
590        match value.r#type {
591            Some(tx) => tx.try_into(),
592            None => Err(ConversionError),
593        }
594    }
595}
596
597impl From<L2ToL1Message> for core::MsgToL1 {
598    fn from(value: L2ToL1Message) -> Self {
599        Self {
600            from_address: value.from_address,
601            // Unwrapping here is safe
602            to_address: Felt::from_bytes_be_slice(&value.to_address.0),
603            payload: value.payload,
604        }
605    }
606}
607
608impl From<Event> for core::Event {
609    fn from(value: Event) -> Self {
610        Self {
611            from_address: value.from_address,
612            keys: value.keys,
613            data: value.data,
614        }
615    }
616}
617
618impl TryFrom<TransactionExecutionStatus> for core::TransactionExecutionStatus {
619    type Error = ConversionError;
620
621    fn try_from(value: TransactionExecutionStatus) -> Result<Self, Self::Error> {
622        match value {
623            TransactionExecutionStatus::Succeeded => Ok(Self::Succeeded),
624            TransactionExecutionStatus::Reverted => Ok(Self::Reverted),
625            TransactionExecutionStatus::Rejected => Err(ConversionError),
626        }
627    }
628}
629
630impl TryFrom<TransactionFinalityStatus> for core::TransactionFinalityStatus {
631    type Error = ConversionError;
632
633    fn try_from(value: TransactionFinalityStatus) -> Result<Self, Self::Error> {
634        match value {
635            TransactionFinalityStatus::NotReceived | TransactionFinalityStatus::Received => {
636                Err(ConversionError)
637            }
638            TransactionFinalityStatus::AcceptedOnL2 => Ok(Self::AcceptedOnL2),
639            TransactionFinalityStatus::AcceptedOnL1 => Ok(Self::AcceptedOnL1),
640        }
641    }
642}
643
644impl From<core::BroadcastedInvokeTransaction> for InvokeFunctionTransactionRequest {
645    fn from(value: core::BroadcastedInvokeTransaction) -> Self {
646        Self::V3(value.into())
647    }
648}
649
650impl From<core::BroadcastedInvokeTransactionV3> for InvokeFunctionV3TransactionRequest {
651    fn from(value: core::BroadcastedInvokeTransactionV3) -> Self {
652        Self {
653            sender_address: value.sender_address,
654            calldata: value.calldata,
655            signature: value.signature,
656            nonce: value.nonce,
657            nonce_data_availability_mode: value.nonce_data_availability_mode.into(),
658            fee_data_availability_mode: value.fee_data_availability_mode.into(),
659            resource_bounds: value.resource_bounds.into(),
660            tip: value.tip,
661            paymaster_data: value.paymaster_data,
662            account_deployment_data: value.account_deployment_data,
663            is_query: value.is_query,
664        }
665    }
666}
667
668impl TryFrom<core::BroadcastedDeclareTransaction> for DeclareTransactionRequest {
669    type Error = ConversionError;
670
671    fn try_from(value: core::BroadcastedDeclareTransaction) -> Result<Self, Self::Error> {
672        Ok(Self::V3(value.try_into()?))
673    }
674}
675
676impl TryFrom<core::BroadcastedDeclareTransactionV3> for DeclareV3TransactionRequest {
677    type Error = ConversionError;
678
679    fn try_from(value: core::BroadcastedDeclareTransactionV3) -> Result<Self, Self::Error> {
680        Ok(Self {
681            contract_class: Arc::new(
682                contract::CompressedSierraClass::from_flattened(&value.contract_class)
683                    .map_err(|_| ConversionError)?,
684            ),
685            compiled_class_hash: value.compiled_class_hash,
686            sender_address: value.sender_address,
687            signature: value.signature,
688            nonce: value.nonce,
689            nonce_data_availability_mode: value.nonce_data_availability_mode.into(),
690            fee_data_availability_mode: value.fee_data_availability_mode.into(),
691            resource_bounds: value.resource_bounds.into(),
692            tip: value.tip,
693            paymaster_data: value.paymaster_data,
694            account_deployment_data: value.account_deployment_data,
695            is_query: value.is_query,
696        })
697    }
698}
699
700impl From<core::BroadcastedDeployAccountTransaction> for DeployAccountTransactionRequest {
701    fn from(value: core::BroadcastedDeployAccountTransaction) -> Self {
702        Self::V3(value.into())
703    }
704}
705
706impl From<core::BroadcastedDeployAccountTransactionV3> for DeployAccountV3TransactionRequest {
707    fn from(value: core::BroadcastedDeployAccountTransactionV3) -> Self {
708        Self {
709            class_hash: value.class_hash,
710            contract_address_salt: value.contract_address_salt,
711            constructor_calldata: value.constructor_calldata,
712            signature: value.signature,
713            nonce: value.nonce,
714            nonce_data_availability_mode: value.nonce_data_availability_mode.into(),
715            fee_data_availability_mode: value.fee_data_availability_mode.into(),
716            resource_bounds: value.resource_bounds.into(),
717            tip: value.tip,
718            paymaster_data: value.paymaster_data,
719            is_query: value.is_query,
720        }
721    }
722}
723
724impl From<core::CompressedLegacyContractClass> for CompressedLegacyContractClass {
725    fn from(value: core::CompressedLegacyContractClass) -> Self {
726        Self {
727            program: value.program,
728            entry_points_by_type: contract_legacy::RawLegacyEntryPoints {
729                constructor: value
730                    .entry_points_by_type
731                    .constructor
732                    .into_iter()
733                    .map(convert_legacy_entry_point)
734                    .collect(),
735                external: value
736                    .entry_points_by_type
737                    .external
738                    .into_iter()
739                    .map(convert_legacy_entry_point)
740                    .collect(),
741                l1_handler: value
742                    .entry_points_by_type
743                    .l1_handler
744                    .into_iter()
745                    .map(convert_legacy_entry_point)
746                    .collect(),
747            },
748            abi: value
749                .abi
750                .map(|abi| abi.into_iter().map(|item| item.into()).collect()),
751        }
752    }
753}
754
755impl TryFrom<DeployedClass> for core::ContractClass {
756    type Error = ConversionError;
757
758    fn try_from(value: DeployedClass) -> Result<Self, Self::Error> {
759        match value {
760            DeployedClass::SierraClass(inner) => Ok(Self::Sierra(inner)),
761            DeployedClass::LegacyClass(inner) => {
762                Ok(Self::Legacy(inner.compress().map_err(|_| ConversionError)?))
763            }
764        }
765    }
766}
767
768impl From<EntryPointType> for core::EntryPointType {
769    fn from(value: EntryPointType) -> Self {
770        match value {
771            EntryPointType::External => Self::External,
772            EntryPointType::L1Handler => Self::L1Handler,
773            EntryPointType::Constructor => Self::Constructor,
774        }
775    }
776}
777
778impl TryFrom<TransactionStatusInfo> for core::TransactionStatus {
779    type Error = ConversionError;
780
781    fn try_from(value: TransactionStatusInfo) -> Result<Self, Self::Error> {
782        if value.status.is_rejected() {
783            // Since Starknet v0.14.0 it's no longer possible to express rejected transactions.
784            return Err(ConversionError);
785        }
786
787        let exec_status = match value.execution_status.ok_or(ConversionError)? {
788            TransactionExecutionStatus::Succeeded => {
789                Some(core::TransactionExecutionStatus::Succeeded)
790            }
791            TransactionExecutionStatus::Reverted => {
792                Some(core::TransactionExecutionStatus::Reverted)
793            }
794            TransactionExecutionStatus::Rejected => None,
795        };
796
797        match value.finality_status {
798            Some(TransactionFinalityStatus::Received) => Ok(Self::Received),
799            Some(TransactionFinalityStatus::AcceptedOnL2) => {
800                let exec = match (
801                    exec_status.ok_or(ConversionError)?,
802                    value.transaction_failure_reason,
803                ) {
804                    (core::TransactionExecutionStatus::Succeeded, None) => {
805                        Ok(core::ExecutionResult::Succeeded)
806                    }
807                    (core::TransactionExecutionStatus::Reverted, Some(reason)) => {
808                        Ok(core::ExecutionResult::Reverted {
809                            reason: reason.error_message.unwrap_or(reason.code),
810                        })
811                    }
812                    _ => Err(ConversionError),
813                };
814
815                Ok(Self::AcceptedOnL2(exec?))
816            }
817            Some(TransactionFinalityStatus::AcceptedOnL1) => {
818                let exec = match (
819                    exec_status.ok_or(ConversionError)?,
820                    value.transaction_failure_reason,
821                ) {
822                    (core::TransactionExecutionStatus::Succeeded, None) => {
823                        Ok(core::ExecutionResult::Succeeded)
824                    }
825                    (core::TransactionExecutionStatus::Reverted, Some(reason)) => {
826                        Ok(core::ExecutionResult::Reverted {
827                            reason: reason.error_message.unwrap_or(reason.code),
828                        })
829                    }
830                    _ => Err(ConversionError),
831                };
832
833                Ok(Self::AcceptedOnL1(exec?))
834            }
835            // `NotReceived` must be handled on the caller before converting
836            _ => Err(ConversionError),
837        }
838    }
839}
840
841impl TryFrom<ConfirmedReceiptWithContext> for core::TransactionReceipt {
842    type Error = ConversionError;
843
844    fn try_from(value: ConfirmedReceiptWithContext) -> Result<Self, Self::Error> {
845        match value.transaction {
846            TransactionType::Declare(_) => Ok(Self::Declare(value.try_into()?)),
847            TransactionType::Deploy(_) => Ok(Self::Deploy(value.try_into()?)),
848            TransactionType::DeployAccount(_) => Ok(Self::DeployAccount(value.try_into()?)),
849            TransactionType::InvokeFunction(_) => Ok(Self::Invoke(value.try_into()?)),
850            TransactionType::L1Handler(_) => Ok(Self::L1Handler(value.try_into()?)),
851        }
852    }
853}
854
855impl TryFrom<ConfirmedReceiptWithContext> for core::DeclareTransactionReceipt {
856    type Error = ConversionError;
857
858    fn try_from(value: ConfirmedReceiptWithContext) -> Result<Self, Self::Error> {
859        Ok(Self {
860            transaction_hash: value.receipt.transaction_hash,
861            actual_fee: core::FeePayment {
862                amount: value.receipt.actual_fee,
863                unit: core::PriceUnit::Wei,
864            },
865            finality_status: value.finality.try_into()?,
866            messages_sent: value
867                .receipt
868                .l2_to_l1_messages
869                .into_iter()
870                .map(|item| item.into())
871                .collect(),
872            events: value
873                .receipt
874                .events
875                .into_iter()
876                .map(|item| item.into())
877                .collect(),
878            execution_resources: value
879                .receipt
880                .execution_resources
881                .ok_or(ConversionError)?
882                .total_gas_consumed
883                .ok_or(ConversionError)?,
884            execution_result: convert_execution_result(
885                value.receipt.execution_status,
886                value.receipt.revert_error,
887            )?,
888        })
889    }
890}
891
892impl TryFrom<ConfirmedReceiptWithContext> for core::DeployTransactionReceipt {
893    type Error = ConversionError;
894
895    fn try_from(value: ConfirmedReceiptWithContext) -> Result<Self, Self::Error> {
896        Ok(Self {
897            transaction_hash: value.receipt.transaction_hash,
898            actual_fee: core::FeePayment {
899                amount: value.receipt.actual_fee,
900                unit: core::PriceUnit::Wei,
901            },
902            finality_status: value.finality.try_into()?,
903            messages_sent: value
904                .receipt
905                .l2_to_l1_messages
906                .into_iter()
907                .map(|item| item.into())
908                .collect(),
909            events: value
910                .receipt
911                .events
912                .into_iter()
913                .map(|item| item.into())
914                .collect(),
915            contract_address: match value.transaction {
916                TransactionType::Deploy(inner) => inner.contract_address,
917                _ => return Err(ConversionError),
918            },
919            execution_resources: value
920                .receipt
921                .execution_resources
922                .ok_or(ConversionError)?
923                .total_gas_consumed
924                .ok_or(ConversionError)?,
925            execution_result: convert_execution_result(
926                value.receipt.execution_status,
927                value.receipt.revert_error,
928            )?,
929        })
930    }
931}
932
933impl TryFrom<ConfirmedReceiptWithContext> for core::DeployAccountTransactionReceipt {
934    type Error = ConversionError;
935
936    fn try_from(value: ConfirmedReceiptWithContext) -> Result<Self, Self::Error> {
937        Ok(Self {
938            transaction_hash: value.receipt.transaction_hash,
939            actual_fee: core::FeePayment {
940                amount: value.receipt.actual_fee,
941                unit: core::PriceUnit::Wei,
942            },
943            finality_status: value.finality.try_into()?,
944            messages_sent: value
945                .receipt
946                .l2_to_l1_messages
947                .into_iter()
948                .map(|item| item.into())
949                .collect(),
950            events: value
951                .receipt
952                .events
953                .into_iter()
954                .map(|item| item.into())
955                .collect(),
956            contract_address: match value.transaction {
957                TransactionType::DeployAccount(inner) => {
958                    inner.contract_address.ok_or(ConversionError)?
959                }
960                _ => return Err(ConversionError),
961            },
962            execution_resources: value
963                .receipt
964                .execution_resources
965                .ok_or(ConversionError)?
966                .total_gas_consumed
967                .ok_or(ConversionError)?,
968            execution_result: convert_execution_result(
969                value.receipt.execution_status,
970                value.receipt.revert_error,
971            )?,
972        })
973    }
974}
975
976impl TryFrom<ConfirmedReceiptWithContext> for core::InvokeTransactionReceipt {
977    type Error = ConversionError;
978
979    fn try_from(value: ConfirmedReceiptWithContext) -> Result<Self, Self::Error> {
980        Ok(Self {
981            transaction_hash: value.receipt.transaction_hash,
982            actual_fee: core::FeePayment {
983                amount: value.receipt.actual_fee,
984                unit: core::PriceUnit::Wei,
985            },
986            finality_status: value.finality.try_into()?,
987            messages_sent: value
988                .receipt
989                .l2_to_l1_messages
990                .into_iter()
991                .map(|item| item.into())
992                .collect(),
993            events: value
994                .receipt
995                .events
996                .into_iter()
997                .map(|item| item.into())
998                .collect(),
999            execution_resources: value
1000                .receipt
1001                .execution_resources
1002                .ok_or(ConversionError)?
1003                .total_gas_consumed
1004                .ok_or(ConversionError)?,
1005            execution_result: convert_execution_result(
1006                value.receipt.execution_status,
1007                value.receipt.revert_error,
1008            )?,
1009        })
1010    }
1011}
1012
1013impl TryFrom<ConfirmedReceiptWithContext> for core::L1HandlerTransactionReceipt {
1014    type Error = ConversionError;
1015
1016    fn try_from(value: ConfirmedReceiptWithContext) -> Result<Self, Self::Error> {
1017        // The sequencer never serves the message hash, so we have to compute it ourselves.
1018        let l1_handler_tx: core::L1HandlerTransaction = match value.transaction {
1019            TransactionType::L1Handler(tx) => tx.try_into().map_err(|_| ConversionError)?,
1020            _ => return Err(ConversionError),
1021        };
1022        let msg_to_l2 = l1_handler_tx
1023            .parse_msg_to_l2()
1024            .map_err(|_| ConversionError)?;
1025
1026        Ok(Self {
1027            transaction_hash: value.receipt.transaction_hash,
1028            actual_fee: core::FeePayment {
1029                amount: value.receipt.actual_fee,
1030                unit: core::PriceUnit::Wei,
1031            },
1032            finality_status: value.finality.try_into()?,
1033            messages_sent: value
1034                .receipt
1035                .l2_to_l1_messages
1036                .into_iter()
1037                .map(|item| item.into())
1038                .collect(),
1039            events: value
1040                .receipt
1041                .events
1042                .into_iter()
1043                .map(|item| item.into())
1044                .collect(),
1045            execution_resources: value
1046                .receipt
1047                .execution_resources
1048                .ok_or(ConversionError)?
1049                .total_gas_consumed
1050                .ok_or(ConversionError)?,
1051            execution_result: convert_execution_result(
1052                value.receipt.execution_status,
1053                value.receipt.revert_error,
1054            )?,
1055            message_hash: msg_to_l2.hash(),
1056        })
1057    }
1058}
1059
1060impl TryFrom<BlockStatus> for core::TransactionFinalityStatus {
1061    type Error = ConversionError;
1062
1063    fn try_from(value: BlockStatus) -> Result<Self, Self::Error> {
1064        match value {
1065            // Transactions in pending blocks are considered "accepted on L2" now
1066            BlockStatus::Pending | BlockStatus::AcceptedOnL2 => Ok(Self::AcceptedOnL2),
1067            BlockStatus::AcceptedOnL1 => Ok(Self::AcceptedOnL1),
1068            BlockStatus::Aborted | BlockStatus::Reverted => Err(ConversionError),
1069        }
1070    }
1071}
1072
1073fn convert_execution_result(
1074    execution_status: Option<TransactionExecutionStatus>,
1075    revert_error: Option<String>,
1076) -> Result<core::ExecutionResult, ConversionError> {
1077    match (execution_status, revert_error) {
1078        (None, None) => {
1079            // This is a response from pre-v0.12.1. Pre-v0.12.1 transactions are always successful
1080            // as long as they're in a block.
1081            //
1082            // After feeder deprecation, the only way to fetch transaction receipts is by fetching
1083            // them as part of a block. Therefore, it's always the case that this tx in question is
1084            // in a block.
1085
1086            Ok(core::ExecutionResult::Succeeded)
1087        }
1088        (Some(TransactionExecutionStatus::Succeeded), None) => Ok(core::ExecutionResult::Succeeded),
1089        (Some(TransactionExecutionStatus::Reverted), Some(revert_error)) => {
1090            Ok(core::ExecutionResult::Reverted {
1091                reason: revert_error,
1092            })
1093        }
1094        // All other combinations are illegal
1095        _ => Err(ConversionError),
1096    }
1097}
1098
1099const fn convert_legacy_entry_point(
1100    value: core::LegacyContractEntryPoint,
1101) -> contract_legacy::RawLegacyEntryPoint {
1102    // WARNING: this causes pre-0.11.0 contract declaration to fail due to `offset` issue
1103    // TODO: support declaring pre-0.11.0 contracts here (do we even care though?)
1104    contract_legacy::RawLegacyEntryPoint {
1105        offset: contract_legacy::LegacyEntrypointOffset::U64AsInt(value.offset),
1106        selector: value.selector,
1107    }
1108}