iota_json_rpc_types/
iota_transaction.rs

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