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