sui_jsonrpc/msgs/
sui_transaction.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::error::Error as StdError;
5use std::fmt::{self, Display, Formatter, Write};
6use std::str::FromStr;
7
8use enum_dispatch::enum_dispatch;
9use serde::{Deserialize, Serialize};
10use serde_with::base64::Base64;
11use serde_with::{DeserializeAs, DisplayFromStr, IfIsHumanReadable, SerializeAs, serde_as};
12use sui_sdk_types::{
13    Address,
14    ConsensusDeterminedVersionAssignments,
15    Digest,
16    EpochId,
17    GasCostSummary,
18    MoveLocation,
19    StructTag,
20    TypeTag,
21    UserSignature,
22    Version,
23};
24use tabled::builder::Builder as TableBuilder;
25use tabled::settings::style::HorizontalLine;
26use tabled::settings::{Panel as TablePanel, Style as TableStyle};
27
28use super::balance_changes::BalanceChange;
29use super::object_changes::ObjectChange;
30use super::{Page, SuiEvent, SuiObjectRef};
31use crate::msgs::Owner;
32use crate::serde::BigInt;
33
34/// similar to EpochId of sui-types but BigInt
35pub type SuiEpochId = BigInt<u64>;
36
37#[derive(Debug, Clone, Deserialize, Serialize, Default)]
38#[serde(
39    rename_all = "camelCase",
40    rename = "TransactionBlockResponseQuery",
41    default
42)]
43pub struct SuiTransactionBlockResponseQuery {
44    /// If None, no filter will be applied
45    pub filter: Option<TransactionFilter>,
46    /// config which fields to include in the response, by default only digest is included
47    pub options: Option<SuiTransactionBlockResponseOptions>,
48}
49
50impl SuiTransactionBlockResponseQuery {
51    pub fn new(
52        filter: Option<TransactionFilter>,
53        options: Option<SuiTransactionBlockResponseOptions>,
54    ) -> Self {
55        Self { filter, options }
56    }
57
58    pub fn new_with_filter(filter: TransactionFilter) -> Self {
59        Self {
60            filter: Some(filter),
61            options: None,
62        }
63    }
64}
65
66pub type TransactionBlocksPage = Page<SuiTransactionBlockResponse, Digest>;
67
68#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)]
69#[serde(
70    rename_all = "camelCase",
71    rename = "TransactionBlockResponseOptions",
72    default
73)]
74pub struct SuiTransactionBlockResponseOptions {
75    /// Whether to show transaction input data. Default to be False
76    pub show_input: bool,
77    /// Whether to show bcs-encoded transaction input data
78    pub show_raw_input: bool,
79    /// Whether to show transaction effects. Default to be False
80    pub show_effects: bool,
81    /// Whether to show transaction events. Default to be False
82    pub show_events: bool,
83    /// Whether to show object_changes. Default to be False
84    pub show_object_changes: bool,
85    /// Whether to show balance_changes. Default to be False
86    pub show_balance_changes: bool,
87    /// Whether to show raw transaction effects. Default to be False
88    pub show_raw_effects: bool,
89}
90
91impl SuiTransactionBlockResponseOptions {
92    pub fn new() -> Self {
93        Self::default()
94    }
95
96    pub fn full_content() -> Self {
97        Self {
98            show_effects: true,
99            show_input: true,
100            show_raw_input: true,
101            show_events: true,
102            show_object_changes: true,
103            show_balance_changes: true,
104            // This field is added for graphql execution. We keep it false here
105            // so current users of `full_content` will not get raw effects unexpectedly.
106            show_raw_effects: false,
107        }
108    }
109
110    pub fn with_input(mut self) -> Self {
111        self.show_input = true;
112        self
113    }
114
115    pub fn with_raw_input(mut self) -> Self {
116        self.show_raw_input = true;
117        self
118    }
119
120    pub fn with_effects(mut self) -> Self {
121        self.show_effects = true;
122        self
123    }
124
125    pub fn with_events(mut self) -> Self {
126        self.show_events = true;
127        self
128    }
129
130    pub fn with_balance_changes(mut self) -> Self {
131        self.show_balance_changes = true;
132        self
133    }
134
135    pub fn with_object_changes(mut self) -> Self {
136        self.show_object_changes = true;
137        self
138    }
139
140    pub fn with_raw_effects(mut self) -> Self {
141        self.show_raw_effects = true;
142        self
143    }
144
145    /// default to return `WaitForEffectsCert` unless some options require
146    /// local execution
147    pub fn default_execution_request_type(&self) -> ExecuteTransactionRequestType {
148        // if people want effects or events, they typically want to wait for local execution
149        if self.require_effects() {
150            ExecuteTransactionRequestType::WaitForLocalExecution
151        } else {
152            ExecuteTransactionRequestType::WaitForEffectsCert
153        }
154    }
155
156    pub fn require_input(&self) -> bool {
157        self.show_input || self.show_raw_input || self.show_object_changes
158    }
159
160    pub fn require_effects(&self) -> bool {
161        self.show_effects
162            || self.show_events
163            || self.show_balance_changes
164            || self.show_object_changes
165            || self.show_raw_effects
166    }
167
168    pub fn only_digest(&self) -> bool {
169        self == &Self::default()
170    }
171}
172
173#[derive(Serialize, Deserialize, Clone, Debug)]
174pub enum ExecuteTransactionRequestType {
175    /// Waits for `TransactionEffectsCert` and then return to client. This mode is a proxy for
176    /// transaction finality.
177    WaitForEffectsCert,
178    /// JSON-RPC now ignores this. It will always behave as if
179    /// [`WaitForEffectsCert`](ExecuteTransactionRequestType::WaitForEffectsCert) was passed.
180    ///
181    /// Originally: waits for `TransactionEffectsCert` and make sure the node executed the
182    /// transaction locally before returning the client. The local execution makes sure this node is
183    /// aware of this transaction when client fires subsequent queries. However if the node fails to
184    /// execute the transaction locally in a timely manner, a bool type in the response is set to
185    /// false to indicated the case. request_type is default to be `WaitForEffectsCert` unless
186    /// options.show_events or options.show_effects is true
187    WaitForLocalExecution,
188}
189
190#[serde_as]
191#[derive(Serialize, Deserialize, Debug, Clone, Default)]
192#[serde(rename_all = "camelCase", rename = "TransactionBlockResponse")]
193pub struct SuiTransactionBlockResponse {
194    pub digest: Digest,
195    /// Transaction input data
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub transaction: Option<SuiTransactionBlock>,
198    /// BCS encoded [SenderSignedData](https://mystenlabs.github.io/sui/sui_types/transaction/struct.SenderSignedData.html)
199    /// that includes input object references returns empty array if `show_raw_transaction` is false
200    #[serde_as(as = "Base64")]
201    #[serde(skip_serializing_if = "Vec::is_empty", default)]
202    pub raw_transaction: Vec<u8>,
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub effects: Option<SuiTransactionBlockEffects>,
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub events: Option<SuiTransactionBlockEvents>,
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub object_changes: Option<Vec<ObjectChange>>,
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub balance_changes: Option<Vec<BalanceChange>>,
211    #[serde(default, skip_serializing_if = "Option::is_none")]
212    #[serde_as(as = "Option<BigInt<u64>>")]
213    pub timestamp_ms: Option<u64>,
214    #[serde(default, skip_serializing_if = "Option::is_none")]
215    pub confirmed_local_execution: Option<bool>,
216    /// The checkpoint number when this transaction was included and hence finalized.
217    /// This is only returned in the read api, not in the transaction execution api.
218    #[serde_as(as = "Option<BigInt<u64>>")]
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub checkpoint: Option<Version>,
221    #[serde(skip_serializing_if = "Vec::is_empty", default)]
222    pub errors: Vec<String>,
223    #[serde(skip_serializing_if = "Vec::is_empty", default)]
224    pub raw_effects: Vec<u8>,
225}
226
227impl SuiTransactionBlockResponse {
228    pub fn new(digest: Digest) -> Self {
229        Self {
230            digest,
231            ..Default::default()
232        }
233    }
234
235    pub fn status_ok(&self) -> Option<bool> {
236        self.effects.as_ref().map(|e| e.status().is_ok())
237    }
238
239    /// Transaction effects as a standard Sui type.
240    ///
241    /// Deserialized from the [`Self::raw_effects`] BCS bytes. If those bytes are empty, the result
242    /// is `None`.
243    pub fn sui_effects(&self) -> Result<Option<sui_sdk_types::TransactionEffects>, ToEffectsError> {
244        self.raw_effects
245            .len()
246            .ge(&0)
247            .then_some(bcs::from_bytes(&self.raw_effects))
248            .transpose()
249            .map_err(From::from)
250            .map_err(ToEffectsError::Generic)
251    }
252
253    pub fn get_transaction(
254        &self,
255    ) -> Result<&SuiTransactionBlock, SuiTransactionBlockResponseError> {
256        self.transaction
257            .as_ref()
258            .ok_or(SuiTransactionBlockResponseError::MissingTransaction)
259    }
260
261    pub fn get_effects(
262        &self,
263    ) -> Result<&SuiTransactionBlockEffectsV1, SuiTransactionBlockResponseError> {
264        let SuiTransactionBlockEffects::V1(effects) = self
265            .effects
266            .as_ref()
267            .ok_or(SuiTransactionBlockResponseError::MissingEffects)?;
268        Ok(effects)
269    }
270
271    pub fn get_events(
272        &self,
273    ) -> Result<&SuiTransactionBlockEvents, SuiTransactionBlockResponseError> {
274        self.events
275            .as_ref()
276            .ok_or(SuiTransactionBlockResponseError::MissingEvents)
277    }
278
279    pub fn get_object_changes(
280        &self,
281    ) -> Result<&Vec<ObjectChange>, SuiTransactionBlockResponseError> {
282        self.object_changes
283            .as_ref()
284            .ok_or(SuiTransactionBlockResponseError::MissingObjectChanges)
285    }
286
287    pub fn get_balance_changes(
288        &self,
289    ) -> Result<&Vec<BalanceChange>, SuiTransactionBlockResponseError> {
290        self.balance_changes
291            .as_ref()
292            .ok_or(SuiTransactionBlockResponseError::MissingBalanceChanges)
293    }
294
295    pub fn try_check_execution_status(&self) -> Result<(), SuiTransactionBlockResponseError> {
296        if let Some(SuiTransactionBlockEffects::V1(effects)) = &self.effects {
297            if let SuiExecutionStatus::Failure { error } = &effects.status {
298                return Err(SuiTransactionBlockResponseError::ExecutionFailure(
299                    error.clone(),
300                ));
301            }
302        }
303        Ok(())
304    }
305
306    pub fn check_execution_status(&self) -> Result<(), SuiTransactionBlockResponseError> {
307        let Some(SuiTransactionBlockEffects::V1(effects)) = &self.effects else {
308            return Err(SuiTransactionBlockResponseError::MissingEffects);
309        };
310        if let SuiExecutionStatus::Failure { error } = &effects.status {
311            return Err(SuiTransactionBlockResponseError::ExecutionFailure(
312                error.clone(),
313            ));
314        }
315        Ok(())
316    }
317
318    pub fn published_package_id(&self) -> Result<Address, SuiTransactionBlockResponseError> {
319        for change in self.get_object_changes()? {
320            if let ObjectChange::Published { package_id, .. } = change {
321                return Ok(*package_id);
322            }
323        }
324        Err(SuiTransactionBlockResponseError::NoPublishedPackage)
325    }
326
327    pub fn into_object_changes(
328        self,
329    ) -> Result<Vec<ObjectChange>, SuiTransactionBlockResponseError> {
330        let Self { object_changes, .. } = self;
331        object_changes.ok_or(SuiTransactionBlockResponseError::MissingObjectChanges)
332    }
333}
334
335#[derive(thiserror::Error, Debug)]
336#[non_exhaustive]
337pub enum ToEffectsError {
338    #[error("Generic: {0:#}")]
339    Generic(Box<dyn StdError + Send + Sync + 'static>),
340}
341
342#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
343pub enum SuiTransactionBlockResponseError {
344    #[error("No transaction in response")]
345    MissingTransaction,
346    #[error("No effects in response")]
347    MissingEffects,
348    #[error("No events in response")]
349    MissingEvents,
350    #[error("No object changes in response")]
351    MissingObjectChanges,
352    #[error("No balance changes in response")]
353    MissingBalanceChanges,
354    #[error("Failed to execute transaction block: {0}")]
355    ExecutionFailure(String),
356    #[error("No 'Published' object change")]
357    NoPublishedPackage,
358}
359
360/// We are specifically ignoring events for now until events become more stable.
361impl PartialEq for SuiTransactionBlockResponse {
362    fn eq(&self, other: &Self) -> bool {
363        self.transaction == other.transaction
364            && self.effects == other.effects
365            && self.timestamp_ms == other.timestamp_ms
366            && self.confirmed_local_execution == other.confirmed_local_execution
367            && self.checkpoint == other.checkpoint
368    }
369}
370
371impl Display for SuiTransactionBlockResponse {
372    fn fmt(&self, writer: &mut Formatter<'_>) -> fmt::Result {
373        writeln!(writer, "Transaction Digest: {}", &self.digest)?;
374
375        if let Some(t) = &self.transaction {
376            writeln!(writer, "{}", t)?;
377        }
378
379        if let Some(e) = &self.effects {
380            writeln!(writer, "{}", e)?;
381        }
382
383        if let Some(e) = &self.events {
384            writeln!(writer, "{}", e)?;
385        }
386
387        if let Some(object_changes) = &self.object_changes {
388            let mut builder = TableBuilder::default();
389            let (
390                mut created,
391                mut deleted,
392                mut mutated,
393                mut published,
394                mut transferred,
395                mut wrapped,
396            ) = (vec![], vec![], vec![], vec![], vec![], vec![]);
397
398            for obj in object_changes {
399                match obj {
400                    ObjectChange::Created { .. } => created.push(obj),
401                    ObjectChange::Deleted { .. } => deleted.push(obj),
402                    ObjectChange::Mutated { .. } => mutated.push(obj),
403                    ObjectChange::Published { .. } => published.push(obj),
404                    ObjectChange::Transferred { .. } => transferred.push(obj),
405                    ObjectChange::Wrapped { .. } => wrapped.push(obj),
406                };
407            }
408
409            write_obj_changes(created, "Created", &mut builder)?;
410            write_obj_changes(deleted, "Deleted", &mut builder)?;
411            write_obj_changes(mutated, "Mutated", &mut builder)?;
412            write_obj_changes(published, "Published", &mut builder)?;
413            write_obj_changes(transferred, "Transferred", &mut builder)?;
414            write_obj_changes(wrapped, "Wrapped", &mut builder)?;
415
416            let mut table = builder.build();
417            table.with(TablePanel::header("Object Changes"));
418            table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
419                1,
420                TableStyle::modern().get_horizontal(),
421            )]));
422            writeln!(writer, "{}", table)?;
423        }
424
425        if let Some(balance_changes) = &self.balance_changes {
426            let mut builder = TableBuilder::default();
427            for balance in balance_changes {
428                builder.push_record(vec![format!("{}", balance)]);
429            }
430            let mut table = builder.build();
431            table.with(TablePanel::header("Balance Changes"));
432            table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
433                1,
434                TableStyle::modern().get_horizontal(),
435            )]));
436            writeln!(writer, "{}", table)?;
437        }
438        Ok(())
439    }
440}
441
442fn write_obj_changes<T: Display>(
443    values: Vec<T>,
444    output_string: &str,
445    builder: &mut TableBuilder,
446) -> std::fmt::Result {
447    if !values.is_empty() {
448        builder.push_record(vec![format!("{} Objects: ", output_string)]);
449        for obj in values {
450            builder.push_record(vec![format!("{}", obj)]);
451        }
452    }
453    Ok(())
454}
455
456pub fn get_new_package_obj_from_response(
457    response: &SuiTransactionBlockResponse,
458) -> Option<(Address, Version, Digest)> {
459    response.object_changes.as_ref().and_then(|changes| {
460        changes
461            .iter()
462            .find(|change| matches!(change, ObjectChange::Published { .. }))
463            .map(|change| change.object_ref())
464    })
465}
466
467pub fn get_new_package_upgrade_cap_from_response(
468    response: &SuiTransactionBlockResponse,
469) -> Option<(Address, Version, Digest)> {
470    response.object_changes.as_ref().and_then(|changes| {
471        changes
472            .iter()
473            .find(|change| {
474                matches!(change, ObjectChange::Created {
475                    owner: Owner::AddressOwner(_),
476                    object_type: StructTag {
477                        address: Address::TWO,
478                        module,
479                        name,
480                        ..
481                    },
482                    ..
483                } if module.as_str() == "package" && name.as_str() == "UpgradeCap")
484            })
485            .map(|change| change.object_ref())
486    })
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
490#[serde(rename = "TransactionBlockKind", tag = "kind")]
491#[non_exhaustive]
492pub enum SuiTransactionBlockKind {
493    /// A system transaction that will update epoch information on-chain.
494    ChangeEpoch(SuiChangeEpoch),
495    /// A system transaction used for initializing the initial state of the chain.
496    Genesis(SuiGenesisTransaction),
497    /// A system transaction marking the start of a series of transactions scheduled as part of a
498    /// checkpoint
499    ConsensusCommitPrologue(SuiConsensusCommitPrologue),
500    /// A series of transactions where the results of one transaction can be used in future
501    /// transactions
502    ProgrammableTransaction(SuiProgrammableTransactionBlock),
503    /// A transaction which updates global authenticator state
504    AuthenticatorStateUpdate(SuiAuthenticatorStateUpdate),
505    /// A transaction which updates global randomness state
506    RandomnessStateUpdate(SuiRandomnessStateUpdate),
507    /// The transaction which occurs only at the end of the epoch
508    EndOfEpochTransaction(SuiEndOfEpochTransaction),
509    ConsensusCommitPrologueV2(SuiConsensusCommitPrologueV2),
510    ConsensusCommitPrologueV3(SuiConsensusCommitPrologueV3),
511    // .. more transaction types go here
512}
513
514impl Display for SuiTransactionBlockKind {
515    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
516        let mut writer = String::new();
517        match &self {
518            Self::ChangeEpoch(e) => {
519                writeln!(writer, "Transaction Kind: Epoch Change")?;
520                writeln!(writer, "New epoch ID: {}", e.epoch)?;
521                writeln!(writer, "Storage gas reward: {}", e.storage_charge)?;
522                writeln!(writer, "Computation gas reward: {}", e.computation_charge)?;
523                writeln!(writer, "Storage rebate: {}", e.storage_rebate)?;
524                writeln!(writer, "Timestamp: {}", e.epoch_start_timestamp_ms)?;
525            }
526            Self::Genesis(_) => {
527                writeln!(writer, "Transaction Kind: Genesis Transaction")?;
528            }
529            Self::ConsensusCommitPrologue(p) => {
530                writeln!(writer, "Transaction Kind: Consensus Commit Prologue")?;
531                writeln!(
532                    writer,
533                    "Epoch: {}, Round: {}, Timestamp: {}",
534                    p.epoch, p.round, p.commit_timestamp_ms
535                )?;
536            }
537            Self::ConsensusCommitPrologueV2(p) => {
538                writeln!(writer, "Transaction Kind: Consensus Commit Prologue V2")?;
539                writeln!(
540                    writer,
541                    "Epoch: {}, Round: {}, Timestamp: {}, Digest: {}",
542                    p.epoch, p.round, p.commit_timestamp_ms, p.consensus_commit_digest
543                )?;
544            }
545            Self::ConsensusCommitPrologueV3(p) => {
546                writeln!(writer, "Transaction Kind: Consensus Commit Prologue V3")?;
547                writeln!(
548                    writer,
549                    "Epoch: {}, Round: {}, SubDagIndex: {:?}, Timestamp: {}, Digest: {}",
550                    p.epoch,
551                    p.round,
552                    p.sub_dag_index,
553                    p.commit_timestamp_ms,
554                    p.consensus_commit_digest
555                )?;
556            }
557            Self::ProgrammableTransaction(p) => {
558                write!(writer, "Transaction Kind: Programmable")?;
559                write!(writer, "{}", super::displays::Pretty(p))?;
560            }
561            Self::AuthenticatorStateUpdate(_) => {
562                writeln!(writer, "Transaction Kind: Authenticator State Update")?;
563            }
564            Self::RandomnessStateUpdate(_) => {
565                writeln!(writer, "Transaction Kind: Randomness State Update")?;
566            }
567            Self::EndOfEpochTransaction(_) => {
568                writeln!(writer, "Transaction Kind: End of Epoch Transaction")?;
569            }
570        }
571        write!(f, "{}", writer)
572    }
573}
574
575impl SuiTransactionBlockKind {
576    pub fn transaction_count(&self) -> usize {
577        match self {
578            Self::ProgrammableTransaction(p) => p.commands.len(),
579            _ => 1,
580        }
581    }
582
583    pub fn name(&self) -> &'static str {
584        match self {
585            Self::ChangeEpoch(_) => "ChangeEpoch",
586            Self::Genesis(_) => "Genesis",
587            Self::ConsensusCommitPrologue(_) => "ConsensusCommitPrologue",
588            Self::ConsensusCommitPrologueV2(_) => "ConsensusCommitPrologueV2",
589            Self::ConsensusCommitPrologueV3(_) => "ConsensusCommitPrologueV3",
590            Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
591            Self::AuthenticatorStateUpdate(_) => "AuthenticatorStateUpdate",
592            Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
593            Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
594        }
595    }
596}
597
598#[serde_as]
599#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
600pub struct SuiChangeEpoch {
601    #[serde_as(as = "BigInt<u64>")]
602    pub epoch: EpochId,
603    #[serde_as(as = "BigInt<u64>")]
604    pub storage_charge: u64,
605    #[serde_as(as = "BigInt<u64>")]
606    pub computation_charge: u64,
607    #[serde_as(as = "BigInt<u64>")]
608    pub storage_rebate: u64,
609    #[serde_as(as = "BigInt<u64>")]
610    pub epoch_start_timestamp_ms: u64,
611}
612
613#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
614#[enum_dispatch(SuiTransactionBlockEffectsAPI)]
615#[serde(
616    rename = "TransactionBlockEffects",
617    rename_all = "camelCase",
618    tag = "messageVersion"
619)]
620pub enum SuiTransactionBlockEffects {
621    V1(SuiTransactionBlockEffectsV1),
622}
623
624#[enum_dispatch]
625pub trait SuiTransactionBlockEffectsAPI {
626    fn status(&self) -> &SuiExecutionStatus;
627    fn into_status(self) -> SuiExecutionStatus;
628    fn shared_objects(&self) -> &[SuiObjectRef];
629    fn created(&self) -> &[OwnedObjectRef];
630    fn mutated(&self) -> &[OwnedObjectRef];
631    fn unwrapped(&self) -> &[OwnedObjectRef];
632    fn deleted(&self) -> &[SuiObjectRef];
633    fn unwrapped_then_deleted(&self) -> &[SuiObjectRef];
634    fn wrapped(&self) -> &[SuiObjectRef];
635    fn gas_object(&self) -> &OwnedObjectRef;
636    fn events_digest(&self) -> Option<&Digest>;
637    fn dependencies(&self) -> &[Digest];
638    fn executed_epoch(&self) -> EpochId;
639    fn transaction_digest(&self) -> &Digest;
640    fn gas_cost_summary(&self) -> &GasCostSummary;
641
642    /// Return an iterator of mutated objects, but excluding the gas object.
643    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef>;
644    fn modified_at_versions(&self) -> Vec<(Address, Version)>;
645    fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>;
646    fn all_deleted_objects(&self) -> Vec<(&SuiObjectRef, DeleteKind)>;
647}
648
649/// Originally from `sui_types::storage`.
650#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
651pub enum WriteKind {
652    /// The object was in storage already but has been modified
653    Mutate,
654    /// The object was created in this transaction
655    Create,
656    /// The object was previously wrapped in another object, but has been restored to storage
657    Unwrap,
658}
659
660/// Originally from `sui_types::storage`.
661#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
662pub enum DeleteKind {
663    /// An object is provided in the call input, and gets deleted.
664    Normal,
665    /// An object is not provided in the call input, but gets unwrapped
666    /// from another object, and then gets deleted.
667    UnwrapThenDelete,
668    /// An object is provided in the call input, and gets wrapped into another object.
669    Wrap,
670}
671
672#[serde_as]
673#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
674#[serde(
675    rename = "TransactionBlockEffectsModifiedAtVersions",
676    rename_all = "camelCase"
677)]
678pub struct SuiTransactionBlockEffectsModifiedAtVersions {
679    object_id: Address,
680    #[serde_as(as = "BigInt<u64>")]
681    sequence_number: Version,
682}
683
684/// The response from processing a transaction or a certified transaction
685#[serde_as]
686#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
687#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")]
688pub struct SuiTransactionBlockEffectsV1 {
689    /// The status of the execution
690    pub status: SuiExecutionStatus,
691    /// The epoch when this transaction was executed.
692    #[serde_as(as = "BigInt<u64>")]
693    pub executed_epoch: EpochId,
694    #[serde_as(as = "serde_with::FromInto<crate::serde::GasCostSummaryJson>")]
695    pub gas_used: GasCostSummary,
696    /// The version that every modified (mutated or deleted) object had before it was modified by
697    /// this transaction.
698    #[serde(default, skip_serializing_if = "Vec::is_empty")]
699    pub modified_at_versions: Vec<SuiTransactionBlockEffectsModifiedAtVersions>,
700    /// The object references of the shared objects used in this transaction. Empty if no shared objects were used.
701    #[serde(default, skip_serializing_if = "Vec::is_empty")]
702    pub shared_objects: Vec<SuiObjectRef>,
703    /// The transaction digest
704    pub transaction_digest: Digest,
705    /// ObjectRef and owner of new objects created.
706    #[serde(default, skip_serializing_if = "Vec::is_empty")]
707    pub created: Vec<OwnedObjectRef>,
708    /// ObjectRef and owner of mutated objects, including gas object.
709    #[serde(default, skip_serializing_if = "Vec::is_empty")]
710    pub mutated: Vec<OwnedObjectRef>,
711    /// ObjectRef and owner of objects that are unwrapped in this transaction.
712    /// Unwrapped objects are objects that were wrapped into other objects in the past,
713    /// and just got extracted out.
714    #[serde(default, skip_serializing_if = "Vec::is_empty")]
715    pub unwrapped: Vec<OwnedObjectRef>,
716    /// Object Refs of objects now deleted (the old refs).
717    #[serde(default, skip_serializing_if = "Vec::is_empty")]
718    pub deleted: Vec<SuiObjectRef>,
719    /// Object refs of objects previously wrapped in other objects but now deleted.
720    #[serde(default, skip_serializing_if = "Vec::is_empty")]
721    pub unwrapped_then_deleted: Vec<SuiObjectRef>,
722    /// Object refs of objects now wrapped in other objects.
723    #[serde(default, skip_serializing_if = "Vec::is_empty")]
724    pub wrapped: Vec<SuiObjectRef>,
725    /// The updated gas object reference. Have a dedicated field for convenient access.
726    /// It's also included in mutated.
727    pub gas_object: OwnedObjectRef,
728    /// The digest of the events emitted during execution,
729    /// can be None if the transaction does not emit any event.
730    #[serde(skip_serializing_if = "Option::is_none")]
731    pub events_digest: Option<Digest>,
732    /// The set of transaction digests this transaction depends on.
733    #[serde(default, skip_serializing_if = "Vec::is_empty")]
734    pub dependencies: Vec<Digest>,
735}
736
737impl SuiTransactionBlockEffectsAPI for SuiTransactionBlockEffectsV1 {
738    fn status(&self) -> &SuiExecutionStatus {
739        &self.status
740    }
741    fn into_status(self) -> SuiExecutionStatus {
742        self.status
743    }
744    fn shared_objects(&self) -> &[SuiObjectRef] {
745        &self.shared_objects
746    }
747    fn created(&self) -> &[OwnedObjectRef] {
748        &self.created
749    }
750    fn mutated(&self) -> &[OwnedObjectRef] {
751        &self.mutated
752    }
753    fn unwrapped(&self) -> &[OwnedObjectRef] {
754        &self.unwrapped
755    }
756    fn deleted(&self) -> &[SuiObjectRef] {
757        &self.deleted
758    }
759    fn unwrapped_then_deleted(&self) -> &[SuiObjectRef] {
760        &self.unwrapped_then_deleted
761    }
762    fn wrapped(&self) -> &[SuiObjectRef] {
763        &self.wrapped
764    }
765    fn gas_object(&self) -> &OwnedObjectRef {
766        &self.gas_object
767    }
768    fn events_digest(&self) -> Option<&Digest> {
769        self.events_digest.as_ref()
770    }
771    fn dependencies(&self) -> &[Digest] {
772        &self.dependencies
773    }
774
775    fn executed_epoch(&self) -> EpochId {
776        self.executed_epoch
777    }
778
779    fn transaction_digest(&self) -> &Digest {
780        &self.transaction_digest
781    }
782
783    fn gas_cost_summary(&self) -> &GasCostSummary {
784        &self.gas_used
785    }
786
787    fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef> {
788        self.mutated
789            .iter()
790            .filter(|o| *o != &self.gas_object)
791            .cloned()
792            .collect()
793    }
794
795    fn modified_at_versions(&self) -> Vec<(Address, Version)> {
796        self.modified_at_versions
797            .iter()
798            .map(|v| (v.object_id, v.sequence_number))
799            .collect::<Vec<_>>()
800    }
801
802    fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> {
803        self.mutated
804            .iter()
805            .map(|owner_ref| (owner_ref, WriteKind::Mutate))
806            .chain(
807                self.created
808                    .iter()
809                    .map(|owner_ref| (owner_ref, WriteKind::Create)),
810            )
811            .chain(
812                self.unwrapped
813                    .iter()
814                    .map(|owner_ref| (owner_ref, WriteKind::Unwrap)),
815            )
816            .collect()
817    }
818
819    fn all_deleted_objects(&self) -> Vec<(&SuiObjectRef, DeleteKind)> {
820        self.deleted
821            .iter()
822            .map(|r| (r, DeleteKind::Normal))
823            .chain(
824                self.unwrapped_then_deleted
825                    .iter()
826                    .map(|r| (r, DeleteKind::UnwrapThenDelete)),
827            )
828            .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap)))
829            .collect()
830    }
831}
832
833fn owned_objref_string(obj: &OwnedObjectRef) -> String {
834    format!(
835        " ┌──\n │ ID: {} \n │ Owner: {:?} \n │ Version: {} \n │ Digest: {}\n └──",
836        obj.reference.object_id, obj.owner, obj.reference.version, obj.reference.digest
837    )
838}
839
840fn objref_string(obj: &SuiObjectRef) -> String {
841    format!(
842        " ┌──\n │ ID: {} \n │ Version: {} \n │ Digest: {}\n └──",
843        obj.object_id, obj.version, obj.digest
844    )
845}
846
847impl Display for SuiTransactionBlockEffects {
848    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
849        let mut builder = TableBuilder::default();
850
851        builder.push_record(vec![format!("Digest: {}", self.transaction_digest())]);
852        builder.push_record(vec![format!("Status: {:?}", self.status())]);
853        builder.push_record(vec![format!("Executed Epoch: {}", self.executed_epoch())]);
854
855        if !self.created().is_empty() {
856            builder.push_record(vec![format!("\nCreated Objects: ")]);
857
858            for oref in self.created() {
859                builder.push_record(vec![owned_objref_string(oref)]);
860            }
861        }
862
863        if !self.mutated().is_empty() {
864            builder.push_record(vec![format!("Mutated Objects: ")]);
865            for oref in self.mutated() {
866                builder.push_record(vec![owned_objref_string(oref)]);
867            }
868        }
869
870        if !self.shared_objects().is_empty() {
871            builder.push_record(vec![format!("Shared Objects: ")]);
872            for oref in self.shared_objects() {
873                builder.push_record(vec![objref_string(oref)]);
874            }
875        }
876
877        if !self.deleted().is_empty() {
878            builder.push_record(vec![format!("Deleted Objects: ")]);
879
880            for oref in self.deleted() {
881                builder.push_record(vec![objref_string(oref)]);
882            }
883        }
884
885        if !self.wrapped().is_empty() {
886            builder.push_record(vec![format!("Wrapped Objects: ")]);
887
888            for oref in self.wrapped() {
889                builder.push_record(vec![objref_string(oref)]);
890            }
891        }
892
893        if !self.unwrapped().is_empty() {
894            builder.push_record(vec![format!("Unwrapped Objects: ")]);
895            for oref in self.unwrapped() {
896                builder.push_record(vec![owned_objref_string(oref)]);
897            }
898        }
899
900        builder.push_record(vec![format!(
901            "Gas Object: \n{}",
902            owned_objref_string(self.gas_object())
903        )]);
904
905        let gas_cost_summary = self.gas_cost_summary();
906        builder.push_record(vec![format!(
907            "Gas Cost Summary:\n   \
908             Storage Cost: {} MIST\n   \
909             Computation Cost: {} MIST\n   \
910             Storage Rebate: {} MIST\n   \
911             Non-refundable Storage Fee: {} MIST",
912            gas_cost_summary.storage_cost,
913            gas_cost_summary.computation_cost,
914            gas_cost_summary.storage_rebate,
915            gas_cost_summary.non_refundable_storage_fee,
916        )]);
917
918        let dependencies = self.dependencies();
919        if !dependencies.is_empty() {
920            builder.push_record(vec![format!("\nTransaction Dependencies:")]);
921            for dependency in dependencies {
922                builder.push_record(vec![format!("   {}", dependency)]);
923            }
924        }
925
926        let mut table = builder.build();
927        table.with(TablePanel::header("Transaction Effects"));
928        table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
929            1,
930            TableStyle::modern().get_horizontal(),
931        )]));
932        write!(f, "{}", table)
933    }
934}
935
936#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
937#[serde(rename_all = "camelCase")]
938#[non_exhaustive]
939pub struct DryRunTransactionBlockResponse {
940    pub effects: SuiTransactionBlockEffects,
941    pub events: SuiTransactionBlockEvents,
942    pub object_changes: Vec<ObjectChange>,
943    pub balance_changes: Vec<BalanceChange>,
944    pub input: SuiTransactionBlockData,
945    #[serde(default)]
946    pub execution_error_source: Option<String>,
947}
948
949#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
950#[serde(rename = "TransactionBlockEvents", transparent)]
951pub struct SuiTransactionBlockEvents {
952    pub data: Vec<SuiEvent>,
953}
954
955impl Display for SuiTransactionBlockEvents {
956    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
957        if self.data.is_empty() {
958            writeln!(f, "╭─────────────────────────────╮")?;
959            writeln!(f, "│ No transaction block events │")?;
960            writeln!(f, "╰─────────────────────────────╯")
961        } else {
962            let mut builder = TableBuilder::default();
963
964            for event in &self.data {
965                builder.push_record(vec![format!("{}", event)]);
966            }
967
968            let mut table = builder.build();
969            table.with(TablePanel::header("Transaction Block Events"));
970            table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
971                1,
972                TableStyle::modern().get_horizontal(),
973            )]));
974            write!(f, "{}", table)
975        }
976    }
977}
978
979/// Additional rguments supplied to dev inspect beyond what is allowed in today's API.
980#[derive(Debug, Default, Clone, Serialize, Deserialize)]
981#[serde(rename = "DevInspectArgs", rename_all = "camelCase")]
982pub struct DevInspectArgs {
983    /// The sponsor of the gas for the transaction, might be different from the sender.
984    pub gas_sponsor: Option<Address>,
985    /// The gas budget for the transaction.
986    pub gas_budget: Option<BigInt<u64>>,
987    /// The gas objects used to pay for the transaction.
988    pub gas_objects: Option<Vec<(Address, Version, Digest)>>,
989    /// Whether to skip transaction checks for the transaction.
990    pub skip_checks: Option<bool>,
991    /// Whether to return the raw transaction data and effects.
992    pub show_raw_txn_data_and_effects: Option<bool>,
993}
994
995/// The response from processing a dev inspect transaction
996#[derive(Debug, Clone, Serialize, Deserialize)]
997#[serde(rename = "DevInspectResults", rename_all = "camelCase")]
998pub struct DevInspectResults {
999    /// Summary of effects that likely would be generated if the transaction is actually run.
1000    /// Note however, that not all dev-inspect transactions are actually usable as transactions so
1001    /// it might not be possible actually generate these effects from a normal transaction.
1002    pub effects: SuiTransactionBlockEffects,
1003    /// Events that likely would be generated if the transaction is actually run.
1004    pub events: SuiTransactionBlockEvents,
1005    /// Execution results (including return values) from executing the transactions
1006    #[serde(skip_serializing_if = "Option::is_none")]
1007    pub results: Option<Vec<SuiExecutionResult>>,
1008    /// Execution error from executing the transactions
1009    #[serde(skip_serializing_if = "Option::is_none")]
1010    pub error: Option<String>,
1011    /// The raw transaction data that was dev inspected.
1012    #[serde(skip_serializing_if = "Vec::is_empty", default)]
1013    pub raw_txn_data: Vec<u8>,
1014    /// The raw effects of the transaction that was dev inspected.
1015    #[serde(skip_serializing_if = "Vec::is_empty", default)]
1016    pub raw_effects: Vec<u8>,
1017}
1018
1019#[derive(Debug, Clone, Serialize, Deserialize)]
1020#[serde(rename = "SuiExecutionResult", rename_all = "camelCase")]
1021pub struct SuiExecutionResult {
1022    /// The value of any arguments that were mutably borrowed.
1023    /// Non-mut borrowed values are not included
1024    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1025    pub mutable_reference_outputs: Vec<(/* argument */ SuiArgument, Vec<u8>, SuiTypeTag)>,
1026    /// The return values from the transaction
1027    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1028    pub return_values: Vec<(Vec<u8>, SuiTypeTag)>,
1029}
1030
1031#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
1032pub enum SuiTransactionBlockBuilderMode {
1033    /// Regular Sui Transactions that are committed on chain
1034    Commit,
1035    /// Simulated transaction that allows calling any Move function with
1036    /// arbitrary values.
1037    DevInspect,
1038}
1039
1040#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
1041#[serde(rename = "ExecutionStatus", rename_all = "camelCase", tag = "status")]
1042pub enum SuiExecutionStatus {
1043    // Gas used in the success case.
1044    Success,
1045    // Gas used in the failed case, and the error.
1046    Failure { error: String },
1047}
1048
1049impl Display for SuiExecutionStatus {
1050    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1051        match self {
1052            Self::Success => write!(f, "success"),
1053            Self::Failure { error } => write!(f, "failure due to {error}"),
1054        }
1055    }
1056}
1057
1058impl SuiExecutionStatus {
1059    const MOVE_ABORT_PATTERN: &str = r#"MoveAbort\(MoveLocation \{ module: ModuleId \{ address: ([[:alnum:]]+), name: Identifier\("([[:word:]]+)"\) \}, function: (\d+), instruction: (\d+), function_name: Some\("([[:word:]]+)"\) \}, (\d+)\)"#;
1060
1061    pub fn is_ok(&self) -> bool {
1062        matches!(self, SuiExecutionStatus::Success)
1063    }
1064
1065    pub fn is_err(&self) -> bool {
1066        matches!(self, SuiExecutionStatus::Failure { .. })
1067    }
1068
1069    /// If the error is a [`MoveAbort`], try extracting it.
1070    ///
1071    /// [`MoveAbort`]: sui_sdk_types::ExecutionError::MoveAbort
1072    pub fn as_move_abort(&self) -> Option<(MoveLocation, u64)> {
1073        let Self::Failure { error } = self else {
1074            return None;
1075        };
1076        let re = regex::Regex::new(Self::MOVE_ABORT_PATTERN).expect("Tested below");
1077
1078        let matches = re.captures(error)?;
1079
1080        // NOTE: prepend with "0x" so that parsing to an address works
1081        let address = "0x".to_owned() + matches.get(1)?.as_str();
1082        Some((
1083            MoveLocation {
1084                package: address.parse().ok()?,
1085                module: matches.get(2)?.as_str().parse().ok()?,
1086                function: matches.get(3)?.as_str().parse().ok()?,
1087                instruction: matches.get(4)?.as_str().parse().ok()?,
1088                function_name: Some(matches.get(5)?.as_str().parse().ok()?),
1089            },
1090            matches.get(6)?.as_str().parse().ok()?,
1091        ))
1092    }
1093}
1094
1095#[serde_as]
1096#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
1097#[serde(rename = "GasData", rename_all = "camelCase")]
1098pub struct SuiGasData {
1099    pub payment: Vec<SuiObjectRef>,
1100    pub owner: Address,
1101    #[serde_as(as = "BigInt<u64>")]
1102    pub price: u64,
1103    #[serde_as(as = "BigInt<u64>")]
1104    pub budget: u64,
1105}
1106
1107impl Display for SuiGasData {
1108    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1109        writeln!(f, "Gas Owner: {}", self.owner)?;
1110        writeln!(f, "Gas Budget: {} MIST", self.budget)?;
1111        writeln!(f, "Gas Price: {} MIST", self.price)?;
1112        writeln!(f, "Gas Payment:")?;
1113        for payment in &self.payment {
1114            write!(f, "{} ", objref_string(payment))?;
1115        }
1116        writeln!(f)
1117    }
1118}
1119
1120#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
1121#[enum_dispatch(SuiTransactionBlockDataAPI)]
1122#[serde(
1123    rename = "TransactionBlockData",
1124    rename_all = "camelCase",
1125    tag = "messageVersion"
1126)]
1127pub enum SuiTransactionBlockData {
1128    V1(SuiTransactionBlockDataV1),
1129}
1130
1131#[enum_dispatch]
1132pub trait SuiTransactionBlockDataAPI {
1133    fn transaction(&self) -> &SuiTransactionBlockKind;
1134    fn sender(&self) -> &Address;
1135    fn gas_data(&self) -> &SuiGasData;
1136}
1137
1138#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
1139#[serde(rename = "TransactionBlockDataV1", rename_all = "camelCase")]
1140pub struct SuiTransactionBlockDataV1 {
1141    pub transaction: SuiTransactionBlockKind,
1142    pub sender: Address,
1143    pub gas_data: SuiGasData,
1144}
1145
1146impl SuiTransactionBlockDataAPI for SuiTransactionBlockDataV1 {
1147    fn transaction(&self) -> &SuiTransactionBlockKind {
1148        &self.transaction
1149    }
1150    fn sender(&self) -> &Address {
1151        &self.sender
1152    }
1153    fn gas_data(&self) -> &SuiGasData {
1154        &self.gas_data
1155    }
1156}
1157
1158impl SuiTransactionBlockData {
1159    pub fn move_calls(&self) -> Vec<&SuiProgrammableMoveCall> {
1160        match self {
1161            Self::V1(data) => match &data.transaction {
1162                SuiTransactionBlockKind::ProgrammableTransaction(pt) => pt
1163                    .commands
1164                    .iter()
1165                    .filter_map(|command| match command {
1166                        SuiCommand::MoveCall(c) => Some(&**c),
1167                        _ => None,
1168                    })
1169                    .collect(),
1170                _ => vec![],
1171            },
1172        }
1173    }
1174}
1175
1176impl Display for SuiTransactionBlockData {
1177    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1178        match self {
1179            Self::V1(data) => {
1180                writeln!(f, "Sender: {}", data.sender)?;
1181                writeln!(f, "{}", self.gas_data())?;
1182                writeln!(f, "{}", data.transaction)
1183            }
1184        }
1185    }
1186}
1187
1188#[serde_as]
1189#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
1190#[serde(rename = "TransactionBlock", rename_all = "camelCase")]
1191pub struct SuiTransactionBlock {
1192    pub data: SuiTransactionBlockData,
1193    #[serde_as(as = "Vec<IfIsHumanReadable<Base64UserSignature>>")]
1194    pub tx_signatures: Vec<UserSignature>,
1195}
1196
1197struct Base64UserSignature;
1198
1199impl<'de> DeserializeAs<'de, UserSignature> for Base64UserSignature {
1200    fn deserialize_as<D>(deserializer: D) -> Result<UserSignature, D::Error>
1201    where
1202        D: serde::Deserializer<'de>,
1203    {
1204        let base64 = Box::<str>::deserialize(deserializer)?;
1205        UserSignature::from_base64(&base64).map_err(serde::de::Error::custom)
1206    }
1207}
1208
1209impl SerializeAs<UserSignature> for Base64UserSignature {
1210    fn serialize_as<S>(source: &UserSignature, serializer: S) -> Result<S::Ok, S::Error>
1211    where
1212        S: serde::Serializer,
1213    {
1214        let base64 = source.to_base64();
1215        base64.serialize(serializer)
1216    }
1217}
1218
1219impl Display for SuiTransactionBlock {
1220    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1221        let mut builder = TableBuilder::default();
1222
1223        builder.push_record(vec![format!("{}", self.data)]);
1224        builder.push_record(vec![format!("Signatures:")]);
1225        for tx_sig in &self.tx_signatures {
1226            builder.push_record(vec![format!("   {}\n", tx_sig.to_base64())]);
1227        }
1228
1229        let mut table = builder.build();
1230        table.with(TablePanel::header("Transaction Data"));
1231        table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1232            1,
1233            TableStyle::modern().get_horizontal(),
1234        )]));
1235        write!(f, "{}", table)
1236    }
1237}
1238
1239#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1240pub struct SuiGenesisTransaction {
1241    pub objects: Vec<Address>,
1242}
1243
1244#[serde_as]
1245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1246pub struct SuiConsensusCommitPrologue {
1247    #[serde_as(as = "BigInt<u64>")]
1248    pub epoch: u64,
1249    #[serde_as(as = "BigInt<u64>")]
1250    pub round: u64,
1251    #[serde_as(as = "BigInt<u64>")]
1252    pub commit_timestamp_ms: u64,
1253}
1254
1255#[serde_as]
1256#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1257pub struct SuiConsensusCommitPrologueV2 {
1258    #[serde_as(as = "BigInt<u64>")]
1259    pub epoch: u64,
1260    #[serde_as(as = "BigInt<u64>")]
1261    pub round: u64,
1262    #[serde_as(as = "BigInt<u64>")]
1263    pub commit_timestamp_ms: u64,
1264    pub consensus_commit_digest: Digest,
1265}
1266
1267#[serde_as]
1268#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1269pub struct SuiConsensusCommitPrologueV3 {
1270    #[serde_as(as = "BigInt<u64>")]
1271    pub epoch: u64,
1272    #[serde_as(as = "BigInt<u64>")]
1273    pub round: u64,
1274    #[serde_as(as = "Option<BigInt<u64>>")]
1275    pub sub_dag_index: Option<u64>,
1276    #[serde_as(as = "BigInt<u64>")]
1277    pub commit_timestamp_ms: u64,
1278    pub consensus_commit_digest: Digest,
1279    pub consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments,
1280}
1281
1282#[serde_as]
1283#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1284pub struct SuiAuthenticatorStateUpdate {
1285    #[serde_as(as = "BigInt<u64>")]
1286    pub epoch: u64,
1287    #[serde_as(as = "BigInt<u64>")]
1288    pub round: u64,
1289
1290    pub new_active_jwks: Vec<SuiActiveJwk>,
1291}
1292
1293#[serde_as]
1294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1295pub struct SuiRandomnessStateUpdate {
1296    #[serde_as(as = "BigInt<u64>")]
1297    pub epoch: u64,
1298
1299    #[serde_as(as = "BigInt<u64>")]
1300    pub randomness_round: u64,
1301    pub random_bytes: Vec<u8>,
1302}
1303
1304#[serde_as]
1305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1306pub struct SuiEndOfEpochTransaction {
1307    pub transactions: Vec<SuiEndOfEpochTransactionKind>,
1308}
1309
1310#[serde_as]
1311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1312#[non_exhaustive]
1313pub enum SuiEndOfEpochTransactionKind {
1314    ChangeEpoch(SuiChangeEpoch),
1315    AuthenticatorStateCreate,
1316    AuthenticatorStateExpire(SuiAuthenticatorStateExpire),
1317    RandomnessStateCreate,
1318    CoinDenyListStateCreate,
1319    BridgeStateCreate(Digest),
1320    BridgeCommitteeUpdate(Version),
1321}
1322
1323#[serde_as]
1324#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1325pub struct SuiAuthenticatorStateExpire {
1326    #[serde_as(as = "BigInt<u64>")]
1327    pub min_epoch: u64,
1328}
1329
1330#[serde_as]
1331#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1332pub struct SuiActiveJwk {
1333    pub jwk_id: SuiJwkId,
1334    pub jwk: SuiJWK,
1335
1336    #[serde_as(as = "BigInt<u64>")]
1337    pub epoch: u64,
1338}
1339
1340#[serde_as]
1341#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1342pub struct SuiJwkId {
1343    pub iss: String,
1344    pub kid: String,
1345}
1346
1347#[serde_as]
1348#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1349pub struct SuiJWK {
1350    pub kty: String,
1351    pub e: String,
1352    pub n: String,
1353    pub alg: String,
1354}
1355
1356#[serde_as]
1357#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
1358#[serde(rename = "InputObjectKind")]
1359pub enum SuiInputObjectKind {
1360    // A Move package, must be immutable.
1361    MovePackage(Address),
1362    // A Move object, either immutable, or owned mutable.
1363    ImmOrOwnedMoveObject(SuiObjectRef),
1364    // A Move object that's shared and mutable.
1365    SharedMoveObject {
1366        id: Address,
1367        #[serde_as(as = "BigInt<u64>")]
1368        initial_shared_version: Version,
1369        #[serde(default)]
1370        mutable: bool,
1371    },
1372}
1373
1374/// A series of commands where the results of one command can be used in future
1375/// commands
1376#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1377pub struct SuiProgrammableTransactionBlock {
1378    /// Input objects or primitive values
1379    pub inputs: Vec<SuiCallArg>,
1380    #[serde(rename = "transactions")]
1381    /// The transactions to be executed sequentially. A failure in any transaction will
1382    /// result in the failure of the entire programmable transaction block.
1383    pub commands: Vec<SuiCommand>,
1384}
1385
1386impl Display for SuiProgrammableTransactionBlock {
1387    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1388        let Self { inputs, commands } = self;
1389        writeln!(f, "Inputs: {inputs:?}")?;
1390        writeln!(f, "Commands: [")?;
1391        for c in commands {
1392            writeln!(f, "  {c},")?;
1393        }
1394        writeln!(f, "]")
1395    }
1396}
1397
1398/// A single transaction in a programmable transaction block.
1399#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1400#[serde(rename = "SuiTransaction")]
1401pub enum SuiCommand {
1402    /// A call to either an entry or a public Move function
1403    MoveCall(Box<SuiProgrammableMoveCall>),
1404    /// `(Vec<forall T:key+store. T>, address)`
1405    /// It sends n-objects to the specified address. These objects must have store
1406    /// (public transfer) and either the previous owner must be an address or the object must
1407    /// be newly created.
1408    TransferObjects(Vec<SuiArgument>, SuiArgument),
1409    /// `(&mut Coin<T>, Vec<u64>)` -> `Vec<Coin<T>>`
1410    /// It splits off some amounts into a new coins with those amounts
1411    SplitCoins(SuiArgument, Vec<SuiArgument>),
1412    /// `(&mut Coin<T>, Vec<Coin<T>>)`
1413    /// It merges n-coins into the first coin
1414    MergeCoins(SuiArgument, Vec<SuiArgument>),
1415    /// Publishes a Move package. It takes the package bytes and a list of the package's transitive
1416    /// dependencies to link against on-chain.
1417    Publish(Vec<Address>),
1418    /// Upgrades a Move package
1419    Upgrade(Vec<Address>, Address, SuiArgument),
1420    /// `forall T: Vec<T> -> vector<T>`
1421    /// Given n-values of the same type, it constructs a vector. For non objects or an empty vector,
1422    /// the type tag must be specified.
1423    MakeMoveVec(Option<String>, Vec<SuiArgument>),
1424}
1425
1426impl Display for SuiCommand {
1427    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1428        match self {
1429            Self::MoveCall(p) => {
1430                write!(f, "MoveCall({p})")
1431            }
1432            Self::MakeMoveVec(ty_opt, elems) => {
1433                write!(f, "MakeMoveVec(")?;
1434                if let Some(ty) = ty_opt {
1435                    write!(f, "Some{ty}")?;
1436                } else {
1437                    write!(f, "None")?;
1438                }
1439                write!(f, ",[")?;
1440                write_sep(f, elems, ",")?;
1441                write!(f, "])")
1442            }
1443            Self::TransferObjects(objs, addr) => {
1444                write!(f, "TransferObjects([")?;
1445                write_sep(f, objs, ",")?;
1446                write!(f, "],{addr})")
1447            }
1448            Self::SplitCoins(coin, amounts) => {
1449                write!(f, "SplitCoins({coin},")?;
1450                write_sep(f, amounts, ",")?;
1451                write!(f, ")")
1452            }
1453            Self::MergeCoins(target, coins) => {
1454                write!(f, "MergeCoins({target},")?;
1455                write_sep(f, coins, ",")?;
1456                write!(f, ")")
1457            }
1458            Self::Publish(deps) => {
1459                write!(f, "Publish(<modules>,")?;
1460                write_sep(f, deps, ",")?;
1461                write!(f, ")")
1462            }
1463            Self::Upgrade(deps, current_package_id, ticket) => {
1464                write!(f, "Upgrade(<modules>, {ticket},")?;
1465                write_sep(f, deps, ",")?;
1466                write!(f, ", {current_package_id}")?;
1467                write!(f, ")")
1468            }
1469        }
1470    }
1471}
1472
1473/// An argument to a transaction in a programmable transaction block
1474#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
1475pub enum SuiArgument {
1476    /// The gas coin. The gas coin can only be used by-ref, except for with
1477    /// `TransferObjects`, which can use it by-value.
1478    GasCoin,
1479    /// One of the input objects or primitive values (from
1480    /// `ProgrammableTransactionBlock` inputs)
1481    Input(u16),
1482    /// The result of another transaction (from `ProgrammableTransactionBlock` transactions)
1483    Result(u16),
1484    /// Like a `Result` but it accesses a nested result. Currently, the only usage
1485    /// of this is to access a value from a Move call with multiple return values.
1486    NestedResult(u16, u16),
1487}
1488
1489impl Display for SuiArgument {
1490    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1491        match self {
1492            Self::GasCoin => write!(f, "GasCoin"),
1493            Self::Input(i) => write!(f, "Input({i})"),
1494            Self::Result(i) => write!(f, "Result({i})"),
1495            Self::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
1496        }
1497    }
1498}
1499
1500/// The transaction for calling a Move function, either an entry function or a public
1501/// function (which cannot return references).
1502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1503pub struct SuiProgrammableMoveCall {
1504    /// The package containing the module and function.
1505    pub package: Address,
1506    /// The specific module in the package containing the function.
1507    pub module: String,
1508    /// The function to be called.
1509    pub function: String,
1510    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1511    /// The type arguments to the function.
1512    pub type_arguments: Vec<String>,
1513    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1514    /// The arguments to the function.
1515    pub arguments: Vec<SuiArgument>,
1516}
1517
1518fn write_sep<T: Display>(
1519    f: &mut Formatter<'_>,
1520    items: impl IntoIterator<Item = T>,
1521    sep: &str,
1522) -> std::fmt::Result {
1523    let mut xs = items.into_iter().peekable();
1524    while let Some(x) = xs.next() {
1525        write!(f, "{x}")?;
1526        if xs.peek().is_some() {
1527            write!(f, "{sep}")?;
1528        }
1529    }
1530    Ok(())
1531}
1532
1533impl Display for SuiProgrammableMoveCall {
1534    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1535        let Self {
1536            package,
1537            module,
1538            function,
1539            type_arguments,
1540            arguments,
1541        } = self;
1542        write!(f, "{package}::{module}::{function}")?;
1543        if !type_arguments.is_empty() {
1544            write!(f, "<")?;
1545            write_sep(f, type_arguments, ",")?;
1546            write!(f, ">")?;
1547        }
1548        write!(f, "(")?;
1549        write_sep(f, arguments, ",")?;
1550        write!(f, ")")
1551    }
1552}
1553
1554#[derive(Debug, Serialize, Deserialize, Clone)]
1555#[serde(rename = "TypeTag", rename_all = "camelCase")]
1556pub struct SuiTypeTag(String);
1557
1558impl SuiTypeTag {
1559    pub fn new(tag: String) -> Self {
1560        Self(tag)
1561    }
1562}
1563
1564impl TryInto<TypeTag> for SuiTypeTag {
1565    type Error = <TypeTag as FromStr>::Err;
1566    fn try_into(self) -> Result<TypeTag, Self::Error> {
1567        self.0.parse()
1568    }
1569}
1570
1571impl From<TypeTag> for SuiTypeTag {
1572    fn from(tag: TypeTag) -> Self {
1573        Self(format!("{}", tag))
1574    }
1575}
1576
1577#[derive(Serialize, Deserialize)]
1578#[serde(rename_all = "camelCase")]
1579pub enum RPCTransactionRequestParams {
1580    TransferObjectRequestParams(TransferObjectParams),
1581    MoveCallRequestParams(MoveCallParams),
1582}
1583
1584#[derive(Serialize, Deserialize)]
1585#[serde(rename_all = "camelCase")]
1586pub struct TransferObjectParams {
1587    pub recipient: Address,
1588    pub object_id: Address,
1589}
1590
1591#[derive(Serialize, Deserialize)]
1592#[serde(rename_all = "camelCase")]
1593pub struct MoveCallParams {
1594    pub package_object_id: Address,
1595    pub module: String,
1596    pub function: String,
1597    #[serde(default)]
1598    pub type_arguments: Vec<SuiTypeTag>,
1599    pub arguments: Vec<serde_json::Value>,
1600}
1601
1602#[serde_as]
1603#[derive(Serialize, Deserialize)]
1604#[serde(rename_all = "camelCase")]
1605pub struct TransactionBlockBytes {
1606    /// BCS serialized transaction data bytes without its type tag, as base-64 encoded string.
1607    pub tx_bytes: String,
1608    /// the gas objects to be used
1609    pub gas: Vec<SuiObjectRef>,
1610    /// objects to be used in this transaction
1611    pub input_objects: Vec<SuiInputObjectKind>,
1612}
1613
1614#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
1615#[serde(rename = "OwnedObjectRef")]
1616pub struct OwnedObjectRef {
1617    pub owner: Owner,
1618    pub reference: SuiObjectRef,
1619}
1620
1621impl OwnedObjectRef {
1622    pub fn object_id(&self) -> Address {
1623        self.reference.object_id
1624    }
1625    pub fn version(&self) -> Version {
1626        self.reference.version
1627    }
1628}
1629
1630#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
1631#[serde(tag = "type", rename_all = "camelCase")]
1632pub enum SuiCallArg {
1633    // Needs to become an Object Ref or Object ID, depending on object type
1634    Object(SuiObjectArg),
1635    // pure value, bcs encoded
1636    Pure(SuiPureValue),
1637}
1638
1639impl SuiCallArg {
1640    pub fn pure(&self) -> Option<&serde_json::Value> {
1641        match self {
1642            SuiCallArg::Pure(v) => Some(&v.value),
1643            _ => None,
1644        }
1645    }
1646
1647    pub fn object(&self) -> Option<&Address> {
1648        match self {
1649            SuiCallArg::Object(SuiObjectArg::SharedObject { object_id, .. })
1650            | SuiCallArg::Object(SuiObjectArg::ImmOrOwnedObject { object_id, .. })
1651            | SuiCallArg::Object(SuiObjectArg::Receiving { object_id, .. }) => Some(object_id),
1652            _ => None,
1653        }
1654    }
1655}
1656
1657#[serde_as]
1658#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
1659#[serde(rename_all = "camelCase")]
1660pub struct SuiPureValue {
1661    // #[serde_as(as = "Option<AsSuiTypeTag>")]
1662    #[serde_as(as = "Option<DisplayFromStr>")]
1663    value_type: Option<TypeTag>,
1664    value: serde_json::Value,
1665}
1666
1667impl SuiPureValue {
1668    pub fn value(&self) -> serde_json::Value {
1669        self.value.clone()
1670    }
1671
1672    pub fn value_type(&self) -> Option<TypeTag> {
1673        self.value_type.clone()
1674    }
1675}
1676
1677#[serde_as]
1678#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
1679#[serde(tag = "objectType", rename_all = "camelCase")]
1680pub enum SuiObjectArg {
1681    // A Move object, either immutable, or owned mutable.
1682    #[serde(rename_all = "camelCase")]
1683    ImmOrOwnedObject {
1684        object_id: Address,
1685        #[serde_as(as = "BigInt<u64>")]
1686        version: Version,
1687        digest: Digest,
1688    },
1689    // A Move object that's shared.
1690    // SharedObject::mutable controls whether caller asks for a mutable reference to shared object.
1691    #[serde(rename_all = "camelCase")]
1692    SharedObject {
1693        object_id: Address,
1694        #[serde_as(as = "BigInt<u64>")]
1695        initial_shared_version: Version,
1696        mutable: bool,
1697    },
1698    // A reference to a Move object that's going to be received in the transaction.
1699    #[serde(rename_all = "camelCase")]
1700    Receiving {
1701        object_id: Address,
1702        #[serde_as(as = "BigInt<u64>")]
1703        version: Version,
1704        digest: Digest,
1705    },
1706}
1707
1708#[serde_as]
1709#[derive(Clone, Debug, Serialize, Deserialize)]
1710#[non_exhaustive]
1711pub enum TransactionFilter {
1712    /// CURRENTLY NOT SUPPORTED. Query by checkpoint.
1713    Checkpoint(#[serde_as(as = "IfIsHumanReadable<BigInt<u64>, _>")] Version),
1714    /// Query by move function.
1715    MoveFunction {
1716        package: Address,
1717        module: Option<String>,
1718        function: Option<String>,
1719    },
1720    /// Query by input object.
1721    InputObject(Address),
1722    /// Query by changed object, including created, mutated and unwrapped objects.
1723    ChangedObject(Address),
1724    /// Query for transactions that touch this object.
1725    AffectedObject(Address),
1726    /// Query by sender address.
1727    FromAddress(Address),
1728    /// Query by recipient address.
1729    ToAddress(Address),
1730    /// Query by sender and recipient address.
1731    FromAndToAddress { from: Address, to: Address },
1732    /// CURRENTLY NOT SUPPORTED. Query txs that have a given address as sender or recipient.
1733    FromOrToAddress { addr: Address },
1734    /// Query by transaction kind
1735    TransactionKind(String),
1736    /// Query transactions of any given kind in the input.
1737    TransactionKindIn(Vec<String>),
1738}
1739
1740#[cfg(test)]
1741mod tests {
1742    use color_eyre::Result;
1743    use itertools::Itertools as _;
1744    use sui_sdk_types::Identifier;
1745
1746    use super::*;
1747
1748    const MOVE_ABORT_ERRORS: [&str; 3] = [
1749        r#"MoveAbort(MoveLocation { module: ModuleId { address: fd6f306bb2f8dce24dd3d4a9bdc51a46e7c932b15007d73ac0cfb38c15de0fea, name: Identifier("market") }, function: 1, instruction: 60, function_name: Some("try_update_funding") }, 1001)"#,
1750        r#"MoveAbort(MoveLocation { module: ModuleId { address: 241537381737a40df6838bc395fb64f04ff604513c18a2ac3308ac810c805fa6, name: Identifier("oracle") }, function: 23, instruction: 42, function_name: Some("update_price_feed_inner") }, 4)"#,
1751        r#"MoveAbort(MoveLocation { module: ModuleId { address: 72a8715095cdc8442b4316f78802d7aefa2e6f0c3c6fac256ce81554034b0d4b, name: Identifier("clearing_house") }, function: 53, instruction: 32, function_name: Some("settled_liquidated_position") }, 2001) in command 3"#,
1752    ];
1753
1754    #[test]
1755    fn move_abort_regex_is_valid() -> Result<()> {
1756        regex::Regex::new(SuiExecutionStatus::MOVE_ABORT_PATTERN)?;
1757        Ok(())
1758    }
1759
1760    #[test]
1761    fn move_abort_extracts() -> Result<()> {
1762        let expected = [
1763            (
1764                MoveLocation {
1765                    package: "0xfd6f306bb2f8dce24dd3d4a9bdc51a46e7c932b15007d73ac0cfb38c15de0fea"
1766                        .parse()?,
1767                    module: Identifier::from_static("market"),
1768                    function: 1,
1769                    instruction: 60,
1770                    function_name: Some("try_update_funding".parse()?),
1771                },
1772                1001,
1773            ),
1774            (
1775                MoveLocation {
1776                    package: "0x241537381737a40df6838bc395fb64f04ff604513c18a2ac3308ac810c805fa6"
1777                        .parse()?,
1778                    module: Identifier::from_static("oracle"),
1779                    function: 23,
1780                    instruction: 42,
1781                    function_name: Some("update_price_feed_inner".parse()?),
1782                },
1783                4,
1784            ),
1785            (
1786                MoveLocation {
1787                    package: "0x72a8715095cdc8442b4316f78802d7aefa2e6f0c3c6fac256ce81554034b0d4b"
1788                        .parse()?,
1789                    module: Identifier::from_static("clearing_house"),
1790                    function: 53,
1791                    instruction: 32,
1792                    function_name: Some("settled_liquidated_position".parse()?),
1793                },
1794                2001,
1795            ),
1796        ];
1797
1798        let errors = MOVE_ABORT_ERRORS
1799            .into_iter()
1800            .map(|msg| SuiExecutionStatus::Failure { error: msg.into() });
1801
1802        for (error, expect) in errors.into_iter().zip_eq(expected) {
1803            assert_eq!(error.as_move_abort(), Some(expect));
1804        }
1805
1806        Ok(())
1807    }
1808
1809    #[test]
1810    fn transaction_block_transaction_deser() {
1811        let value = serde_json::json!({
1812          "data": {
1813            "messageVersion": "v1",
1814            "transaction": {
1815              "kind": "ProgrammableTransaction",
1816              "inputs": [
1817                {
1818                  "type": "pure",
1819                  "valueType": "u64",
1820                  "value": "5"
1821                },
1822                {
1823                  "type": "pure",
1824                  "valueType": "u64",
1825                  "value": "13"
1826                },
1827                {
1828                  "type": "pure",
1829                  "valueType": "u64",
1830                  "value": "20"
1831                },
1832                {
1833                  "type": "object",
1834                  "objectType": "immOrOwnedObject",
1835                  "objectId": "0xa3926c709e6ed570217aec468c9b81c35c4e7178a18f610f4427f4a075f63680",
1836                  "version": "3",
1837                  "digest": "7YeqF7kSbFrdzE2zyG4BdTctiHQrjapUkzqT3kqGAg6D"
1838                },
1839                {
1840                  "type": "pure",
1841                  "valueType": "u256",
1842                  "value": "100000000000000000"
1843                },
1844                {
1845                  "type": "pure",
1846                  "valueType": "u256",
1847                  "value": "50000000000000000"
1848                },
1849                {
1850                  "type": "pure",
1851                  "valueType": "u64",
1852                  "value": "3600000"
1853                },
1854                {
1855                  "type": "pure",
1856                  "valueType": "u64",
1857                  "value": "86400000"
1858                },
1859                {
1860                  "type": "pure",
1861                  "valueType": "u64",
1862                  "value": "5000"
1863                },
1864                {
1865                  "type": "pure",
1866                  "valueType": "u256",
1867                  "value": "1000000000000000"
1868                },
1869                {
1870                  "type": "pure",
1871                  "valueType": "u256",
1872                  "value": "100000000000000"
1873                },
1874                {
1875                  "type": "pure",
1876                  "valueType": "u64",
1877                  "value": "1000000"
1878                },
1879                {
1880                  "type": "pure",
1881                  "valueType": "u64",
1882                  "value": "1000"
1883                },
1884                {
1885                  "type": "object",
1886                  "objectType": "immOrOwnedObject",
1887                  "objectId": "0xf8cea4da5f0b9cf6cea67217fb99de1ac7dcc905863c5d2b1f8d4ab4530b726a",
1888                  "version": "3",
1889                  "digest": "6cACC7x9FFY1SdnzT6aH1z9UPMmvm4NGUz3skXGBn1Yd"
1890                },
1891                {
1892                  "type": "object",
1893                  "objectType": "sharedObject",
1894                  "objectId": "0x981bfebd35bf22ab220853d6756568fd0556d127b856d7d08a12822da0cf1a4b",
1895                  "initialSharedVersion": "3",
1896                  "mutable": true
1897                },
1898                {
1899                  "type": "object",
1900                  "objectType": "sharedObject",
1901                  "objectId": "0xd9b347da14a2600c9392d6718096d712e2b5497ce87c871854bfa92023099530",
1902                  "initialSharedVersion": "509",
1903                  "mutable": false
1904                },
1905                {
1906                  "type": "object",
1907                  "objectType": "sharedObject",
1908                  "objectId": "0x7bbfd7177ef8042d2752deb2bfd5016114ad91e8c0abae6f78f35b4fd4db95d1",
1909                  "initialSharedVersion": "510",
1910                  "mutable": false
1911                },
1912                {
1913                  "type": "object",
1914                  "objectType": "sharedObject",
1915                  "objectId": "0x0000000000000000000000000000000000000000000000000000000000000006",
1916                  "initialSharedVersion": "1",
1917                  "mutable": false
1918                }
1919              ],
1920              "transactions": [
1921                {
1922                  "MoveCall": {
1923                    "package": "0xb9f430cddd9cbe60f0beea60fce6da590688abbf2948dccff7cdda5a874be9ad",
1924                    "module": "interface",
1925                    "function": "create_orderbook",
1926                    "arguments": [
1927                      {
1928                        "Input": 13
1929                      },
1930                      {
1931                        "Input": 0
1932                      },
1933                      {
1934                        "Input": 1
1935                      },
1936                      {
1937                        "Input": 2
1938                      },
1939                      {
1940                        "Input": 0
1941                      },
1942                      {
1943                        "Input": 1
1944                      },
1945                      {
1946                        "Input": 2
1947                      }
1948                    ]
1949                  }
1950                },
1951                {
1952                  "MoveCall": {
1953                    "package": "0xb9f430cddd9cbe60f0beea60fce6da590688abbf2948dccff7cdda5a874be9ad",
1954                    "module": "interface",
1955                    "function": "create_clearing_house",
1956                    "type_arguments": [
1957                      "0x6f048d2b0929f0f2c16dd7ccf643b59b7b029d180766780ba83ce9f847306715::usdc::USDC"
1958                    ],
1959                    "arguments": [
1960                      {
1961                        "Input": 13
1962                      },
1963                      {
1964                        "Result": 0
1965                      },
1966                      {
1967                        "Input": 3
1968                      },
1969                      {
1970                        "Input": 17
1971                      },
1972                      {
1973                        "Input": 15
1974                      },
1975                      {
1976                        "Input": 16
1977                      },
1978                      {
1979                        "Input": 4
1980                      },
1981                      {
1982                        "Input": 5
1983                      },
1984                      {
1985                        "Input": 6
1986                      },
1987                      {
1988                        "Input": 7
1989                      },
1990                      {
1991                        "Input": 8
1992                      },
1993                      {
1994                        "Input": 6
1995                      },
1996                      {
1997                        "Input": 8
1998                      },
1999                      {
2000                        "Input": 6
2001                      },
2002                      {
2003                        "Input": 9
2004                      },
2005                      {
2006                        "Input": 9
2007                      },
2008                      {
2009                        "Input": 9
2010                      },
2011                      {
2012                        "Input": 10
2013                      },
2014                      {
2015                        "Input": 9
2016                      },
2017                      {
2018                        "Input": 11
2019                      },
2020                      {
2021                        "Input": 12
2022                      }
2023                    ]
2024                  }
2025                },
2026                {
2027                  "MoveCall": {
2028                    "package": "0xb9f430cddd9cbe60f0beea60fce6da590688abbf2948dccff7cdda5a874be9ad",
2029                    "module": "interface",
2030                    "function": "register_market",
2031                    "type_arguments": [
2032                      "0x6f048d2b0929f0f2c16dd7ccf643b59b7b029d180766780ba83ce9f847306715::usdc::USDC"
2033                    ],
2034                    "arguments": [
2035                      {
2036                        "Input": 13
2037                      },
2038                      {
2039                        "Input": 14
2040                      },
2041                      {
2042                        "Result": 1
2043                      }
2044                    ]
2045                  }
2046                },
2047                {
2048                  "MoveCall": {
2049                    "package": "0xb9f430cddd9cbe60f0beea60fce6da590688abbf2948dccff7cdda5a874be9ad",
2050                    "module": "interface",
2051                    "function": "share_clearing_house",
2052                    "type_arguments": [
2053                      "0x6f048d2b0929f0f2c16dd7ccf643b59b7b029d180766780ba83ce9f847306715::usdc::USDC"
2054                    ],
2055                    "arguments": [
2056                      {
2057                        "Result": 1
2058                      }
2059                    ]
2060                  }
2061                }
2062              ]
2063            },
2064            "sender": "0x98e9cafb116af9d69f77ce0d644c60e384f850f8af050b268377d8293d7fe7c6",
2065            "gasData": {
2066              "payment": [
2067                {
2068                  "objectId": "0x39e44ce0f8ee07f02d38c2f5fc7d9805bee241d0acbb2153e1e3fa005abf9736",
2069                  "version": 553,
2070                  "digest": "A1NSYfktRWLNCVBC6jHmRs5SyhkJYoo4CtNg7QZq3vZC"
2071                }
2072              ],
2073              "owner": "0x98e9cafb116af9d69f77ce0d644c60e384f850f8af050b268377d8293d7fe7c6",
2074              "price": "1000",
2075              "budget": "39172808"
2076            }
2077          },
2078          "txSignatures": [
2079            "ABfUuGXoWtGL54zCZh2Ef3NsNAHQqgibuIFUieVUox8EsTpNgH3WiKq/UgHwnlB3xW7D+AeC5hoBcVO6KbGPBAmvVqH0xXcgXDKTMc2JG4DUIii4K/ah/Is/TjelRccIlg=="
2080          ]
2081        });
2082        let block: SuiTransactionBlock = serde_json::from_value(value.clone()).unwrap();
2083        let restored = serde_json::to_value(&block).unwrap();
2084        assert_eq!(value, restored);
2085    }
2086}