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