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