iota_json_rpc_types/
iota_transaction.rs

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