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