sui_sdk_types/
object.rs

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