iota_json_rpc_types/
iota_transaction.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    fmt::{self, Display, Formatter, Write},
7    sync::Arc,
8};
9
10use enum_dispatch::enum_dispatch;
11use fastcrypto::encoding::Base64;
12use iota_json::{IotaJsonValue, primitive_type};
13use iota_metrics::monitored_scope;
14use iota_package_resolver::{PackageStore, Resolver};
15use iota_types::{
16    IOTA_FRAMEWORK_ADDRESS,
17    authenticator_state::ActiveJwk,
18    base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest},
19    crypto::IotaSignature,
20    digests::{ConsensusCommitDigest, ObjectDigest, TransactionEventsDigest},
21    effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents},
22    error::{ExecutionError, IotaError, IotaResult},
23    event::EventID,
24    execution_status::ExecutionStatus,
25    gas::GasCostSummary,
26    iota_serde::{
27        BigInt, IotaTypeTag as AsIotaTypeTag, Readable, SequenceNumber as AsSequenceNumber,
28    },
29    layout_resolver::{LayoutResolver, get_layout_from_struct_tag},
30    messages_checkpoint::CheckpointSequenceNumber,
31    messages_consensus::ConsensusDeterminedVersionAssignments,
32    object::Owner,
33    parse_iota_type_tag,
34    quorum_driver_types::ExecuteTransactionRequestType,
35    signature::GenericSignature,
36    storage::{DeleteKind, WriteKind},
37    transaction::{
38        Argument, CallArg, ChangeEpoch, ChangeEpochV2, ChangeEpochV3, Command,
39        EndOfEpochTransactionKind, GenesisObject, InputObjectKind, ObjectArg, ProgrammableMoveCall,
40        ProgrammableTransaction, SenderSignedData, TransactionData, TransactionDataAPI,
41        TransactionKind,
42    },
43};
44use move_binary_format::CompiledModule;
45use move_bytecode_utils::module_cache::GetModule;
46use move_core_types::{
47    annotated_value::MoveTypeLayout,
48    identifier::{IdentStr, Identifier},
49    language_storage::{ModuleId, StructTag, TypeTag},
50};
51use schemars::JsonSchema;
52use serde::{Deserialize, Serialize};
53use serde_with::serde_as;
54use strum::{Display, EnumString};
55use tabled::{
56    builder::Builder as TableBuilder,
57    settings::{Panel as TablePanel, Style as TableStyle, style::HorizontalLine},
58};
59
60use crate::{
61    Filter, IotaEvent, IotaObjectRef, Page, balance_changes::BalanceChange,
62    iota_transaction::GenericSignature::Signature, object_changes::ObjectChange,
63};
64
65// similar to EpochId of iota-types but BigInt
66pub type IotaEpochId = BigInt<u64>;
67
68#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
69#[serde(
70    rename_all = "camelCase",
71    rename = "TransactionBlockResponseQuery",
72    default
73)]
74pub struct IotaTransactionBlockResponseQuery {
75    /// If None, no filter will be applied
76    pub filter: Option<TransactionFilter>,
77    /// config which fields to include in the response, by default only digest
78    /// is included
79    pub options: Option<IotaTransactionBlockResponseOptions>,
80}
81
82impl IotaTransactionBlockResponseQuery {
83    pub fn new(
84        filter: Option<TransactionFilter>,
85        options: Option<IotaTransactionBlockResponseOptions>,
86    ) -> Self {
87        Self { filter, options }
88    }
89
90    pub fn new_with_filter(filter: TransactionFilter) -> Self {
91        Self {
92            filter: Some(filter),
93            options: None,
94        }
95    }
96}
97
98#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
99#[serde(
100    rename_all = "camelCase",
101    rename = "TransactionBlockResponseQuery",
102    default
103)]
104pub struct IotaTransactionBlockResponseQueryV2 {
105    /// If None, no filter will be applied
106    pub filter: Option<TransactionFilterV2>,
107    /// config which fields to include in the response, by default only digest
108    /// is included
109    pub options: Option<IotaTransactionBlockResponseOptions>,
110}
111
112impl IotaTransactionBlockResponseQueryV2 {
113    pub fn new(
114        filter: Option<TransactionFilterV2>,
115        options: Option<IotaTransactionBlockResponseOptions>,
116    ) -> Self {
117        Self { filter, options }
118    }
119
120    pub fn new_with_filter(filter: TransactionFilterV2) -> Self {
121        Self {
122            filter: Some(filter),
123            options: None,
124        }
125    }
126}
127
128pub type TransactionBlocksPage = Page<IotaTransactionBlockResponse, TransactionDigest>;
129
130#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Default)]
131#[serde(
132    rename_all = "camelCase",
133    rename = "TransactionBlockResponseOptions",
134    default
135)]
136pub struct IotaTransactionBlockResponseOptions {
137    /// Whether to show transaction input data. Default to be False
138    pub show_input: bool,
139    /// Whether to show bcs-encoded transaction input data
140    pub show_raw_input: bool,
141    /// Whether to show transaction effects. Default to be False
142    pub show_effects: bool,
143    /// Whether to show transaction events. Default to be False
144    pub show_events: bool,
145    /// Whether to show object_changes. Default to be False
146    pub show_object_changes: bool,
147    /// Whether to show balance_changes. Default to be False
148    pub show_balance_changes: bool,
149    /// Whether to show raw transaction effects. Default to be False
150    pub show_raw_effects: bool,
151}
152
153impl IotaTransactionBlockResponseOptions {
154    pub fn new() -> Self {
155        Self::default()
156    }
157
158    pub fn full_content() -> Self {
159        Self {
160            show_effects: true,
161            show_input: true,
162            show_raw_input: true,
163            show_events: true,
164            show_object_changes: true,
165            show_balance_changes: true,
166            // This field is added for graphql execution. We keep it false here
167            // so current users of `full_content` will not get raw effects unexpectedly.
168            show_raw_effects: false,
169        }
170    }
171
172    pub fn with_input(mut self) -> Self {
173        self.show_input = true;
174        self
175    }
176
177    pub fn with_raw_input(mut self) -> Self {
178        self.show_raw_input = true;
179        self
180    }
181
182    pub fn with_effects(mut self) -> Self {
183        self.show_effects = true;
184        self
185    }
186
187    pub fn with_events(mut self) -> Self {
188        self.show_events = true;
189        self
190    }
191
192    pub fn with_balance_changes(mut self) -> Self {
193        self.show_balance_changes = true;
194        self
195    }
196
197    pub fn with_object_changes(mut self) -> Self {
198        self.show_object_changes = true;
199        self
200    }
201
202    pub fn with_raw_effects(mut self) -> Self {
203        self.show_raw_effects = true;
204        self
205    }
206
207    /// default to return `WaitForEffectsCert` unless some options require
208    /// local execution
209    pub fn default_execution_request_type(&self) -> ExecuteTransactionRequestType {
210        // if people want effects or events, they typically want to wait for local
211        // execution
212        if self.require_effects() {
213            ExecuteTransactionRequestType::WaitForLocalExecution
214        } else {
215            ExecuteTransactionRequestType::WaitForEffectsCert
216        }
217    }
218
219    pub fn require_input(&self) -> bool {
220        self.show_input || self.show_raw_input || self.show_object_changes
221    }
222
223    pub fn require_effects(&self) -> bool {
224        self.show_effects
225            || self.show_events
226            || self.show_balance_changes
227            || self.show_object_changes
228            || self.show_raw_effects
229    }
230
231    pub fn only_digest(&self) -> bool {
232        self == &Self::default()
233    }
234}
235
236#[serde_as]
237#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
238#[serde(rename_all = "camelCase", rename = "TransactionBlockResponse")]
239pub struct IotaTransactionBlockResponse {
240    pub digest: TransactionDigest,
241    /// Transaction input data
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub transaction: Option<IotaTransactionBlock>,
244    /// BCS encoded [SenderSignedData] that includes input object references
245    /// returns empty array if `show_raw_transaction` is false
246    #[serde_as(as = "Base64")]
247    #[schemars(with = "Base64")]
248    #[serde(skip_serializing_if = "Vec::is_empty", default)]
249    pub raw_transaction: Vec<u8>,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub effects: Option<IotaTransactionBlockEffects>,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub events: Option<IotaTransactionBlockEvents>,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub object_changes: Option<Vec<ObjectChange>>,
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub balance_changes: Option<Vec<BalanceChange>>,
258    #[serde(default, skip_serializing_if = "Option::is_none")]
259    #[schemars(with = "Option<BigInt<u64>>")]
260    #[serde_as(as = "Option<BigInt<u64>>")]
261    pub timestamp_ms: Option<u64>,
262    #[serde(default, skip_serializing_if = "Option::is_none")]
263    pub confirmed_local_execution: Option<bool>,
264    /// The checkpoint number when this transaction was included and hence
265    /// finalized. This is only returned in the read api, not in the
266    /// transaction execution api.
267    #[schemars(with = "Option<BigInt<u64>>")]
268    #[serde_as(as = "Option<BigInt<u64>>")]
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub checkpoint: Option<CheckpointSequenceNumber>,
271    #[serde(skip_serializing_if = "Vec::is_empty", default)]
272    pub errors: Vec<String>,
273    #[serde(skip_serializing_if = "Vec::is_empty", default)]
274    pub raw_effects: Vec<u8>,
275}
276
277impl IotaTransactionBlockResponse {
278    pub fn new(digest: TransactionDigest) -> Self {
279        Self {
280            digest,
281            ..Default::default()
282        }
283    }
284
285    pub fn status_ok(&self) -> Option<bool> {
286        self.effects.as_ref().map(|e| e.status().is_ok())
287    }
288
289    /// Get mutated objects if any
290    pub fn mutated_objects(&self) -> impl Iterator<Item = ObjectRef> + '_ {
291        self.object_changes.iter().flat_map(|obj_changes| {
292            obj_changes
293                .iter()
294                .filter(|change| matches!(change, ObjectChange::Mutated { .. }))
295                .map(|change| change.object_ref())
296        })
297    }
298}
299
300/// We are specifically ignoring events for now until events become more stable.
301impl PartialEq for IotaTransactionBlockResponse {
302    fn eq(&self, other: &Self) -> bool {
303        self.transaction == other.transaction
304            && self.effects == other.effects
305            && self.timestamp_ms == other.timestamp_ms
306            && self.confirmed_local_execution == other.confirmed_local_execution
307            && self.checkpoint == other.checkpoint
308    }
309}
310
311impl Display for IotaTransactionBlockResponse {
312    fn fmt(&self, writer: &mut Formatter<'_>) -> fmt::Result {
313        writeln!(writer, "Transaction Digest: {}", &self.digest)?;
314
315        if let Some(t) = &self.transaction {
316            writeln!(writer, "{t}")?;
317        }
318
319        if let Some(e) = &self.effects {
320            writeln!(writer, "{e}")?;
321        }
322
323        if let Some(e) = &self.events {
324            writeln!(writer, "{e}")?;
325        }
326
327        if let Some(object_changes) = &self.object_changes {
328            let mut builder = TableBuilder::default();
329            let (
330                mut created,
331                mut deleted,
332                mut mutated,
333                mut published,
334                mut transferred,
335                mut wrapped,
336            ) = (vec![], vec![], vec![], vec![], vec![], vec![]);
337
338            for obj in object_changes {
339                match obj {
340                    ObjectChange::Created { .. } => created.push(obj),
341                    ObjectChange::Deleted { .. } => deleted.push(obj),
342                    ObjectChange::Mutated { .. } => mutated.push(obj),
343                    ObjectChange::Published { .. } => published.push(obj),
344                    ObjectChange::Transferred { .. } => transferred.push(obj),
345                    ObjectChange::Wrapped { .. } => wrapped.push(obj),
346                };
347            }
348
349            write_obj_changes(created, "Created", &mut builder)?;
350            write_obj_changes(deleted, "Deleted", &mut builder)?;
351            write_obj_changes(mutated, "Mutated", &mut builder)?;
352            write_obj_changes(published, "Published", &mut builder)?;
353            write_obj_changes(transferred, "Transferred", &mut builder)?;
354            write_obj_changes(wrapped, "Wrapped", &mut builder)?;
355
356            let mut table = builder.build();
357            table.with(TablePanel::header("Object Changes"));
358            table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
359                1,
360                TableStyle::modern().get_horizontal(),
361            )]));
362            writeln!(writer, "{table}")?;
363        }
364
365        if let Some(balance_changes) = &self.balance_changes {
366            // Only build a table if the vector of balance changes is non-empty.
367            // Empty balance changes occur, for example, for system transactions
368            // like `ConsensusCommitPrologueV1`
369            if !balance_changes.is_empty() {
370                let mut builder = TableBuilder::default();
371                for balance in balance_changes {
372                    builder.push_record(vec![format!("{balance}")]);
373                }
374                let mut table = builder.build();
375                table.with(TablePanel::header("Balance Changes"));
376                table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
377                    1,
378                    TableStyle::modern().get_horizontal(),
379                )]));
380                writeln!(writer, "{table}")?;
381            } else {
382                writeln!(writer, "╭────────────────────╮")?;
383                writeln!(writer, "│ No balance changes │")?;
384                writeln!(writer, "╰────────────────────╯")?;
385            }
386        }
387        Ok(())
388    }
389}
390
391fn write_obj_changes<T: Display>(
392    values: Vec<T>,
393    output_string: &str,
394    builder: &mut TableBuilder,
395) -> std::fmt::Result {
396    if !values.is_empty() {
397        builder.push_record(vec![format!("{output_string} Objects: ")]);
398        for obj in values {
399            builder.push_record(vec![format!("{obj}")]);
400        }
401    }
402    Ok(())
403}
404
405pub fn get_new_package_obj_from_response(
406    response: &IotaTransactionBlockResponse,
407) -> Option<ObjectRef> {
408    response.object_changes.as_ref().and_then(|changes| {
409        changes
410            .iter()
411            .find(|change| matches!(change, ObjectChange::Published { .. }))
412            .map(|change| change.object_ref())
413    })
414}
415
416pub fn get_new_package_upgrade_cap_from_response(
417    response: &IotaTransactionBlockResponse,
418) -> Option<ObjectRef> {
419    response.object_changes.as_ref().and_then(|changes| {
420        changes
421            .iter()
422            .find(|change| {
423                matches!(change, ObjectChange::Created {
424                    owner: Owner::AddressOwner(_),
425                    object_type: StructTag {
426                        address: IOTA_FRAMEWORK_ADDRESS,
427                        module,
428                        name,
429                        ..
430                    },
431                    ..
432                } if module.as_str() == "package" && name.as_str() == "UpgradeCap")
433            })
434            .map(|change| change.object_ref())
435    })
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
439#[serde(rename = "TransactionBlockKind", tag = "kind")]
440pub enum IotaTransactionBlockKind {
441    /// A system transaction used for initializing the initial state of the
442    /// chain.
443    Genesis(IotaGenesisTransaction),
444    /// A system transaction marking the start of a series of transactions
445    /// scheduled as part of a checkpoint
446    ConsensusCommitPrologueV1(IotaConsensusCommitPrologueV1),
447    /// A series of transactions where the results of one transaction can be
448    /// used in future transactions
449    ProgrammableTransaction(IotaProgrammableTransactionBlock),
450    /// A transaction which updates global authenticator state
451    AuthenticatorStateUpdateV1(IotaAuthenticatorStateUpdateV1),
452    /// A transaction which updates global randomness state
453    RandomnessStateUpdate(IotaRandomnessStateUpdate),
454    /// The transaction which occurs only at the end of the epoch
455    EndOfEpochTransaction(IotaEndOfEpochTransaction),
456    // .. more transaction types go here
457}
458
459impl Display for IotaTransactionBlockKind {
460    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
461        let mut writer = String::new();
462        match &self {
463            Self::Genesis(_) => {
464                writeln!(writer, "Transaction Kind: Genesis Transaction")?;
465            }
466            Self::ConsensusCommitPrologueV1(p) => {
467                writeln!(writer, "Transaction Kind: Consensus Commit Prologue V1")?;
468                writeln!(
469                    writer,
470                    "Epoch: {}, Round: {}, SubDagIndex: {:?}, Timestamp: {}, ConsensusCommitDigest: {}",
471                    p.epoch,
472                    p.round,
473                    p.sub_dag_index,
474                    p.commit_timestamp_ms,
475                    p.consensus_commit_digest
476                )?;
477            }
478            Self::ProgrammableTransaction(p) => {
479                write!(writer, "Transaction Kind: Programmable")?;
480                write!(writer, "{}", crate::displays::Pretty(p))?;
481            }
482            Self::AuthenticatorStateUpdateV1(_) => {
483                writeln!(writer, "Transaction Kind: Authenticator State Update")?;
484            }
485            Self::RandomnessStateUpdate(_) => {
486                writeln!(writer, "Transaction Kind: Randomness State Update")?;
487            }
488            Self::EndOfEpochTransaction(_) => {
489                writeln!(writer, "Transaction Kind: End of Epoch Transaction")?;
490            }
491        }
492        write!(f, "{writer}")
493    }
494}
495
496impl IotaTransactionBlockKind {
497    fn try_from(
498        tx: TransactionKind,
499        module_cache: &impl GetModule,
500        tx_digest: TransactionDigest,
501    ) -> Result<Self, anyhow::Error> {
502        Ok(match tx {
503            TransactionKind::Genesis(g) => Self::Genesis(IotaGenesisTransaction {
504                objects: g.objects.iter().map(GenesisObject::id).collect(),
505                events: g
506                    .events
507                    .into_iter()
508                    .enumerate()
509                    .map(|(seq, _event)| EventID::from((tx_digest, seq as u64)))
510                    .collect(),
511            }),
512            TransactionKind::ConsensusCommitPrologueV1(p) => {
513                Self::ConsensusCommitPrologueV1(IotaConsensusCommitPrologueV1 {
514                    epoch: p.epoch,
515                    round: p.round,
516                    sub_dag_index: p.sub_dag_index,
517                    commit_timestamp_ms: p.commit_timestamp_ms,
518                    consensus_commit_digest: p.consensus_commit_digest,
519                    consensus_determined_version_assignments: p
520                        .consensus_determined_version_assignments,
521                })
522            }
523            TransactionKind::ProgrammableTransaction(p) => Self::ProgrammableTransaction(
524                IotaProgrammableTransactionBlock::try_from(p, module_cache)?,
525            ),
526            TransactionKind::AuthenticatorStateUpdateV1(update) => {
527                Self::AuthenticatorStateUpdateV1(IotaAuthenticatorStateUpdateV1 {
528                    epoch: update.epoch,
529                    round: update.round,
530                    new_active_jwks: update
531                        .new_active_jwks
532                        .into_iter()
533                        .map(IotaActiveJwk::from)
534                        .collect(),
535                })
536            }
537            TransactionKind::RandomnessStateUpdate(update) => {
538                Self::RandomnessStateUpdate(IotaRandomnessStateUpdate {
539                    epoch: update.epoch,
540                    randomness_round: update.randomness_round.0,
541                    random_bytes: update.random_bytes,
542                })
543            }
544            TransactionKind::EndOfEpochTransaction(end_of_epoch_tx) => {
545                Self::EndOfEpochTransaction(IotaEndOfEpochTransaction {
546                    transactions: end_of_epoch_tx
547                        .into_iter()
548                        .map(|tx| match tx {
549                            EndOfEpochTransactionKind::ChangeEpoch(e) => {
550                                IotaEndOfEpochTransactionKind::ChangeEpoch(e.into())
551                            }
552                            EndOfEpochTransactionKind::ChangeEpochV2(e) => {
553                                IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
554                            }
555                            EndOfEpochTransactionKind::ChangeEpochV3(e) => {
556                                IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
557                            }
558                            EndOfEpochTransactionKind::AuthenticatorStateCreate => {
559                                IotaEndOfEpochTransactionKind::AuthenticatorStateCreate
560                            }
561                            EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
562                                IotaEndOfEpochTransactionKind::AuthenticatorStateExpire(
563                                    IotaAuthenticatorStateExpire {
564                                        min_epoch: expire.min_epoch,
565                                    },
566                                )
567                            }
568                        })
569                        .collect(),
570                })
571            }
572        })
573    }
574
575    async fn try_from_with_package_resolver(
576        tx: TransactionKind,
577        package_resolver: Arc<Resolver<impl PackageStore>>,
578        tx_digest: TransactionDigest,
579    ) -> Result<Self, anyhow::Error> {
580        Ok(match tx {
581            TransactionKind::Genesis(g) => Self::Genesis(IotaGenesisTransaction {
582                objects: g.objects.iter().map(GenesisObject::id).collect(),
583                events: g
584                    .events
585                    .into_iter()
586                    .enumerate()
587                    .map(|(seq, _event)| EventID::from((tx_digest, seq as u64)))
588                    .collect(),
589            }),
590            TransactionKind::ConsensusCommitPrologueV1(p) => {
591                Self::ConsensusCommitPrologueV1(IotaConsensusCommitPrologueV1 {
592                    epoch: p.epoch,
593                    round: p.round,
594                    sub_dag_index: p.sub_dag_index,
595                    commit_timestamp_ms: p.commit_timestamp_ms,
596                    consensus_commit_digest: p.consensus_commit_digest,
597                    consensus_determined_version_assignments: p
598                        .consensus_determined_version_assignments,
599                })
600            }
601            TransactionKind::ProgrammableTransaction(p) => Self::ProgrammableTransaction(
602                IotaProgrammableTransactionBlock::try_from_with_package_resolver(
603                    p,
604                    package_resolver,
605                )
606                .await?,
607            ),
608            TransactionKind::AuthenticatorStateUpdateV1(update) => {
609                Self::AuthenticatorStateUpdateV1(IotaAuthenticatorStateUpdateV1 {
610                    epoch: update.epoch,
611                    round: update.round,
612                    new_active_jwks: update
613                        .new_active_jwks
614                        .into_iter()
615                        .map(IotaActiveJwk::from)
616                        .collect(),
617                })
618            }
619            TransactionKind::RandomnessStateUpdate(update) => {
620                Self::RandomnessStateUpdate(IotaRandomnessStateUpdate {
621                    epoch: update.epoch,
622                    randomness_round: update.randomness_round.0,
623                    random_bytes: update.random_bytes,
624                })
625            }
626            TransactionKind::EndOfEpochTransaction(end_of_epoch_tx) => {
627                Self::EndOfEpochTransaction(IotaEndOfEpochTransaction {
628                    transactions: end_of_epoch_tx
629                        .into_iter()
630                        .map(|tx| match tx {
631                            EndOfEpochTransactionKind::ChangeEpoch(e) => {
632                                IotaEndOfEpochTransactionKind::ChangeEpoch(e.into())
633                            }
634                            EndOfEpochTransactionKind::ChangeEpochV2(e) => {
635                                IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
636                            }
637                            EndOfEpochTransactionKind::ChangeEpochV3(e) => {
638                                IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
639                            }
640                            EndOfEpochTransactionKind::AuthenticatorStateCreate => {
641                                IotaEndOfEpochTransactionKind::AuthenticatorStateCreate
642                            }
643                            EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
644                                IotaEndOfEpochTransactionKind::AuthenticatorStateExpire(
645                                    IotaAuthenticatorStateExpire {
646                                        min_epoch: expire.min_epoch,
647                                    },
648                                )
649                            }
650                        })
651                        .collect(),
652                })
653            }
654        })
655    }
656
657    pub fn transaction_count(&self) -> usize {
658        match self {
659            Self::ProgrammableTransaction(p) => p.commands.len(),
660            _ => 1,
661        }
662    }
663
664    pub fn name(&self) -> &'static str {
665        match self {
666            Self::Genesis(_) => "Genesis",
667            Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
668            Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
669            Self::AuthenticatorStateUpdateV1(_) => "AuthenticatorStateUpdateV1",
670            Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
671            Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
672        }
673    }
674}
675
676#[serde_as]
677#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
678pub struct IotaChangeEpoch {
679    #[schemars(with = "BigInt<u64>")]
680    #[serde_as(as = "BigInt<u64>")]
681    pub epoch: EpochId,
682    #[schemars(with = "BigInt<u64>")]
683    #[serde_as(as = "BigInt<u64>")]
684    pub storage_charge: u64,
685    #[schemars(with = "BigInt<u64>")]
686    #[serde_as(as = "BigInt<u64>")]
687    pub computation_charge: u64,
688    #[schemars(with = "BigInt<u64>")]
689    #[serde_as(as = "BigInt<u64>")]
690    pub storage_rebate: u64,
691    #[schemars(with = "BigInt<u64>")]
692    #[serde_as(as = "BigInt<u64>")]
693    pub epoch_start_timestamp_ms: u64,
694}
695
696impl From<ChangeEpoch> for IotaChangeEpoch {
697    fn from(e: ChangeEpoch) -> Self {
698        Self {
699            epoch: e.epoch,
700            storage_charge: e.storage_charge,
701            computation_charge: e.computation_charge,
702            storage_rebate: e.storage_rebate,
703            epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
704        }
705    }
706}
707
708#[serde_as]
709#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
710pub struct IotaChangeEpochV2 {
711    #[schemars(with = "BigInt<u64>")]
712    #[serde_as(as = "BigInt<u64>")]
713    pub epoch: EpochId,
714    #[schemars(with = "BigInt<u64>")]
715    #[serde_as(as = "BigInt<u64>")]
716    pub storage_charge: u64,
717    #[schemars(with = "BigInt<u64>")]
718    #[serde_as(as = "BigInt<u64>")]
719    pub computation_charge: u64,
720    #[schemars(with = "BigInt<u64>")]
721    #[serde_as(as = "BigInt<u64>")]
722    pub computation_charge_burned: u64,
723    #[schemars(with = "BigInt<u64>")]
724    #[serde_as(as = "BigInt<u64>")]
725    pub storage_rebate: u64,
726    #[schemars(with = "BigInt<u64>")]
727    #[serde_as(as = "BigInt<u64>")]
728    pub epoch_start_timestamp_ms: u64,
729    #[schemars(with = "Option<Vec<BigInt<u64>>>")]
730    #[serde_as(as = "Option<Vec<BigInt<u64>>>")]
731    #[serde(skip_serializing_if = "Option::is_none", default)]
732    pub eligible_active_validators: Option<Vec<u64>>,
733}
734
735impl From<ChangeEpochV2> for IotaChangeEpochV2 {
736    fn from(e: ChangeEpochV2) -> Self {
737        Self {
738            epoch: e.epoch,
739            storage_charge: e.storage_charge,
740            computation_charge: e.computation_charge,
741            computation_charge_burned: e.computation_charge_burned,
742            storage_rebate: e.storage_rebate,
743            epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
744            eligible_active_validators: None,
745        }
746    }
747}
748
749impl From<ChangeEpochV3> for IotaChangeEpochV2 {
750    fn from(e: ChangeEpochV3) -> Self {
751        Self {
752            epoch: e.epoch,
753            storage_charge: e.storage_charge,
754            computation_charge: e.computation_charge,
755            computation_charge_burned: e.computation_charge_burned,
756            storage_rebate: e.storage_rebate,
757            epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
758            eligible_active_validators: Some(e.eligible_active_validators),
759        }
760    }
761}
762
763#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
764#[enum_dispatch(IotaTransactionBlockEffectsAPI)]
765#[serde(
766    rename = "TransactionBlockEffects",
767    rename_all = "camelCase",
768    tag = "messageVersion"
769)]
770pub enum IotaTransactionBlockEffects {
771    V1(IotaTransactionBlockEffectsV1),
772}
773
774#[enum_dispatch]
775pub trait IotaTransactionBlockEffectsAPI {
776    fn status(&self) -> &IotaExecutionStatus;
777    fn into_status(self) -> IotaExecutionStatus;
778    fn shared_objects(&self) -> &[IotaObjectRef];
779    fn created(&self) -> &[OwnedObjectRef];
780    fn mutated(&self) -> &[OwnedObjectRef];
781    fn unwrapped(&self) -> &[OwnedObjectRef];
782    fn deleted(&self) -> &[IotaObjectRef];
783    fn unwrapped_then_deleted(&self) -> &[IotaObjectRef];
784    fn wrapped(&self) -> &[IotaObjectRef];
785    fn gas_object(&self) -> &OwnedObjectRef;
786    fn events_digest(&self) -> Option<&TransactionEventsDigest>;
787    fn dependencies(&self) -> &[TransactionDigest];
788    fn executed_epoch(&self) -> EpochId;
789    fn transaction_digest(&self) -> &TransactionDigest;
790    fn gas_cost_summary(&self) -> &GasCostSummary;
791
792    /// Return an iterator of mutated objects, but excluding the gas object.
793    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef>;
794    fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
795    fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>;
796    fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)>;
797}
798
799#[serde_as]
800#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
801#[serde(
802    rename = "TransactionBlockEffectsModifiedAtVersions",
803    rename_all = "camelCase"
804)]
805pub struct IotaTransactionBlockEffectsModifiedAtVersions {
806    object_id: ObjectID,
807    #[schemars(with = "AsSequenceNumber")]
808    #[serde_as(as = "AsSequenceNumber")]
809    sequence_number: SequenceNumber,
810}
811
812/// The response from processing a transaction or a certified transaction
813#[serde_as]
814#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
815#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")]
816pub struct IotaTransactionBlockEffectsV1 {
817    /// The status of the execution
818    pub status: IotaExecutionStatus,
819    /// The epoch when this transaction was executed.
820    #[schemars(with = "BigInt<u64>")]
821    #[serde_as(as = "BigInt<u64>")]
822    pub executed_epoch: EpochId,
823    pub gas_used: GasCostSummary,
824    /// The version that every modified (mutated or deleted) object had before
825    /// it was modified by this transaction.
826    #[serde(default, skip_serializing_if = "Vec::is_empty")]
827    pub modified_at_versions: Vec<IotaTransactionBlockEffectsModifiedAtVersions>,
828    /// The object references of the shared objects used in this transaction.
829    /// Empty if no shared objects were used.
830    #[serde(default, skip_serializing_if = "Vec::is_empty")]
831    pub shared_objects: Vec<IotaObjectRef>,
832    /// The transaction digest
833    pub transaction_digest: TransactionDigest,
834    /// ObjectRef and owner of new objects created.
835    #[serde(default, skip_serializing_if = "Vec::is_empty")]
836    pub created: Vec<OwnedObjectRef>,
837    /// ObjectRef and owner of mutated objects, including gas object.
838    #[serde(default, skip_serializing_if = "Vec::is_empty")]
839    pub mutated: Vec<OwnedObjectRef>,
840    /// ObjectRef and owner of objects that are unwrapped in this transaction.
841    /// Unwrapped objects are objects that were wrapped into other objects in
842    /// the past, and just got extracted out.
843    #[serde(default, skip_serializing_if = "Vec::is_empty")]
844    pub unwrapped: Vec<OwnedObjectRef>,
845    /// Object Refs of objects now deleted (the old refs).
846    #[serde(default, skip_serializing_if = "Vec::is_empty")]
847    pub deleted: Vec<IotaObjectRef>,
848    /// Object refs of objects previously wrapped in other objects but now
849    /// deleted.
850    #[serde(default, skip_serializing_if = "Vec::is_empty")]
851    pub unwrapped_then_deleted: Vec<IotaObjectRef>,
852    /// Object refs of objects now wrapped in other objects.
853    #[serde(default, skip_serializing_if = "Vec::is_empty")]
854    pub wrapped: Vec<IotaObjectRef>,
855    /// The updated gas object reference. Have a dedicated field for convenient
856    /// access. It's also included in mutated.
857    pub gas_object: OwnedObjectRef,
858    /// The digest of the events emitted during execution,
859    /// can be None if the transaction does not emit any event.
860    #[serde(skip_serializing_if = "Option::is_none")]
861    pub events_digest: Option<TransactionEventsDigest>,
862    /// The set of transaction digests this transaction depends on.
863    #[serde(default, skip_serializing_if = "Vec::is_empty")]
864    pub dependencies: Vec<TransactionDigest>,
865}
866
867impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 {
868    fn status(&self) -> &IotaExecutionStatus {
869        &self.status
870    }
871    fn into_status(self) -> IotaExecutionStatus {
872        self.status
873    }
874    fn shared_objects(&self) -> &[IotaObjectRef] {
875        &self.shared_objects
876    }
877    fn created(&self) -> &[OwnedObjectRef] {
878        &self.created
879    }
880    fn mutated(&self) -> &[OwnedObjectRef] {
881        &self.mutated
882    }
883    fn unwrapped(&self) -> &[OwnedObjectRef] {
884        &self.unwrapped
885    }
886    fn deleted(&self) -> &[IotaObjectRef] {
887        &self.deleted
888    }
889    fn unwrapped_then_deleted(&self) -> &[IotaObjectRef] {
890        &self.unwrapped_then_deleted
891    }
892    fn wrapped(&self) -> &[IotaObjectRef] {
893        &self.wrapped
894    }
895    fn gas_object(&self) -> &OwnedObjectRef {
896        &self.gas_object
897    }
898    fn events_digest(&self) -> Option<&TransactionEventsDigest> {
899        self.events_digest.as_ref()
900    }
901    fn dependencies(&self) -> &[TransactionDigest] {
902        &self.dependencies
903    }
904
905    fn executed_epoch(&self) -> EpochId {
906        self.executed_epoch
907    }
908
909    fn transaction_digest(&self) -> &TransactionDigest {
910        &self.transaction_digest
911    }
912
913    fn gas_cost_summary(&self) -> &GasCostSummary {
914        &self.gas_used
915    }
916
917    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef> {
918        self.mutated
919            .iter()
920            .filter(|o| *o != &self.gas_object)
921            .cloned()
922            .collect()
923    }
924
925    fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
926        self.modified_at_versions
927            .iter()
928            .map(|v| (v.object_id, v.sequence_number))
929            .collect::<Vec<_>>()
930    }
931
932    fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> {
933        self.mutated
934            .iter()
935            .map(|owner_ref| (owner_ref, WriteKind::Mutate))
936            .chain(
937                self.created
938                    .iter()
939                    .map(|owner_ref| (owner_ref, WriteKind::Create)),
940            )
941            .chain(
942                self.unwrapped
943                    .iter()
944                    .map(|owner_ref| (owner_ref, WriteKind::Unwrap)),
945            )
946            .collect()
947    }
948
949    fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)> {
950        self.deleted
951            .iter()
952            .map(|r| (r, DeleteKind::Normal))
953            .chain(
954                self.unwrapped_then_deleted
955                    .iter()
956                    .map(|r| (r, DeleteKind::UnwrapThenDelete)),
957            )
958            .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap)))
959            .collect()
960    }
961}
962
963impl IotaTransactionBlockEffects {
964    pub fn new_for_testing(
965        transaction_digest: TransactionDigest,
966        status: IotaExecutionStatus,
967    ) -> Self {
968        Self::V1(IotaTransactionBlockEffectsV1 {
969            transaction_digest,
970            status,
971            gas_object: OwnedObjectRef {
972                owner: Owner::AddressOwner(IotaAddress::random_for_testing_only()),
973                reference: iota_types::base_types::random_object_ref().into(),
974            },
975            executed_epoch: 0,
976            modified_at_versions: vec![],
977            gas_used: GasCostSummary::default(),
978            shared_objects: vec![],
979            created: vec![],
980            mutated: vec![],
981            unwrapped: vec![],
982            deleted: vec![],
983            unwrapped_then_deleted: vec![],
984            wrapped: vec![],
985            events_digest: None,
986            dependencies: vec![],
987        })
988    }
989}
990
991impl TryFrom<TransactionEffects> for IotaTransactionBlockEffects {
992    type Error = IotaError;
993
994    fn try_from(effect: TransactionEffects) -> Result<Self, Self::Error> {
995        Ok(IotaTransactionBlockEffects::V1(
996            IotaTransactionBlockEffectsV1 {
997                status: effect.status().clone().into(),
998                executed_epoch: effect.executed_epoch(),
999                modified_at_versions: effect
1000                    .modified_at_versions()
1001                    .into_iter()
1002                    .map(|(object_id, sequence_number)| {
1003                        IotaTransactionBlockEffectsModifiedAtVersions {
1004                            object_id,
1005                            sequence_number,
1006                        }
1007                    })
1008                    .collect(),
1009                gas_used: effect.gas_cost_summary().clone(),
1010                shared_objects: to_iota_object_ref(
1011                    effect
1012                        .input_shared_objects()
1013                        .into_iter()
1014                        .map(|kind| kind.object_ref())
1015                        .collect(),
1016                ),
1017                transaction_digest: *effect.transaction_digest(),
1018                created: to_owned_ref(effect.created()),
1019                mutated: to_owned_ref(effect.mutated().to_vec()),
1020                unwrapped: to_owned_ref(effect.unwrapped().to_vec()),
1021                deleted: to_iota_object_ref(effect.deleted().to_vec()),
1022                unwrapped_then_deleted: to_iota_object_ref(
1023                    effect.unwrapped_then_deleted().to_vec(),
1024                ),
1025                wrapped: to_iota_object_ref(effect.wrapped().to_vec()),
1026                gas_object: OwnedObjectRef {
1027                    owner: effect.gas_object().1,
1028                    reference: effect.gas_object().0.into(),
1029                },
1030                events_digest: effect.events_digest().copied(),
1031                dependencies: effect.dependencies().to_vec(),
1032            },
1033        ))
1034    }
1035}
1036
1037fn owned_objref_string(obj: &OwnedObjectRef) -> String {
1038    format!(
1039        " ┌──\n │ ID: {} \n │ Owner: {} \n │ Version: {} \n │ Digest: {}\n └──",
1040        obj.reference.object_id,
1041        obj.owner,
1042        u64::from(obj.reference.version),
1043        obj.reference.digest
1044    )
1045}
1046
1047fn objref_string(obj: &IotaObjectRef) -> String {
1048    format!(
1049        " ┌──\n │ ID: {} \n │ Version: {} \n │ Digest: {}\n └──",
1050        obj.object_id,
1051        u64::from(obj.version),
1052        obj.digest
1053    )
1054}
1055
1056impl Display for IotaTransactionBlockEffects {
1057    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1058        let mut builder = TableBuilder::default();
1059
1060        builder.push_record(vec![format!("Digest: {}", self.transaction_digest())]);
1061        builder.push_record(vec![format!("Status: {:?}", self.status())]);
1062        builder.push_record(vec![format!("Executed Epoch: {}", self.executed_epoch())]);
1063
1064        if !self.created().is_empty() {
1065            builder.push_record(vec![format!("\nCreated Objects: ")]);
1066
1067            for oref in self.created() {
1068                builder.push_record(vec![owned_objref_string(oref)]);
1069            }
1070        }
1071
1072        if !self.mutated().is_empty() {
1073            builder.push_record(vec![format!("Mutated Objects: ")]);
1074            for oref in self.mutated() {
1075                builder.push_record(vec![owned_objref_string(oref)]);
1076            }
1077        }
1078
1079        if !self.shared_objects().is_empty() {
1080            builder.push_record(vec![format!("Shared Objects: ")]);
1081            for oref in self.shared_objects() {
1082                builder.push_record(vec![objref_string(oref)]);
1083            }
1084        }
1085
1086        if !self.deleted().is_empty() {
1087            builder.push_record(vec![format!("Deleted Objects: ")]);
1088
1089            for oref in self.deleted() {
1090                builder.push_record(vec![objref_string(oref)]);
1091            }
1092        }
1093
1094        if !self.wrapped().is_empty() {
1095            builder.push_record(vec![format!("Wrapped Objects: ")]);
1096
1097            for oref in self.wrapped() {
1098                builder.push_record(vec![objref_string(oref)]);
1099            }
1100        }
1101
1102        if !self.unwrapped().is_empty() {
1103            builder.push_record(vec![format!("Unwrapped Objects: ")]);
1104            for oref in self.unwrapped() {
1105                builder.push_record(vec![owned_objref_string(oref)]);
1106            }
1107        }
1108
1109        builder.push_record(vec![format!(
1110            "Gas Object: \n{}",
1111            owned_objref_string(self.gas_object())
1112        )]);
1113
1114        let gas_cost_summary = self.gas_cost_summary();
1115        builder.push_record(vec![format!(
1116            "Gas Cost Summary:\n   \
1117             Storage Cost: {} NANOS\n   \
1118             Computation Cost: {} NANOS\n   \
1119             Computation Cost Burned: {} NANOS\n   \
1120             Storage Rebate: {} NANOS\n   \
1121             Non-refundable Storage Fee: {} NANOS",
1122            gas_cost_summary.storage_cost,
1123            gas_cost_summary.computation_cost,
1124            gas_cost_summary.computation_cost_burned,
1125            gas_cost_summary.storage_rebate,
1126            gas_cost_summary.non_refundable_storage_fee,
1127        )]);
1128
1129        let dependencies = self.dependencies();
1130        if !dependencies.is_empty() {
1131            builder.push_record(vec![format!("\nTransaction Dependencies:")]);
1132            for dependency in dependencies {
1133                builder.push_record(vec![format!("   {dependency}")]);
1134            }
1135        }
1136
1137        let mut table = builder.build();
1138        table.with(TablePanel::header("Transaction Effects"));
1139        table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1140            1,
1141            TableStyle::modern().get_horizontal(),
1142        )]));
1143        write!(f, "{table}")
1144    }
1145}
1146
1147#[serde_as]
1148#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1149#[serde(rename_all = "camelCase")]
1150pub struct DryRunTransactionBlockResponse {
1151    pub effects: IotaTransactionBlockEffects,
1152    pub events: IotaTransactionBlockEvents,
1153    pub object_changes: Vec<ObjectChange>,
1154    pub balance_changes: Vec<BalanceChange>,
1155    pub input: IotaTransactionBlockData,
1156    /// If an input object is congested, suggest a gas price to use.
1157    #[serde(default, skip_serializing_if = "Option::is_none")]
1158    #[schemars(with = "Option<BigInt<u64>>")]
1159    #[serde_as(as = "Option<BigInt<u64>>")]
1160    pub suggested_gas_price: Option<u64>,
1161}
1162
1163#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1164#[serde(rename = "TransactionBlockEvents", transparent)]
1165pub struct IotaTransactionBlockEvents {
1166    pub data: Vec<IotaEvent>,
1167}
1168
1169impl IotaTransactionBlockEvents {
1170    pub fn try_from(
1171        events: TransactionEvents,
1172        tx_digest: TransactionDigest,
1173        timestamp_ms: Option<u64>,
1174        resolver: &mut dyn LayoutResolver,
1175    ) -> IotaResult<Self> {
1176        Ok(Self {
1177            data: events
1178                .data
1179                .into_iter()
1180                .enumerate()
1181                .map(|(seq, event)| {
1182                    let layout = resolver.get_annotated_layout(&event.type_)?;
1183                    IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1184                })
1185                .collect::<Result<_, _>>()?,
1186        })
1187    }
1188
1189    // TODO: this is only called from the indexer. Remove this once indexer moves to
1190    // its own resolver.
1191    pub fn try_from_using_module_resolver(
1192        events: TransactionEvents,
1193        tx_digest: TransactionDigest,
1194        timestamp_ms: Option<u64>,
1195        resolver: &impl GetModule,
1196    ) -> IotaResult<Self> {
1197        Ok(Self {
1198            data: events
1199                .data
1200                .into_iter()
1201                .enumerate()
1202                .map(|(seq, event)| {
1203                    let layout = get_layout_from_struct_tag(event.type_.clone(), resolver)?;
1204                    IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1205                })
1206                .collect::<Result<_, _>>()?,
1207        })
1208    }
1209}
1210
1211impl Display for IotaTransactionBlockEvents {
1212    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1213        if self.data.is_empty() {
1214            writeln!(f, "╭─────────────────────────────╮")?;
1215            writeln!(f, "│ No transaction block events │")?;
1216            writeln!(f, "╰─────────────────────────────╯")
1217        } else {
1218            let mut builder = TableBuilder::default();
1219
1220            for event in &self.data {
1221                builder.push_record(vec![format!("{event}")]);
1222            }
1223
1224            let mut table = builder.build();
1225            table.with(TablePanel::header("Transaction Block Events"));
1226            table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1227                1,
1228                TableStyle::modern().get_horizontal(),
1229            )]));
1230            write!(f, "{table}")
1231        }
1232    }
1233}
1234
1235// TODO: this file might not be the best place for this struct.
1236/// Additional arguments supplied to dev inspect beyond what is allowed in
1237/// today's API.
1238#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
1239#[serde(rename = "DevInspectArgs", rename_all = "camelCase")]
1240pub struct DevInspectArgs {
1241    /// The sponsor of the gas for the transaction, might be different from the
1242    /// sender.
1243    pub gas_sponsor: Option<IotaAddress>,
1244    /// The gas budget for the transaction.
1245    pub gas_budget: Option<BigInt<u64>>,
1246    /// The gas objects used to pay for the transaction.
1247    pub gas_objects: Option<Vec<ObjectRef>>,
1248    /// Whether to skip transaction checks for the transaction.
1249    pub skip_checks: Option<bool>,
1250    /// Whether to return the raw transaction data and effects.
1251    pub show_raw_txn_data_and_effects: Option<bool>,
1252}
1253
1254/// The response from processing a dev inspect transaction
1255#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1256#[serde(rename = "DevInspectResults", rename_all = "camelCase")]
1257pub struct DevInspectResults {
1258    /// Summary of effects that likely would be generated if the transaction is
1259    /// actually run. Note however, that not all dev-inspect transactions
1260    /// are actually usable as transactions so it might not be possible
1261    /// actually generate these effects from a normal transaction.
1262    pub effects: IotaTransactionBlockEffects,
1263    /// Events that likely would be generated if the transaction is actually
1264    /// run.
1265    pub events: IotaTransactionBlockEvents,
1266    /// Execution results (including return values) from executing the
1267    /// transactions
1268    #[serde(skip_serializing_if = "Option::is_none")]
1269    pub results: Option<Vec<IotaExecutionResult>>,
1270    /// Execution error from executing the transactions
1271    #[serde(skip_serializing_if = "Option::is_none")]
1272    pub error: Option<String>,
1273    /// The raw transaction data that was dev inspected.
1274    #[serde(skip_serializing_if = "Vec::is_empty", default)]
1275    pub raw_txn_data: Vec<u8>,
1276    /// The raw effects of the transaction that was dev inspected.
1277    #[serde(skip_serializing_if = "Vec::is_empty", default)]
1278    pub raw_effects: Vec<u8>,
1279}
1280
1281#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1282#[serde(rename = "IotaExecutionResult", rename_all = "camelCase")]
1283pub struct IotaExecutionResult {
1284    /// The value of any arguments that were mutably borrowed.
1285    /// Non-mut borrowed values are not included
1286    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1287    pub mutable_reference_outputs: Vec<(/* argument */ IotaArgument, Vec<u8>, IotaTypeTag)>,
1288    /// The return values from the transaction
1289    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1290    pub return_values: Vec<(Vec<u8>, IotaTypeTag)>,
1291}
1292
1293type ExecutionResult = (
1294    // mutable_reference_outputs
1295    Vec<(Argument, Vec<u8>, TypeTag)>,
1296    // return_values
1297    Vec<(Vec<u8>, TypeTag)>,
1298);
1299
1300impl DevInspectResults {
1301    pub fn new(
1302        effects: TransactionEffects,
1303        events: TransactionEvents,
1304        return_values: Result<Vec<ExecutionResult>, ExecutionError>,
1305        raw_txn_data: Vec<u8>,
1306        raw_effects: Vec<u8>,
1307        resolver: &mut dyn LayoutResolver,
1308    ) -> IotaResult<Self> {
1309        let tx_digest = *effects.transaction_digest();
1310        let mut error = None;
1311        let mut results = None;
1312        match return_values {
1313            Err(e) => error = Some(e.to_string()),
1314            Ok(srvs) => {
1315                results = Some(
1316                    srvs.into_iter()
1317                        .map(|srv| {
1318                            let (mutable_reference_outputs, return_values) = srv;
1319                            let mutable_reference_outputs = mutable_reference_outputs
1320                                .into_iter()
1321                                .map(|(a, bytes, tag)| (a.into(), bytes, IotaTypeTag::from(tag)))
1322                                .collect();
1323                            let return_values = return_values
1324                                .into_iter()
1325                                .map(|(bytes, tag)| (bytes, IotaTypeTag::from(tag)))
1326                                .collect();
1327                            IotaExecutionResult {
1328                                mutable_reference_outputs,
1329                                return_values,
1330                            }
1331                        })
1332                        .collect(),
1333                )
1334            }
1335        };
1336        Ok(Self {
1337            effects: effects.try_into()?,
1338            events: IotaTransactionBlockEvents::try_from(events, tx_digest, None, resolver)?,
1339            results,
1340            error,
1341            raw_txn_data,
1342            raw_effects,
1343        })
1344    }
1345}
1346
1347#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1348pub enum IotaTransactionBlockBuilderMode {
1349    /// Regular IOTA Transactions that are committed on chain
1350    Commit,
1351    /// Simulated transaction that allows calling any Move function with
1352    /// arbitrary values.
1353    DevInspect,
1354}
1355
1356#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1357#[serde(rename = "ExecutionStatus", rename_all = "camelCase", tag = "status")]
1358pub enum IotaExecutionStatus {
1359    // Gas used in the success case.
1360    Success,
1361    // Gas used in the failed case, and the error.
1362    Failure { error: String },
1363}
1364
1365impl Display for IotaExecutionStatus {
1366    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1367        match self {
1368            Self::Success => write!(f, "success"),
1369            Self::Failure { error } => write!(f, "failure due to {error}"),
1370        }
1371    }
1372}
1373
1374impl IotaExecutionStatus {
1375    pub fn is_ok(&self) -> bool {
1376        matches!(self, IotaExecutionStatus::Success)
1377    }
1378    pub fn is_err(&self) -> bool {
1379        matches!(self, IotaExecutionStatus::Failure { .. })
1380    }
1381}
1382
1383impl From<ExecutionStatus> for IotaExecutionStatus {
1384    fn from(status: ExecutionStatus) -> Self {
1385        match status {
1386            ExecutionStatus::Success => Self::Success,
1387            ExecutionStatus::Failure {
1388                error,
1389                command: None,
1390            } => Self::Failure {
1391                error: format!("{error:?}"),
1392            },
1393            ExecutionStatus::Failure {
1394                error,
1395                command: Some(idx),
1396            } => Self::Failure {
1397                error: format!("{error:?} in command {idx}"),
1398            },
1399        }
1400    }
1401}
1402
1403fn to_iota_object_ref(refs: Vec<ObjectRef>) -> Vec<IotaObjectRef> {
1404    refs.into_iter().map(IotaObjectRef::from).collect()
1405}
1406
1407fn to_owned_ref(owned_refs: Vec<(ObjectRef, Owner)>) -> Vec<OwnedObjectRef> {
1408    owned_refs
1409        .into_iter()
1410        .map(|(oref, owner)| OwnedObjectRef {
1411            owner,
1412            reference: oref.into(),
1413        })
1414        .collect()
1415}
1416
1417#[serde_as]
1418#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1419#[serde(rename = "GasData", rename_all = "camelCase")]
1420pub struct IotaGasData {
1421    pub payment: Vec<IotaObjectRef>,
1422    pub owner: IotaAddress,
1423    #[schemars(with = "BigInt<u64>")]
1424    #[serde_as(as = "BigInt<u64>")]
1425    pub price: u64,
1426    #[schemars(with = "BigInt<u64>")]
1427    #[serde_as(as = "BigInt<u64>")]
1428    pub budget: u64,
1429}
1430
1431impl Display for IotaGasData {
1432    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1433        writeln!(f, "Gas Owner: {}", self.owner)?;
1434        writeln!(f, "Gas Budget: {} NANOS", self.budget)?;
1435        writeln!(f, "Gas Price: {} NANOS", self.price)?;
1436        writeln!(f, "Gas Payment:")?;
1437        for payment in &self.payment {
1438            write!(f, "{} ", objref_string(payment))?;
1439        }
1440        writeln!(f)
1441    }
1442}
1443
1444#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1445#[enum_dispatch(IotaTransactionBlockDataAPI)]
1446#[serde(
1447    rename = "TransactionBlockData",
1448    rename_all = "camelCase",
1449    tag = "messageVersion"
1450)]
1451pub enum IotaTransactionBlockData {
1452    V1(IotaTransactionBlockDataV1),
1453}
1454
1455#[enum_dispatch]
1456pub trait IotaTransactionBlockDataAPI {
1457    fn transaction(&self) -> &IotaTransactionBlockKind;
1458    fn sender(&self) -> &IotaAddress;
1459    fn gas_data(&self) -> &IotaGasData;
1460}
1461
1462#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1463#[serde(rename = "TransactionBlockDataV1", rename_all = "camelCase")]
1464pub struct IotaTransactionBlockDataV1 {
1465    pub transaction: IotaTransactionBlockKind,
1466    pub sender: IotaAddress,
1467    pub gas_data: IotaGasData,
1468}
1469
1470impl IotaTransactionBlockDataAPI for IotaTransactionBlockDataV1 {
1471    fn transaction(&self) -> &IotaTransactionBlockKind {
1472        &self.transaction
1473    }
1474    fn sender(&self) -> &IotaAddress {
1475        &self.sender
1476    }
1477    fn gas_data(&self) -> &IotaGasData {
1478        &self.gas_data
1479    }
1480}
1481
1482impl IotaTransactionBlockData {
1483    pub fn move_calls(&self) -> Vec<&IotaProgrammableMoveCall> {
1484        match self {
1485            Self::V1(data) => match &data.transaction {
1486                IotaTransactionBlockKind::ProgrammableTransaction(pt) => pt
1487                    .commands
1488                    .iter()
1489                    .filter_map(|command| match command {
1490                        IotaCommand::MoveCall(c) => Some(&**c),
1491                        _ => None,
1492                    })
1493                    .collect(),
1494                _ => vec![],
1495            },
1496        }
1497    }
1498}
1499
1500impl Display for IotaTransactionBlockData {
1501    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1502        match self {
1503            Self::V1(data) => {
1504                writeln!(f, "Sender: {}", data.sender)?;
1505                writeln!(f, "{}", self.gas_data())?;
1506                writeln!(f, "{}", data.transaction)
1507            }
1508        }
1509    }
1510}
1511
1512impl IotaTransactionBlockData {
1513    pub fn try_from(
1514        data: TransactionData,
1515        module_cache: &impl GetModule,
1516        tx_digest: TransactionDigest,
1517    ) -> Result<Self, anyhow::Error> {
1518        let message_version = data.message_version();
1519        let sender = data.sender();
1520        let gas_data = IotaGasData {
1521            payment: data
1522                .gas()
1523                .iter()
1524                .map(|obj_ref| IotaObjectRef::from(*obj_ref))
1525                .collect(),
1526            owner: data.gas_owner(),
1527            price: data.gas_price(),
1528            budget: data.gas_budget(),
1529        };
1530        let transaction =
1531            IotaTransactionBlockKind::try_from(data.into_kind(), module_cache, tx_digest)?;
1532        match message_version {
1533            1 => Ok(IotaTransactionBlockData::V1(IotaTransactionBlockDataV1 {
1534                transaction,
1535                sender,
1536                gas_data,
1537            })),
1538            _ => Err(anyhow::anyhow!(
1539                "Support for TransactionData version {} not implemented",
1540                message_version
1541            )),
1542        }
1543    }
1544
1545    pub async fn try_from_with_package_resolver(
1546        data: TransactionData,
1547        package_resolver: Arc<Resolver<impl PackageStore>>,
1548        tx_digest: TransactionDigest,
1549    ) -> Result<Self, anyhow::Error> {
1550        let message_version = data.message_version();
1551        let sender = data.sender();
1552        let gas_data = IotaGasData {
1553            payment: data
1554                .gas()
1555                .iter()
1556                .map(|obj_ref| IotaObjectRef::from(*obj_ref))
1557                .collect(),
1558            owner: data.gas_owner(),
1559            price: data.gas_price(),
1560            budget: data.gas_budget(),
1561        };
1562        let transaction = IotaTransactionBlockKind::try_from_with_package_resolver(
1563            data.into_kind(),
1564            package_resolver,
1565            tx_digest,
1566        )
1567        .await?;
1568        match message_version {
1569            1 => Ok(IotaTransactionBlockData::V1(IotaTransactionBlockDataV1 {
1570                transaction,
1571                sender,
1572                gas_data,
1573            })),
1574            _ => Err(anyhow::anyhow!(
1575                "Support for TransactionData version {message_version} not implemented"
1576            )),
1577        }
1578    }
1579}
1580
1581#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1582#[serde(rename = "TransactionBlock", rename_all = "camelCase")]
1583pub struct IotaTransactionBlock {
1584    pub data: IotaTransactionBlockData,
1585    pub tx_signatures: Vec<GenericSignature>,
1586}
1587
1588impl IotaTransactionBlock {
1589    pub fn try_from(
1590        data: SenderSignedData,
1591        module_cache: &impl GetModule,
1592        tx_digest: TransactionDigest,
1593    ) -> Result<Self, anyhow::Error> {
1594        Ok(Self {
1595            data: IotaTransactionBlockData::try_from(
1596                data.intent_message().value.clone(),
1597                module_cache,
1598                tx_digest,
1599            )?,
1600            tx_signatures: data.tx_signatures().to_vec(),
1601        })
1602    }
1603
1604    // TODO: the IotaTransactionBlock `try_from` can be removed after cleaning up
1605    // indexer v1, so are the related `try_from` methods for nested structs like
1606    // IotaTransactionBlockData etc.
1607    pub async fn try_from_with_package_resolver(
1608        data: SenderSignedData,
1609        package_resolver: Arc<Resolver<impl PackageStore>>,
1610        tx_digest: TransactionDigest,
1611    ) -> Result<Self, anyhow::Error> {
1612        Ok(Self {
1613            data: IotaTransactionBlockData::try_from_with_package_resolver(
1614                data.intent_message().value.clone(),
1615                package_resolver,
1616                tx_digest,
1617            )
1618            .await?,
1619            tx_signatures: data.tx_signatures().to_vec(),
1620        })
1621    }
1622}
1623
1624impl Display for IotaTransactionBlock {
1625    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1626        let mut builder = TableBuilder::default();
1627
1628        builder.push_record(vec![format!("{}", self.data)]);
1629        builder.push_record(vec![format!("Signatures:")]);
1630        for tx_sig in &self.tx_signatures {
1631            builder.push_record(vec![format!(
1632                "   {}\n",
1633                match tx_sig {
1634                    Signature(sig) => Base64::from_bytes(sig.signature_bytes()).encoded(),
1635                    // the signatures for multisig and zklogin
1636                    // are not suited to be parsed out. they
1637                    // should be interpreted as a whole
1638                    _ => Base64::from_bytes(tx_sig.as_ref()).encoded(),
1639                }
1640            )]);
1641        }
1642
1643        let mut table = builder.build();
1644        table.with(TablePanel::header("Transaction Data"));
1645        table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1646            1,
1647            TableStyle::modern().get_horizontal(),
1648        )]));
1649        write!(f, "{table}")
1650    }
1651}
1652
1653#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1654pub struct IotaGenesisTransaction {
1655    pub objects: Vec<ObjectID>,
1656    pub events: Vec<EventID>,
1657}
1658
1659#[serde_as]
1660#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1661pub struct IotaConsensusCommitPrologueV1 {
1662    #[schemars(with = "BigInt<u64>")]
1663    #[serde_as(as = "BigInt<u64>")]
1664    pub epoch: u64,
1665    #[schemars(with = "BigInt<u64>")]
1666    #[serde_as(as = "BigInt<u64>")]
1667    pub round: u64,
1668    #[schemars(with = "Option<BigInt<u64>>")]
1669    #[serde_as(as = "Option<BigInt<u64>>")]
1670    pub sub_dag_index: Option<u64>,
1671    #[schemars(with = "BigInt<u64>")]
1672    #[serde_as(as = "BigInt<u64>")]
1673    pub commit_timestamp_ms: u64,
1674    pub consensus_commit_digest: ConsensusCommitDigest,
1675    pub consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments,
1676}
1677
1678#[serde_as]
1679#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1680pub struct IotaAuthenticatorStateUpdateV1 {
1681    #[schemars(with = "BigInt<u64>")]
1682    #[serde_as(as = "BigInt<u64>")]
1683    pub epoch: u64,
1684    #[schemars(with = "BigInt<u64>")]
1685    #[serde_as(as = "BigInt<u64>")]
1686    pub round: u64,
1687
1688    pub new_active_jwks: Vec<IotaActiveJwk>,
1689}
1690
1691#[serde_as]
1692#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1693pub struct IotaRandomnessStateUpdate {
1694    #[schemars(with = "BigInt<u64>")]
1695    #[serde_as(as = "BigInt<u64>")]
1696    pub epoch: u64,
1697
1698    #[schemars(with = "BigInt<u64>")]
1699    #[serde_as(as = "BigInt<u64>")]
1700    pub randomness_round: u64,
1701    pub random_bytes: Vec<u8>,
1702}
1703
1704#[serde_as]
1705#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1706pub struct IotaEndOfEpochTransaction {
1707    pub transactions: Vec<IotaEndOfEpochTransactionKind>,
1708}
1709
1710#[serde_as]
1711#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1712pub enum IotaEndOfEpochTransactionKind {
1713    ChangeEpoch(IotaChangeEpoch),
1714    ChangeEpochV2(IotaChangeEpochV2),
1715    AuthenticatorStateCreate,
1716    AuthenticatorStateExpire(IotaAuthenticatorStateExpire),
1717}
1718
1719#[serde_as]
1720#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1721pub struct IotaAuthenticatorStateExpire {
1722    #[schemars(with = "BigInt<u64>")]
1723    #[serde_as(as = "BigInt<u64>")]
1724    pub min_epoch: u64,
1725}
1726
1727#[serde_as]
1728#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1729pub struct IotaActiveJwk {
1730    pub jwk_id: IotaJwkId,
1731    pub jwk: IotaJWK,
1732
1733    #[schemars(with = "BigInt<u64>")]
1734    #[serde_as(as = "BigInt<u64>")]
1735    pub epoch: u64,
1736}
1737
1738impl From<ActiveJwk> for IotaActiveJwk {
1739    fn from(active_jwk: ActiveJwk) -> Self {
1740        Self {
1741            jwk_id: IotaJwkId {
1742                iss: active_jwk.jwk_id.iss.clone(),
1743                kid: active_jwk.jwk_id.kid.clone(),
1744            },
1745            jwk: IotaJWK {
1746                kty: active_jwk.jwk.kty.clone(),
1747                e: active_jwk.jwk.e.clone(),
1748                n: active_jwk.jwk.n.clone(),
1749                alg: active_jwk.jwk.alg.clone(),
1750            },
1751            epoch: active_jwk.epoch,
1752        }
1753    }
1754}
1755
1756#[serde_as]
1757#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1758pub struct IotaJwkId {
1759    pub iss: String,
1760    pub kid: String,
1761}
1762
1763#[serde_as]
1764#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1765pub struct IotaJWK {
1766    pub kty: String,
1767    pub e: String,
1768    pub n: String,
1769    pub alg: String,
1770}
1771
1772#[serde_as]
1773#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
1774#[serde(rename = "InputObjectKind")]
1775pub enum IotaInputObjectKind {
1776    // A Move package, must be immutable.
1777    MovePackage(ObjectID),
1778    // A Move object, either immutable, or owned mutable.
1779    ImmOrOwnedMoveObject(IotaObjectRef),
1780    // A Move object that's shared and mutable.
1781    SharedMoveObject {
1782        id: ObjectID,
1783        #[schemars(with = "AsSequenceNumber")]
1784        #[serde_as(as = "AsSequenceNumber")]
1785        initial_shared_version: SequenceNumber,
1786        #[serde(default = "default_shared_object_mutability")]
1787        mutable: bool,
1788    },
1789}
1790
1791/// A series of commands where the results of one command can be used in future
1792/// commands
1793#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1794pub struct IotaProgrammableTransactionBlock {
1795    /// Input objects or primitive values
1796    pub inputs: Vec<IotaCallArg>,
1797    #[serde(rename = "transactions")]
1798    /// The transactions to be executed sequentially. A failure in any
1799    /// transaction will result in the failure of the entire programmable
1800    /// transaction block.
1801    pub commands: Vec<IotaCommand>,
1802}
1803
1804impl Display for IotaProgrammableTransactionBlock {
1805    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1806        let Self { inputs, commands } = self;
1807        writeln!(f, "Inputs: {inputs:?}")?;
1808        writeln!(f, "Commands: [")?;
1809        for c in commands {
1810            writeln!(f, "  {c},")?;
1811        }
1812        writeln!(f, "]")
1813    }
1814}
1815
1816impl IotaProgrammableTransactionBlock {
1817    fn try_from(
1818        value: ProgrammableTransaction,
1819        module_cache: &impl GetModule,
1820    ) -> Result<Self, anyhow::Error> {
1821        let ProgrammableTransaction { inputs, commands } = value;
1822        let input_types = Self::resolve_input_type(&inputs, &commands, module_cache);
1823        Ok(IotaProgrammableTransactionBlock {
1824            inputs: inputs
1825                .into_iter()
1826                .zip(input_types)
1827                .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
1828                .collect::<Result<_, _>>()?,
1829            commands: commands.into_iter().map(IotaCommand::from).collect(),
1830        })
1831    }
1832
1833    async fn try_from_with_package_resolver(
1834        value: ProgrammableTransaction,
1835        package_resolver: Arc<Resolver<impl PackageStore>>,
1836    ) -> Result<Self, anyhow::Error> {
1837        // If the pure input layouts cannot be built, we will use `None` for the input
1838        // types.
1839        let input_types = package_resolver
1840            .pure_input_layouts(&value)
1841            .await
1842            .unwrap_or_else(|e| {
1843                tracing::warn!("pure_input_layouts failed: {:?}", e);
1844                vec![None; value.inputs.len()]
1845            });
1846
1847        let ProgrammableTransaction { inputs, commands } = value;
1848        Ok(IotaProgrammableTransactionBlock {
1849            inputs: inputs
1850                .into_iter()
1851                .zip(input_types)
1852                .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
1853                .collect::<Result<_, _>>()?,
1854            commands: commands.into_iter().map(IotaCommand::from).collect(),
1855        })
1856    }
1857
1858    fn resolve_input_type(
1859        inputs: &[CallArg],
1860        commands: &[Command],
1861        module_cache: &impl GetModule,
1862    ) -> Vec<Option<MoveTypeLayout>> {
1863        let mut result_types = vec![None; inputs.len()];
1864        for command in commands.iter() {
1865            match command {
1866                Command::MoveCall(c) => {
1867                    let Ok(module) = Identifier::new(c.module.clone()) else {
1868                        return result_types;
1869                    };
1870
1871                    let Ok(function) = Identifier::new(c.function.clone()) else {
1872                        return result_types;
1873                    };
1874
1875                    let id = ModuleId::new(c.package.into(), module);
1876                    let Some(types) =
1877                        get_signature_types(id, function.as_ident_str(), module_cache)
1878                    else {
1879                        return result_types;
1880                    };
1881                    for (arg, type_) in c.arguments.iter().zip(types) {
1882                        if let (&Argument::Input(i), Some(type_)) = (arg, type_) {
1883                            if let Some(x) = result_types.get_mut(i as usize) {
1884                                x.replace(type_);
1885                            }
1886                        }
1887                    }
1888                }
1889                Command::SplitCoins(_, amounts) => {
1890                    for arg in amounts {
1891                        if let &Argument::Input(i) = arg {
1892                            if let Some(x) = result_types.get_mut(i as usize) {
1893                                x.replace(MoveTypeLayout::U64);
1894                            }
1895                        }
1896                    }
1897                }
1898                Command::TransferObjects(_, Argument::Input(i)) => {
1899                    if let Some(x) = result_types.get_mut((*i) as usize) {
1900                        x.replace(MoveTypeLayout::Address);
1901                    }
1902                }
1903                _ => {}
1904            }
1905        }
1906        result_types
1907    }
1908}
1909
1910fn get_signature_types(
1911    id: ModuleId,
1912    function: &IdentStr,
1913    module_cache: &impl GetModule,
1914) -> Option<Vec<Option<MoveTypeLayout>>> {
1915    use std::borrow::Borrow;
1916    if let Ok(Some(module)) = module_cache.get_module_by_id(&id) {
1917        let module: &CompiledModule = module.borrow();
1918        let func = module
1919            .function_handles
1920            .iter()
1921            .find(|f| module.identifier_at(f.name) == function)?;
1922        Some(
1923            module
1924                .signature_at(func.parameters)
1925                .0
1926                .iter()
1927                .map(|s| primitive_type(module, &[], s))
1928                .collect(),
1929        )
1930    } else {
1931        None
1932    }
1933}
1934
1935/// A single transaction in a programmable transaction block.
1936#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1937#[serde(rename = "IotaTransaction")]
1938pub enum IotaCommand {
1939    /// A call to either an entry or a public Move function
1940    MoveCall(Box<IotaProgrammableMoveCall>),
1941    /// `(Vec<forall T:key+store. T>, address)`
1942    /// It sends n-objects to the specified address. These objects must have
1943    /// store (public transfer) and either the previous owner must be an
1944    /// address or the object must be newly created.
1945    TransferObjects(Vec<IotaArgument>, IotaArgument),
1946    /// `(&mut Coin<T>, Vec<u64>)` -> `Vec<Coin<T>>`
1947    /// It splits off some amounts into a new coins with those amounts
1948    SplitCoins(IotaArgument, Vec<IotaArgument>),
1949    /// `(&mut Coin<T>, Vec<Coin<T>>)`
1950    /// It merges n-coins into the first coin
1951    MergeCoins(IotaArgument, Vec<IotaArgument>),
1952    /// Publishes a Move package. It takes the package bytes and a list of the
1953    /// package's transitive dependencies to link against on-chain.
1954    Publish(Vec<ObjectID>),
1955    /// Upgrades a Move package
1956    Upgrade(Vec<ObjectID>, ObjectID, IotaArgument),
1957    /// `forall T: Vec<T> -> vector<T>`
1958    /// Given n-values of the same type, it constructs a vector. For non objects
1959    /// or an empty vector, the type tag must be specified.
1960    MakeMoveVec(Option<String>, Vec<IotaArgument>),
1961}
1962
1963impl Display for IotaCommand {
1964    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1965        match self {
1966            Self::MoveCall(p) => {
1967                write!(f, "MoveCall({p})")
1968            }
1969            Self::MakeMoveVec(ty_opt, elems) => {
1970                write!(f, "MakeMoveVec(")?;
1971                if let Some(ty) = ty_opt {
1972                    write!(f, "Some{ty}")?;
1973                } else {
1974                    write!(f, "None")?;
1975                }
1976                write!(f, ",[")?;
1977                write_sep(f, elems, ",")?;
1978                write!(f, "])")
1979            }
1980            Self::TransferObjects(objs, addr) => {
1981                write!(f, "TransferObjects([")?;
1982                write_sep(f, objs, ",")?;
1983                write!(f, "],{addr})")
1984            }
1985            Self::SplitCoins(coin, amounts) => {
1986                write!(f, "SplitCoins({coin},")?;
1987                write_sep(f, amounts, ",")?;
1988                write!(f, ")")
1989            }
1990            Self::MergeCoins(target, coins) => {
1991                write!(f, "MergeCoins({target},")?;
1992                write_sep(f, coins, ",")?;
1993                write!(f, ")")
1994            }
1995            Self::Publish(deps) => {
1996                write!(f, "Publish(<modules>,")?;
1997                write_sep(f, deps, ",")?;
1998                write!(f, ")")
1999            }
2000            Self::Upgrade(deps, current_package_id, ticket) => {
2001                write!(f, "Upgrade(<modules>, {ticket},")?;
2002                write_sep(f, deps, ",")?;
2003                write!(f, ", {current_package_id}")?;
2004                write!(f, ")")
2005            }
2006        }
2007    }
2008}
2009
2010impl From<Command> for IotaCommand {
2011    fn from(value: Command) -> Self {
2012        match value {
2013            Command::MoveCall(m) => IotaCommand::MoveCall(Box::new((*m).into())),
2014            Command::TransferObjects(args, arg) => IotaCommand::TransferObjects(
2015                args.into_iter().map(IotaArgument::from).collect(),
2016                arg.into(),
2017            ),
2018            Command::SplitCoins(arg, args) => IotaCommand::SplitCoins(
2019                arg.into(),
2020                args.into_iter().map(IotaArgument::from).collect(),
2021            ),
2022            Command::MergeCoins(arg, args) => IotaCommand::MergeCoins(
2023                arg.into(),
2024                args.into_iter().map(IotaArgument::from).collect(),
2025            ),
2026            Command::Publish(_modules, dep_ids) => IotaCommand::Publish(dep_ids),
2027            Command::MakeMoveVec(tag_opt, args) => IotaCommand::MakeMoveVec(
2028                tag_opt.map(|tag| tag.to_string()),
2029                args.into_iter().map(IotaArgument::from).collect(),
2030            ),
2031            Command::Upgrade(_modules, dep_ids, current_package_id, ticket) => {
2032                IotaCommand::Upgrade(dep_ids, current_package_id, IotaArgument::from(ticket))
2033            }
2034        }
2035    }
2036}
2037
2038/// An argument to a transaction in a programmable transaction block
2039#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2040pub enum IotaArgument {
2041    /// The gas coin. The gas coin can only be used by-ref, except for with
2042    /// `TransferObjects`, which can use it by-value.
2043    GasCoin,
2044    /// One of the input objects or primitive values (from
2045    /// `ProgrammableTransactionBlock` inputs)
2046    Input(u16),
2047    /// The result of another transaction (from `ProgrammableTransactionBlock`
2048    /// transactions)
2049    Result(u16),
2050    /// Like a `Result` but it accesses a nested result. Currently, the only
2051    /// usage of this is to access a value from a Move call with multiple
2052    /// return values.
2053    NestedResult(u16, u16),
2054}
2055
2056impl Display for IotaArgument {
2057    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2058        match self {
2059            Self::GasCoin => write!(f, "GasCoin"),
2060            Self::Input(i) => write!(f, "Input({i})"),
2061            Self::Result(i) => write!(f, "Result({i})"),
2062            Self::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
2063        }
2064    }
2065}
2066
2067impl From<Argument> for IotaArgument {
2068    fn from(value: Argument) -> Self {
2069        match value {
2070            Argument::GasCoin => Self::GasCoin,
2071            Argument::Input(i) => Self::Input(i),
2072            Argument::Result(i) => Self::Result(i),
2073            Argument::NestedResult(i, j) => Self::NestedResult(i, j),
2074        }
2075    }
2076}
2077
2078/// The transaction for calling a Move function, either an entry function or a
2079/// public function (which cannot return references).
2080#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2081pub struct IotaProgrammableMoveCall {
2082    /// The package containing the module and function.
2083    pub package: ObjectID,
2084    /// The specific module in the package containing the function.
2085    pub module: String,
2086    /// The function to be called.
2087    pub function: String,
2088    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2089    /// The type arguments to the function.
2090    pub type_arguments: Vec<String>,
2091    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2092    /// The arguments to the function.
2093    pub arguments: Vec<IotaArgument>,
2094}
2095
2096fn write_sep<T: Display>(
2097    f: &mut Formatter<'_>,
2098    items: impl IntoIterator<Item = T>,
2099    sep: &str,
2100) -> std::fmt::Result {
2101    let mut xs = items.into_iter().peekable();
2102    while let Some(x) = xs.next() {
2103        write!(f, "{x}")?;
2104        if xs.peek().is_some() {
2105            write!(f, "{sep}")?;
2106        }
2107    }
2108    Ok(())
2109}
2110
2111impl Display for IotaProgrammableMoveCall {
2112    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2113        let Self {
2114            package,
2115            module,
2116            function,
2117            type_arguments,
2118            arguments,
2119        } = self;
2120        write!(f, "{package}::{module}::{function}")?;
2121        if !type_arguments.is_empty() {
2122            write!(f, "<")?;
2123            write_sep(f, type_arguments, ",")?;
2124            write!(f, ">")?;
2125        }
2126        write!(f, "(")?;
2127        write_sep(f, arguments, ",")?;
2128        write!(f, ")")
2129    }
2130}
2131
2132impl From<ProgrammableMoveCall> for IotaProgrammableMoveCall {
2133    fn from(value: ProgrammableMoveCall) -> Self {
2134        let ProgrammableMoveCall {
2135            package,
2136            module,
2137            function,
2138            type_arguments,
2139            arguments,
2140        } = value;
2141        Self {
2142            package,
2143            module: module.to_string(),
2144            function: function.to_string(),
2145            type_arguments: type_arguments.into_iter().map(|t| t.to_string()).collect(),
2146            arguments: arguments.into_iter().map(IotaArgument::from).collect(),
2147        }
2148    }
2149}
2150
2151const fn default_shared_object_mutability() -> bool {
2152    true
2153}
2154
2155impl From<InputObjectKind> for IotaInputObjectKind {
2156    fn from(input: InputObjectKind) -> Self {
2157        match input {
2158            InputObjectKind::MovePackage(id) => Self::MovePackage(id),
2159            InputObjectKind::ImmOrOwnedMoveObject(oref) => Self::ImmOrOwnedMoveObject(oref.into()),
2160            InputObjectKind::SharedMoveObject {
2161                id,
2162                initial_shared_version,
2163                mutable,
2164            } => Self::SharedMoveObject {
2165                id,
2166                initial_shared_version,
2167                mutable,
2168            },
2169        }
2170    }
2171}
2172
2173#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
2174#[serde(rename = "TypeTag", rename_all = "camelCase")]
2175pub struct IotaTypeTag(String);
2176
2177impl IotaTypeTag {
2178    pub fn new(tag: String) -> Self {
2179        Self(tag)
2180    }
2181}
2182
2183impl AsRef<str> for IotaTypeTag {
2184    fn as_ref(&self) -> &str {
2185        &self.0
2186    }
2187}
2188
2189impl TryFrom<IotaTypeTag> for TypeTag {
2190    type Error = anyhow::Error;
2191    fn try_from(tag: IotaTypeTag) -> Result<Self, Self::Error> {
2192        parse_iota_type_tag(&tag.0)
2193    }
2194}
2195
2196impl From<TypeTag> for IotaTypeTag {
2197    fn from(tag: TypeTag) -> Self {
2198        Self(format!("{tag}"))
2199    }
2200}
2201
2202#[derive(Serialize, Deserialize, JsonSchema)]
2203#[serde(rename_all = "camelCase")]
2204pub enum RPCTransactionRequestParams {
2205    TransferObjectRequestParams(TransferObjectParams),
2206    MoveCallRequestParams(MoveCallParams),
2207}
2208
2209#[derive(Serialize, Deserialize, JsonSchema)]
2210#[serde(rename_all = "camelCase")]
2211pub struct TransferObjectParams {
2212    pub recipient: IotaAddress,
2213    pub object_id: ObjectID,
2214}
2215
2216#[derive(Serialize, Deserialize, JsonSchema)]
2217#[serde(rename_all = "camelCase")]
2218pub struct MoveCallParams {
2219    pub package_object_id: ObjectID,
2220    pub module: String,
2221    pub function: String,
2222    #[serde(default)]
2223    pub type_arguments: Vec<IotaTypeTag>,
2224    pub arguments: Vec<IotaJsonValue>,
2225}
2226
2227#[serde_as]
2228#[derive(Serialize, Deserialize, Clone, JsonSchema)]
2229#[serde(rename_all = "camelCase")]
2230pub struct TransactionBlockBytes {
2231    /// BCS serialized transaction data bytes without its type tag, as base-64
2232    /// encoded string.
2233    pub tx_bytes: Base64,
2234    /// the gas objects to be used
2235    pub gas: Vec<IotaObjectRef>,
2236    /// objects to be used in this transaction
2237    pub input_objects: Vec<IotaInputObjectKind>,
2238}
2239
2240impl TransactionBlockBytes {
2241    pub fn from_data(data: TransactionData) -> Result<Self, anyhow::Error> {
2242        Ok(Self {
2243            tx_bytes: Base64::from_bytes(bcs::to_bytes(&data)?.as_slice()),
2244            gas: data
2245                .gas()
2246                .iter()
2247                .map(|obj_ref| IotaObjectRef::from(*obj_ref))
2248                .collect(),
2249            input_objects: data
2250                .input_objects()?
2251                .into_iter()
2252                .map(IotaInputObjectKind::from)
2253                .collect(),
2254        })
2255    }
2256
2257    pub fn to_data(self) -> Result<TransactionData, anyhow::Error> {
2258        bcs::from_bytes::<TransactionData>(&self.tx_bytes.to_vec().map_err(|e| anyhow::anyhow!(e))?)
2259            .map_err(|e| anyhow::anyhow!(e))
2260    }
2261}
2262
2263#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
2264#[serde(rename = "OwnedObjectRef")]
2265pub struct OwnedObjectRef {
2266    pub owner: Owner,
2267    pub reference: IotaObjectRef,
2268}
2269
2270impl OwnedObjectRef {
2271    pub fn object_id(&self) -> ObjectID {
2272        self.reference.object_id
2273    }
2274    pub fn version(&self) -> SequenceNumber {
2275        self.reference.version
2276    }
2277}
2278
2279#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2280#[serde(tag = "type", rename_all = "camelCase")]
2281pub enum IotaCallArg {
2282    // Needs to become an Object Ref or Object ID, depending on object type
2283    Object(IotaObjectArg),
2284    // pure value, bcs encoded
2285    Pure(IotaPureValue),
2286}
2287
2288impl IotaCallArg {
2289    pub fn try_from(
2290        value: CallArg,
2291        layout: Option<&MoveTypeLayout>,
2292    ) -> Result<Self, anyhow::Error> {
2293        Ok(match value {
2294            CallArg::Pure(p) => IotaCallArg::Pure(IotaPureValue {
2295                value_type: layout.map(|l| l.into()),
2296                value: IotaJsonValue::from_bcs_bytes(layout, &p)?,
2297            }),
2298            CallArg::Object(ObjectArg::ImmOrOwnedObject((id, version, digest))) => {
2299                IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject {
2300                    object_id: id,
2301                    version,
2302                    digest,
2303                })
2304            }
2305            CallArg::Object(ObjectArg::SharedObject {
2306                id,
2307                initial_shared_version,
2308                mutable,
2309            }) => IotaCallArg::Object(IotaObjectArg::SharedObject {
2310                object_id: id,
2311                initial_shared_version,
2312                mutable,
2313            }),
2314            CallArg::Object(ObjectArg::Receiving((object_id, version, digest))) => {
2315                IotaCallArg::Object(IotaObjectArg::Receiving {
2316                    object_id,
2317                    version,
2318                    digest,
2319                })
2320            }
2321        })
2322    }
2323
2324    pub fn pure(&self) -> Option<&IotaJsonValue> {
2325        match self {
2326            IotaCallArg::Pure(v) => Some(&v.value),
2327            _ => None,
2328        }
2329    }
2330
2331    pub fn object(&self) -> Option<&ObjectID> {
2332        match self {
2333            IotaCallArg::Object(IotaObjectArg::SharedObject { object_id, .. })
2334            | IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject { object_id, .. })
2335            | IotaCallArg::Object(IotaObjectArg::Receiving { object_id, .. }) => Some(object_id),
2336            _ => None,
2337        }
2338    }
2339}
2340
2341#[serde_as]
2342#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2343#[serde(rename_all = "camelCase")]
2344pub struct IotaPureValue {
2345    #[schemars(with = "Option<String>")]
2346    #[serde_as(as = "Option<AsIotaTypeTag>")]
2347    value_type: Option<TypeTag>,
2348    value: IotaJsonValue,
2349}
2350
2351impl IotaPureValue {
2352    pub fn value(&self) -> IotaJsonValue {
2353        self.value.clone()
2354    }
2355
2356    pub fn value_type(&self) -> Option<TypeTag> {
2357        self.value_type.clone()
2358    }
2359}
2360
2361#[serde_as]
2362#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2363#[serde(tag = "objectType", rename_all = "camelCase")]
2364pub enum IotaObjectArg {
2365    // A Move object, either immutable, or owned mutable.
2366    #[serde(rename_all = "camelCase")]
2367    ImmOrOwnedObject {
2368        object_id: ObjectID,
2369        #[schemars(with = "AsSequenceNumber")]
2370        #[serde_as(as = "AsSequenceNumber")]
2371        version: SequenceNumber,
2372        digest: ObjectDigest,
2373    },
2374    // A Move object that's shared.
2375    // SharedObject::mutable controls whether caller asks for a mutable reference to shared
2376    // object.
2377    #[serde(rename_all = "camelCase")]
2378    SharedObject {
2379        object_id: ObjectID,
2380        #[schemars(with = "AsSequenceNumber")]
2381        #[serde_as(as = "AsSequenceNumber")]
2382        initial_shared_version: SequenceNumber,
2383        mutable: bool,
2384    },
2385    // A reference to a Move object that's going to be received in the transaction.
2386    #[serde(rename_all = "camelCase")]
2387    Receiving {
2388        object_id: ObjectID,
2389        #[schemars(with = "AsSequenceNumber")]
2390        #[serde_as(as = "AsSequenceNumber")]
2391        version: SequenceNumber,
2392        digest: ObjectDigest,
2393    },
2394}
2395
2396#[derive(Clone)]
2397pub struct EffectsWithInput {
2398    pub effects: IotaTransactionBlockEffects,
2399    pub input: TransactionData,
2400}
2401
2402impl From<EffectsWithInput> for IotaTransactionBlockEffects {
2403    fn from(e: EffectsWithInput) -> Self {
2404        e.effects
2405    }
2406}
2407
2408#[serde_as]
2409#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2410pub enum TransactionFilter {
2411    /// Query by checkpoint.
2412    Checkpoint(
2413        #[schemars(with = "BigInt<u64>")]
2414        #[serde_as(as = "Readable<BigInt<u64>, _>")]
2415        CheckpointSequenceNumber,
2416    ),
2417    /// Query by move function.
2418    MoveFunction {
2419        package: ObjectID,
2420        module: Option<String>,
2421        function: Option<String>,
2422    },
2423    /// Query by input object.
2424    InputObject(ObjectID),
2425    /// Query by changed object, including created, mutated and unwrapped
2426    /// objects.
2427    ChangedObject(ObjectID),
2428    /// Query by sender address.
2429    FromAddress(IotaAddress),
2430    /// Query by recipient address.
2431    ToAddress(IotaAddress),
2432    /// Query by sender and recipient address.
2433    FromAndToAddress { from: IotaAddress, to: IotaAddress },
2434    /// Query txs that have a given address as sender or recipient.
2435    FromOrToAddress { addr: IotaAddress },
2436    /// Query by transaction kind
2437    TransactionKind(IotaTransactionKind),
2438    /// Query transactions of any given kind in the input.
2439    TransactionKindIn(Vec<IotaTransactionKind>),
2440}
2441
2442impl TransactionFilter {
2443    pub fn as_v2(&self) -> TransactionFilterV2 {
2444        match self {
2445            TransactionFilter::InputObject(o) => TransactionFilterV2::InputObject(*o),
2446            TransactionFilter::ChangedObject(o) => TransactionFilterV2::ChangedObject(*o),
2447            TransactionFilter::FromAddress(a) => TransactionFilterV2::FromAddress(*a),
2448            TransactionFilter::ToAddress(a) => TransactionFilterV2::ToAddress(*a),
2449            TransactionFilter::FromAndToAddress { from, to } => {
2450                TransactionFilterV2::FromAndToAddress {
2451                    from: *from,
2452                    to: *to,
2453                }
2454            }
2455            TransactionFilter::FromOrToAddress { addr } => {
2456                TransactionFilterV2::FromOrToAddress { addr: *addr }
2457            }
2458            TransactionFilter::MoveFunction {
2459                package,
2460                module,
2461                function,
2462            } => TransactionFilterV2::MoveFunction {
2463                package: *package,
2464                module: module.clone(),
2465                function: function.clone(),
2466            },
2467            TransactionFilter::TransactionKind(kind) => TransactionFilterV2::TransactionKind(*kind),
2468            TransactionFilter::TransactionKindIn(kinds) => {
2469                TransactionFilterV2::TransactionKindIn(kinds.clone())
2470            }
2471            TransactionFilter::Checkpoint(checkpoint) => {
2472                TransactionFilterV2::Checkpoint(*checkpoint)
2473            }
2474        }
2475    }
2476}
2477
2478impl Filter<EffectsWithInput> for TransactionFilter {
2479    fn matches(&self, item: &EffectsWithInput) -> bool {
2480        let _scope = monitored_scope("TransactionFilter::matches");
2481        match self {
2482            TransactionFilter::InputObject(o) => {
2483                let Ok(input_objects) = item.input.input_objects() else {
2484                    return false;
2485                };
2486                input_objects.iter().any(|object| object.object_id() == *o)
2487            }
2488            TransactionFilter::ChangedObject(o) => item
2489                .effects
2490                .mutated()
2491                .iter()
2492                .any(|oref: &OwnedObjectRef| &oref.reference.object_id == o),
2493            TransactionFilter::FromAddress(a) => &item.input.sender() == a,
2494            TransactionFilter::ToAddress(a) => {
2495                let mutated: &[OwnedObjectRef] = item.effects.mutated();
2496                mutated.iter().chain(item.effects.unwrapped().iter()).any(|oref: &OwnedObjectRef| {
2497                    matches!(oref.owner, Owner::AddressOwner(owner) if owner == *a)
2498                })
2499            }
2500            TransactionFilter::FromAndToAddress { from, to } => {
2501                Self::FromAddress(*from).matches(item) && Self::ToAddress(*to).matches(item)
2502            }
2503            TransactionFilter::FromOrToAddress { addr } => {
2504                Self::FromAddress(*addr).matches(item) || Self::ToAddress(*addr).matches(item)
2505            }
2506            TransactionFilter::MoveFunction {
2507                package,
2508                module,
2509                function,
2510            } => item.input.move_calls().into_iter().any(|(p, m, f)| {
2511                p == package
2512                    && (module.is_none() || matches!(module,  Some(m2) if m2 == &m.to_string()))
2513                    && (function.is_none() || matches!(function, Some(f2) if f2 == &f.to_string()))
2514            }),
2515            TransactionFilter::TransactionKind(kind) => {
2516                kind == &IotaTransactionKind::from(item.input.kind())
2517            }
2518            TransactionFilter::TransactionKindIn(kinds) => kinds
2519                .iter()
2520                .any(|kind| kind == &IotaTransactionKind::from(item.input.kind())),
2521            // this filter is not supported, RPC will reject it on subscription
2522            TransactionFilter::Checkpoint(_) => false,
2523        }
2524    }
2525}
2526
2527#[serde_as]
2528#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2529#[non_exhaustive]
2530pub enum TransactionFilterV2 {
2531    /// Query by checkpoint.
2532    Checkpoint(
2533        #[schemars(with = "BigInt<u64>")]
2534        #[serde_as(as = "Readable<BigInt<u64>, _>")]
2535        CheckpointSequenceNumber,
2536    ),
2537    /// Query by move function.
2538    MoveFunction {
2539        package: ObjectID,
2540        module: Option<String>,
2541        function: Option<String>,
2542    },
2543    /// Query by input object.
2544    InputObject(ObjectID),
2545    /// Query by changed object, including created, mutated and unwrapped
2546    /// objects.
2547    ChangedObject(ObjectID),
2548    /// Query transactions that wrapped or deleted the specified object.
2549    /// Includes transactions that either created and immediately wrapped
2550    /// the object or unwrapped and immediately deleted it.
2551    WrappedOrDeletedObject(ObjectID),
2552    /// Query by sender address.
2553    FromAddress(IotaAddress),
2554    /// Query by recipient address.
2555    ToAddress(IotaAddress),
2556    /// Query by sender and recipient address.
2557    FromAndToAddress { from: IotaAddress, to: IotaAddress },
2558    /// Query txs that have a given address as sender or recipient.
2559    FromOrToAddress { addr: IotaAddress },
2560    /// Query by transaction kind
2561    TransactionKind(IotaTransactionKind),
2562    /// Query transactions of any given kind in the input.
2563    TransactionKindIn(Vec<IotaTransactionKind>),
2564}
2565
2566impl TransactionFilterV2 {
2567    pub fn as_v1(&self) -> Option<TransactionFilter> {
2568        match self {
2569            TransactionFilterV2::InputObject(o) => Some(TransactionFilter::InputObject(*o)),
2570            TransactionFilterV2::ChangedObject(o) => Some(TransactionFilter::ChangedObject(*o)),
2571            TransactionFilterV2::FromAddress(a) => Some(TransactionFilter::FromAddress(*a)),
2572            TransactionFilterV2::ToAddress(a) => Some(TransactionFilter::ToAddress(*a)),
2573            TransactionFilterV2::FromAndToAddress { from, to } => {
2574                Some(TransactionFilter::FromAndToAddress {
2575                    from: *from,
2576                    to: *to,
2577                })
2578            }
2579            TransactionFilterV2::FromOrToAddress { addr } => {
2580                Some(TransactionFilter::FromOrToAddress { addr: *addr })
2581            }
2582            TransactionFilterV2::MoveFunction {
2583                package,
2584                module,
2585                function,
2586            } => Some(TransactionFilter::MoveFunction {
2587                package: *package,
2588                module: module.clone(),
2589                function: function.clone(),
2590            }),
2591            TransactionFilterV2::TransactionKind(kind) => {
2592                Some(TransactionFilter::TransactionKind(*kind))
2593            }
2594            TransactionFilterV2::TransactionKindIn(kinds) => {
2595                Some(TransactionFilter::TransactionKindIn(kinds.clone()))
2596            }
2597            TransactionFilterV2::Checkpoint(checkpoint) => {
2598                Some(TransactionFilter::Checkpoint(*checkpoint))
2599            }
2600            // V2-only variants which do not have a V1 equivalent
2601            TransactionFilterV2::WrappedOrDeletedObject(_) => None,
2602        }
2603    }
2604}
2605
2606impl Filter<EffectsWithInput> for TransactionFilterV2 {
2607    fn matches(&self, item: &EffectsWithInput) -> bool {
2608        let _scope = monitored_scope("TransactionFilterV2::matches");
2609        if let Some(v1) = self.as_v1() {
2610            return v1.matches(item);
2611        }
2612        // Fallback for new V2-only variants:
2613        match self {
2614            TransactionFilterV2::WrappedOrDeletedObject(o) => item
2615                .effects
2616                .wrapped()
2617                .iter()
2618                .chain(item.effects.deleted())
2619                .chain(item.effects.unwrapped_then_deleted())
2620                .any(|oref| &oref.object_id == o),
2621
2622            _ => false,
2623        }
2624    }
2625}
2626
2627/// Represents the type of a transaction. All transactions except
2628/// `ProgrammableTransaction` are considered system transactions.
2629#[derive(
2630    Debug, Clone, Copy, PartialEq, Eq, EnumString, Display, Serialize, Deserialize, JsonSchema,
2631)]
2632#[non_exhaustive]
2633pub enum IotaTransactionKind {
2634    /// The `SystemTransaction` variant can be used to filter for all types of
2635    /// system transactions.
2636    SystemTransaction = 0,
2637    ProgrammableTransaction = 1,
2638    Genesis = 2,
2639    ConsensusCommitPrologueV1 = 3,
2640    AuthenticatorStateUpdateV1 = 4,
2641    RandomnessStateUpdate = 5,
2642    EndOfEpochTransaction = 6,
2643}
2644
2645impl IotaTransactionKind {
2646    /// Returns true if the transaction is a system transaction.
2647    pub fn is_system_transaction(&self) -> bool {
2648        !matches!(self, Self::ProgrammableTransaction)
2649    }
2650}
2651
2652impl From<&TransactionKind> for IotaTransactionKind {
2653    fn from(kind: &TransactionKind) -> Self {
2654        match kind {
2655            TransactionKind::Genesis(_) => Self::Genesis,
2656            TransactionKind::ConsensusCommitPrologueV1(_) => Self::ConsensusCommitPrologueV1,
2657            TransactionKind::AuthenticatorStateUpdateV1(_) => Self::AuthenticatorStateUpdateV1,
2658            TransactionKind::RandomnessStateUpdate(_) => Self::RandomnessStateUpdate,
2659            TransactionKind::EndOfEpochTransaction(_) => Self::EndOfEpochTransaction,
2660            TransactionKind::ProgrammableTransaction(_) => Self::ProgrammableTransaction,
2661        }
2662    }
2663}