Skip to main content

iota_json_rpc_types/
iota_transaction.rs

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