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        // NOTE: if adding a new type here, and there are existing on-chain objects of that
556        // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
557        // to make sure the new type and Other(_) are interpreted consistently.
558    }
559
560    /// See `MoveStructType`
561    #[derive(serde_derive::Serialize)]
562    enum MoveStructTypeRef<'a> {
563        /// A type that is not `0x2::coin::Coin<T>`
564        Other(&'a StructTag),
565        /// A SUI coin (i.e., `0x2::coin::Coin<0x2::sui::SUI>`)
566        GasCoin,
567        /// A record of a staked SUI coin (i.e., `0x3::staking_pool::StakedSui`)
568        StakedSui,
569        /// A non-SUI coin type (i.e., `0x2::coin::Coin<T> where T != 0x2::sui::SUI`)
570        Coin(&'a TypeTag),
571        // NOTE: if adding a new type here, and there are existing on-chain objects of that
572        // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
573        // to make sure the new type and Other(_) are interpreted consistently.
574    }
575
576    impl MoveStructType {
577        fn into_struct_tag(self) -> StructTag {
578            match self {
579                MoveStructType::Other(tag) => tag,
580                MoveStructType::GasCoin => StructTag::gas_coin(),
581                MoveStructType::StakedSui => StructTag::staked_sui(),
582                MoveStructType::Coin(type_tag) => StructTag::coin(type_tag),
583            }
584        }
585    }
586
587    impl<'a> MoveStructTypeRef<'a> {
588        fn from_struct_tag(s: &'a StructTag) -> Self {
589            let address = s.address();
590            let module = s.module();
591            let name = s.name();
592            let type_params = s.type_params();
593
594            if let Some(coin_type) = s.is_coin() {
595                if let TypeTag::Struct(s_inner) = coin_type {
596                    let address = s_inner.address();
597                    let module = s_inner.module();
598                    let name = s_inner.name();
599                    let type_params = s_inner.type_params();
600
601                    if address == &Address::TWO
602                        && module == "sui"
603                        && name == "SUI"
604                        && type_params.is_empty()
605                    {
606                        return Self::GasCoin;
607                    }
608                }
609
610                Self::Coin(coin_type)
611            } else if address == &Address::THREE
612                && module == "staking_pool"
613                && name == "StakedSui"
614                && type_params.is_empty()
615            {
616                Self::StakedSui
617            } else {
618                Self::Other(s)
619            }
620        }
621    }
622
623    pub(super) struct BinaryMoveStructType;
624
625    impl SerializeAs<StructTag> for BinaryMoveStructType {
626        fn serialize_as<S>(source: &StructTag, serializer: S) -> Result<S::Ok, S::Error>
627        where
628            S: Serializer,
629        {
630            let move_object_type = MoveStructTypeRef::from_struct_tag(source);
631            move_object_type.serialize(serializer)
632        }
633    }
634
635    impl<'de> DeserializeAs<'de, StructTag> for BinaryMoveStructType {
636        fn deserialize_as<D>(deserializer: D) -> Result<StructTag, D::Error>
637        where
638            D: Deserializer<'de>,
639        {
640            let struct_type = MoveStructType::deserialize(deserializer)?;
641            Ok(struct_type.into_struct_tag())
642        }
643    }
644
645    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
646    enum BinaryGenesisObject {
647        RawObject { data: ObjectData, owner: Owner },
648    }
649
650    impl Serialize for GenesisObject {
651        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
652        where
653            S: Serializer,
654        {
655            let binary = BinaryGenesisObject::RawObject {
656                data: self.data.clone(),
657                owner: self.owner,
658            };
659            binary.serialize(serializer)
660        }
661    }
662
663    impl<'de> Deserialize<'de> for GenesisObject {
664        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
665        where
666            D: Deserializer<'de>,
667        {
668            let BinaryGenesisObject::RawObject { data, owner } =
669                Deserialize::deserialize(deserializer)?;
670
671            Ok(GenesisObject { data, owner })
672        }
673    }
674
675    #[cfg(test)]
676    mod test {
677        use crate::object::Object;
678
679        #[cfg(target_arch = "wasm32")]
680        use wasm_bindgen_test::wasm_bindgen_test as test;
681
682        #[test]
683        fn object_fixture() {
684            const SUI_COIN: &[u8] = &[
685                0, 1, 1, 32, 79, 43, 0, 0, 0, 0, 0, 40, 35, 95, 175, 213, 151, 87, 206, 190, 35,
686                131, 79, 35, 254, 22, 15, 181, 40, 108, 28, 77, 68, 229, 107, 254, 191, 160, 196,
687                186, 42, 2, 122, 53, 52, 133, 199, 58, 0, 0, 0, 0, 0, 79, 255, 208, 0, 85, 34, 190,
688                75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155, 144, 230, 47,
689                97, 220, 21, 24, 30, 26, 62, 32, 17, 197, 192, 38, 64, 173, 142, 143, 49, 111, 15,
690                211, 92, 84, 48, 160, 243, 102, 229, 253, 251, 137, 210, 101, 119, 173, 228, 51,
691                141, 20, 15, 85, 96, 19, 15, 0, 0, 0, 0, 0,
692            ];
693
694            const SUI_STAKE: &[u8] = &[
695                0, 2, 1, 154, 1, 52, 5, 0, 0, 0, 0, 80, 3, 112, 71, 231, 166, 234, 205, 164, 99,
696                237, 29, 56, 97, 170, 21, 96, 105, 158, 227, 122, 22, 251, 60, 162, 12, 97, 151,
697                218, 71, 253, 231, 239, 116, 138, 12, 233, 128, 195, 128, 77, 33, 38, 122, 77, 53,
698                154, 197, 198, 75, 212, 12, 182, 163, 224, 42, 82, 123, 69, 248, 40, 207, 143, 211,
699                13, 106, 1, 0, 0, 0, 0, 0, 0, 59, 81, 183, 246, 112, 0, 0, 0, 0, 79, 255, 208, 0,
700                85, 34, 190, 75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155,
701                144, 230, 47, 97, 220, 21, 24, 30, 26, 62, 32, 247, 239, 248, 71, 247, 102, 190,
702                149, 232, 153, 138, 67, 169, 209, 203, 29, 255, 215, 223, 57, 159, 44, 40, 218,
703                166, 13, 80, 71, 14, 188, 232, 68, 0, 0, 0, 0, 0, 0, 0, 0,
704            ];
705
706            const NFT: &[u8] = &[
707                0, 0, 97, 201, 195, 159, 216, 97, 133, 173, 96, 215, 56, 212, 229, 43, 208, 139,
708                218, 7, 29, 54, 106, 205, 224, 126, 7, 195, 145, 106, 45, 117, 168, 22, 12, 100,
709                105, 115, 116, 114, 105, 98, 117, 116, 105, 111, 110, 11, 68, 69, 69, 80, 87, 114,
710                97, 112, 112, 101, 114, 0, 0, 124, 24, 223, 4, 0, 0, 0, 0, 40, 31, 8, 18, 84, 38,
711                164, 252, 84, 115, 250, 246, 137, 132, 128, 186, 156, 36, 62, 18, 140, 21, 4, 90,
712                209, 105, 85, 84, 92, 214, 97, 81, 207, 64, 194, 198, 208, 21, 0, 0, 0, 0, 79, 255,
713                208, 0, 85, 34, 190, 75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160,
714                155, 144, 230, 47, 97, 220, 21, 24, 30, 26, 62, 32, 170, 4, 94, 114, 207, 155, 31,
715                80, 62, 254, 220, 206, 240, 218, 83, 54, 204, 197, 255, 239, 41, 66, 199, 150, 56,
716                189, 86, 217, 166, 216, 128, 241, 64, 205, 21, 0, 0, 0, 0, 0,
717            ];
718
719            const FUD_COIN: &[u8] = &[
720                0, 3, 7, 118, 203, 129, 155, 1, 171, 237, 80, 43, 238, 138, 112, 43, 76, 45, 84,
721                117, 50, 193, 47, 37, 0, 28, 157, 234, 121, 90, 94, 99, 28, 38, 241, 3, 102, 117,
722                100, 3, 70, 85, 68, 0, 1, 193, 89, 252, 3, 0, 0, 0, 0, 40, 33, 214, 90, 11, 56,
723                243, 115, 10, 250, 121, 250, 28, 34, 237, 104, 130, 148, 40, 130, 29, 248, 137,
724                244, 27, 138, 94, 150, 28, 182, 104, 162, 185, 0, 152, 247, 62, 93, 1, 0, 0, 0, 42,
725                95, 32, 226, 13, 31, 128, 91, 188, 127, 235, 12, 75, 73, 116, 112, 3, 227, 244,
726                126, 59, 81, 214, 118, 144, 243, 195, 17, 82, 216, 119, 170, 32, 239, 247, 71, 249,
727                241, 98, 133, 53, 46, 37, 100, 242, 94, 231, 241, 184, 8, 69, 192, 69, 67, 1, 116,
728                251, 229, 226, 99, 119, 79, 255, 71, 43, 64, 242, 19, 0, 0, 0, 0, 0,
729            ];
730
731            const BULLSHARK_PACKAGE: &[u8] = &[
732                1, 135, 35, 29, 28, 138, 126, 114, 145, 204, 122, 145, 8, 244, 199, 188, 26, 10,
733                28, 14, 182, 55, 91, 91, 97, 10, 245, 202, 35, 223, 14, 140, 86, 1, 0, 0, 0, 0, 0,
734                0, 0, 1, 9, 98, 117, 108, 108, 115, 104, 97, 114, 107, 162, 6, 161, 28, 235, 11, 6,
735                0, 0, 0, 10, 1, 0, 12, 2, 12, 36, 3, 48, 61, 4, 109, 12, 5, 121, 137, 1, 7, 130, 2,
736                239, 1, 8, 241, 3, 96, 6, 209, 4, 82, 10, 163, 5, 5, 12, 168, 5, 75, 0, 7, 1, 16,
737                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,
738                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,
739                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,
740                14, 17, 1, 1, 0, 3, 17, 7, 1, 1, 12, 3, 18, 16, 1, 1, 12, 4, 19, 13, 14, 0, 5, 15,
741                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,
742                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,
743                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,
744                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,
745                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,
746                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,
747                72, 65, 82, 75, 4, 67, 111, 105, 110, 12, 67, 111, 105, 110, 77, 101, 116, 97, 100,
748                97, 116, 97, 6, 79, 112, 116, 105, 111, 110, 11, 84, 114, 101, 97, 115, 117, 114,
749                121, 67, 97, 112, 9, 84, 120, 67, 111, 110, 116, 101, 120, 116, 3, 85, 114, 108, 9,
750                98, 117, 108, 108, 115, 104, 97, 114, 107, 4, 98, 117, 114, 110, 4, 99, 111, 105,
751                110, 15, 99, 114, 101, 97, 116, 101, 95, 99, 117, 114, 114, 101, 110, 99, 121, 11,
752                100, 117, 109, 109, 121, 95, 102, 105, 101, 108, 100, 4, 105, 110, 105, 116, 4,
753                109, 105, 110, 116, 17, 109, 105, 110, 116, 95, 97, 110, 100, 95, 116, 114, 97,
754                110, 115, 102, 101, 114, 21, 110, 101, 119, 95, 117, 110, 115, 97, 102, 101, 95,
755                102, 114, 111, 109, 95, 98, 121, 116, 101, 115, 6, 111, 112, 116, 105, 111, 110,
756                20, 112, 117, 98, 108, 105, 99, 95, 102, 114, 101, 101, 122, 101, 95, 111, 98, 106,
757                101, 99, 116, 15, 112, 117, 98, 108, 105, 99, 95, 116, 114, 97, 110, 115, 102, 101,
758                114, 6, 115, 101, 110, 100, 101, 114, 4, 115, 111, 109, 101, 8, 116, 114, 97, 110,
759                115, 102, 101, 114, 10, 116, 120, 95, 99, 111, 110, 116, 101, 120, 116, 3, 117,
760                114, 108, 135, 35, 29, 28, 138, 126, 114, 145, 204, 122, 145, 8, 244, 199, 188, 26,
761                10, 28, 14, 182, 55, 91, 91, 97, 10, 245, 202, 35, 223, 14, 140, 86, 0, 0, 0, 0, 0,
762                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,
763                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,
764                0, 0, 2, 10, 2, 10, 9, 66, 85, 76, 76, 83, 72, 65, 82, 75, 10, 2, 20, 19, 66, 117,
765                108, 108, 32, 83, 104, 97, 114, 107, 32, 83, 117, 105, 70, 114, 101, 110, 115, 10,
766                2, 1, 0, 10, 2, 39, 38, 104, 116, 116, 112, 115, 58, 47, 47, 105, 46, 105, 98, 98,
767                46, 99, 111, 47, 104, 87, 89, 50, 87, 53, 120, 47, 98, 117, 108, 108, 115, 104, 97,
768                114, 107, 46, 112, 110, 103, 0, 2, 1, 11, 1, 0, 0, 0, 0, 4, 20, 11, 0, 49, 6, 7, 0,
769                7, 1, 7, 2, 7, 3, 17, 10, 56, 0, 10, 1, 56, 1, 12, 2, 12, 3, 11, 2, 56, 2, 11, 3,
770                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,
771                2, 1, 4, 0, 1, 5, 11, 0, 11, 1, 56, 5, 1, 2, 0, 1, 9, 98, 117, 108, 108, 115, 104,
772                97, 114, 107, 9, 66, 85, 76, 76, 83, 72, 65, 82, 75, 135, 35, 29, 28, 138, 126,
773                114, 145, 204, 122, 145, 8, 244, 199, 188, 26, 10, 28, 14, 182, 55, 91, 91, 97, 10,
774                245, 202, 35, 223, 14, 140, 86, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
775                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,
776                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,
777                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,
778                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,
779                0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 3, 32, 87, 145, 191, 231, 147, 185,
780                46, 159, 240, 181, 95, 126, 236, 65, 154, 55, 16, 196, 229, 218, 47, 59, 99, 197,
781                13, 89, 18, 159, 205, 129, 112, 131, 112, 192, 126, 0, 0, 0, 0, 0,
782            ];
783
784            for fixture in [SUI_COIN, SUI_STAKE, NFT, FUD_COIN, BULLSHARK_PACKAGE] {
785                let object: Object = bcs::from_bytes(fixture).unwrap();
786                assert_eq!(bcs::to_bytes(&object).unwrap(), fixture);
787
788                let json = serde_json::to_string_pretty(&object).unwrap();
789                println!("{json}");
790                assert_eq!(object, serde_json::from_str(&json).unwrap());
791            }
792        }
793    }
794}