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