Skip to main content

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