sui_jsonrpc/msgs/
sui_object.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::cmp::Ordering;
5use std::collections::BTreeMap;
6use std::fmt;
7use std::fmt::{Display, Formatter, Write};
8use std::str::FromStr;
9
10use af_sui_types::{
11    Address as SuiAddress,
12    Identifier,
13    MoveObjectType,
14    ObjectArg,
15    ObjectDigest,
16    ObjectId,
17    ObjectRef,
18    Owner,
19    StructTag,
20    TransactionDigest,
21    TypeOrigin,
22    UpgradeInfo,
23};
24use colored::Colorize;
25use serde::{Deserialize, Serialize};
26use serde_json::Value;
27use serde_with::base64::Base64;
28use serde_with::{DisplayFromStr, serde_as};
29use sui_sdk_types::Version;
30
31use super::{Page, SuiMoveStruct, SuiMoveValue};
32use crate::serde::BigInt;
33
34// =============================================================================
35//  SuiObjectResponse
36// =============================================================================
37
38#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
39#[error("Could not get object_id, something went wrong with SuiObjectResponse construction.")]
40pub struct MissingObjectIdError;
41
42#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
43pub struct SuiObjectResponse {
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub data: Option<SuiObjectData>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub error: Option<SuiObjectResponseError>,
48}
49
50impl SuiObjectResponse {
51    pub fn new(data: Option<SuiObjectData>, error: Option<SuiObjectResponseError>) -> Self {
52        Self { data, error }
53    }
54
55    pub fn new_with_data(data: SuiObjectData) -> Self {
56        Self {
57            data: Some(data),
58            error: None,
59        }
60    }
61
62    pub fn new_with_error(error: SuiObjectResponseError) -> Self {
63        Self {
64            data: None,
65            error: Some(error),
66        }
67    }
68
69    /// Returns a reference to the object if there is any, otherwise an Err if
70    /// the object does not exist or is deleted.
71    pub fn object(&self) -> Result<&SuiObjectData, SuiObjectResponseError> {
72        if let Some(data) = &self.data {
73            Ok(data)
74        } else if let Some(error) = &self.error {
75            Err(error.clone())
76        } else {
77            // We really shouldn't reach this code block since either data, or error field should always be filled.
78            Err(SuiObjectResponseError::Unknown)
79        }
80    }
81
82    /// Returns the object value if there is any, otherwise an Err if
83    /// the object does not exist or is deleted.
84    pub fn into_object(self) -> Result<SuiObjectData, SuiObjectResponseError> {
85        match self.object() {
86            Ok(data) => Ok(data.clone()),
87            Err(error) => Err(error),
88        }
89    }
90
91    pub fn move_object_bcs(&self) -> Option<&Vec<u8>> {
92        match &self.data {
93            Some(SuiObjectData {
94                bcs: Some(SuiRawData::MoveObject(obj)),
95                ..
96            }) => Some(&obj.bcs_bytes),
97            _ => None,
98        }
99    }
100
101    pub fn owner(&self) -> Option<Owner> {
102        if let Some(data) = &self.data {
103            return data.owner.clone();
104        }
105        None
106    }
107
108    pub fn object_id(&self) -> Result<ObjectId, MissingObjectIdError> {
109        match (&self.data, &self.error) {
110            (Some(obj_data), None) => Ok(obj_data.object_id),
111            (None, Some(SuiObjectResponseError::NotExists { object_id })) => Ok(*object_id),
112            (
113                None,
114                Some(SuiObjectResponseError::Deleted {
115                    object_id,
116                    version: _,
117                    digest: _,
118                }),
119            ) => Ok(*object_id),
120            _ => Err(MissingObjectIdError),
121        }
122    }
123
124    pub fn object_ref_if_exists(&self) -> Option<ObjectRef> {
125        match (&self.data, &self.error) {
126            (Some(obj_data), None) => Some(obj_data.object_ref()),
127            _ => None,
128        }
129    }
130}
131
132impl Ord for SuiObjectResponse {
133    fn cmp(&self, other: &Self) -> Ordering {
134        match (&self.data, &other.data) {
135            (Some(data), Some(data_2)) => {
136                if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Greater) {
137                    return Ordering::Greater;
138                } else if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Less) {
139                    return Ordering::Less;
140                }
141                Ordering::Equal
142            }
143            // In this ordering those with data will come before SuiObjectResponses that are errors.
144            (Some(_), None) => Ordering::Less,
145            (None, Some(_)) => Ordering::Greater,
146            // SuiObjectResponses that are errors are just considered equal.
147            _ => Ordering::Equal,
148        }
149    }
150}
151
152impl PartialOrd for SuiObjectResponse {
153    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
154        Some(self.cmp(other))
155    }
156}
157
158/// Originally from `sui_types::error`.
159#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
160#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
161pub enum SuiObjectResponseError {
162    #[error("Object {:?} does not exist.", object_id)]
163    NotExists { object_id: ObjectId },
164    #[error("Cannot find dynamic field for parent object {:?}.", parent_object_id)]
165    DynamicFieldNotFound { parent_object_id: ObjectId },
166    #[error(
167        "Object has been deleted object_id: {:?} at version: {:?} in digest {:?}",
168        object_id,
169        version,
170        digest
171    )]
172    Deleted {
173        object_id: ObjectId,
174        /// Object version.
175        version: Version,
176        /// Base64 string representing the object digest
177        digest: ObjectDigest,
178    },
179    #[error("Unknown Error.")]
180    Unknown,
181    #[error("Display Error: {:?}", error)]
182    DisplayError { error: String },
183}
184
185#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
186pub struct DisplayFieldsResponse {
187    pub data: Option<BTreeMap<String, String>>,
188    pub error: Option<SuiObjectResponseError>,
189}
190
191// =============================================================================
192//  SuiObjectData
193// =============================================================================
194
195#[derive(thiserror::Error, Debug)]
196pub enum SuiObjectDataError {
197    #[error("Missing object type")]
198    MissingObjectType,
199    #[error("Missing BCS encoding")]
200    MissingBcs,
201    #[error("Missing object owner")]
202    MissingOwner,
203    #[error("Not a Move object")]
204    NotMoveObject,
205    #[error("Not an immutable or owned object")]
206    NotImmOrOwned,
207    #[error("Not a shared object")]
208    NotShared,
209    #[error(transparent)]
210    ObjectType(#[from] NotMoveStructError),
211}
212
213#[serde_as]
214#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
215#[serde(rename_all = "camelCase", rename = "ObjectData")]
216pub struct SuiObjectData {
217    pub object_id: ObjectId,
218    /// Object version.
219    #[serde_as(as = "BigInt<u64>")]
220    pub version: Version,
221    /// Base64 string representing the object digest
222    pub digest: ObjectDigest,
223    /// The type of the object. Default to be None unless SuiObjectDataOptions.showType is set to true
224    #[serde_as(as = "Option<DisplayFromStr>")]
225    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
226    pub type_: Option<ObjectType>,
227    // Default to be None because otherwise it will be repeated for the getOwnedObjects endpoint
228    /// The owner of this object. Default to be None unless SuiObjectDataOptions.showOwner is set to true
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub owner: Option<Owner>,
231    /// The digest of the transaction that created or last mutated this object. Default to be None unless
232    /// SuiObjectDataOptions.showPreviousTransaction is set to true
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub previous_transaction: Option<TransactionDigest>,
235    /// The amount of SUI we would rebate if this object gets deleted.
236    /// This number is re-calculated each time the object is mutated based on
237    /// the present storage gas price.
238    #[serde_as(as = "Option<BigInt<u64>>")]
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub storage_rebate: Option<u64>,
241    /// The Display metadata for frontend UI rendering, default to be None unless SuiObjectDataOptions.showContent is set to true
242    /// This can also be None if the struct type does not have Display defined
243    /// See more details in <https://forums.sui.io/t/nft-object-display-proposal/4872>
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub display: Option<DisplayFieldsResponse>,
246    /// Move object content or package content, default to be None unless SuiObjectDataOptions.showContent is set to true
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub content: Option<SuiParsedData>,
249    /// Move object content or package content in BCS, default to be None unless SuiObjectDataOptions.showBcs is set to true
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub bcs: Option<SuiRawData>,
252}
253
254impl SuiObjectData {
255    pub fn object_ref(&self) -> ObjectRef {
256        (self.object_id, self.version, self.digest)
257    }
258
259    pub fn object_type(&self) -> Result<ObjectType, SuiObjectDataError> {
260        self.type_
261            .as_ref()
262            .ok_or(SuiObjectDataError::MissingObjectType)
263            .cloned()
264    }
265
266    pub fn is_gas_coin(&self) -> bool {
267        match self.type_.as_ref() {
268            Some(ObjectType::Struct(ty)) if ty.is_gas_coin() => true,
269            Some(_) => false,
270            None => false,
271        }
272    }
273
274    pub fn struct_tag(&self) -> Result<StructTag, SuiObjectDataError> {
275        Ok(self
276            .type_
277            .clone()
278            .ok_or(SuiObjectDataError::MissingObjectType)?
279            .try_into()?)
280    }
281
282    pub fn take_object_type(&mut self) -> Result<ObjectType, SuiObjectDataError> {
283        self.type_
284            .take()
285            .ok_or(SuiObjectDataError::MissingObjectType)
286    }
287
288    pub fn take_raw_object(&mut self) -> Result<SuiRawMoveObject, SuiObjectDataError> {
289        self.take_raw_data()?
290            .try_into_move()
291            .ok_or(SuiObjectDataError::NotMoveObject)
292    }
293
294    pub fn take_raw_data(&mut self) -> Result<SuiRawData, SuiObjectDataError> {
295        self.bcs.take().ok_or(SuiObjectDataError::MissingBcs)
296    }
297
298    pub fn shared_object_arg(&self, mutable: bool) -> Result<ObjectArg, SuiObjectDataError> {
299        let Owner::Shared {
300            initial_shared_version,
301        } = self.owner()?
302        else {
303            return Err(SuiObjectDataError::NotShared);
304        };
305        Ok(ObjectArg::SharedObject {
306            id: self.object_id,
307            initial_shared_version,
308            mutable,
309        })
310    }
311
312    pub fn imm_or_owned_object_arg(&self) -> Result<ObjectArg, SuiObjectDataError> {
313        use Owner::*;
314        if !matches!(self.owner()?, AddressOwner(_) | ObjectOwner(_) | Immutable) {
315            return Err(SuiObjectDataError::NotImmOrOwned);
316        };
317        let (i, v, d) = self.object_ref();
318        Ok(ObjectArg::ImmOrOwnedObject((i, v, d)))
319    }
320
321    pub fn owner(&self) -> Result<Owner, SuiObjectDataError> {
322        self.owner.clone().ok_or(SuiObjectDataError::MissingOwner)
323    }
324}
325
326impl Display for SuiObjectData {
327    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
328        let type_ = if let Some(type_) = &self.type_ {
329            type_.to_string()
330        } else {
331            "Unknown Type".into()
332        };
333        let mut writer = String::new();
334        writeln!(
335            writer,
336            "{}",
337            format!("----- {type_} ({}[{}]) -----", self.object_id, self.version).bold()
338        )?;
339        if let Some(ref owner) = self.owner {
340            writeln!(writer, "{}: {}", "Owner".bold().bright_black(), owner)?;
341        }
342
343        writeln!(
344            writer,
345            "{}: {}",
346            "Version".bold().bright_black(),
347            self.version
348        )?;
349        if let Some(storage_rebate) = self.storage_rebate {
350            writeln!(
351                writer,
352                "{}: {}",
353                "Storage Rebate".bold().bright_black(),
354                storage_rebate
355            )?;
356        }
357
358        if let Some(previous_transaction) = self.previous_transaction {
359            writeln!(
360                writer,
361                "{}: {:?}",
362                "Previous Transaction".bold().bright_black(),
363                previous_transaction
364            )?;
365        }
366        if let Some(content) = self.content.as_ref() {
367            writeln!(writer, "{}", "----- Data -----".bold())?;
368            write!(writer, "{}", content)?;
369        }
370
371        write!(f, "{}", writer)
372    }
373}
374
375// =============================================================================
376//  ObjectType
377// =============================================================================
378
379const PACKAGE: &str = "package";
380/// Type of a Sui object
381///
382/// Originally from `sui_types::base_types`.
383#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
384pub enum ObjectType {
385    /// Move package containing one or more bytecode modules
386    Package,
387    /// A Move struct of the given type
388    Struct(MoveObjectType),
389}
390
391impl TryFrom<ObjectType> for StructTag {
392    type Error = NotMoveStructError;
393
394    fn try_from(o: ObjectType) -> Result<Self, Self::Error> {
395        match o {
396            ObjectType::Package => Err(NotMoveStructError),
397            ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
398        }
399    }
400}
401
402#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
403#[error("Cannot create StructTag from Package")]
404pub struct NotMoveStructError;
405
406impl Display for ObjectType {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
408        match self {
409            ObjectType::Package => write!(f, "{}", PACKAGE),
410            ObjectType::Struct(t) => write!(f, "{}", t),
411        }
412    }
413}
414
415impl FromStr for ObjectType {
416    type Err = <StructTag as FromStr>::Err;
417
418    fn from_str(s: &str) -> Result<Self, Self::Err> {
419        if s.to_lowercase() == PACKAGE {
420            Ok(ObjectType::Package)
421        } else {
422            let tag: StructTag = s.parse()?;
423            Ok(ObjectType::Struct(MoveObjectType::from(tag)))
424        }
425    }
426}
427
428// =============================================================================
429//  SuiObjectDataOptions
430// =============================================================================
431
432#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)]
433#[serde(rename_all = "camelCase", rename = "ObjectDataOptions", default)]
434pub struct SuiObjectDataOptions {
435    /// Whether to show the type of the object. Default to be False
436    pub show_type: bool,
437    /// Whether to show the owner of the object. Default to be False
438    pub show_owner: bool,
439    /// Whether to show the previous transaction digest of the object. Default to be False
440    pub show_previous_transaction: bool,
441    /// Whether to show the Display metadata of the object for frontend rendering. Default to be False
442    pub show_display: bool,
443    /// Whether to show the content(i.e., package content or Move struct content) of the object.
444    /// Default to be False
445    pub show_content: bool,
446    /// Whether to show the content in BCS format. Default to be False
447    pub show_bcs: bool,
448    /// Whether to show the storage rebate of the object. Default to be False
449    pub show_storage_rebate: bool,
450}
451
452impl SuiObjectDataOptions {
453    pub fn new() -> Self {
454        Self::default()
455    }
456
457    /// return BCS data and all other metadata such as storage rebate
458    pub fn bcs_lossless() -> Self {
459        Self {
460            show_bcs: true,
461            show_type: true,
462            show_owner: true,
463            show_previous_transaction: true,
464            show_display: false,
465            show_content: false,
466            show_storage_rebate: true,
467        }
468    }
469
470    /// return full content except bcs
471    pub fn full_content() -> Self {
472        Self {
473            show_bcs: false,
474            show_type: true,
475            show_owner: true,
476            show_previous_transaction: true,
477            show_display: false,
478            show_content: true,
479            show_storage_rebate: true,
480        }
481    }
482
483    pub fn with_content(mut self) -> Self {
484        self.show_content = true;
485        self
486    }
487
488    pub fn with_owner(mut self) -> Self {
489        self.show_owner = true;
490        self
491    }
492
493    pub fn with_type(mut self) -> Self {
494        self.show_type = true;
495        self
496    }
497
498    pub fn with_display(mut self) -> Self {
499        self.show_display = true;
500        self
501    }
502
503    pub fn with_bcs(mut self) -> Self {
504        self.show_bcs = true;
505        self
506    }
507
508    pub fn with_previous_transaction(mut self) -> Self {
509        self.show_previous_transaction = true;
510        self
511    }
512
513    pub fn is_not_in_object_info(&self) -> bool {
514        self.show_bcs || self.show_content || self.show_display || self.show_storage_rebate
515    }
516}
517
518// =============================================================================
519//  SuiObjectRef
520// =============================================================================
521
522#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)]
523#[serde(rename_all = "camelCase", rename = "ObjectRef")]
524pub struct SuiObjectRef {
525    /// Hex code as string representing the object id
526    pub object_id: ObjectId,
527    /// Object version.
528    pub version: Version,
529    /// Base64 string representing the object digest
530    pub digest: ObjectDigest,
531}
532
533impl SuiObjectRef {
534    pub fn to_object_ref(&self) -> ObjectRef {
535        (self.object_id, self.version, self.digest)
536    }
537}
538
539impl Display for SuiObjectRef {
540    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
541        write!(
542            f,
543            "Object ID: {}, version: {}, digest: {}",
544            self.object_id, self.version, self.digest
545        )
546    }
547}
548
549impl From<ObjectRef> for SuiObjectRef {
550    fn from(oref: ObjectRef) -> Self {
551        Self {
552            object_id: oref.0,
553            version: oref.1,
554            digest: oref.2,
555        }
556    }
557}
558
559// =============================================================================
560//  SuiData
561// =============================================================================
562
563pub trait SuiData: Sized {
564    type ObjectType;
565    type PackageType;
566    fn try_as_move(&self) -> Option<&Self::ObjectType>;
567    fn try_into_move(self) -> Option<Self::ObjectType>;
568    fn try_as_package(&self) -> Option<&Self::PackageType>;
569    fn type_(&self) -> Option<&StructTag>;
570}
571
572#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
573#[serde(tag = "dataType", rename_all = "camelCase", rename = "RawData")]
574pub enum SuiRawData {
575    // Manually handle generic schema generation
576    MoveObject(SuiRawMoveObject),
577    Package(SuiRawMovePackage),
578}
579
580impl SuiData for SuiRawData {
581    type ObjectType = SuiRawMoveObject;
582    type PackageType = SuiRawMovePackage;
583
584    fn try_as_move(&self) -> Option<&Self::ObjectType> {
585        match self {
586            Self::MoveObject(o) => Some(o),
587            Self::Package(_) => None,
588        }
589    }
590
591    fn try_into_move(self) -> Option<Self::ObjectType> {
592        match self {
593            Self::MoveObject(o) => Some(o),
594            Self::Package(_) => None,
595        }
596    }
597
598    fn try_as_package(&self) -> Option<&Self::PackageType> {
599        match self {
600            Self::MoveObject(_) => None,
601            Self::Package(p) => Some(p),
602        }
603    }
604
605    fn type_(&self) -> Option<&StructTag> {
606        match self {
607            Self::MoveObject(o) => Some(&o.type_),
608            Self::Package(_) => None,
609        }
610    }
611}
612
613#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
614#[serde(tag = "dataType", rename_all = "camelCase", rename = "Data")]
615pub enum SuiParsedData {
616    // Manually handle generic schema generation
617    MoveObject(SuiParsedMoveObject),
618    Package(SuiMovePackage),
619}
620
621impl SuiData for SuiParsedData {
622    type ObjectType = SuiParsedMoveObject;
623    type PackageType = SuiMovePackage;
624
625    fn try_as_move(&self) -> Option<&Self::ObjectType> {
626        match self {
627            Self::MoveObject(o) => Some(o),
628            Self::Package(_) => None,
629        }
630    }
631
632    fn try_into_move(self) -> Option<Self::ObjectType> {
633        match self {
634            Self::MoveObject(o) => Some(o),
635            Self::Package(_) => None,
636        }
637    }
638
639    fn try_as_package(&self) -> Option<&Self::PackageType> {
640        match self {
641            Self::MoveObject(_) => None,
642            Self::Package(p) => Some(p),
643        }
644    }
645
646    fn type_(&self) -> Option<&StructTag> {
647        match self {
648            Self::MoveObject(o) => Some(&o.type_),
649            Self::Package(_) => None,
650        }
651    }
652}
653
654impl Display for SuiParsedData {
655    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
656        let mut writer = String::new();
657        match self {
658            SuiParsedData::MoveObject(o) => {
659                writeln!(writer, "{}: {}", "type".bold().bright_black(), o.type_)?;
660                write!(writer, "{}", &o.fields)?;
661            }
662            SuiParsedData::Package(p) => {
663                write!(
664                    writer,
665                    "{}: {:?}",
666                    "Modules".bold().bright_black(),
667                    p.disassembled.keys()
668                )?;
669            }
670        }
671        write!(f, "{}", writer)
672    }
673}
674
675pub trait SuiMoveObject: Sized {
676    fn type_(&self) -> &StructTag;
677}
678
679#[serde_as]
680#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
681#[serde(rename = "MoveObject", rename_all = "camelCase")]
682pub struct SuiParsedMoveObject {
683    #[serde(rename = "type")]
684    // #[serde_as(as = "SuiStructTag")]
685    #[serde_as(as = "DisplayFromStr")]
686    pub type_: StructTag,
687    pub has_public_transfer: bool,
688    pub fields: SuiMoveStruct,
689}
690
691impl SuiMoveObject for SuiParsedMoveObject {
692    fn type_(&self) -> &StructTag {
693        &self.type_
694    }
695}
696
697impl SuiParsedMoveObject {
698    pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<SuiMoveValue> {
699        match &self.fields {
700            SuiMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
701            SuiMoveStruct::WithTypes { fields, .. } => fields.get(field_name).cloned(),
702            _ => None,
703        }
704    }
705}
706
707#[serde_as]
708#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
709#[serde(rename = "RawMoveObject", rename_all = "camelCase")]
710pub struct SuiRawMoveObject {
711    #[serde(rename = "type")]
712    // #[serde_as(as = "SuiStructTag")]
713    #[serde_as(as = "DisplayFromStr")]
714    pub type_: StructTag,
715    pub has_public_transfer: bool,
716    pub version: Version,
717    #[serde_as(as = "Base64")]
718    pub bcs_bytes: Vec<u8>,
719}
720
721impl SuiMoveObject for SuiRawMoveObject {
722    fn type_(&self) -> &StructTag {
723        &self.type_
724    }
725}
726
727impl SuiRawMoveObject {
728    pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result<T, bcs::Error> {
729        bcs::from_bytes(self.bcs_bytes.as_slice())
730    }
731}
732
733#[serde_as]
734#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
735#[serde(rename = "RawMovePackage", rename_all = "camelCase")]
736pub struct SuiRawMovePackage {
737    pub id: ObjectId,
738    pub version: Version,
739    #[serde_as(as = "BTreeMap<_, Base64>")]
740    pub module_map: BTreeMap<String, Vec<u8>>,
741    pub type_origin_table: Vec<TypeOrigin>,
742    pub linkage_table: BTreeMap<ObjectId, UpgradeInfo>,
743}
744
745/// Errors for [`SuiPastObjectResponse`].
746#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
747pub enum SuiPastObjectResponseError {
748    #[error("Could not find the referenced object {object_id:?} at version {version:?}.")]
749    ObjectNotFound {
750        object_id: ObjectId,
751        version: Option<Version>,
752    },
753
754    #[error(
755        "Could not find the referenced object {object_id:?} \
756            as the asked version {asked_version:?} \
757            is higher than the latest {latest_version:?}"
758    )]
759    ObjectSequenceNumberTooHigh {
760        object_id: ObjectId,
761        asked_version: Version,
762        latest_version: Version,
763    },
764
765    #[error("Object deleted at reference {object_ref:?}.")]
766    ObjectDeleted { object_ref: ObjectRef },
767}
768
769#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
770#[serde(tag = "status", content = "details", rename = "ObjectRead")]
771pub enum SuiPastObjectResponse {
772    /// The object exists and is found with this version
773    VersionFound(SuiObjectData),
774    /// The object does not exist
775    ObjectNotExists(ObjectId),
776    /// The object is found to be deleted with this version
777    ObjectDeleted(SuiObjectRef),
778    /// The object exists but not found with this version
779    VersionNotFound(ObjectId, Version),
780    /// The asked object version is higher than the latest
781    VersionTooHigh {
782        object_id: ObjectId,
783        asked_version: Version,
784        latest_version: Version,
785    },
786}
787
788impl SuiPastObjectResponse {
789    /// Returns a reference to the object if there is any, otherwise an Err
790    pub fn object(&self) -> Result<&SuiObjectData, SuiPastObjectResponseError> {
791        match &self {
792            Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
793                object_ref: oref.to_object_ref(),
794            }),
795            Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
796                object_id: *id,
797                version: None,
798            }),
799            Self::VersionFound(o) => Ok(o),
800            Self::VersionNotFound(id, seq_num) => Err(SuiPastObjectResponseError::ObjectNotFound {
801                object_id: *id,
802                version: Some(*seq_num),
803            }),
804            Self::VersionTooHigh {
805                object_id,
806                asked_version,
807                latest_version,
808            } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
809                object_id: *object_id,
810                asked_version: *asked_version,
811                latest_version: *latest_version,
812            }),
813        }
814    }
815
816    /// Returns the object value if there is any, otherwise an Err
817    pub fn into_object(self) -> Result<SuiObjectData, SuiPastObjectResponseError> {
818        match self {
819            Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
820                object_ref: oref.to_object_ref(),
821            }),
822            Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
823                object_id: id,
824                version: None,
825            }),
826            Self::VersionFound(o) => Ok(o),
827            Self::VersionNotFound(object_id, version) => {
828                Err(SuiPastObjectResponseError::ObjectNotFound {
829                    object_id,
830                    version: Some(version),
831                })
832            }
833            Self::VersionTooHigh {
834                object_id,
835                asked_version,
836                latest_version,
837            } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
838                object_id,
839                asked_version,
840                latest_version,
841            }),
842        }
843    }
844}
845
846#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
847#[serde(rename = "MovePackage", rename_all = "camelCase")]
848pub struct SuiMovePackage {
849    pub disassembled: BTreeMap<String, Value>,
850}
851
852pub type QueryObjectsPage = Page<SuiObjectResponse, CheckpointedObjectId>;
853pub type ObjectsPage = Page<SuiObjectResponse, ObjectId>;
854
855#[serde_as]
856#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
857#[serde(rename_all = "camelCase")]
858pub struct CheckpointedObjectId {
859    pub object_id: ObjectId,
860    #[serde_as(as = "Option<BigInt<u64>>")]
861    #[serde(skip_serializing_if = "Option::is_none")]
862    pub at_checkpoint: Option<Version>,
863}
864
865#[serde_as]
866#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
867#[serde(rename = "GetPastObjectRequest", rename_all = "camelCase")]
868pub struct SuiGetPastObjectRequest {
869    /// the ID of the queried object
870    pub object_id: ObjectId,
871    /// the version of the queried object.
872    #[serde_as(as = "BigInt<u64>")]
873    pub version: Version,
874}
875
876#[serde_as]
877#[derive(Clone, Debug, Serialize, Deserialize)]
878pub enum SuiObjectDataFilter {
879    MatchAll(Vec<SuiObjectDataFilter>),
880    MatchAny(Vec<SuiObjectDataFilter>),
881    MatchNone(Vec<SuiObjectDataFilter>),
882    /// Query by type a specified Package.
883    Package(ObjectId),
884    /// Query by type a specified Move module.
885    MoveModule {
886        /// the Move package ID
887        package: ObjectId,
888        /// the module name
889        #[serde_as(as = "DisplayFromStr")]
890        module: Identifier,
891    },
892    /// Query by type
893    // StructType(#[serde_as(as = "SuiStructTag")] StructTag),
894    StructType(#[serde_as(as = "DisplayFromStr")] StructTag),
895    AddressOwner(SuiAddress),
896    ObjectOwner(ObjectId),
897    ObjectId(ObjectId),
898    // allow querying for multiple object ids
899    ObjectIds(Vec<ObjectId>),
900    Version(#[serde_as(as = "BigInt<u64>")] u64),
901}
902
903impl SuiObjectDataFilter {
904    pub fn gas_coin() -> Self {
905        Self::StructType(StructTag::gas_coin())
906    }
907
908    pub fn and(self, other: Self) -> Self {
909        Self::MatchAll(vec![self, other])
910    }
911    pub fn or(self, other: Self) -> Self {
912        Self::MatchAny(vec![self, other])
913    }
914    pub fn not(self, other: Self) -> Self {
915        Self::MatchNone(vec![self, other])
916    }
917}
918
919#[derive(Debug, Clone, Deserialize, Serialize, Default)]
920#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)]
921pub struct SuiObjectResponseQuery {
922    /// If None, no filter will be applied
923    pub filter: Option<SuiObjectDataFilter>,
924    /// config which fields to include in the response, by default only digest is included
925    pub options: Option<SuiObjectDataOptions>,
926}
927
928impl SuiObjectResponseQuery {
929    pub fn new(filter: Option<SuiObjectDataFilter>, options: Option<SuiObjectDataOptions>) -> Self {
930        Self { filter, options }
931    }
932
933    pub fn new_with_filter(filter: SuiObjectDataFilter) -> Self {
934        Self {
935            filter: Some(filter),
936            options: None,
937        }
938    }
939
940    pub fn new_with_options(options: SuiObjectDataOptions) -> Self {
941        Self {
942            filter: None,
943            options: Some(options),
944        }
945    }
946}