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