starknet_providers/sequencer/models/
conversions.rs

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