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