sui_json_rpc_types/
sui_transaction.rs

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