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