iota_indexer/
types.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_json_rpc_types::{
6    IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, IotaTransactionKind,
7    ObjectChange,
8};
9use iota_types::{
10    base_types::{IotaAddress, ObjectDigest, ObjectID, SequenceNumber},
11    crypto::AggregateAuthoritySignature,
12    digests::TransactionDigest,
13    dynamic_field::DynamicFieldType,
14    effects::TransactionEffects,
15    event::{SystemEpochInfoEvent, SystemEpochInfoEventV1, SystemEpochInfoEventV2},
16    iota_serde::IotaStructTag,
17    iota_system_state::iota_system_state_summary::{IotaSystemStateSummary, IotaValidatorSummary},
18    messages_checkpoint::{
19        CertifiedCheckpointSummary, CheckpointCommitment, CheckpointDigest,
20        CheckpointSequenceNumber, EndOfEpochData,
21    },
22    move_package::MovePackage,
23    object::{Object, Owner},
24    transaction::SenderSignedData,
25};
26use move_core_types::language_storage::StructTag;
27use serde::{Deserialize, Serialize};
28use serde_with::serde_as;
29
30use crate::errors::IndexerError;
31
32pub type IndexerResult<T> = Result<T, IndexerError>;
33
34#[derive(Debug)]
35pub struct IndexedCheckpoint {
36    pub sequence_number: u64,
37    pub checkpoint_digest: CheckpointDigest,
38    pub epoch: u64,
39    pub tx_digests: Vec<TransactionDigest>,
40    pub network_total_transactions: u64,
41    pub previous_checkpoint_digest: Option<CheckpointDigest>,
42    pub timestamp_ms: u64,
43    pub total_gas_cost: i64, // total gas cost could be negative
44    pub computation_cost: u64,
45    pub computation_cost_burned: u64,
46    pub storage_cost: u64,
47    pub storage_rebate: u64,
48    pub non_refundable_storage_fee: u64,
49    pub checkpoint_commitments: Vec<CheckpointCommitment>,
50    pub validator_signature: AggregateAuthoritySignature,
51    pub successful_tx_num: usize,
52    pub end_of_epoch_data: Option<EndOfEpochData>,
53    pub end_of_epoch: bool,
54    pub min_tx_sequence_number: u64,
55    pub max_tx_sequence_number: u64,
56}
57
58impl IndexedCheckpoint {
59    pub fn from_iota_checkpoint(
60        checkpoint: &iota_types::messages_checkpoint::CertifiedCheckpointSummary,
61        contents: &iota_types::messages_checkpoint::CheckpointContents,
62        successful_tx_num: usize,
63    ) -> Self {
64        let total_gas_cost = checkpoint.epoch_rolling_gas_cost_summary.computation_cost as i64
65            + checkpoint.epoch_rolling_gas_cost_summary.storage_cost as i64
66            - checkpoint.epoch_rolling_gas_cost_summary.storage_rebate as i64;
67        let tx_digests = contents.iter().map(|t| t.transaction).collect::<Vec<_>>();
68        let max_tx_sequence_number = checkpoint.network_total_transactions - 1;
69        // NOTE: + 1u64 first to avoid subtraction with overflow
70        let min_tx_sequence_number = max_tx_sequence_number + 1u64 - tx_digests.len() as u64;
71        let auth_sig = &checkpoint.auth_sig().signature;
72        Self {
73            sequence_number: checkpoint.sequence_number,
74            checkpoint_digest: *checkpoint.digest(),
75            epoch: checkpoint.epoch,
76            tx_digests,
77            previous_checkpoint_digest: checkpoint.previous_digest,
78            end_of_epoch_data: checkpoint.end_of_epoch_data.clone(),
79            end_of_epoch: checkpoint.end_of_epoch_data.clone().is_some(),
80            total_gas_cost,
81            computation_cost: checkpoint.epoch_rolling_gas_cost_summary.computation_cost,
82            computation_cost_burned: checkpoint
83                .epoch_rolling_gas_cost_summary
84                .computation_cost_burned,
85            storage_cost: checkpoint.epoch_rolling_gas_cost_summary.storage_cost,
86            storage_rebate: checkpoint.epoch_rolling_gas_cost_summary.storage_rebate,
87            non_refundable_storage_fee: checkpoint
88                .epoch_rolling_gas_cost_summary
89                .non_refundable_storage_fee,
90            successful_tx_num,
91            network_total_transactions: checkpoint.network_total_transactions,
92            timestamp_ms: checkpoint.timestamp_ms,
93            validator_signature: auth_sig.clone(),
94            checkpoint_commitments: checkpoint.checkpoint_commitments.clone(),
95            min_tx_sequence_number,
96            max_tx_sequence_number,
97        }
98    }
99}
100
101/// Represents system state and summary info at the start and end of an epoch.
102/// Optional fields are populated at epoch boundary, since they cannot be
103/// determined at the start of the epoch.
104#[derive(Clone, Debug, Default)]
105pub struct IndexedEpochInfo {
106    pub epoch: u64,
107    pub first_checkpoint_id: u64,
108    pub epoch_start_timestamp: u64,
109    pub reference_gas_price: u64,
110    pub protocol_version: u64,
111    pub total_stake: u64,
112    pub storage_fund_balance: u64,
113    pub system_state: Vec<u8>,
114    pub epoch_total_transactions: Option<u64>,
115    pub last_checkpoint_id: Option<u64>,
116    pub epoch_end_timestamp: Option<u64>,
117    pub storage_charge: Option<u64>,
118    pub storage_rebate: Option<u64>,
119    pub total_gas_fees: Option<u64>,
120    pub total_stake_rewards_distributed: Option<u64>,
121    pub epoch_commitments: Option<Vec<CheckpointCommitment>>,
122    pub burnt_tokens_amount: Option<u64>,
123    pub minted_tokens_amount: Option<u64>,
124    pub tips_amount: Option<u64>,
125}
126
127/// View on the common inner state-summary fields.
128///
129/// This exposes whatever makes sense to the scope of this
130/// library.
131pub(crate) trait IotaSystemStateSummaryView {
132    fn epoch(&self) -> u64;
133    fn epoch_start_timestamp_ms(&self) -> u64;
134    fn reference_gas_price(&self) -> u64;
135    fn protocol_version(&self) -> u64;
136    fn iota_total_supply(&self) -> u64;
137    fn active_validators(&self) -> &[IotaValidatorSummary];
138    fn inactive_pools_id(&self) -> ObjectID;
139    fn inactive_pools_size(&self) -> u64;
140    fn validator_candidates_id(&self) -> ObjectID;
141    fn validator_candidates_size(&self) -> u64;
142    fn to_committee_members(&self) -> Vec<u64>;
143}
144
145/// Access common fields of the inner variants wrapped by
146/// [`IotaSystemStateSummary`].
147///
148/// ## Panics
149///
150/// If the `field` identifier does not correspond to an existing field in any
151/// of the inner types wrapped in the variants.
152macro_rules! state_summary_get {
153    ($enum:expr, $field:ident) => {{
154        match $enum {
155            IotaSystemStateSummary::V1(ref inner) => &inner.$field,
156            IotaSystemStateSummary::V2(ref inner) => &inner.$field,
157            _ => unimplemented!(),
158        }
159    }};
160}
161
162impl IotaSystemStateSummaryView for IotaSystemStateSummary {
163    fn epoch(&self) -> u64 {
164        *state_summary_get!(self, epoch)
165    }
166
167    fn epoch_start_timestamp_ms(&self) -> u64 {
168        *state_summary_get!(self, epoch_start_timestamp_ms)
169    }
170
171    fn reference_gas_price(&self) -> u64 {
172        *state_summary_get!(self, reference_gas_price)
173    }
174
175    fn protocol_version(&self) -> u64 {
176        *state_summary_get!(self, protocol_version)
177    }
178
179    fn iota_total_supply(&self) -> u64 {
180        *state_summary_get!(self, iota_total_supply)
181    }
182
183    fn active_validators(&self) -> &[IotaValidatorSummary] {
184        state_summary_get!(self, active_validators)
185    }
186
187    fn inactive_pools_id(&self) -> ObjectID {
188        *state_summary_get!(self, inactive_pools_id)
189    }
190
191    fn inactive_pools_size(&self) -> u64 {
192        *state_summary_get!(self, inactive_pools_size)
193    }
194
195    fn validator_candidates_id(&self) -> ObjectID {
196        *state_summary_get!(self, validator_candidates_id)
197    }
198
199    fn validator_candidates_size(&self) -> u64 {
200        *state_summary_get!(self, validator_candidates_size)
201    }
202
203    fn to_committee_members(&self) -> Vec<u64> {
204        match self {
205            IotaSystemStateSummary::V1(inner) => (0..inner.active_validators.len())
206                .map(|i| i as u64)
207                .collect(),
208            IotaSystemStateSummary::V2(inner) => inner.committee_members.clone(),
209            _ => unimplemented!(),
210        }
211    }
212}
213
214impl IndexedEpochInfo {
215    pub fn from_new_system_state_summary(
216        new_system_state_summary: &IotaSystemStateSummary,
217        first_checkpoint_id: u64,
218        event: Option<&SystemEpochInfoEvent>,
219    ) -> IndexedEpochInfo {
220        // NOTE: total_stake and storage_fund_balance are about new epoch,
221        // although the event is generated at the end of the previous epoch,
222        // the event is optional b/c no such event for the first epoch.
223        let (total_stake, storage_fund_balance) = match event {
224            Some(SystemEpochInfoEvent::V1(e)) => (e.total_stake, e.storage_fund_balance),
225            Some(SystemEpochInfoEvent::V2(e)) => (e.total_stake, e.storage_fund_balance),
226            None => (0, 0),
227        };
228        Self {
229            epoch: new_system_state_summary.epoch(),
230            first_checkpoint_id,
231            epoch_start_timestamp: new_system_state_summary.epoch_start_timestamp_ms(),
232            reference_gas_price: new_system_state_summary.reference_gas_price(),
233            protocol_version: new_system_state_summary.protocol_version(),
234            total_stake,
235            storage_fund_balance,
236            system_state: bcs::to_bytes(new_system_state_summary).unwrap(),
237            ..Default::default()
238        }
239    }
240
241    /// Creates `IndexedEpochInfo` for epoch X-1 at the boundary of epoch X-1 to
242    /// X. `network_total_tx_num_at_last_epoch_end` is needed to determine
243    /// the number of transactions that occurred in the epoch X-1.
244    pub fn from_end_of_epoch_data(
245        system_state_summary: &IotaSystemStateSummary,
246        last_checkpoint_summary: &CertifiedCheckpointSummary,
247        event: &SystemEpochInfoEvent,
248        network_total_tx_num_at_last_epoch_end: u64,
249    ) -> IndexedEpochInfo {
250        let event = IndexedEpochInfoEvent::from(event);
251        Self {
252            epoch: last_checkpoint_summary.epoch(),
253            epoch_total_transactions: Some(
254                last_checkpoint_summary.network_total_transactions
255                    - network_total_tx_num_at_last_epoch_end,
256            ),
257            last_checkpoint_id: Some(*last_checkpoint_summary.sequence_number()),
258            epoch_end_timestamp: Some(last_checkpoint_summary.timestamp_ms),
259            storage_charge: Some(event.storage_charge),
260            storage_rebate: Some(event.storage_rebate),
261            total_gas_fees: Some(event.total_gas_fees),
262            total_stake_rewards_distributed: Some(event.total_stake_rewards_distributed),
263            epoch_commitments: last_checkpoint_summary
264                .end_of_epoch_data
265                .as_ref()
266                .map(|e| e.epoch_commitments.clone()),
267            system_state: bcs::to_bytes(system_state_summary).unwrap(),
268            // The following felds will not and shall not be upserted
269            // into DB. We have them below to make compiler and diesel happy
270            first_checkpoint_id: 0,
271            epoch_start_timestamp: 0,
272            reference_gas_price: 0,
273            protocol_version: 0,
274            total_stake: 0,
275            storage_fund_balance: 0,
276            burnt_tokens_amount: Some(event.burnt_tokens_amount),
277            minted_tokens_amount: Some(event.minted_tokens_amount),
278            tips_amount: Some(event.tips_amount),
279        }
280    }
281}
282
283#[derive(Debug, Clone, Default)]
284pub(crate) struct IndexedEpochInfoEvent {
285    pub storage_charge: u64,
286    pub storage_rebate: u64,
287    pub total_gas_fees: u64,
288    pub total_stake_rewards_distributed: u64,
289    pub burnt_tokens_amount: u64,
290    pub minted_tokens_amount: u64,
291    pub tips_amount: u64,
292}
293
294impl From<&SystemEpochInfoEventV1> for IndexedEpochInfoEvent {
295    fn from(event: &SystemEpochInfoEventV1) -> Self {
296        Self {
297            storage_charge: event.storage_charge,
298            storage_rebate: event.storage_rebate,
299            total_gas_fees: event.total_gas_fees,
300            total_stake_rewards_distributed: event.total_stake_rewards_distributed,
301            burnt_tokens_amount: event.burnt_tokens_amount,
302            minted_tokens_amount: event.minted_tokens_amount,
303            tips_amount: 0,
304        }
305    }
306}
307
308impl From<&SystemEpochInfoEventV2> for IndexedEpochInfoEvent {
309    fn from(event: &SystemEpochInfoEventV2) -> Self {
310        Self {
311            storage_charge: event.storage_charge,
312            storage_rebate: event.storage_rebate,
313            total_gas_fees: event.total_gas_fees,
314            total_stake_rewards_distributed: event.total_stake_rewards_distributed,
315            burnt_tokens_amount: event.burnt_tokens_amount,
316            minted_tokens_amount: event.minted_tokens_amount,
317            tips_amount: event.tips_amount,
318        }
319    }
320}
321
322impl From<&SystemEpochInfoEvent> for IndexedEpochInfoEvent {
323    fn from(event: &SystemEpochInfoEvent) -> Self {
324        match event {
325            SystemEpochInfoEvent::V1(inner) => inner.into(),
326            SystemEpochInfoEvent::V2(inner) => inner.into(),
327        }
328    }
329}
330
331#[derive(Debug, Clone)]
332pub struct IndexedEvent {
333    pub tx_sequence_number: u64,
334    pub event_sequence_number: u64,
335    pub checkpoint_sequence_number: u64,
336    pub transaction_digest: TransactionDigest,
337    pub senders: Vec<IotaAddress>,
338    pub package: ObjectID,
339    pub module: String,
340    pub event_type: String,
341    pub event_type_package: ObjectID,
342    pub event_type_module: String,
343    /// Struct name of the event, without type parameters.
344    pub event_type_name: String,
345    pub bcs: Vec<u8>,
346    pub timestamp_ms: u64,
347}
348
349impl IndexedEvent {
350    pub fn from_event(
351        tx_sequence_number: u64,
352        event_sequence_number: u64,
353        checkpoint_sequence_number: u64,
354        transaction_digest: TransactionDigest,
355        event: &iota_types::event::Event,
356        timestamp_ms: u64,
357    ) -> Self {
358        Self {
359            tx_sequence_number,
360            event_sequence_number,
361            checkpoint_sequence_number,
362            transaction_digest,
363            senders: vec![event.sender],
364            package: event.package_id,
365            module: event.transaction_module.to_string(),
366            event_type: event.type_.to_canonical_string(/* with_prefix */ true),
367            event_type_package: event.type_.address.into(),
368            event_type_module: event.type_.module.to_string(),
369            event_type_name: event.type_.name.to_string(),
370            bcs: event.contents.clone(),
371            timestamp_ms,
372        }
373    }
374}
375
376#[derive(Debug, Clone)]
377pub struct EventIndex {
378    pub tx_sequence_number: u64,
379    pub event_sequence_number: u64,
380    pub sender: IotaAddress,
381    pub emit_package: ObjectID,
382    pub emit_module: String,
383    pub type_package: ObjectID,
384    pub type_module: String,
385    /// Struct name of the event, without type parameters.
386    pub type_name: String,
387    /// Type instantiation of the event, with type name and type parameters, if
388    /// any.
389    pub type_instantiation: String,
390}
391
392impl EventIndex {
393    pub fn from_event(
394        tx_sequence_number: u64,
395        event_sequence_number: u64,
396        event: &iota_types::event::Event,
397    ) -> Self {
398        let type_instantiation = event
399            .type_
400            .to_canonical_string(/* with_prefix */ true)
401            .splitn(3, "::")
402            .collect::<Vec<_>>()[2]
403            .to_string();
404        Self {
405            tx_sequence_number,
406            event_sequence_number,
407            sender: event.sender,
408            emit_package: event.package_id,
409            emit_module: event.transaction_module.to_string(),
410            type_package: event.type_.address.into(),
411            type_module: event.type_.module.to_string(),
412            type_name: event.type_.name.to_string(),
413            type_instantiation,
414        }
415    }
416}
417
418#[derive(Debug, Copy, Clone)]
419pub enum OwnerType {
420    Immutable = 0,
421    Address = 1,
422    Object = 2,
423    Shared = 3,
424}
425
426pub enum ObjectStatus {
427    Active = 0,
428    WrappedOrDeleted = 1,
429}
430
431impl TryFrom<i16> for ObjectStatus {
432    type Error = IndexerError;
433
434    fn try_from(value: i16) -> Result<Self, Self::Error> {
435        Ok(match value {
436            0 => ObjectStatus::Active,
437            1 => ObjectStatus::WrappedOrDeleted,
438            value => {
439                return Err(IndexerError::PersistentStorageDataCorruption(format!(
440                    "{value} as ObjectStatus"
441                )));
442            }
443        })
444    }
445}
446
447impl TryFrom<i16> for OwnerType {
448    type Error = IndexerError;
449
450    fn try_from(value: i16) -> Result<Self, Self::Error> {
451        Ok(match value {
452            0 => OwnerType::Immutable,
453            1 => OwnerType::Address,
454            2 => OwnerType::Object,
455            3 => OwnerType::Shared,
456            value => {
457                return Err(IndexerError::PersistentStorageDataCorruption(format!(
458                    "{value} as OwnerType"
459                )));
460            }
461        })
462    }
463}
464
465// Returns owner_type, owner_address
466pub fn owner_to_owner_info(owner: &Owner) -> (OwnerType, Option<IotaAddress>) {
467    match owner {
468        Owner::AddressOwner(address) => (OwnerType::Address, Some(*address)),
469        Owner::ObjectOwner(address) => (OwnerType::Object, Some(*address)),
470        Owner::Shared { .. } => (OwnerType::Shared, None),
471        Owner::Immutable => (OwnerType::Immutable, None),
472    }
473}
474
475#[derive(Debug, Copy, Clone)]
476pub enum DynamicFieldKind {
477    DynamicField = 0,
478    DynamicObject = 1,
479}
480
481#[derive(Clone, Debug)]
482pub struct IndexedObject {
483    pub checkpoint_sequence_number: CheckpointSequenceNumber,
484    pub object: Object,
485    pub df_kind: Option<DynamicFieldType>,
486}
487
488impl IndexedObject {
489    pub fn from_object(
490        checkpoint_sequence_number: CheckpointSequenceNumber,
491        object: Object,
492        df_kind: Option<DynamicFieldType>,
493    ) -> Self {
494        Self {
495            checkpoint_sequence_number,
496            object,
497            df_kind,
498        }
499    }
500}
501
502#[derive(Clone, Debug)]
503pub struct IndexedDeletedObject {
504    pub object_id: ObjectID,
505    pub object_version: u64,
506    pub checkpoint_sequence_number: u64,
507}
508
509#[derive(Debug)]
510pub struct IndexedPackage {
511    pub package_id: ObjectID,
512    pub move_package: MovePackage,
513    pub checkpoint_sequence_number: u64,
514}
515
516#[derive(Debug, Clone)]
517pub struct IndexedTransaction {
518    pub tx_sequence_number: u64,
519    pub tx_digest: TransactionDigest,
520    pub sender_signed_data: SenderSignedData,
521    pub effects: TransactionEffects,
522    pub checkpoint_sequence_number: u64,
523    pub timestamp_ms: u64,
524    pub object_changes: Vec<IndexedObjectChange>,
525    pub balance_change: Vec<iota_json_rpc_types::BalanceChange>,
526    pub events: Vec<iota_types::event::Event>,
527    pub transaction_kind: IotaTransactionKind,
528    pub successful_tx_num: u64,
529}
530
531#[derive(Debug, Clone)]
532pub struct TxIndex {
533    pub tx_sequence_number: u64,
534    pub tx_kind: IotaTransactionKind,
535    pub transaction_digest: TransactionDigest,
536    pub checkpoint_sequence_number: u64,
537    pub input_objects: Vec<ObjectID>,
538    pub changed_objects: Vec<ObjectID>,
539    pub payers: Vec<IotaAddress>,
540    pub sender: IotaAddress,
541    pub recipients: Vec<IotaAddress>,
542    pub move_calls: Vec<(ObjectID, String, String)>,
543}
544
545// ObjectChange is not bcs deserializable, IndexedObjectChange is.
546#[serde_as]
547#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
548pub enum IndexedObjectChange {
549    Published {
550        package_id: ObjectID,
551        version: SequenceNumber,
552        digest: ObjectDigest,
553        modules: Vec<String>,
554    },
555    Transferred {
556        sender: IotaAddress,
557        recipient: Owner,
558        #[serde_as(as = "IotaStructTag")]
559        object_type: StructTag,
560        object_id: ObjectID,
561        version: SequenceNumber,
562        digest: ObjectDigest,
563    },
564    /// Object mutated.
565    Mutated {
566        sender: IotaAddress,
567        owner: Owner,
568        #[serde_as(as = "IotaStructTag")]
569        object_type: StructTag,
570        object_id: ObjectID,
571        version: SequenceNumber,
572        previous_version: SequenceNumber,
573        digest: ObjectDigest,
574    },
575    /// Delete object
576    Deleted {
577        sender: IotaAddress,
578        #[serde_as(as = "IotaStructTag")]
579        object_type: StructTag,
580        object_id: ObjectID,
581        version: SequenceNumber,
582    },
583    /// Wrapped object
584    Wrapped {
585        sender: IotaAddress,
586        #[serde_as(as = "IotaStructTag")]
587        object_type: StructTag,
588        object_id: ObjectID,
589        version: SequenceNumber,
590    },
591    /// New object creation
592    Created {
593        sender: IotaAddress,
594        owner: Owner,
595        #[serde_as(as = "IotaStructTag")]
596        object_type: StructTag,
597        object_id: ObjectID,
598        version: SequenceNumber,
599        digest: ObjectDigest,
600    },
601}
602
603impl From<ObjectChange> for IndexedObjectChange {
604    fn from(oc: ObjectChange) -> Self {
605        match oc {
606            ObjectChange::Published {
607                package_id,
608                version,
609                digest,
610                modules,
611            } => Self::Published {
612                package_id,
613                version,
614                digest,
615                modules,
616            },
617            ObjectChange::Transferred {
618                sender,
619                recipient,
620                object_type,
621                object_id,
622                version,
623                digest,
624            } => Self::Transferred {
625                sender,
626                recipient,
627                object_type,
628                object_id,
629                version,
630                digest,
631            },
632            ObjectChange::Mutated {
633                sender,
634                owner,
635                object_type,
636                object_id,
637                version,
638                previous_version,
639                digest,
640            } => Self::Mutated {
641                sender,
642                owner,
643                object_type,
644                object_id,
645                version,
646                previous_version,
647                digest,
648            },
649            ObjectChange::Deleted {
650                sender,
651                object_type,
652                object_id,
653                version,
654            } => Self::Deleted {
655                sender,
656                object_type,
657                object_id,
658                version,
659            },
660            ObjectChange::Wrapped {
661                sender,
662                object_type,
663                object_id,
664                version,
665            } => Self::Wrapped {
666                sender,
667                object_type,
668                object_id,
669                version,
670            },
671            ObjectChange::Created {
672                sender,
673                owner,
674                object_type,
675                object_id,
676                version,
677                digest,
678            } => Self::Created {
679                sender,
680                owner,
681                object_type,
682                object_id,
683                version,
684                digest,
685            },
686        }
687    }
688}
689
690impl From<IndexedObjectChange> for ObjectChange {
691    fn from(val: IndexedObjectChange) -> Self {
692        match val {
693            IndexedObjectChange::Published {
694                package_id,
695                version,
696                digest,
697                modules,
698            } => ObjectChange::Published {
699                package_id,
700                version,
701                digest,
702                modules,
703            },
704            IndexedObjectChange::Transferred {
705                sender,
706                recipient,
707                object_type,
708                object_id,
709                version,
710                digest,
711            } => ObjectChange::Transferred {
712                sender,
713                recipient,
714                object_type,
715                object_id,
716                version,
717                digest,
718            },
719            IndexedObjectChange::Mutated {
720                sender,
721                owner,
722                object_type,
723                object_id,
724                version,
725                previous_version,
726                digest,
727            } => ObjectChange::Mutated {
728                sender,
729                owner,
730                object_type,
731                object_id,
732                version,
733                previous_version,
734                digest,
735            },
736            IndexedObjectChange::Deleted {
737                sender,
738                object_type,
739                object_id,
740                version,
741            } => ObjectChange::Deleted {
742                sender,
743                object_type,
744                object_id,
745                version,
746            },
747            IndexedObjectChange::Wrapped {
748                sender,
749                object_type,
750                object_id,
751                version,
752            } => ObjectChange::Wrapped {
753                sender,
754                object_type,
755                object_id,
756                version,
757            },
758            IndexedObjectChange::Created {
759                sender,
760                owner,
761                object_type,
762                object_id,
763                version,
764                digest,
765            } => ObjectChange::Created {
766                sender,
767                owner,
768                object_type,
769                object_id,
770                version,
771                digest,
772            },
773        }
774    }
775}
776
777// IotaTransactionBlockResponseWithOptions is only used on the reading path
778pub struct IotaTransactionBlockResponseWithOptions {
779    pub response: IotaTransactionBlockResponse,
780    pub options: IotaTransactionBlockResponseOptions,
781}
782
783impl From<IotaTransactionBlockResponseWithOptions> for IotaTransactionBlockResponse {
784    fn from(value: IotaTransactionBlockResponseWithOptions) -> Self {
785        let IotaTransactionBlockResponseWithOptions { response, options } = value;
786
787        IotaTransactionBlockResponse {
788            digest: response.digest,
789            transaction: options.show_input.then_some(response.transaction).flatten(),
790            raw_transaction: options
791                .show_raw_input
792                .then_some(response.raw_transaction)
793                .unwrap_or_default(),
794            effects: options.show_effects.then_some(response.effects).flatten(),
795            events: options.show_events.then_some(response.events).flatten(),
796            object_changes: options
797                .show_object_changes
798                .then_some(response.object_changes)
799                .flatten(),
800            balance_changes: options
801                .show_balance_changes
802                .then_some(response.balance_changes)
803                .flatten(),
804            timestamp_ms: response.timestamp_ms,
805            confirmed_local_execution: response.confirmed_local_execution,
806            checkpoint: response.checkpoint,
807            errors: vec![],
808            raw_effects: options
809                .show_raw_effects
810                .then_some(response.raw_effects)
811                .unwrap_or_default(),
812        }
813    }
814}