1use 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 MoveObject,
14 MoveObjectType,
15 Object,
16 ObjectArg,
17 ObjectDigest,
18 ObjectId,
19 ObjectRef,
20 Owner,
21 StructTag,
22 TransactionDigest,
23 TypeOrigin,
24 UpgradeInfo,
25};
26use colored::Colorize;
27use serde::{Deserialize, Serialize};
28use serde_json::Value;
29use serde_with::base64::Base64;
30use serde_with::{DisplayFromStr, serde_as};
31use sui_sdk_types::Version;
32
33use super::{Page, SuiMoveStruct, SuiMoveValue};
34use crate::serde::BigInt;
35
36#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
41#[error("Could not get object_id, something went wrong with SuiObjectResponse construction.")]
42pub struct MissingObjectIdError;
43
44#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
45pub struct SuiObjectResponse {
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub data: Option<SuiObjectData>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub error: Option<SuiObjectResponseError>,
50}
51
52impl SuiObjectResponse {
53 pub fn new(data: Option<SuiObjectData>, error: Option<SuiObjectResponseError>) -> Self {
54 Self { data, error }
55 }
56
57 pub fn new_with_data(data: SuiObjectData) -> Self {
58 Self {
59 data: Some(data),
60 error: None,
61 }
62 }
63
64 pub fn new_with_error(error: SuiObjectResponseError) -> Self {
65 Self {
66 data: None,
67 error: Some(error),
68 }
69 }
70
71 pub fn object(&self) -> Result<&SuiObjectData, SuiObjectResponseError> {
74 if let Some(data) = &self.data {
75 Ok(data)
76 } else if let Some(error) = &self.error {
77 Err(error.clone())
78 } else {
79 Err(SuiObjectResponseError::Unknown)
81 }
82 }
83
84 pub fn into_object(self) -> Result<SuiObjectData, SuiObjectResponseError> {
87 match self.object() {
88 Ok(data) => Ok(data.clone()),
89 Err(error) => Err(error),
90 }
91 }
92
93 pub fn move_object_bcs(&self) -> Option<&Vec<u8>> {
94 match &self.data {
95 Some(SuiObjectData {
96 bcs: Some(SuiRawData::MoveObject(obj)),
97 ..
98 }) => Some(&obj.bcs_bytes),
99 _ => None,
100 }
101 }
102
103 pub fn owner(&self) -> Option<Owner> {
104 if let Some(data) = &self.data {
105 return data.owner.clone();
106 }
107 None
108 }
109
110 pub fn object_id(&self) -> Result<ObjectId, MissingObjectIdError> {
111 match (&self.data, &self.error) {
112 (Some(obj_data), None) => Ok(obj_data.object_id),
113 (None, Some(SuiObjectResponseError::NotExists { object_id })) => Ok(*object_id),
114 (
115 None,
116 Some(SuiObjectResponseError::Deleted {
117 object_id,
118 version: _,
119 digest: _,
120 }),
121 ) => Ok(*object_id),
122 _ => Err(MissingObjectIdError),
123 }
124 }
125
126 pub fn object_ref_if_exists(&self) -> Option<ObjectRef> {
127 match (&self.data, &self.error) {
128 (Some(obj_data), None) => Some(obj_data.object_ref()),
129 _ => None,
130 }
131 }
132}
133
134impl Ord for SuiObjectResponse {
135 fn cmp(&self, other: &Self) -> Ordering {
136 match (&self.data, &other.data) {
137 (Some(data), Some(data_2)) => {
138 if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Greater) {
139 return Ordering::Greater;
140 } else if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Less) {
141 return Ordering::Less;
142 }
143 Ordering::Equal
144 }
145 (Some(_), None) => Ordering::Less,
147 (None, Some(_)) => Ordering::Greater,
148 _ => Ordering::Equal,
150 }
151 }
152}
153
154impl PartialOrd for SuiObjectResponse {
155 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
156 Some(self.cmp(other))
157 }
158}
159
160#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
162#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
163pub enum SuiObjectResponseError {
164 #[error("Object {:?} does not exist.", object_id)]
165 NotExists { object_id: ObjectId },
166 #[error("Cannot find dynamic field for parent object {:?}.", parent_object_id)]
167 DynamicFieldNotFound { parent_object_id: ObjectId },
168 #[error(
169 "Object has been deleted object_id: {:?} at version: {:?} in digest {:?}",
170 object_id,
171 version,
172 digest
173 )]
174 Deleted {
175 object_id: ObjectId,
176 version: Version,
178 digest: ObjectDigest,
180 },
181 #[error("Unknown Error.")]
182 Unknown,
183 #[error("Display Error: {:?}", error)]
184 DisplayError { error: String },
185}
186
187#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
188pub struct DisplayFieldsResponse {
189 pub data: Option<BTreeMap<String, String>>,
190 pub error: Option<SuiObjectResponseError>,
191}
192
193#[derive(thiserror::Error, Debug)]
198pub enum SuiObjectDataError {
199 #[error("Missing object type")]
200 MissingObjectType,
201 #[error("Missing BCS encoding")]
202 MissingBcs,
203 #[error("Missing object owner")]
204 MissingOwner,
205 #[error("Not a Move object")]
206 NotMoveObject,
207 #[error("Not an immutable or owned object")]
208 NotImmOrOwned,
209 #[error("Not a shared object")]
210 NotShared,
211 #[error(transparent)]
212 ObjectType(#[from] NotMoveStructError),
213}
214
215#[derive(thiserror::Error, Debug)]
217#[non_exhaustive]
218pub enum FullObjectDataError {
219 #[error("Missing BCS encoding")]
220 MissingBcs,
221 #[error("Missing object owner")]
222 MissingOwner,
223 #[error("Missing previous transaction digest")]
224 MissingPreviousTransaction,
225 #[error("Missing storage rebate")]
226 MissingStorageRebate,
227 #[error("MoveObject BCS doesn't start with ObjectId")]
228 InvalidBcs,
229 #[error("Invalid identifier: {ident}\nReason: {source}")]
230 InvalidIdentifier {
231 ident: Box<str>,
232 #[source]
233 source: af_sui_types::TypeParseError,
234 },
235}
236
237#[serde_as]
238#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)]
239#[serde(rename_all = "camelCase", rename = "ObjectData")]
240pub struct SuiObjectData {
241 pub object_id: ObjectId,
242 #[serde_as(as = "BigInt<u64>")]
244 pub version: Version,
245 pub digest: ObjectDigest,
247 #[serde_as(as = "Option<DisplayFromStr>")]
249 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
250 pub type_: Option<ObjectType>,
251 #[serde(skip_serializing_if = "Option::is_none")]
254 pub owner: Option<Owner>,
255 #[serde(skip_serializing_if = "Option::is_none")]
258 pub previous_transaction: Option<TransactionDigest>,
259 #[serde_as(as = "Option<BigInt<u64>>")]
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub storage_rebate: Option<u64>,
265 #[serde(skip_serializing_if = "Option::is_none")]
269 pub display: Option<DisplayFieldsResponse>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub content: Option<SuiParsedData>,
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub bcs: Option<SuiRawData>,
276}
277
278impl SuiObjectData {
279 pub fn object_ref(&self) -> ObjectRef {
280 (self.object_id, self.version, self.digest)
281 }
282
283 pub fn object_type(&self) -> Result<ObjectType, SuiObjectDataError> {
284 self.type_
285 .as_ref()
286 .ok_or(SuiObjectDataError::MissingObjectType)
287 .cloned()
288 }
289
290 pub fn is_gas_coin(&self) -> bool {
291 match self.type_.as_ref() {
292 Some(ObjectType::Struct(ty)) if ty.is_gas_coin() => true,
293 Some(_) => false,
294 None => false,
295 }
296 }
297
298 pub fn struct_tag(&self) -> Result<StructTag, SuiObjectDataError> {
299 Ok(self
300 .type_
301 .clone()
302 .ok_or(SuiObjectDataError::MissingObjectType)?
303 .try_into()?)
304 }
305
306 pub fn take_object_type(&mut self) -> Result<ObjectType, SuiObjectDataError> {
307 self.type_
308 .take()
309 .ok_or(SuiObjectDataError::MissingObjectType)
310 }
311
312 pub fn take_raw_object(&mut self) -> Result<SuiRawMoveObject, SuiObjectDataError> {
313 self.take_raw_data()?
314 .try_into_move()
315 .ok_or(SuiObjectDataError::NotMoveObject)
316 }
317
318 pub fn take_raw_data(&mut self) -> Result<SuiRawData, SuiObjectDataError> {
319 self.bcs.take().ok_or(SuiObjectDataError::MissingBcs)
320 }
321
322 pub fn shared_object_arg(&self, mutable: bool) -> Result<ObjectArg, SuiObjectDataError> {
323 let Owner::Shared {
324 initial_shared_version,
325 } = self.owner()?
326 else {
327 return Err(SuiObjectDataError::NotShared);
328 };
329 Ok(ObjectArg::SharedObject {
330 id: self.object_id,
331 initial_shared_version,
332 mutable,
333 })
334 }
335
336 pub fn imm_or_owned_object_arg(&self) -> Result<ObjectArg, SuiObjectDataError> {
337 use Owner::*;
338 if !matches!(self.owner()?, AddressOwner(_) | ObjectOwner(_) | Immutable) {
339 return Err(SuiObjectDataError::NotImmOrOwned);
340 };
341 let (i, v, d) = self.object_ref();
342 Ok(ObjectArg::ImmOrOwnedObject((i, v, d)))
343 }
344
345 #[cfg(feature = "client")]
346 pub(crate) fn object_arg(&self, mutable: bool) -> Result<ObjectArg, SuiObjectDataError> {
347 use Owner as O;
348 Ok(match self.owner()? {
349 O::AddressOwner(_) | O::ObjectOwner(_) | O::Immutable => {
350 ObjectArg::ImmOrOwnedObject(self.object_ref())
351 }
352 O::Shared {
353 initial_shared_version,
354 }
355 | O::ConsensusV2 {
356 start_version: initial_shared_version,
357 ..
358 } => ObjectArg::SharedObject {
359 id: self.object_id,
360 initial_shared_version,
361 mutable,
362 },
363 })
364 }
365
366 pub fn owner(&self) -> Result<Owner, SuiObjectDataError> {
367 self.owner.clone().ok_or(SuiObjectDataError::MissingOwner)
368 }
369
370 pub fn into_full_object(self) -> Result<Object, FullObjectDataError> {
372 use itertools::Itertools as _;
373 let Self {
374 owner,
375 previous_transaction,
376 storage_rebate,
377 bcs,
378 ..
379 } = self;
380 let owner = owner.ok_or(FullObjectDataError::MissingOwner)?;
381 let previous_transaction =
382 previous_transaction.ok_or(FullObjectDataError::MissingPreviousTransaction)?;
383 let storage_rebate = storage_rebate.ok_or(FullObjectDataError::MissingStorageRebate)?;
384
385 match bcs.ok_or(FullObjectDataError::MissingBcs)? {
386 SuiRawData::Package(p) => {
387 let modules = p
388 .module_map
389 .into_iter()
390 .map(|(s, bytes)| {
391 Ok((
392 s.parse()
393 .map_err(|e| FullObjectDataError::InvalidIdentifier {
394 ident: s.into(),
395 source: e,
396 })?,
397 bytes,
398 ))
399 })
400 .try_collect()?;
401 let inner = af_sui_types::MovePackage {
402 id: p.id,
403 version: p.version,
404 modules,
405 type_origin_table: p.type_origin_table,
406 linkage_table: p.linkage_table,
407 };
408 Ok(Object::new_package(
409 inner,
410 owner,
411 previous_transaction,
412 storage_rebate,
413 ))
414 }
415 SuiRawData::MoveObject(raw_struct) => {
416 let struct_ = MoveObject::new(
417 raw_struct.type_,
418 raw_struct.has_public_transfer,
419 raw_struct.version,
420 raw_struct.bcs_bytes,
421 )
422 .ok_or(FullObjectDataError::InvalidBcs)?;
423 Ok(Object::new_struct(
424 struct_,
425 owner,
426 previous_transaction,
427 storage_rebate,
428 ))
429 }
430 }
431 }
432}
433
434impl Display for SuiObjectData {
435 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
436 let type_ = if let Some(type_) = &self.type_ {
437 type_.to_string()
438 } else {
439 "Unknown Type".into()
440 };
441 let mut writer = String::new();
442 writeln!(
443 writer,
444 "{}",
445 format!("----- {type_} ({}[{}]) -----", self.object_id, self.version).bold()
446 )?;
447 if let Some(ref owner) = self.owner {
448 writeln!(writer, "{}: {}", "Owner".bold().bright_black(), owner)?;
449 }
450
451 writeln!(
452 writer,
453 "{}: {}",
454 "Version".bold().bright_black(),
455 self.version
456 )?;
457 if let Some(storage_rebate) = self.storage_rebate {
458 writeln!(
459 writer,
460 "{}: {}",
461 "Storage Rebate".bold().bright_black(),
462 storage_rebate
463 )?;
464 }
465
466 if let Some(previous_transaction) = self.previous_transaction {
467 writeln!(
468 writer,
469 "{}: {:?}",
470 "Previous Transaction".bold().bright_black(),
471 previous_transaction
472 )?;
473 }
474 if let Some(content) = self.content.as_ref() {
475 writeln!(writer, "{}", "----- Data -----".bold())?;
476 write!(writer, "{}", content)?;
477 }
478
479 write!(f, "{}", writer)
480 }
481}
482
483const PACKAGE: &str = "package";
488#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
492pub enum ObjectType {
493 Package,
495 Struct(MoveObjectType),
497}
498
499impl TryFrom<ObjectType> for StructTag {
500 type Error = NotMoveStructError;
501
502 fn try_from(o: ObjectType) -> Result<Self, Self::Error> {
503 match o {
504 ObjectType::Package => Err(NotMoveStructError),
505 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
506 }
507 }
508}
509
510#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
511#[error("Cannot create StructTag from Package")]
512pub struct NotMoveStructError;
513
514impl Display for ObjectType {
515 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
516 match self {
517 ObjectType::Package => write!(f, "{}", PACKAGE),
518 ObjectType::Struct(t) => write!(f, "{}", t),
519 }
520 }
521}
522
523impl FromStr for ObjectType {
524 type Err = <StructTag as FromStr>::Err;
525
526 fn from_str(s: &str) -> Result<Self, Self::Err> {
527 if s.to_lowercase() == PACKAGE {
528 Ok(ObjectType::Package)
529 } else {
530 let tag: StructTag = s.parse()?;
531 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
532 }
533 }
534}
535
536#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)]
541#[serde(rename_all = "camelCase", rename = "ObjectDataOptions", default)]
542pub struct SuiObjectDataOptions {
543 pub show_type: bool,
545 pub show_owner: bool,
547 pub show_previous_transaction: bool,
549 pub show_display: bool,
551 pub show_content: bool,
554 pub show_bcs: bool,
556 pub show_storage_rebate: bool,
558}
559
560impl SuiObjectDataOptions {
561 pub fn new() -> Self {
562 Self::default()
563 }
564
565 pub fn full_object() -> Self {
567 Self {
568 show_bcs: true,
569 show_owner: true,
570 show_storage_rebate: true,
571 show_previous_transaction: true,
572 show_content: false,
573 show_display: false,
574 show_type: false,
575 }
576 }
577
578 pub fn bcs_lossless() -> Self {
580 Self {
581 show_bcs: true,
582 show_type: true,
583 show_owner: true,
584 show_previous_transaction: true,
585 show_display: false,
586 show_content: false,
587 show_storage_rebate: true,
588 }
589 }
590
591 pub fn full_content() -> Self {
593 Self {
594 show_bcs: false,
595 show_type: true,
596 show_owner: true,
597 show_previous_transaction: true,
598 show_display: false,
599 show_content: true,
600 show_storage_rebate: true,
601 }
602 }
603
604 pub fn with_content(mut self) -> Self {
605 self.show_content = true;
606 self
607 }
608
609 pub fn with_owner(mut self) -> Self {
610 self.show_owner = true;
611 self
612 }
613
614 pub fn with_type(mut self) -> Self {
615 self.show_type = true;
616 self
617 }
618
619 pub fn with_display(mut self) -> Self {
620 self.show_display = true;
621 self
622 }
623
624 pub fn with_bcs(mut self) -> Self {
625 self.show_bcs = true;
626 self
627 }
628
629 pub fn with_previous_transaction(mut self) -> Self {
630 self.show_previous_transaction = true;
631 self
632 }
633
634 pub fn is_not_in_object_info(&self) -> bool {
635 self.show_bcs || self.show_content || self.show_display || self.show_storage_rebate
636 }
637}
638
639#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)]
644#[serde(rename_all = "camelCase", rename = "ObjectRef")]
645pub struct SuiObjectRef {
646 pub object_id: ObjectId,
648 pub version: Version,
650 pub digest: ObjectDigest,
652}
653
654impl SuiObjectRef {
655 pub fn to_object_ref(&self) -> ObjectRef {
656 (self.object_id, self.version, self.digest)
657 }
658}
659
660impl Display for SuiObjectRef {
661 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
662 write!(
663 f,
664 "Object ID: {}, version: {}, digest: {}",
665 self.object_id, self.version, self.digest
666 )
667 }
668}
669
670impl From<ObjectRef> for SuiObjectRef {
671 fn from(oref: ObjectRef) -> Self {
672 Self {
673 object_id: oref.0,
674 version: oref.1,
675 digest: oref.2,
676 }
677 }
678}
679
680pub trait SuiData: Sized {
685 type ObjectType;
686 type PackageType;
687 fn try_as_move(&self) -> Option<&Self::ObjectType>;
688 fn try_into_move(self) -> Option<Self::ObjectType>;
689 fn try_as_package(&self) -> Option<&Self::PackageType>;
690 fn type_(&self) -> Option<&StructTag>;
691}
692
693#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
694#[serde(tag = "dataType", rename_all = "camelCase", rename = "RawData")]
695pub enum SuiRawData {
696 MoveObject(SuiRawMoveObject),
698 Package(SuiRawMovePackage),
699}
700
701impl SuiData for SuiRawData {
702 type ObjectType = SuiRawMoveObject;
703 type PackageType = SuiRawMovePackage;
704
705 fn try_as_move(&self) -> Option<&Self::ObjectType> {
706 match self {
707 Self::MoveObject(o) => Some(o),
708 Self::Package(_) => None,
709 }
710 }
711
712 fn try_into_move(self) -> Option<Self::ObjectType> {
713 match self {
714 Self::MoveObject(o) => Some(o),
715 Self::Package(_) => None,
716 }
717 }
718
719 fn try_as_package(&self) -> Option<&Self::PackageType> {
720 match self {
721 Self::MoveObject(_) => None,
722 Self::Package(p) => Some(p),
723 }
724 }
725
726 fn type_(&self) -> Option<&StructTag> {
727 match self {
728 Self::MoveObject(o) => Some(&o.type_),
729 Self::Package(_) => None,
730 }
731 }
732}
733
734#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
735#[serde(tag = "dataType", rename_all = "camelCase", rename = "Data")]
736pub enum SuiParsedData {
737 MoveObject(SuiParsedMoveObject),
739 Package(SuiMovePackage),
740}
741
742impl SuiData for SuiParsedData {
743 type ObjectType = SuiParsedMoveObject;
744 type PackageType = SuiMovePackage;
745
746 fn try_as_move(&self) -> Option<&Self::ObjectType> {
747 match self {
748 Self::MoveObject(o) => Some(o),
749 Self::Package(_) => None,
750 }
751 }
752
753 fn try_into_move(self) -> Option<Self::ObjectType> {
754 match self {
755 Self::MoveObject(o) => Some(o),
756 Self::Package(_) => None,
757 }
758 }
759
760 fn try_as_package(&self) -> Option<&Self::PackageType> {
761 match self {
762 Self::MoveObject(_) => None,
763 Self::Package(p) => Some(p),
764 }
765 }
766
767 fn type_(&self) -> Option<&StructTag> {
768 match self {
769 Self::MoveObject(o) => Some(&o.type_),
770 Self::Package(_) => None,
771 }
772 }
773}
774
775impl Display for SuiParsedData {
776 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
777 let mut writer = String::new();
778 match self {
779 SuiParsedData::MoveObject(o) => {
780 writeln!(writer, "{}: {}", "type".bold().bright_black(), o.type_)?;
781 write!(writer, "{}", &o.fields)?;
782 }
783 SuiParsedData::Package(p) => {
784 write!(
785 writer,
786 "{}: {:?}",
787 "Modules".bold().bright_black(),
788 p.disassembled.keys()
789 )?;
790 }
791 }
792 write!(f, "{}", writer)
793 }
794}
795
796pub trait SuiMoveObject: Sized {
797 fn type_(&self) -> &StructTag;
798}
799
800#[serde_as]
801#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
802#[serde(rename = "MoveObject", rename_all = "camelCase")]
803pub struct SuiParsedMoveObject {
804 #[serde(rename = "type")]
805 #[serde_as(as = "DisplayFromStr")]
807 pub type_: StructTag,
808 pub has_public_transfer: bool,
809 pub fields: SuiMoveStruct,
810}
811
812impl SuiMoveObject for SuiParsedMoveObject {
813 fn type_(&self) -> &StructTag {
814 &self.type_
815 }
816}
817
818impl SuiParsedMoveObject {
819 pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<SuiMoveValue> {
820 match &self.fields {
821 SuiMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
822 SuiMoveStruct::WithTypes { fields, .. } => fields.get(field_name).cloned(),
823 _ => None,
824 }
825 }
826}
827
828#[serde_as]
829#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
830#[serde(rename = "RawMoveObject", rename_all = "camelCase")]
831pub struct SuiRawMoveObject {
832 #[serde(rename = "type")]
833 #[serde_as(as = "DisplayFromStr")]
835 pub type_: StructTag,
836 pub has_public_transfer: bool,
837 pub version: Version,
838 #[serde_as(as = "Base64")]
839 pub bcs_bytes: Vec<u8>,
840}
841
842impl SuiMoveObject for SuiRawMoveObject {
843 fn type_(&self) -> &StructTag {
844 &self.type_
845 }
846}
847
848impl SuiRawMoveObject {
849 pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result<T, bcs::Error> {
850 bcs::from_bytes(self.bcs_bytes.as_slice())
851 }
852}
853
854#[serde_as]
855#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
856#[serde(rename = "RawMovePackage", rename_all = "camelCase")]
857pub struct SuiRawMovePackage {
858 pub id: ObjectId,
859 pub version: Version,
860 #[serde_as(as = "BTreeMap<_, Base64>")]
861 pub module_map: BTreeMap<String, Vec<u8>>,
862 pub type_origin_table: Vec<TypeOrigin>,
863 pub linkage_table: BTreeMap<ObjectId, UpgradeInfo>,
864}
865
866#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
868pub enum SuiPastObjectResponseError {
869 #[error("Could not find the referenced object {object_id:?} at version {version:?}.")]
870 ObjectNotFound {
871 object_id: ObjectId,
872 version: Option<Version>,
873 },
874
875 #[error(
876 "Could not find the referenced object {object_id:?} \
877 as the asked version {asked_version:?} \
878 is higher than the latest {latest_version:?}"
879 )]
880 ObjectSequenceNumberTooHigh {
881 object_id: ObjectId,
882 asked_version: Version,
883 latest_version: Version,
884 },
885
886 #[error("Object deleted at reference {object_ref:?}.")]
887 ObjectDeleted { object_ref: ObjectRef },
888}
889
890#[rustversion::attr(nightly, expect(clippy::large_enum_variant))]
891#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
892#[serde(tag = "status", content = "details", rename = "ObjectRead")]
893pub enum SuiPastObjectResponse {
894 VersionFound(SuiObjectData),
896 ObjectNotExists(ObjectId),
898 ObjectDeleted(SuiObjectRef),
900 VersionNotFound(ObjectId, Version),
902 VersionTooHigh {
904 object_id: ObjectId,
905 asked_version: Version,
906 latest_version: Version,
907 },
908}
909
910impl SuiPastObjectResponse {
911 pub fn object(&self) -> Result<&SuiObjectData, SuiPastObjectResponseError> {
913 match &self {
914 Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
915 object_ref: oref.to_object_ref(),
916 }),
917 Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
918 object_id: *id,
919 version: None,
920 }),
921 Self::VersionFound(o) => Ok(o),
922 Self::VersionNotFound(id, seq_num) => Err(SuiPastObjectResponseError::ObjectNotFound {
923 object_id: *id,
924 version: Some(*seq_num),
925 }),
926 Self::VersionTooHigh {
927 object_id,
928 asked_version,
929 latest_version,
930 } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
931 object_id: *object_id,
932 asked_version: *asked_version,
933 latest_version: *latest_version,
934 }),
935 }
936 }
937
938 pub fn into_object(self) -> Result<SuiObjectData, SuiPastObjectResponseError> {
940 match self {
941 Self::ObjectDeleted(oref) => Err(SuiPastObjectResponseError::ObjectDeleted {
942 object_ref: oref.to_object_ref(),
943 }),
944 Self::ObjectNotExists(id) => Err(SuiPastObjectResponseError::ObjectNotFound {
945 object_id: id,
946 version: None,
947 }),
948 Self::VersionFound(o) => Ok(o),
949 Self::VersionNotFound(object_id, version) => {
950 Err(SuiPastObjectResponseError::ObjectNotFound {
951 object_id,
952 version: Some(version),
953 })
954 }
955 Self::VersionTooHigh {
956 object_id,
957 asked_version,
958 latest_version,
959 } => Err(SuiPastObjectResponseError::ObjectSequenceNumberTooHigh {
960 object_id,
961 asked_version,
962 latest_version,
963 }),
964 }
965 }
966}
967
968#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
969#[serde(rename = "MovePackage", rename_all = "camelCase")]
970pub struct SuiMovePackage {
971 pub disassembled: BTreeMap<String, Value>,
972}
973
974pub type QueryObjectsPage = Page<SuiObjectResponse, CheckpointedObjectId>;
975pub type ObjectsPage = Page<SuiObjectResponse, ObjectId>;
976
977#[serde_as]
978#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
979#[serde(rename_all = "camelCase")]
980pub struct CheckpointedObjectId {
981 pub object_id: ObjectId,
982 #[serde_as(as = "Option<BigInt<u64>>")]
983 #[serde(skip_serializing_if = "Option::is_none")]
984 pub at_checkpoint: Option<Version>,
985}
986
987#[serde_as]
988#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
989#[serde(rename = "GetPastObjectRequest", rename_all = "camelCase")]
990pub struct SuiGetPastObjectRequest {
991 pub object_id: ObjectId,
993 #[serde_as(as = "BigInt<u64>")]
995 pub version: Version,
996}
997
998#[serde_as]
999#[derive(Clone, Debug, Serialize, Deserialize)]
1000pub enum SuiObjectDataFilter {
1001 MatchAll(Vec<SuiObjectDataFilter>),
1002 MatchAny(Vec<SuiObjectDataFilter>),
1003 MatchNone(Vec<SuiObjectDataFilter>),
1004 Package(ObjectId),
1006 MoveModule {
1008 package: ObjectId,
1010 #[serde_as(as = "DisplayFromStr")]
1012 module: Identifier,
1013 },
1014 StructType(#[serde_as(as = "DisplayFromStr")] StructTag),
1017 AddressOwner(SuiAddress),
1018 ObjectOwner(ObjectId),
1019 ObjectId(ObjectId),
1020 ObjectIds(Vec<ObjectId>),
1022 Version(#[serde_as(as = "BigInt<u64>")] u64),
1023}
1024
1025impl SuiObjectDataFilter {
1026 pub fn gas_coin() -> Self {
1027 Self::StructType(StructTag::gas_coin())
1028 }
1029
1030 pub fn and(self, other: Self) -> Self {
1031 Self::MatchAll(vec![self, other])
1032 }
1033 pub fn or(self, other: Self) -> Self {
1034 Self::MatchAny(vec![self, other])
1035 }
1036 pub fn not(self, other: Self) -> Self {
1037 Self::MatchNone(vec![self, other])
1038 }
1039}
1040
1041#[derive(Debug, Clone, Deserialize, Serialize, Default)]
1042#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)]
1043pub struct SuiObjectResponseQuery {
1044 pub filter: Option<SuiObjectDataFilter>,
1046 pub options: Option<SuiObjectDataOptions>,
1048}
1049
1050impl SuiObjectResponseQuery {
1051 pub fn new(filter: Option<SuiObjectDataFilter>, options: Option<SuiObjectDataOptions>) -> Self {
1052 Self { filter, options }
1053 }
1054
1055 pub fn new_with_filter(filter: SuiObjectDataFilter) -> Self {
1056 Self {
1057 filter: Some(filter),
1058 options: None,
1059 }
1060 }
1061
1062 pub fn new_with_options(options: SuiObjectDataOptions) -> Self {
1063 Self {
1064 filter: None,
1065 options: Some(options),
1066 }
1067 }
1068}