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