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(s: StructTag) -> Self {
558 Self(if s == StructTag::gas_coin() {
559 MoveObjectType_::GasCoin
560 } else if let Some(coin_type) = s.is_coin() {
561 MoveObjectType_::Coin(coin_type.clone())
562 } else if s == StructTag::staked_sui() {
563 MoveObjectType_::StakedSui
564 } else {
565 MoveObjectType_::Other(s)
566 })
567 }
568}
569
570impl From<MoveObjectType> for StructTag {
571 fn from(t: MoveObjectType) -> Self {
572 match t.0 {
573 MoveObjectType_::GasCoin => Self::gas_coin(),
574 MoveObjectType_::StakedSui => Self::staked_sui(),
575 MoveObjectType_::Coin(inner) => Self::coin(inner),
576 MoveObjectType_::Other(s) => s,
577 }
578 }
579}
580
581impl From<MoveObjectType> for TypeTag {
582 fn from(o: MoveObjectType) -> Self {
583 let s: StructTag = o.into();
584 Self::Struct(Box::new(s))
585 }
586}
587
588#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
593enum MoveObjectType_ {
594 Other(StructTag),
596 GasCoin,
598 StakedSui,
600 Coin(TypeTag),
602 }
606
607const PACKAGE: &str = "package";
612#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
616pub enum ObjectType {
617 Package,
619 Struct(MoveObjectType),
621}
622
623impl TryFrom<ObjectType> for StructTag {
624 type Error = NotMoveStructError;
625
626 fn try_from(o: ObjectType) -> Result<Self, Self::Error> {
627 match o {
628 ObjectType::Package => Err(NotMoveStructError),
629 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
630 }
631 }
632}
633
634#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
635#[error("Cannot create StructTag from Package")]
636pub struct NotMoveStructError;
637
638impl Display for ObjectType {
639 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
640 match self {
641 ObjectType::Package => write!(f, "{}", PACKAGE),
642 ObjectType::Struct(t) => write!(f, "{}", t),
643 }
644 }
645}
646
647impl FromStr for ObjectType {
648 type Err = <StructTag as FromStr>::Err;
649
650 fn from_str(s: &str) -> Result<Self, Self::Err> {
651 if s.to_lowercase() == PACKAGE {
652 Ok(ObjectType::Package)
653 } else {
654 let tag: StructTag = s.parse()?;
655 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
656 }
657 }
658}
659
660#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)]
665#[serde(rename_all = "camelCase", rename = "ObjectDataOptions", default)]
666pub struct SuiObjectDataOptions {
667 pub show_type: bool,
669 pub show_owner: bool,
671 pub show_previous_transaction: bool,
673 pub show_display: bool,
675 pub show_content: bool,
678 pub show_bcs: bool,
680 pub show_storage_rebate: bool,
682}
683
684impl SuiObjectDataOptions {
685 pub fn new() -> Self {
686 Self::default()
687 }
688
689 pub fn full_object() -> Self {
691 Self {
692 show_bcs: true,
693 show_owner: true,
694 show_storage_rebate: true,
695 show_previous_transaction: true,
696 show_content: false,
697 show_display: false,
698 show_type: false,
699 }
700 }
701
702 pub fn bcs_lossless() -> Self {
704 Self {
705 show_bcs: true,
706 show_type: true,
707 show_owner: true,
708 show_previous_transaction: true,
709 show_display: false,
710 show_content: false,
711 show_storage_rebate: true,
712 }
713 }
714
715 pub fn full_content() -> Self {
717 Self {
718 show_bcs: false,
719 show_type: true,
720 show_owner: true,
721 show_previous_transaction: true,
722 show_display: false,
723 show_content: true,
724 show_storage_rebate: true,
725 }
726 }
727
728 pub fn with_content(mut self) -> Self {
729 self.show_content = true;
730 self
731 }
732
733 pub fn with_owner(mut self) -> Self {
734 self.show_owner = true;
735 self
736 }
737
738 pub fn with_type(mut self) -> Self {
739 self.show_type = true;
740 self
741 }
742
743 pub fn with_display(mut self) -> Self {
744 self.show_display = true;
745 self
746 }
747
748 pub fn with_bcs(mut self) -> Self {
749 self.show_bcs = true;
750 self
751 }
752
753 pub fn with_previous_transaction(mut self) -> Self {
754 self.show_previous_transaction = true;
755 self
756 }
757
758 pub fn is_not_in_object_info(&self) -> bool {
759 self.show_bcs || self.show_content || self.show_display || self.show_storage_rebate
760 }
761}
762
763#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)]
768#[serde(rename_all = "camelCase", rename = "ObjectRef")]
769pub struct SuiObjectRef {
770 pub object_id: Address,
772 pub version: Version,
774 pub digest: Digest,
776}
777
778impl SuiObjectRef {
779 pub fn to_object_ref(&self) -> (Address, Version, Digest) {
780 (self.object_id, self.version, self.digest)
781 }
782}
783
784impl Display for SuiObjectRef {
785 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
786 write!(
787 f,
788 "Object ID: {}, version: {}, digest: {}",
789 self.object_id, self.version, self.digest
790 )
791 }
792}
793
794impl From<(Address, Version, Digest)> for SuiObjectRef {
795 fn from(oref: (Address, Version, Digest)) -> Self {
796 Self {
797 object_id: oref.0,
798 version: oref.1,
799 digest: oref.2,
800 }
801 }
802}
803
804pub trait SuiData: Sized {
809 type ObjectType;
810 type PackageType;
811 fn try_as_move(&self) -> Option<&Self::ObjectType>;
812 fn try_into_move(self) -> Option<Self::ObjectType>;
813 fn try_as_package(&self) -> Option<&Self::PackageType>;
814 fn type_(&self) -> Option<&StructTag>;
815}
816
817#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
818#[serde(tag = "dataType", rename_all = "camelCase", rename = "RawData")]
819pub enum SuiRawData {
820 MoveObject(SuiRawMoveObject),
822 Package(SuiRawMovePackage),
823}
824
825impl SuiData for SuiRawData {
826 type ObjectType = SuiRawMoveObject;
827 type PackageType = SuiRawMovePackage;
828
829 fn try_as_move(&self) -> Option<&Self::ObjectType> {
830 match self {
831 Self::MoveObject(o) => Some(o),
832 Self::Package(_) => None,
833 }
834 }
835
836 fn try_into_move(self) -> Option<Self::ObjectType> {
837 match self {
838 Self::MoveObject(o) => Some(o),
839 Self::Package(_) => None,
840 }
841 }
842
843 fn try_as_package(&self) -> Option<&Self::PackageType> {
844 match self {
845 Self::MoveObject(_) => None,
846 Self::Package(p) => Some(p),
847 }
848 }
849
850 fn type_(&self) -> Option<&StructTag> {
851 match self {
852 Self::MoveObject(o) => Some(&o.type_),
853 Self::Package(_) => None,
854 }
855 }
856}
857
858#[allow(clippy::large_enum_variant)]
859#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
860#[serde(tag = "dataType", rename_all = "camelCase", rename = "Data")]
861pub enum SuiParsedData {
862 MoveObject(SuiParsedMoveObject),
864 Package(SuiMovePackage),
865}
866
867impl SuiData for SuiParsedData {
868 type ObjectType = SuiParsedMoveObject;
869 type PackageType = SuiMovePackage;
870
871 fn try_as_move(&self) -> Option<&Self::ObjectType> {
872 match self {
873 Self::MoveObject(o) => Some(o),
874 Self::Package(_) => None,
875 }
876 }
877
878 fn try_into_move(self) -> Option<Self::ObjectType> {
879 match self {
880 Self::MoveObject(o) => Some(o),
881 Self::Package(_) => None,
882 }
883 }
884
885 fn try_as_package(&self) -> Option<&Self::PackageType> {
886 match self {
887 Self::MoveObject(_) => None,
888 Self::Package(p) => Some(p),
889 }
890 }
891
892 fn type_(&self) -> Option<&StructTag> {
893 match self {
894 Self::MoveObject(o) => Some(&o.type_),
895 Self::Package(_) => None,
896 }
897 }
898}
899
900impl Display for SuiParsedData {
901 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
902 let mut writer = String::new();
903 match self {
904 SuiParsedData::MoveObject(o) => {
905 writeln!(writer, "{}: {}", "type".bold().bright_black(), o.type_)?;
906 write!(writer, "{}", &o.fields)?;
907 }
908 SuiParsedData::Package(p) => {
909 write!(
910 writer,
911 "{}: {:?}",
912 "Modules".bold().bright_black(),
913 p.disassembled.keys()
914 )?;
915 }
916 }
917 write!(f, "{}", writer)
918 }
919}
920
921pub trait SuiMoveObject: Sized {
922 fn type_(&self) -> &StructTag;
923}
924
925#[serde_as]
926#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
927#[serde(rename = "MoveObject", rename_all = "camelCase")]
928pub struct SuiParsedMoveObject {
929 #[serde(rename = "type")]
930 #[serde_as(as = "DisplayFromStr")]
932 pub type_: StructTag,
933 pub has_public_transfer: bool,
934 pub fields: SuiMoveStruct,
935}
936
937impl SuiMoveObject for SuiParsedMoveObject {
938 fn type_(&self) -> &StructTag {
939 &self.type_
940 }
941}
942
943impl SuiParsedMoveObject {
944 pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<SuiMoveValue> {
945 match &self.fields {
946 SuiMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
947 SuiMoveStruct::WithTypes { fields, .. } => fields.get(field_name).cloned(),
948 _ => None,
949 }
950 }
951}
952
953#[serde_as]
954#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
955#[serde(rename = "RawMoveObject", rename_all = "camelCase")]
956pub struct SuiRawMoveObject {
957 #[serde(rename = "type")]
958 #[serde_as(as = "DisplayFromStr")]
960 pub type_: StructTag,
961 pub has_public_transfer: bool,
962 pub version: Version,
963 #[serde_as(as = "Base64")]
964 pub bcs_bytes: Vec<u8>,
965}
966
967impl SuiMoveObject for SuiRawMoveObject {
968 fn type_(&self) -> &StructTag {
969 &self.type_
970 }
971}
972
973impl SuiRawMoveObject {
974 pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result<T, sui_sdk_types::bcs::Error> {
975 sui_sdk_types::bcs::FromBcs::from_bcs(&self.bcs_bytes)
976 }
977}
978
979#[serde_as]
980#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
981#[serde(rename = "RawMovePackage", rename_all = "camelCase")]
982pub struct SuiRawMovePackage {
983 pub id: Address,
984 pub version: Version,
985 #[serde_as(as = "BTreeMap<_, Base64>")]
986 pub module_map: BTreeMap<String, Vec<u8>>,
987 pub type_origin_table: Vec<TypeOrigin>,
988 pub linkage_table: BTreeMap<Address, UpgradeInfo>,
989}
990
991#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
993pub enum SuiPastObjectResponseError {
994 #[error("Could not find the referenced object {object_id:?} at version {version:?}.")]
995 ObjectNotFound {
996 object_id: Address,
997 version: Option<Version>,
998 },
999
1000 #[error(
1001 "Could not find the referenced object {object_id:?} \
1002 as the asked version {asked_version:?} \
1003 is higher than the latest {latest_version:?}"
1004 )]
1005 ObjectSequenceNumberTooHigh {
1006 object_id: Address,
1007 asked_version: Version,
1008 latest_version: Version,
1009 },
1010
1011 #[error("Object deleted at reference {object_ref:?}.")]
1012 ObjectDeleted {
1013 object_ref: (Address, Version, Digest),
1014 },
1015}
1016
1017#[rustversion::attr(nightly, expect(clippy::large_enum_variant))]
1018#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
1019#[serde(tag = "status", content = "details", rename = "ObjectRead")]
1020pub enum SuiPastObjectResponse {
1021 VersionFound(SuiObjectData),
1023 ObjectNotExists(Address),
1025 ObjectDeleted(SuiObjectRef),
1027 VersionNotFound(Address, Version),
1029 VersionTooHigh {
1031 object_id: Address,
1032 asked_version: Version,
1033 latest_version: Version,
1034 },
1035}
1036
1037impl SuiPastObjectResponse {
1038 pub fn object(&self) -> Result<&SuiObjectData, SuiPastObjectResponseError> {
1040 match &self {
1041 Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
1042 object_ref: oref.to_object_ref(),
1043 }),
1044 Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
1045 object_id: *id,
1046 version: None,
1047 }),
1048 Self::VersionFound(o) => Ok(o),
1049 Self::VersionNotFound(id, seq_num) => Err(SuiPastObjectResponseError::ObjectNotFound {
1050 object_id: *id,
1051 version: Some(*seq_num),
1052 }),
1053 Self::VersionTooHigh {
1054 object_id,
1055 asked_version,
1056 latest_version,
1057 } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
1058 object_id: *object_id,
1059 asked_version: *asked_version,
1060 latest_version: *latest_version,
1061 }),
1062 }
1063 }
1064
1065 pub fn into_object(self) -> Result<SuiObjectData, SuiPastObjectResponseError> {
1067 match self {
1068 Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
1069 object_ref: oref.to_object_ref(),
1070 }),
1071 Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
1072 object_id: id,
1073 version: None,
1074 }),
1075 Self::VersionFound(o) => Ok(o),
1076 Self::VersionNotFound(object_id, version) => {
1077 Err(SuiPastObjectResponseError::ObjectNotFound {
1078 object_id,
1079 version: Some(version),
1080 })
1081 }
1082 Self::VersionTooHigh {
1083 object_id,
1084 asked_version,
1085 latest_version,
1086 } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
1087 object_id,
1088 asked_version,
1089 latest_version,
1090 }),
1091 }
1092 }
1093}
1094
1095#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
1096#[serde(rename = "MovePackage", rename_all = "camelCase")]
1097pub struct SuiMovePackage {
1098 pub disassembled: BTreeMap<String, Value>,
1099}
1100
1101pub type QueryObjectsPage = Page<SuiObjectResponse, CheckpointedObjectId>;
1102pub type ObjectsPage = Page<SuiObjectResponse, Address>;
1103
1104#[serde_as]
1105#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
1106#[serde(rename_all = "camelCase")]
1107pub struct CheckpointedObjectId {
1108 pub object_id: Address,
1109 #[serde_as(as = "Option<BigInt<u64>>")]
1110 #[serde(skip_serializing_if = "Option::is_none")]
1111 pub at_checkpoint: Option<Version>,
1112}
1113
1114#[serde_as]
1115#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
1116#[serde(rename = "GetPastObjectRequest", rename_all = "camelCase")]
1117pub struct SuiGetPastObjectRequest {
1118 pub object_id: Address,
1120 #[serde_as(as = "BigInt<u64>")]
1122 pub version: Version,
1123}
1124
1125#[serde_as]
1126#[derive(Clone, Debug, Serialize, Deserialize)]
1127pub enum SuiObjectDataFilter {
1128 MatchAll(Vec<SuiObjectDataFilter>),
1129 MatchAny(Vec<SuiObjectDataFilter>),
1130 MatchNone(Vec<SuiObjectDataFilter>),
1131 Package(Address),
1133 MoveModule {
1135 package: Address,
1137 #[serde_as(as = "DisplayFromStr")]
1139 module: Identifier,
1140 },
1141 StructType(#[serde_as(as = "DisplayFromStr")] StructTag),
1144 AddressOwner(Address),
1145 ObjectOwner(Address),
1146 Address(Address),
1147 ObjectIds(Vec<Address>),
1149 Version(#[serde_as(as = "BigInt<u64>")] u64),
1150}
1151
1152impl SuiObjectDataFilter {
1153 pub fn gas_coin() -> Self {
1154 Self::StructType(StructTag::gas_coin())
1155 }
1156
1157 pub fn and(self, other: Self) -> Self {
1158 Self::MatchAll(vec![self, other])
1159 }
1160 pub fn or(self, other: Self) -> Self {
1161 Self::MatchAny(vec![self, other])
1162 }
1163 pub fn not(self, other: Self) -> Self {
1164 Self::MatchNone(vec![self, other])
1165 }
1166}
1167
1168#[derive(Debug, Clone, Deserialize, Serialize, Default)]
1169#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)]
1170pub struct SuiObjectResponseQuery {
1171 pub filter: Option<SuiObjectDataFilter>,
1173 pub options: Option<SuiObjectDataOptions>,
1175}
1176
1177impl SuiObjectResponseQuery {
1178 pub fn new(filter: Option<SuiObjectDataFilter>, options: Option<SuiObjectDataOptions>) -> Self {
1179 Self { filter, options }
1180 }
1181
1182 pub fn new_with_filter(filter: SuiObjectDataFilter) -> Self {
1183 Self {
1184 filter: Some(filter),
1185 options: None,
1186 }
1187 }
1188
1189 pub fn new_with_options(options: SuiObjectDataOptions) -> Self {
1190 Self {
1191 filter: None,
1192 options: Some(options),
1193 }
1194 }
1195}