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