sui_sdk_types/
object.rs

1use std::collections::BTreeMap;
2
3use super::Address;
4use super::Digest;
5use super::Identifier;
6use super::StructTag;
7
8pub type Version = u64;
9
10/// Reference to an object
11///
12/// Contains sufficient information to uniquely identify a specific object.
13///
14/// # BCS
15///
16/// The BCS serialized form for this type is defined by the following ABNF:
17///
18/// ```text
19/// object-ref = address u64 digest
20/// ```
21#[derive(Clone, Debug, PartialEq, Eq)]
22#[cfg_attr(
23    feature = "serde",
24    derive(serde_derive::Serialize, serde_derive::Deserialize)
25)]
26#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
27pub struct ObjectReference {
28    /// The object id of this object.
29    object_id: Address,
30    /// The version of this object.
31    version: Version,
32    /// The digest of this object.
33    digest: Digest,
34}
35
36impl ObjectReference {
37    /// Creates a new object reference from the object's id, version, and digest.
38    pub fn new(object_id: Address, version: Version, digest: Digest) -> Self {
39        Self {
40            object_id,
41            version,
42            digest,
43        }
44    }
45
46    /// Returns a reference to the object id that this ObjectReference is referring to.
47    pub fn object_id(&self) -> &Address {
48        &self.object_id
49    }
50
51    /// Returns the version of the object that this ObjectReference is referring to.
52    pub fn version(&self) -> Version {
53        self.version
54    }
55
56    /// Returns the digest of the object that this ObjectReference is referring to.
57    pub fn digest(&self) -> &Digest {
58        &self.digest
59    }
60
61    /// Returns a 3-tuple containing the object id, version, and digest.
62    pub fn into_parts(self) -> (Address, Version, Digest) {
63        let Self {
64            object_id,
65            version,
66            digest,
67        } = self;
68
69        (object_id, version, digest)
70    }
71}
72
73/// Enum of different types of ownership for an object.
74///
75/// # BCS
76///
77/// The BCS serialized form for this type is defined by the following ABNF:
78///
79/// ```text
80/// owner = owner-address / owner-object / owner-shared / owner-immutable
81///
82/// owner-address   = %x00 address
83/// owner-object    = %x01 address
84/// owner-shared    = %x02 u64
85/// owner-immutable = %x03
86/// ```
87#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
88#[cfg_attr(
89    feature = "serde",
90    derive(serde_derive::Serialize, serde_derive::Deserialize),
91    serde(rename_all = "lowercase")
92)]
93#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
94#[non_exhaustive]
95pub enum Owner {
96    /// Object is exclusively owned by a single address, and is mutable.
97    Address(Address),
98    /// Object is exclusively owned by a single object, and is mutable.
99    Object(Address),
100    /// Object is shared, can be used by any address, and is mutable.
101    Shared(
102        /// The version at which the object became shared
103        Version,
104    ),
105    /// Object is immutable, and hence ownership doesn't matter.
106    Immutable,
107
108    /// Object is exclusively owned by a single address and sequenced via consensus.
109    ConsensusAddress {
110        /// The version at which the object most recently became a consensus object.
111        /// This serves the same function as `initial_shared_version`, except it may change
112        /// if the object's Owner type changes.
113        start_version: Version,
114
115        /// The owner of the object.
116        owner: Address,
117    },
118}
119
120/// Object data, either a package or struct
121///
122/// # BCS
123///
124/// The BCS serialized form for this type is defined by the following ABNF:
125///
126/// ```text
127/// object-data = object-data-struct / object-data-package
128///
129/// object-data-struct  = %x00 object-move-struct
130/// object-data-package = %x01 object-move-package
131/// ```
132#[derive(Clone, Debug, PartialEq, Eq, Hash)]
133#[cfg_attr(
134    feature = "serde",
135    derive(serde_derive::Serialize, serde_derive::Deserialize)
136)]
137#[allow(clippy::large_enum_variant)]
138#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
139//TODO think about hiding this type and not exposing it
140pub enum ObjectData {
141    /// An object whose governing logic lives in a published Move module
142    Struct(MoveStruct),
143    /// Map from each module name to raw serialized Move module bytes
144    Package(MovePackage),
145    // ... Sui "native" types go here
146}
147
148/// A move package
149///
150/// # BCS
151///
152/// The BCS serialized form for this type is defined by the following ABNF:
153///
154/// ```text
155/// object-move-package = address u64 move-modules type-origin-table linkage-table
156///
157/// move-modules = map (identifier bytes)
158/// type-origin-table = vector type-origin
159/// linkage-table = map (address upgrade-info)
160/// ```
161#[derive(Eq, PartialEq, Debug, Clone, Hash)]
162#[cfg_attr(
163    feature = "serde",
164    derive(serde_derive::Serialize, serde_derive::Deserialize)
165)]
166#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
167pub struct MovePackage {
168    /// Address or Id of this package
169    pub id: Address,
170
171    /// Most move packages are uniquely identified by their ID (i.e. there is only one version per
172    /// ID), but the version is still stored because one package may be an upgrade of another (at a
173    /// different ID), in which case its version will be one greater than the version of the
174    /// upgraded package.
175    ///
176    /// Framework packages are an exception to this rule -- all versions of the framework packages
177    /// exist at the same ID, at increasing versions.
178    ///
179    /// In all cases, packages are referred to by move calls using just their ID, and they are
180    /// always loaded at their latest version.
181    pub version: Version,
182
183    /// Set of modules defined by this package
184    #[cfg_attr(
185        feature = "serde",
186        serde(with = "::serde_with::As::<BTreeMap<::serde_with::Same, ::serde_with::Bytes>>")
187    )]
188    #[cfg_attr(
189        feature = "proptest",
190        strategy(
191            proptest::collection::btree_map(proptest::arbitrary::any::<Identifier>(), proptest::collection::vec(proptest::arbitrary::any::<u8>(), 0..=1024), 0..=5)
192        )
193    )]
194    pub modules: BTreeMap<Identifier, Vec<u8>>,
195
196    /// Maps struct/module to a package version where it was first defined, stored as a vector for
197    /// simple serialization and deserialization.
198    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
199    pub type_origin_table: Vec<TypeOrigin>,
200
201    /// For each dependency, maps original package ID to the info about the (upgraded) dependency
202    /// version that this package is using
203    #[cfg_attr(
204        feature = "proptest",
205        strategy(
206            proptest::collection::btree_map(proptest::arbitrary::any::<Address>(), proptest::arbitrary::any::<UpgradeInfo>(), 0..=5)
207        )
208    )]
209    pub linkage_table: BTreeMap<Address, UpgradeInfo>,
210}
211
212/// Identifies a struct and the module it was defined in
213///
214/// # BCS
215///
216/// The BCS serialized form for this type is defined by the following ABNF:
217///
218/// ```text
219/// type-origin = identifier identifier address
220/// ```
221#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
222#[cfg_attr(
223    feature = "serde",
224    derive(serde_derive::Serialize, serde_derive::Deserialize)
225)]
226#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
227pub struct TypeOrigin {
228    pub module_name: Identifier,
229    pub struct_name: Identifier,
230    pub package: Address,
231}
232
233/// Upgraded package info for the linkage table
234///
235/// # BCS
236///
237/// The BCS serialized form for this type is defined by the following ABNF:
238///
239/// ```text
240/// upgrade-info = address u64
241/// ```
242#[derive(Eq, PartialEq, Debug, Clone, Hash)]
243#[cfg_attr(
244    feature = "serde",
245    derive(serde_derive::Serialize, serde_derive::Deserialize)
246)]
247#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
248pub struct UpgradeInfo {
249    /// Id of the upgraded packages
250    pub upgraded_id: Address,
251    /// Version of the upgraded package
252    pub upgraded_version: Version,
253}
254
255/// A move struct
256///
257/// # BCS
258///
259/// The BCS serialized form for this type is defined by the following ABNF:
260///
261/// ```text
262/// object-move-struct = compressed-struct-tag bool u64 object-contents
263///
264/// compressed-struct-tag = other-struct-type / gas-coin-type / staked-sui-type / coin-type
265/// other-struct-type     = %x00 struct-tag
266/// gas-coin-type         = %x01
267/// staked-sui-type       = %x02
268/// coin-type             = %x03 type-tag
269///
270/// ; first 32 bytes of the contents are the object's address
271/// object-contents = uleb128 (address *OCTET) ; length followed by contents
272/// ```
273#[derive(Eq, PartialEq, Debug, Clone, Hash)]
274//TODO hand-roll a Deserialize impl to enforce that an objectid is present
275#[cfg_attr(
276    feature = "serde",
277    derive(serde_derive::Serialize, serde_derive::Deserialize)
278)]
279#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
280pub struct MoveStruct {
281    /// The type of this object
282    #[cfg_attr(
283        feature = "serde",
284        serde(with = "::serde_with::As::<serialization::BinaryMoveStructType>")
285    )]
286    pub(crate) type_: StructTag,
287
288    /// DEPRECATED this field is no longer used to determine whether a tx can transfer this
289    /// object. Instead, it is always calculated from the objects type when loaded in execution
290    has_public_transfer: bool,
291
292    /// Number that increases each time a tx takes this object as a mutable input
293    /// This is a lamport timestamp, not a sequentially increasing version
294    version: Version,
295
296    /// BCS bytes of a Move struct value
297    #[cfg_attr(
298        feature = "serde",
299        serde(with = "crate::_serde::ReadableBase64Encoded")
300    )]
301    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(32..=1024).lift()))]
302    pub(crate) contents: Vec<u8>,
303}
304
305impl MoveStruct {
306    /// Construct a move struct
307    pub fn new(
308        type_: StructTag,
309        has_public_transfer: bool,
310        version: Version,
311        contents: Vec<u8>,
312    ) -> Option<Self> {
313        id_opt(&contents).map(|_| Self {
314            type_,
315            has_public_transfer,
316            version,
317            contents,
318        })
319    }
320
321    /// Return the type of the struct
322    pub fn object_type(&self) -> &StructTag {
323        &self.type_
324    }
325
326    /// Return if this object can be publicly transferred
327    ///
328    /// DEPRECATED
329    ///
330    /// This field is no longer used to determine whether a tx can transfer this object. Instead,
331    /// it is always calculated from the objects type when loaded in execution.
332    #[doc(hidden)]
333    pub fn has_public_transfer(&self) -> bool {
334        self.has_public_transfer
335    }
336
337    /// Return the version of this object
338    pub fn version(&self) -> Version {
339        self.version
340    }
341
342    /// Return the raw contents of this struct
343    pub fn contents(&self) -> &[u8] {
344        &self.contents
345    }
346
347    /// Return the ObjectId of this object
348    pub fn object_id(&self) -> Address {
349        id_opt(self.contents()).unwrap()
350    }
351}
352
353/// Type of a Sui object
354#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
355pub enum ObjectType {
356    /// Move package containing one or more bytecode modules
357    Package,
358    /// A Move struct of the given type
359    Struct(StructTag),
360}
361
362/// An object on the sui blockchain
363///
364/// # BCS
365///
366/// The BCS serialized form for this type is defined by the following ABNF:
367///
368/// ```text
369/// object = object-data owner digest u64
370/// ```
371#[derive(Clone, Debug, PartialEq, Eq)]
372#[cfg_attr(
373    feature = "serde",
374    derive(serde_derive::Serialize, serde_derive::Deserialize)
375)]
376#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
377pub struct Object {
378    /// The meat of the object
379    pub(crate) data: ObjectData,
380
381    /// The owner that unlocks this object
382    owner: Owner,
383
384    /// The digest of the transaction that created or last mutated this object
385    previous_transaction: Digest,
386
387    /// The amount of SUI we would rebate if this object gets deleted.
388    /// This number is re-calculated each time the object is mutated based on
389    /// the present storage gas price.
390    storage_rebate: u64,
391}
392
393impl Object {
394    /// Build an object
395    pub fn new(
396        data: ObjectData,
397        owner: Owner,
398        previous_transaction: Digest,
399        storage_rebate: u64,
400    ) -> Self {
401        Self {
402            data,
403            owner,
404            previous_transaction,
405            storage_rebate,
406        }
407    }
408
409    /// Return this object's id
410    pub fn object_id(&self) -> Address {
411        match &self.data {
412            ObjectData::Struct(struct_) => id_opt(&struct_.contents).unwrap(),
413            ObjectData::Package(package) => package.id,
414        }
415    }
416
417    /// Return this object's version
418    pub fn version(&self) -> Version {
419        match &self.data {
420            ObjectData::Struct(struct_) => struct_.version,
421            ObjectData::Package(package) => package.version,
422        }
423    }
424
425    /// Return this object's type
426    pub fn object_type(&self) -> ObjectType {
427        match &self.data {
428            ObjectData::Struct(struct_) => ObjectType::Struct(struct_.type_.clone()),
429            ObjectData::Package(_) => ObjectType::Package,
430        }
431    }
432
433    /// Try to interpret this object as a move struct
434    pub fn as_struct(&self) -> Option<&MoveStruct> {
435        match &self.data {
436            ObjectData::Struct(struct_) => Some(struct_),
437            _ => None,
438        }
439    }
440
441    /// Return this object's owner
442    pub fn owner(&self) -> &Owner {
443        &self.owner
444    }
445
446    /// Return this object's data
447    pub fn data(&self) -> &ObjectData {
448        &self.data
449    }
450
451    /// Return the digest of the transaction that last modified this object
452    pub fn previous_transaction(&self) -> Digest {
453        self.previous_transaction
454    }
455
456    /// Return the storage rebate locked in this object
457    ///
458    /// Storage rebates are credited to the gas coin used in a transaction that deletes this
459    /// object.
460    pub fn storage_rebate(&self) -> u64 {
461        self.storage_rebate
462    }
463}
464
465fn id_opt(contents: &[u8]) -> Option<Address> {
466    if Address::LENGTH > contents.len() {
467        return None;
468    }
469
470    Some(Address::from_bytes(&contents[..Address::LENGTH]).unwrap())
471}
472
473/// An object part of the initial chain state
474///
475/// `GenesisObject`'s are included as a part of genesis, the initial checkpoint/transaction, that
476/// initializes the state of the blockchain.
477///
478/// # BCS
479///
480/// The BCS serialized form for this type is defined by the following ABNF:
481///
482/// ```text
483/// genesis-object = object-data owner
484/// ```
485#[derive(Clone, Debug, PartialEq, Eq)]
486#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
487pub struct GenesisObject {
488    data: ObjectData,
489    owner: Owner,
490}
491
492impl GenesisObject {
493    pub fn new(data: ObjectData, owner: Owner) -> Self {
494        Self { data, owner }
495    }
496
497    pub fn object_id(&self) -> Address {
498        match &self.data {
499            ObjectData::Struct(struct_) => id_opt(&struct_.contents).unwrap(),
500            ObjectData::Package(package) => package.id,
501        }
502    }
503
504    pub fn version(&self) -> Version {
505        match &self.data {
506            ObjectData::Struct(struct_) => struct_.version,
507            ObjectData::Package(package) => package.version,
508        }
509    }
510
511    pub fn object_type(&self) -> ObjectType {
512        match &self.data {
513            ObjectData::Struct(struct_) => ObjectType::Struct(struct_.type_.clone()),
514            ObjectData::Package(_) => ObjectType::Package,
515        }
516    }
517
518    pub fn owner(&self) -> &Owner {
519        &self.owner
520    }
521
522    pub fn data(&self) -> &ObjectData {
523        &self.data
524    }
525}
526
527//TODO improve ser/de to do borrowing to avoid clones where possible
528#[cfg(feature = "serde")]
529#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
530mod serialization {
531    use serde::Deserialize;
532    use serde::Deserializer;
533    use serde::Serialize;
534    use serde::Serializer;
535    use serde_with::DeserializeAs;
536    use serde_with::SerializeAs;
537
538    use super::*;
539    use crate::TypeTag;
540
541    /// Wrapper around StructTag with a space-efficient representation for common types like coins
542    /// The StructTag for a gas coin is 84 bytes, so using 1 byte instead is a win.
543    /// The inner representation is private to prevent incorrectly constructing an `Other` instead of
544    /// one of the specialized variants, e.g. `Other(GasCoin::type_())` instead of `GasCoin`
545    #[derive(serde_derive::Deserialize)]
546    enum MoveStructType {
547        /// A type that is not `0x2::coin::Coin<T>`
548        Other(StructTag),
549        /// A SUI coin (i.e., `0x2::coin::Coin<0x2::sui::SUI>`)
550        GasCoin,
551        /// A record of a staked SUI coin (i.e., `0x3::staking_pool::StakedSui`)
552        StakedSui,
553        /// A non-SUI coin type (i.e., `0x2::coin::Coin<T> where T != 0x2::sui::SUI`)
554        Coin(TypeTag),
555        /// A SUI balance accumulator field
556        /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<0x2::sui::SUI>>, 0x2::accumulator::U128>`)
557        SuiBalanceAccumulatorField,
558        /// A non-SUI balance accumulator field
559        /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<T>>, 0x2::accumulator::U128>`
560        /// where T != 0x2::sui::SUI)
561        BalanceAccumulatorField(TypeTag),
562        // NOTE: if adding a new type here, and there are existing on-chain objects of that
563        // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
564        // to make sure the new type and Other(_) are interpreted consistently.
565    }
566
567    /// See `MoveStructType`
568    #[derive(serde_derive::Serialize)]
569    enum MoveStructTypeRef<'a> {
570        /// A type that is not `0x2::coin::Coin<T>`
571        Other(&'a StructTag),
572        /// A SUI coin (i.e., `0x2::coin::Coin<0x2::sui::SUI>`)
573        GasCoin,
574        /// A record of a staked SUI coin (i.e., `0x3::staking_pool::StakedSui`)
575        StakedSui,
576        /// A non-SUI coin type (i.e., `0x2::coin::Coin<T> where T != 0x2::sui::SUI`)
577        Coin(&'a TypeTag),
578        /// A SUI balance accumulator field
579        /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<0x2::sui::SUI>>, 0x2::accumulator::U128>`)
580        SuiBalanceAccumulatorField,
581        /// A non-SUI balance accumulator field
582        /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<T>>, 0x2::accumulator::U128>`
583        /// where T != 0x2::sui::SUI)
584        BalanceAccumulatorField(&'a TypeTag),
585        // NOTE: if adding a new type here, and there are existing on-chain objects of that
586        // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
587        // to make sure the new type and Other(_) are interpreted consistently.
588    }
589
590    impl MoveStructType {
591        fn into_struct_tag(self) -> StructTag {
592            match self {
593                MoveStructType::Other(tag) => tag,
594                MoveStructType::GasCoin => StructTag::gas_coin(),
595                MoveStructType::StakedSui => StructTag::staked_sui(),
596                MoveStructType::Coin(type_tag) => StructTag::coin(type_tag),
597                MoveStructType::SuiBalanceAccumulatorField => {
598                    StructTag::balance_accumulator_field(StructTag::sui().into())
599                }
600                MoveStructType::BalanceAccumulatorField(type_tag) => {
601                    StructTag::balance_accumulator_field(type_tag)
602                }
603            }
604        }
605    }
606
607    impl<'a> MoveStructTypeRef<'a> {
608        fn from_struct_tag(s: &'a StructTag) -> Self {
609            let address = s.address();
610            let module = s.module();
611            let name = s.name();
612            let type_params = s.type_params();
613
614            if let Some(coin_type) = s.is_coin() {
615                if let TypeTag::Struct(s_inner) = coin_type
616                    && s_inner.is_gas()
617                {
618                    Self::GasCoin
619                } else {
620                    Self::Coin(coin_type)
621                }
622            } else if address == &Address::THREE
623                && module == "staking_pool"
624                && name == "StakedSui"
625                && type_params.is_empty()
626            {
627                Self::StakedSui
628            } else if let Some(coin_type) = s.is_balance_accumulator_field() {
629                if let TypeTag::Struct(s_inner) = coin_type
630                    && s_inner.is_gas()
631                {
632                    Self::SuiBalanceAccumulatorField
633                } else {
634                    Self::BalanceAccumulatorField(coin_type)
635                }
636            } else {
637                Self::Other(s)
638            }
639        }
640    }
641
642    pub(super) struct BinaryMoveStructType;
643
644    impl SerializeAs<StructTag> for BinaryMoveStructType {
645        fn serialize_as<S>(source: &StructTag, serializer: S) -> Result<S::Ok, S::Error>
646        where
647            S: Serializer,
648        {
649            let move_object_type = MoveStructTypeRef::from_struct_tag(source);
650            move_object_type.serialize(serializer)
651        }
652    }
653
654    impl<'de> DeserializeAs<'de, StructTag> for BinaryMoveStructType {
655        fn deserialize_as<D>(deserializer: D) -> Result<StructTag, D::Error>
656        where
657            D: Deserializer<'de>,
658        {
659            let struct_type = MoveStructType::deserialize(deserializer)?;
660            Ok(struct_type.into_struct_tag())
661        }
662    }
663
664    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
665    enum BinaryGenesisObject {
666        RawObject { data: ObjectData, owner: Owner },
667    }
668
669    impl Serialize for GenesisObject {
670        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
671        where
672            S: Serializer,
673        {
674            let binary = BinaryGenesisObject::RawObject {
675                data: self.data.clone(),
676                owner: self.owner,
677            };
678            binary.serialize(serializer)
679        }
680    }
681
682    impl<'de> Deserialize<'de> for GenesisObject {
683        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
684        where
685            D: Deserializer<'de>,
686        {
687            let BinaryGenesisObject::RawObject { data, owner } =
688                Deserialize::deserialize(deserializer)?;
689
690            Ok(GenesisObject { data, owner })
691        }
692    }
693
694    #[cfg(test)]
695    mod test {
696        use crate::bcs::FromBcs;
697        use crate::bcs::ToBcs;
698        use crate::object::Object;
699
700        #[cfg(target_arch = "wasm32")]
701        use wasm_bindgen_test::wasm_bindgen_test as test;
702
703        #[test]
704        fn object_fixture() {
705            const SUI_COIN: &[u8] = &[
706                0, 1, 1, 32, 79, 43, 0, 0, 0, 0, 0, 40, 35, 95, 175, 213, 151, 87, 206, 190, 35,
707                131, 79, 35, 254, 22, 15, 181, 40, 108, 28, 77, 68, 229, 107, 254, 191, 160, 196,
708                186, 42, 2, 122, 53, 52, 133, 199, 58, 0, 0, 0, 0, 0, 79, 255, 208, 0, 85, 34, 190,
709                75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155, 144, 230, 47,
710                97, 220, 21, 24, 30, 26, 62, 32, 17, 197, 192, 38, 64, 173, 142, 143, 49, 111, 15,
711                211, 92, 84, 48, 160, 243, 102, 229, 253, 251, 137, 210, 101, 119, 173, 228, 51,
712                141, 20, 15, 85, 96, 19, 15, 0, 0, 0, 0, 0,
713            ];
714
715            const SUI_STAKE: &[u8] = &[
716                0, 2, 1, 154, 1, 52, 5, 0, 0, 0, 0, 80, 3, 112, 71, 231, 166, 234, 205, 164, 99,
717                237, 29, 56, 97, 170, 21, 96, 105, 158, 227, 122, 22, 251, 60, 162, 12, 97, 151,
718                218, 71, 253, 231, 239, 116, 138, 12, 233, 128, 195, 128, 77, 33, 38, 122, 77, 53,
719                154, 197, 198, 75, 212, 12, 182, 163, 224, 42, 82, 123, 69, 248, 40, 207, 143, 211,
720                13, 106, 1, 0, 0, 0, 0, 0, 0, 59, 81, 183, 246, 112, 0, 0, 0, 0, 79, 255, 208, 0,
721                85, 34, 190, 75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155,
722                144, 230, 47, 97, 220, 21, 24, 30, 26, 62, 32, 247, 239, 248, 71, 247, 102, 190,
723                149, 232, 153, 138, 67, 169, 209, 203, 29, 255, 215, 223, 57, 159, 44, 40, 218,
724                166, 13, 80, 71, 14, 188, 232, 68, 0, 0, 0, 0, 0, 0, 0, 0,
725            ];
726
727            const NFT: &[u8] = &[
728                0, 0, 97, 201, 195, 159, 216, 97, 133, 173, 96, 215, 56, 212, 229, 43, 208, 139,
729                218, 7, 29, 54, 106, 205, 224, 126, 7, 195, 145, 106, 45, 117, 168, 22, 12, 100,
730                105, 115, 116, 114, 105, 98, 117, 116, 105, 111, 110, 11, 68, 69, 69, 80, 87, 114,
731                97, 112, 112, 101, 114, 0, 0, 124, 24, 223, 4, 0, 0, 0, 0, 40, 31, 8, 18, 84, 38,
732                164, 252, 84, 115, 250, 246, 137, 132, 128, 186, 156, 36, 62, 18, 140, 21, 4, 90,
733                209, 105, 85, 84, 92, 214, 97, 81, 207, 64, 194, 198, 208, 21, 0, 0, 0, 0, 79, 255,
734                208, 0, 85, 34, 190, 75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160,
735                155, 144, 230, 47, 97, 220, 21, 24, 30, 26, 62, 32, 170, 4, 94, 114, 207, 155, 31,
736                80, 62, 254, 220, 206, 240, 218, 83, 54, 204, 197, 255, 239, 41, 66, 199, 150, 56,
737                189, 86, 217, 166, 216, 128, 241, 64, 205, 21, 0, 0, 0, 0, 0,
738            ];
739
740            const FUD_COIN: &[u8] = &[
741                0, 3, 7, 118, 203, 129, 155, 1, 171, 237, 80, 43, 238, 138, 112, 43, 76, 45, 84,
742                117, 50, 193, 47, 37, 0, 28, 157, 234, 121, 90, 94, 99, 28, 38, 241, 3, 102, 117,
743                100, 3, 70, 85, 68, 0, 1, 193, 89, 252, 3, 0, 0, 0, 0, 40, 33, 214, 90, 11, 56,
744                243, 115, 10, 250, 121, 250, 28, 34, 237, 104, 130, 148, 40, 130, 29, 248, 137,
745                244, 27, 138, 94, 150, 28, 182, 104, 162, 185, 0, 152, 247, 62, 93, 1, 0, 0, 0, 42,
746                95, 32, 226, 13, 31, 128, 91, 188, 127, 235, 12, 75, 73, 116, 112, 3, 227, 244,
747                126, 59, 81, 214, 118, 144, 243, 195, 17, 82, 216, 119, 170, 32, 239, 247, 71, 249,
748                241, 98, 133, 53, 46, 37, 100, 242, 94, 231, 241, 184, 8, 69, 192, 69, 67, 1, 116,
749                251, 229, 226, 99, 119, 79, 255, 71, 43, 64, 242, 19, 0, 0, 0, 0, 0,
750            ];
751
752            const BULLSHARK_PACKAGE: &[u8] = &[
753                1, 135, 35, 29, 28, 138, 126, 114, 145, 204, 122, 145, 8, 244, 199, 188, 26, 10,
754                28, 14, 182, 55, 91, 91, 97, 10, 245, 202, 35, 223, 14, 140, 86, 1, 0, 0, 0, 0, 0,
755                0, 0, 1, 9, 98, 117, 108, 108, 115, 104, 97, 114, 107, 162, 6, 161, 28, 235, 11, 6,
756                0, 0, 0, 10, 1, 0, 12, 2, 12, 36, 3, 48, 61, 4, 109, 12, 5, 121, 137, 1, 7, 130, 2,
757                239, 1, 8, 241, 3, 96, 6, 209, 4, 82, 10, 163, 5, 5, 12, 168, 5, 75, 0, 7, 1, 16,
758                2, 9, 2, 21, 2, 22, 2, 23, 0, 0, 2, 0, 1, 3, 7, 1, 0, 0, 2, 1, 12, 1, 0, 1, 2, 2,
759                12, 1, 0, 1, 2, 4, 12, 1, 0, 1, 4, 5, 2, 0, 5, 6, 7, 0, 0, 12, 0, 1, 0, 0, 13, 2,
760                1, 0, 0, 8, 3, 1, 0, 1, 20, 7, 8, 1, 0, 2, 8, 18, 19, 1, 0, 2, 10, 10, 11, 1, 2, 2,
761                14, 17, 1, 1, 0, 3, 17, 7, 1, 1, 12, 3, 18, 16, 1, 1, 12, 4, 19, 13, 14, 0, 5, 15,
762                5, 6, 0, 3, 6, 5, 9, 7, 12, 8, 15, 6, 9, 4, 9, 2, 8, 0, 7, 8, 5, 0, 4, 7, 11, 4, 1,
763                8, 0, 3, 5, 7, 8, 5, 2, 7, 11, 4, 1, 8, 0, 11, 2, 1, 8, 0, 2, 11, 3, 1, 8, 0, 11,
764                4, 1, 8, 0, 1, 10, 2, 1, 8, 6, 1, 9, 0, 1, 11, 1, 1, 9, 0, 1, 8, 0, 7, 9, 0, 2, 10,
765                2, 10, 2, 10, 2, 11, 1, 1, 8, 6, 7, 8, 5, 2, 11, 4, 1, 9, 0, 11, 3, 1, 9, 0, 1, 11,
766                3, 1, 8, 0, 1, 6, 8, 5, 1, 5, 1, 11, 4, 1, 8, 0, 2, 9, 0, 5, 4, 7, 11, 4, 1, 9, 0,
767                3, 5, 7, 8, 5, 2, 7, 11, 4, 1, 9, 0, 11, 2, 1, 9, 0, 1, 3, 9, 66, 85, 76, 76, 83,
768                72, 65, 82, 75, 4, 67, 111, 105, 110, 12, 67, 111, 105, 110, 77, 101, 116, 97, 100,
769                97, 116, 97, 6, 79, 112, 116, 105, 111, 110, 11, 84, 114, 101, 97, 115, 117, 114,
770                121, 67, 97, 112, 9, 84, 120, 67, 111, 110, 116, 101, 120, 116, 3, 85, 114, 108, 9,
771                98, 117, 108, 108, 115, 104, 97, 114, 107, 4, 98, 117, 114, 110, 4, 99, 111, 105,
772                110, 15, 99, 114, 101, 97, 116, 101, 95, 99, 117, 114, 114, 101, 110, 99, 121, 11,
773                100, 117, 109, 109, 121, 95, 102, 105, 101, 108, 100, 4, 105, 110, 105, 116, 4,
774                109, 105, 110, 116, 17, 109, 105, 110, 116, 95, 97, 110, 100, 95, 116, 114, 97,
775                110, 115, 102, 101, 114, 21, 110, 101, 119, 95, 117, 110, 115, 97, 102, 101, 95,
776                102, 114, 111, 109, 95, 98, 121, 116, 101, 115, 6, 111, 112, 116, 105, 111, 110,
777                20, 112, 117, 98, 108, 105, 99, 95, 102, 114, 101, 101, 122, 101, 95, 111, 98, 106,
778                101, 99, 116, 15, 112, 117, 98, 108, 105, 99, 95, 116, 114, 97, 110, 115, 102, 101,
779                114, 6, 115, 101, 110, 100, 101, 114, 4, 115, 111, 109, 101, 8, 116, 114, 97, 110,
780                115, 102, 101, 114, 10, 116, 120, 95, 99, 111, 110, 116, 101, 120, 116, 3, 117,
781                114, 108, 135, 35, 29, 28, 138, 126, 114, 145, 204, 122, 145, 8, 244, 199, 188, 26,
782                10, 28, 14, 182, 55, 91, 91, 97, 10, 245, 202, 35, 223, 14, 140, 86, 0, 0, 0, 0, 0,
783                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
784                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
785                0, 0, 2, 10, 2, 10, 9, 66, 85, 76, 76, 83, 72, 65, 82, 75, 10, 2, 20, 19, 66, 117,
786                108, 108, 32, 83, 104, 97, 114, 107, 32, 83, 117, 105, 70, 114, 101, 110, 115, 10,
787                2, 1, 0, 10, 2, 39, 38, 104, 116, 116, 112, 115, 58, 47, 47, 105, 46, 105, 98, 98,
788                46, 99, 111, 47, 104, 87, 89, 50, 87, 53, 120, 47, 98, 117, 108, 108, 115, 104, 97,
789                114, 107, 46, 112, 110, 103, 0, 2, 1, 11, 1, 0, 0, 0, 0, 4, 20, 11, 0, 49, 6, 7, 0,
790                7, 1, 7, 2, 7, 3, 17, 10, 56, 0, 10, 1, 56, 1, 12, 2, 12, 3, 11, 2, 56, 2, 11, 3,
791                11, 1, 46, 17, 9, 56, 3, 2, 1, 1, 4, 0, 1, 6, 11, 0, 11, 1, 11, 2, 11, 3, 56, 4, 2,
792                2, 1, 4, 0, 1, 5, 11, 0, 11, 1, 56, 5, 1, 2, 0, 1, 9, 98, 117, 108, 108, 115, 104,
793                97, 114, 107, 9, 66, 85, 76, 76, 83, 72, 65, 82, 75, 135, 35, 29, 28, 138, 126,
794                114, 145, 204, 122, 145, 8, 244, 199, 188, 26, 10, 28, 14, 182, 55, 91, 91, 97, 10,
795                245, 202, 35, 223, 14, 140, 86, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
796                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
797                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
798                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
799                0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
800                0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 3, 32, 87, 145, 191, 231, 147, 185,
801                46, 159, 240, 181, 95, 126, 236, 65, 154, 55, 16, 196, 229, 218, 47, 59, 99, 197,
802                13, 89, 18, 159, 205, 129, 112, 131, 112, 192, 126, 0, 0, 0, 0, 0,
803            ];
804
805            for fixture in [SUI_COIN, SUI_STAKE, NFT, FUD_COIN, BULLSHARK_PACKAGE] {
806                let object: Object = bcs::from_bytes(fixture).unwrap();
807                assert_eq!(bcs::to_bytes(&object).unwrap(), fixture);
808
809                let json = serde_json::to_string_pretty(&object).unwrap();
810                println!("{json}");
811                assert_eq!(object, serde_json::from_str(&json).unwrap());
812            }
813        }
814
815        // Test to ensure we properly serialize and deserialize the new MoveStructType variants for
816        // address balances
817        #[test]
818        fn address_balance_objects() {
819            let non_sui_address_balance_type = "AAUHIRSU0QWQjjOQW/wFv4O24+PddAa8JQ45YwhB+i7EfzgGY29pbl9hBkNPSU5fQQAAEAAAAAAAAABQCSgWThHEQ1NqPKQQsXVKd/yFD0FSDYUtrvXx1xt+jIk0Bsyk3bbd4hLE1MDxwok6jzp0k3365HVXhJgmi+4vjcQJAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACswgmPj1SN1ZkPLtiVtVs0XD3QCgS/YYUFBh9Q6p4b+zoJAAAAAAAAAAAA==";
820
821            let non_sui = Object::from_bcs_base64(non_sui_address_balance_type).unwrap();
822            assert_eq!(
823                non_sui.to_bcs_base64().unwrap(),
824                non_sui_address_balance_type
825            );
826
827            let sui_address_balance_type = "AAQAAgAAAAAAAABQlJ321C1hKFc15SQmGZUdTDrwVh7xQ46GoV2zEnFK88b/JOPl1wGyhHd/R1itnNXhAzGoyXuDHuOL3V34auvxf+gDAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACswgjRbaObiwu6bn07xewfd3V9iFJfbhcaWy7K6YgNsZdKkAAAAAAAAAAA==";
828
829            let sui = Object::from_bcs_base64(sui_address_balance_type).unwrap();
830            assert_eq!(sui.to_bcs_base64().unwrap(), sui_address_balance_type);
831        }
832    }
833}