sui_json_rpc_types/
sui_transaction.rs

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