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