1use std::cmp::Ordering;
5use std::collections::BTreeMap;
6use std::fmt;
7use std::fmt::{Display, Formatter, Write};
8use std::str::FromStr;
9
10use colored::Colorize;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use serde_with::base64::Base64;
14use serde_with::{DisplayFromStr, serde_as};
15use sui_sdk_types::{
16 Address,
17 Digest,
18 Identifier,
19 Input,
20 Object,
21 ObjectReference,
22 StructTag,
23 TypeOrigin,
24 TypeTag,
25 UpgradeInfo,
26 Version,
27};
28
29use super::{Page, SuiMoveStruct, SuiMoveValue};
30use crate::serde::BigInt;
31
32#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
37#[error("Could not get object_id, something went wrong with SuiObjectResponse construction.")]
38pub struct MissingObjectIdError;
39
40#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
41pub struct SuiObjectResponse {
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub data: Option<SuiObjectData>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub error: Option<SuiObjectResponseError>,
46}
47
48impl SuiObjectResponse {
49 pub fn new(data: Option<SuiObjectData>, error: Option<SuiObjectResponseError>) -> Self {
50 Self { data, error }
51 }
52
53 pub fn new_with_data(data: SuiObjectData) -> Self {
54 Self {
55 data: Some(data),
56 error: None,
57 }
58 }
59
60 pub fn new_with_error(error: SuiObjectResponseError) -> Self {
61 Self {
62 data: None,
63 error: Some(error),
64 }
65 }
66
67 pub fn object(&self) -> Result<&SuiObjectData, SuiObjectResponseError> {
70 if let Some(data) = &self.data {
71 Ok(data)
72 } else if let Some(error) = &self.error {
73 Err(error.clone())
74 } else {
75 Err(SuiObjectResponseError::Unknown)
77 }
78 }
79
80 pub fn into_object(self) -> Result<SuiObjectData, SuiObjectResponseError> {
83 match self.object() {
84 Ok(data) => Ok(data.clone()),
85 Err(error) => Err(error),
86 }
87 }
88
89 pub fn move_object_bcs(&self) -> Option<&Vec<u8>> {
90 match &self.data {
91 Some(SuiObjectData {
92 bcs: Some(SuiRawData::MoveObject(obj)),
93 ..
94 }) => Some(&obj.bcs_bytes),
95 _ => None,
96 }
97 }
98
99 pub fn owner(&self) -> Option<Owner> {
100 if let Some(data) = &self.data {
101 return data.owner.clone();
102 }
103 None
104 }
105
106 pub fn object_id(&self) -> Result<Address, MissingObjectIdError> {
107 match (&self.data, &self.error) {
108 (Some(obj_data), None) => Ok(obj_data.object_id),
109 (None, Some(SuiObjectResponseError::NotExists { object_id })) => Ok(*object_id),
110 (
111 None,
112 Some(SuiObjectResponseError::Deleted {
113 object_id,
114 version: _,
115 digest: _,
116 }),
117 ) => Ok(*object_id),
118 _ => Err(MissingObjectIdError),
119 }
120 }
121
122 pub fn object_ref_if_exists(&self) -> Option<(Address, Version, Digest)> {
123 match (&self.data, &self.error) {
124 (Some(obj_data), None) => Some(obj_data.object_ref()),
125 _ => None,
126 }
127 }
128}
129
130impl Ord for SuiObjectResponse {
131 fn cmp(&self, other: &Self) -> Ordering {
132 match (&self.data, &other.data) {
133 (Some(data), Some(data_2)) => {
134 if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Greater) {
135 return Ordering::Greater;
136 } else if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Less) {
137 return Ordering::Less;
138 }
139 Ordering::Equal
140 }
141 (Some(_), None) => Ordering::Less,
143 (None, Some(_)) => Ordering::Greater,
144 _ => Ordering::Equal,
146 }
147 }
148}
149
150impl PartialOrd for SuiObjectResponse {
151 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
152 Some(self.cmp(other))
153 }
154}
155
156#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
158#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
159pub enum SuiObjectResponseError {
160 #[error("Object {:?} does not exist.", object_id)]
161 NotExists { object_id: Address },
162 #[error("Cannot find dynamic field for parent object {:?}.", parent_object_id)]
163 DynamicFieldNotFound { parent_object_id: Address },
164 #[error(
165 "Object has been deleted object_id: {:?} at version: {:?} in digest {:?}",
166 object_id,
167 version,
168 digest
169 )]
170 Deleted {
171 object_id: Address,
172 version: Version,
174 digest: Digest,
176 },
177 #[error("Unknown Error.")]
178 Unknown,
179 #[error("Display Error: {:?}", error)]
180 DisplayError { error: String },
181}
182
183#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
184pub struct DisplayFieldsResponse {
185 pub data: Option<BTreeMap<String, String>>,
186 pub error: Option<SuiObjectResponseError>,
187}
188
189#[derive(thiserror::Error, Debug)]
194pub enum SuiObjectDataError {
195 #[error("Missing object type")]
196 MissingObjectType,
197 #[error("Missing BCS encoding")]
198 MissingBcs,
199 #[error("Missing object owner")]
200 MissingOwner,
201 #[error("Not a Move object")]
202 NotMoveObject,
203 #[error("Not an immutable or owned object")]
204 NotImmOrOwned,
205 #[error("Not a shared object")]
206 NotShared,
207 #[error(transparent)]
208 ObjectType(#[from] NotMoveStructError),
209}
210
211#[derive(thiserror::Error, Debug)]
213#[non_exhaustive]
214pub enum FullObjectDataError {
215 #[error("Missing BCS encoding")]
216 MissingBcs,
217 #[error("Missing object owner")]
218 MissingOwner,
219 #[error("Missing previous transaction digest")]
220 MissingPreviousTransaction,
221 #[error("Missing storage rebate")]
222 MissingStorageRebate,
223 #[error("MoveObject BCS doesn't start with Address")]
224 InvalidBcs,
225 #[error("Invalid identifier: {ident}\nReason: {source}")]
226 InvalidIdentifier {
227 ident: Box<str>,
228 #[source]
229 source: sui_sdk_types::TypeParseError,
230 },
231}
232
233#[serde_as]
234#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
235#[serde(rename_all = "camelCase", rename = "ObjectData")]
236pub struct SuiObjectData {
237 pub object_id: Address,
238 #[serde_as(as = "BigInt<u64>")]
240 pub version: Version,
241 pub digest: Digest,
243 #[serde_as(as = "Option<DisplayFromStr>")]
245 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
246 pub type_: Option<ObjectType>,
247 #[serde(skip_serializing_if = "Option::is_none")]
250 pub owner: Option<Owner>,
251 #[serde(skip_serializing_if = "Option::is_none")]
254 pub previous_transaction: Option<Digest>,
255 #[serde_as(as = "Option<BigInt<u64>>")]
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub storage_rebate: Option<u64>,
261 #[serde(skip_serializing_if = "Option::is_none")]
265 pub display: Option<DisplayFieldsResponse>,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub content: Option<SuiParsedData>,
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub bcs: Option<SuiRawData>,
272}
273
274impl SuiObjectData {
275 pub fn object_ref(&self) -> (Address, Version, Digest) {
276 (self.object_id, self.version, self.digest)
277 }
278
279 pub fn object_type(&self) -> Result<ObjectType, SuiObjectDataError> {
280 self.type_
281 .as_ref()
282 .ok_or(SuiObjectDataError::MissingObjectType)
283 .cloned()
284 }
285
286 pub fn is_gas_coin(&self) -> bool {
287 match self.type_.as_ref() {
288 Some(ObjectType::Struct(ty)) if ty.is_gas_coin() => true,
289 Some(_) => false,
290 None => false,
291 }
292 }
293
294 pub fn struct_tag(&self) -> Result<StructTag, SuiObjectDataError> {
295 Ok(self
296 .type_
297 .clone()
298 .ok_or(SuiObjectDataError::MissingObjectType)?
299 .try_into()?)
300 }
301
302 pub fn take_object_type(&mut self) -> Result<ObjectType, SuiObjectDataError> {
303 self.type_
304 .take()
305 .ok_or(SuiObjectDataError::MissingObjectType)
306 }
307
308 pub fn take_raw_object(&mut self) -> Result<SuiRawMoveObject, SuiObjectDataError> {
309 self.take_raw_data()?
310 .try_into_move()
311 .ok_or(SuiObjectDataError::NotMoveObject)
312 }
313
314 pub fn take_raw_data(&mut self) -> Result<SuiRawData, SuiObjectDataError> {
315 self.bcs.take().ok_or(SuiObjectDataError::MissingBcs)
316 }
317
318 pub fn shared_object_arg(&self, mutable: bool) -> Result<Input, SuiObjectDataError> {
319 let Owner::Shared {
320 initial_shared_version,
321 } = self.owner()?
322 else {
323 return Err(SuiObjectDataError::NotShared);
324 };
325 Ok(Input::Shared {
326 object_id: self.object_id,
327 initial_shared_version,
328 mutable,
329 })
330 }
331
332 pub fn imm_or_owned_object_arg(&self) -> Result<Input, SuiObjectDataError> {
333 use Owner::*;
334 if !matches!(self.owner()?, AddressOwner(_) | ObjectOwner(_) | Immutable) {
335 return Err(SuiObjectDataError::NotImmOrOwned);
336 };
337 let (i, v, d) = self.object_ref();
338 let object_reference = ObjectReference::new(i, v, d);
339 Ok(Input::ImmutableOrOwned(object_reference))
340 }
341
342 #[cfg(feature = "client")]
343 pub(crate) fn object_arg(&self, mutable: bool) -> Result<Input, SuiObjectDataError> {
344 use Owner as O;
345 Ok(match self.owner()? {
346 O::AddressOwner(_) | O::ObjectOwner(_) | O::Immutable => {
347 let (i, v, d) = self.object_ref();
348 let object_reference = ObjectReference::new(i, v, d);
349 Input::ImmutableOrOwned(object_reference)
350 }
351 O::Shared {
352 initial_shared_version,
353 }
354 | O::ConsensusAddressOwner {
355 start_version: initial_shared_version,
356 ..
357 } => Input::Shared {
358 object_id: self.object_id,
359 initial_shared_version,
360 mutable,
361 },
362 })
363 }
364
365 pub fn owner(&self) -> Result<Owner, SuiObjectDataError> {
366 self.owner.clone().ok_or(SuiObjectDataError::MissingOwner)
367 }
368
369 pub fn into_full_object(self) -> Result<Object, FullObjectDataError> {
371 use itertools::Itertools as _;
372 let Self {
373 owner,
374 previous_transaction,
375 storage_rebate,
376 bcs,
377 ..
378 } = self;
379 let owner = owner.ok_or(FullObjectDataError::MissingOwner)?;
380 let previous_transaction =
381 previous_transaction.ok_or(FullObjectDataError::MissingPreviousTransaction)?;
382 let storage_rebate = storage_rebate.ok_or(FullObjectDataError::MissingStorageRebate)?;
383
384 match bcs.ok_or(FullObjectDataError::MissingBcs)? {
385 SuiRawData::Package(p) => {
386 let modules = p
387 .module_map
388 .into_iter()
389 .map(|(s, bytes)| {
390 Ok((
391 s.parse()
392 .map_err(|e| FullObjectDataError::InvalidIdentifier {
393 ident: s.into(),
394 source: e,
395 })?,
396 bytes,
397 ))
398 })
399 .try_collect()?;
400 let inner = sui_sdk_types::MovePackage {
401 id: p.id,
402 version: p.version,
403 modules,
404 type_origin_table: p.type_origin_table,
405 linkage_table: p.linkage_table,
406 };
407 Ok(Object::new(
408 sui_sdk_types::ObjectData::Package(inner),
409 owner.into(),
410 previous_transaction,
411 storage_rebate,
412 ))
413 }
414 SuiRawData::MoveObject(raw_struct) => {
415 let inner = sui_sdk_types::MoveStruct::new(
416 raw_struct.type_,
417 raw_struct.has_public_transfer,
418 raw_struct.version,
419 raw_struct.bcs_bytes,
420 )
421 .ok_or(FullObjectDataError::InvalidBcs)?;
422 Ok(Object::new(
423 sui_sdk_types::ObjectData::Struct(inner),
424 owner.into(),
425 previous_transaction,
426 storage_rebate,
427 ))
428 }
429 }
430 }
431}
432
433impl Display for SuiObjectData {
434 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
435 let type_ = if let Some(type_) = &self.type_ {
436 type_.to_string()
437 } else {
438 "Unknown Type".into()
439 };
440 let mut writer = String::new();
441 writeln!(
442 writer,
443 "{}",
444 format!("----- {type_} ({}[{}]) -----", self.object_id, self.version).bold()
445 )?;
446 if let Some(ref owner) = self.owner {
447 writeln!(writer, "{}: {:?}", "Owner".bold().bright_black(), owner)?;
448 }
449
450 writeln!(
451 writer,
452 "{}: {}",
453 "Version".bold().bright_black(),
454 self.version
455 )?;
456 if let Some(storage_rebate) = self.storage_rebate {
457 writeln!(
458 writer,
459 "{}: {}",
460 "Storage Rebate".bold().bright_black(),
461 storage_rebate
462 )?;
463 }
464
465 if let Some(previous_transaction) = self.previous_transaction {
466 writeln!(
467 writer,
468 "{}: {:?}",
469 "Previous Transaction".bold().bright_black(),
470 previous_transaction
471 )?;
472 }
473 if let Some(content) = self.content.as_ref() {
474 writeln!(writer, "{}", "----- Data -----".bold())?;
475 write!(writer, "{}", content)?;
476 }
477
478 write!(f, "{}", writer)
479 }
480}
481
482#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash, Ord, PartialOrd)]
483pub enum Owner {
484 AddressOwner(Address),
486 ObjectOwner(Address),
489 Shared {
491 initial_shared_version: Version,
493 },
494 Immutable,
496 ConsensusAddressOwner {
498 start_version: Version,
502 owner: Address,
504 },
505}
506
507impl From<Owner> for sui_sdk_types::Owner {
508 fn from(value: Owner) -> sui_sdk_types::Owner {
509 match value {
510 Owner::AddressOwner(a) => sui_sdk_types::Owner::Address(a),
511 Owner::ObjectOwner(o) => sui_sdk_types::Owner::Object(o),
512 Owner::Shared {
513 initial_shared_version,
514 } => sui_sdk_types::Owner::Shared(initial_shared_version),
515 Owner::Immutable => sui_sdk_types::Owner::Immutable,
516 Owner::ConsensusAddressOwner {
517 start_version,
518 owner,
519 } => sui_sdk_types::Owner::ConsensusAddress {
520 start_version,
521 owner,
522 },
523 }
524 }
525}
526
527#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
535pub struct MoveObjectType(MoveObjectType_);
536
537impl fmt::Display for MoveObjectType {
538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
539 let s: StructTag = self.clone().into();
540 write!(f, "{s}")
541 }
542}
543
544impl MoveObjectType {
545 pub const fn is_gas_coin(&self) -> bool {
547 match &self.0 {
548 MoveObjectType_::GasCoin => true,
549 MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => {
550 false
551 }
552 }
553 }
554}
555
556impl From<StructTag> for MoveObjectType {
557 fn from(mut s: StructTag) -> Self {
558 Self(if s == StructTag::gas_coin() {
559 MoveObjectType_::GasCoin
560 } else if s.is_coin().is_some() {
561 MoveObjectType_::Coin(
563 s.type_params
564 .pop()
565 .expect("Coin should have exactly one type parameter"),
566 )
567 } else if s == StructTag::staked_sui() {
568 MoveObjectType_::StakedSui
569 } else {
570 MoveObjectType_::Other(s)
571 })
572 }
573}
574
575impl From<MoveObjectType> for StructTag {
576 fn from(t: MoveObjectType) -> Self {
577 match t.0 {
578 MoveObjectType_::GasCoin => Self::gas_coin(),
579 MoveObjectType_::StakedSui => Self::staked_sui(),
580 MoveObjectType_::Coin(inner) => Self::coin(inner),
581 MoveObjectType_::Other(s) => s,
582 }
583 }
584}
585
586impl From<MoveObjectType> for TypeTag {
587 fn from(o: MoveObjectType) -> Self {
588 let s: StructTag = o.into();
589 Self::Struct(Box::new(s))
590 }
591}
592
593#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
598enum MoveObjectType_ {
599 Other(StructTag),
601 GasCoin,
603 StakedSui,
605 Coin(TypeTag),
607 }
611
612const PACKAGE: &str = "package";
617#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
621pub enum ObjectType {
622 Package,
624 Struct(MoveObjectType),
626}
627
628impl TryFrom<ObjectType> for StructTag {
629 type Error = NotMoveStructError;
630
631 fn try_from(o: ObjectType) -> Result<Self, Self::Error> {
632 match o {
633 ObjectType::Package => Err(NotMoveStructError),
634 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
635 }
636 }
637}
638
639#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
640#[error("Cannot create StructTag from Package")]
641pub struct NotMoveStructError;
642
643impl Display for ObjectType {
644 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
645 match self {
646 ObjectType::Package => write!(f, "{}", PACKAGE),
647 ObjectType::Struct(t) => write!(f, "{}", t),
648 }
649 }
650}
651
652impl FromStr for ObjectType {
653 type Err = <StructTag as FromStr>::Err;
654
655 fn from_str(s: &str) -> Result<Self, Self::Err> {
656 if s.to_lowercase() == PACKAGE {
657 Ok(ObjectType::Package)
658 } else {
659 let tag: StructTag = s.parse()?;
660 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
661 }
662 }
663}
664
665#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)]
670#[serde(rename_all = "camelCase", rename = "ObjectDataOptions", default)]
671pub struct SuiObjectDataOptions {
672 pub show_type: bool,
674 pub show_owner: bool,
676 pub show_previous_transaction: bool,
678 pub show_display: bool,
680 pub show_content: bool,
683 pub show_bcs: bool,
685 pub show_storage_rebate: bool,
687}
688
689impl SuiObjectDataOptions {
690 pub fn new() -> Self {
691 Self::default()
692 }
693
694 pub fn full_object() -> Self {
696 Self {
697 show_bcs: true,
698 show_owner: true,
699 show_storage_rebate: true,
700 show_previous_transaction: true,
701 show_content: false,
702 show_display: false,
703 show_type: false,
704 }
705 }
706
707 pub fn bcs_lossless() -> Self {
709 Self {
710 show_bcs: true,
711 show_type: true,
712 show_owner: true,
713 show_previous_transaction: true,
714 show_display: false,
715 show_content: false,
716 show_storage_rebate: true,
717 }
718 }
719
720 pub fn full_content() -> Self {
722 Self {
723 show_bcs: false,
724 show_type: true,
725 show_owner: true,
726 show_previous_transaction: true,
727 show_display: false,
728 show_content: true,
729 show_storage_rebate: true,
730 }
731 }
732
733 pub fn with_content(mut self) -> Self {
734 self.show_content = true;
735 self
736 }
737
738 pub fn with_owner(mut self) -> Self {
739 self.show_owner = true;
740 self
741 }
742
743 pub fn with_type(mut self) -> Self {
744 self.show_type = true;
745 self
746 }
747
748 pub fn with_display(mut self) -> Self {
749 self.show_display = true;
750 self
751 }
752
753 pub fn with_bcs(mut self) -> Self {
754 self.show_bcs = true;
755 self
756 }
757
758 pub fn with_previous_transaction(mut self) -> Self {
759 self.show_previous_transaction = true;
760 self
761 }
762
763 pub fn is_not_in_object_info(&self) -> bool {
764 self.show_bcs || self.show_content || self.show_display || self.show_storage_rebate
765 }
766}
767
768#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)]
773#[serde(rename_all = "camelCase", rename = "ObjectRef")]
774pub struct SuiObjectRef {
775 pub object_id: Address,
777 pub version: Version,
779 pub digest: Digest,
781}
782
783impl SuiObjectRef {
784 pub fn to_object_ref(&self) -> (Address, Version, Digest) {
785 (self.object_id, self.version, self.digest)
786 }
787}
788
789impl Display for SuiObjectRef {
790 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
791 write!(
792 f,
793 "Object ID: {}, version: {}, digest: {}",
794 self.object_id, self.version, self.digest
795 )
796 }
797}
798
799impl From<(Address, Version, Digest)> for SuiObjectRef {
800 fn from(oref: (Address, Version, Digest)) -> Self {
801 Self {
802 object_id: oref.0,
803 version: oref.1,
804 digest: oref.2,
805 }
806 }
807}
808
809pub trait SuiData: Sized {
814 type ObjectType;
815 type PackageType;
816 fn try_as_move(&self) -> Option<&Self::ObjectType>;
817 fn try_into_move(self) -> Option<Self::ObjectType>;
818 fn try_as_package(&self) -> Option<&Self::PackageType>;
819 fn type_(&self) -> Option<&StructTag>;
820}
821
822#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
823#[serde(tag = "dataType", rename_all = "camelCase", rename = "RawData")]
824pub enum SuiRawData {
825 MoveObject(SuiRawMoveObject),
827 Package(SuiRawMovePackage),
828}
829
830impl SuiData for SuiRawData {
831 type ObjectType = SuiRawMoveObject;
832 type PackageType = SuiRawMovePackage;
833
834 fn try_as_move(&self) -> Option<&Self::ObjectType> {
835 match self {
836 Self::MoveObject(o) => Some(o),
837 Self::Package(_) => None,
838 }
839 }
840
841 fn try_into_move(self) -> Option<Self::ObjectType> {
842 match self {
843 Self::MoveObject(o) => Some(o),
844 Self::Package(_) => None,
845 }
846 }
847
848 fn try_as_package(&self) -> Option<&Self::PackageType> {
849 match self {
850 Self::MoveObject(_) => None,
851 Self::Package(p) => Some(p),
852 }
853 }
854
855 fn type_(&self) -> Option<&StructTag> {
856 match self {
857 Self::MoveObject(o) => Some(&o.type_),
858 Self::Package(_) => None,
859 }
860 }
861}
862
863#[allow(clippy::large_enum_variant)]
864#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
865#[serde(tag = "dataType", rename_all = "camelCase", rename = "Data")]
866pub enum SuiParsedData {
867 MoveObject(SuiParsedMoveObject),
869 Package(SuiMovePackage),
870}
871
872impl SuiData for SuiParsedData {
873 type ObjectType = SuiParsedMoveObject;
874 type PackageType = SuiMovePackage;
875
876 fn try_as_move(&self) -> Option<&Self::ObjectType> {
877 match self {
878 Self::MoveObject(o) => Some(o),
879 Self::Package(_) => None,
880 }
881 }
882
883 fn try_into_move(self) -> Option<Self::ObjectType> {
884 match self {
885 Self::MoveObject(o) => Some(o),
886 Self::Package(_) => None,
887 }
888 }
889
890 fn try_as_package(&self) -> Option<&Self::PackageType> {
891 match self {
892 Self::MoveObject(_) => None,
893 Self::Package(p) => Some(p),
894 }
895 }
896
897 fn type_(&self) -> Option<&StructTag> {
898 match self {
899 Self::MoveObject(o) => Some(&o.type_),
900 Self::Package(_) => None,
901 }
902 }
903}
904
905impl Display for SuiParsedData {
906 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
907 let mut writer = String::new();
908 match self {
909 SuiParsedData::MoveObject(o) => {
910 writeln!(writer, "{}: {}", "type".bold().bright_black(), o.type_)?;
911 write!(writer, "{}", &o.fields)?;
912 }
913 SuiParsedData::Package(p) => {
914 write!(
915 writer,
916 "{}: {:?}",
917 "Modules".bold().bright_black(),
918 p.disassembled.keys()
919 )?;
920 }
921 }
922 write!(f, "{}", writer)
923 }
924}
925
926pub trait SuiMoveObject: Sized {
927 fn type_(&self) -> &StructTag;
928}
929
930#[serde_as]
931#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
932#[serde(rename = "MoveObject", rename_all = "camelCase")]
933pub struct SuiParsedMoveObject {
934 #[serde(rename = "type")]
935 #[serde_as(as = "DisplayFromStr")]
937 pub type_: StructTag,
938 pub has_public_transfer: bool,
939 pub fields: SuiMoveStruct,
940}
941
942impl SuiMoveObject for SuiParsedMoveObject {
943 fn type_(&self) -> &StructTag {
944 &self.type_
945 }
946}
947
948impl SuiParsedMoveObject {
949 pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<SuiMoveValue> {
950 match &self.fields {
951 SuiMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
952 SuiMoveStruct::WithTypes { fields, .. } => fields.get(field_name).cloned(),
953 _ => None,
954 }
955 }
956}
957
958#[serde_as]
959#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
960#[serde(rename = "RawMoveObject", rename_all = "camelCase")]
961pub struct SuiRawMoveObject {
962 #[serde(rename = "type")]
963 #[serde_as(as = "DisplayFromStr")]
965 pub type_: StructTag,
966 pub has_public_transfer: bool,
967 pub version: Version,
968 #[serde_as(as = "Base64")]
969 pub bcs_bytes: Vec<u8>,
970}
971
972impl SuiMoveObject for SuiRawMoveObject {
973 fn type_(&self) -> &StructTag {
974 &self.type_
975 }
976}
977
978impl SuiRawMoveObject {
979 pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result<T, bcs::Error> {
980 bcs::from_bytes(self.bcs_bytes.as_slice())
981 }
982}
983
984#[serde_as]
985#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
986#[serde(rename = "RawMovePackage", rename_all = "camelCase")]
987pub struct SuiRawMovePackage {
988 pub id: Address,
989 pub version: Version,
990 #[serde_as(as = "BTreeMap<_, Base64>")]
991 pub module_map: BTreeMap<String, Vec<u8>>,
992 pub type_origin_table: Vec<TypeOrigin>,
993 pub linkage_table: BTreeMap<Address, UpgradeInfo>,
994}
995
996#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
998pub enum SuiPastObjectResponseError {
999 #[error("Could not find the referenced object {object_id:?} at version {version:?}.")]
1000 ObjectNotFound {
1001 object_id: Address,
1002 version: Option<Version>,
1003 },
1004
1005 #[error(
1006 "Could not find the referenced object {object_id:?} \
1007 as the asked version {asked_version:?} \
1008 is higher than the latest {latest_version:?}"
1009 )]
1010 ObjectSequenceNumberTooHigh {
1011 object_id: Address,
1012 asked_version: Version,
1013 latest_version: Version,
1014 },
1015
1016 #[error("Object deleted at reference {object_ref:?}.")]
1017 ObjectDeleted {
1018 object_ref: (Address, Version, Digest),
1019 },
1020}
1021
1022#[rustversion::attr(nightly, expect(clippy::large_enum_variant))]
1023#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
1024#[serde(tag = "status", content = "details", rename = "ObjectRead")]
1025pub enum SuiPastObjectResponse {
1026 VersionFound(SuiObjectData),
1028 ObjectNotExists(Address),
1030 ObjectDeleted(SuiObjectRef),
1032 VersionNotFound(Address, Version),
1034 VersionTooHigh {
1036 object_id: Address,
1037 asked_version: Version,
1038 latest_version: Version,
1039 },
1040}
1041
1042impl SuiPastObjectResponse {
1043 pub fn object(&self) -> Result<&SuiObjectData, SuiPastObjectResponseError> {
1045 match &self {
1046 Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
1047 object_ref: oref.to_object_ref(),
1048 }),
1049 Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
1050 object_id: *id,
1051 version: None,
1052 }),
1053 Self::VersionFound(o) => Ok(o),
1054 Self::VersionNotFound(id, seq_num) => Err(SuiPastObjectResponseError::ObjectNotFound {
1055 object_id: *id,
1056 version: Some(*seq_num),
1057 }),
1058 Self::VersionTooHigh {
1059 object_id,
1060 asked_version,
1061 latest_version,
1062 } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
1063 object_id: *object_id,
1064 asked_version: *asked_version,
1065 latest_version: *latest_version,
1066 }),
1067 }
1068 }
1069
1070 pub fn into_object(self) -> Result<SuiObjectData, SuiPastObjectResponseError> {
1072 match self {
1073 Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
1074 object_ref: oref.to_object_ref(),
1075 }),
1076 Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
1077 object_id: id,
1078 version: None,
1079 }),
1080 Self::VersionFound(o) => Ok(o),
1081 Self::VersionNotFound(object_id, version) => {
1082 Err(SuiPastObjectResponseError::ObjectNotFound {
1083 object_id,
1084 version: Some(version),
1085 })
1086 }
1087 Self::VersionTooHigh {
1088 object_id,
1089 asked_version,
1090 latest_version,
1091 } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
1092 object_id,
1093 asked_version,
1094 latest_version,
1095 }),
1096 }
1097 }
1098}
1099
1100#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
1101#[serde(rename = "MovePackage", rename_all = "camelCase")]
1102pub struct SuiMovePackage {
1103 pub disassembled: BTreeMap<String, Value>,
1104}
1105
1106pub type QueryObjectsPage = Page<SuiObjectResponse, CheckpointedObjectId>;
1107pub type ObjectsPage = Page<SuiObjectResponse, Address>;
1108
1109#[serde_as]
1110#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
1111#[serde(rename_all = "camelCase")]
1112pub struct CheckpointedObjectId {
1113 pub object_id: Address,
1114 #[serde_as(as = "Option<BigInt<u64>>")]
1115 #[serde(skip_serializing_if = "Option::is_none")]
1116 pub at_checkpoint: Option<Version>,
1117}
1118
1119#[serde_as]
1120#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
1121#[serde(rename = "GetPastObjectRequest", rename_all = "camelCase")]
1122pub struct SuiGetPastObjectRequest {
1123 pub object_id: Address,
1125 #[serde_as(as = "BigInt<u64>")]
1127 pub version: Version,
1128}
1129
1130#[serde_as]
1131#[derive(Clone, Debug, Serialize, Deserialize)]
1132pub enum SuiObjectDataFilter {
1133 MatchAll(Vec<SuiObjectDataFilter>),
1134 MatchAny(Vec<SuiObjectDataFilter>),
1135 MatchNone(Vec<SuiObjectDataFilter>),
1136 Package(Address),
1138 MoveModule {
1140 package: Address,
1142 #[serde_as(as = "DisplayFromStr")]
1144 module: Identifier,
1145 },
1146 StructType(#[serde_as(as = "DisplayFromStr")] StructTag),
1149 AddressOwner(Address),
1150 ObjectOwner(Address),
1151 Address(Address),
1152 ObjectIds(Vec<Address>),
1154 Version(#[serde_as(as = "BigInt<u64>")] u64),
1155}
1156
1157impl SuiObjectDataFilter {
1158 pub fn gas_coin() -> Self {
1159 Self::StructType(StructTag::gas_coin())
1160 }
1161
1162 pub fn and(self, other: Self) -> Self {
1163 Self::MatchAll(vec![self, other])
1164 }
1165 pub fn or(self, other: Self) -> Self {
1166 Self::MatchAny(vec![self, other])
1167 }
1168 pub fn not(self, other: Self) -> Self {
1169 Self::MatchNone(vec![self, other])
1170 }
1171}
1172
1173#[derive(Debug, Clone, Deserialize, Serialize, Default)]
1174#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)]
1175pub struct SuiObjectResponseQuery {
1176 pub filter: Option<SuiObjectDataFilter>,
1178 pub options: Option<SuiObjectDataOptions>,
1180}
1181
1182impl SuiObjectResponseQuery {
1183 pub fn new(filter: Option<SuiObjectDataFilter>, options: Option<SuiObjectDataOptions>) -> Self {
1184 Self { filter, options }
1185 }
1186
1187 pub fn new_with_filter(filter: SuiObjectDataFilter) -> Self {
1188 Self {
1189 filter: Some(filter),
1190 options: None,
1191 }
1192 }
1193
1194 pub fn new_with_options(options: SuiObjectDataOptions) -> Self {
1195 Self {
1196 filter: None,
1197 options: Some(options),
1198 }
1199 }
1200}