iota_sdk_types/
object.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::BTreeMap;
6
7use super::{Address, Digest, Identifier, ObjectId, StructTag};
8
9pub type Version = u64;
10
11/// Reference to an object
12///
13/// Contains sufficient information to uniquely identify a specific object.
14///
15/// # BCS
16///
17/// The BCS serialized form for this type is defined by the following ABNF:
18///
19/// ```text
20/// object-ref = object-id u64 digest
21/// ```
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
25#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
26pub struct ObjectReference {
27    /// The object id of this object.
28    pub object_id: ObjectId,
29    /// The version of this object.
30    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
31    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
32    pub version: Version,
33    /// The digest of this object.
34    pub digest: Digest,
35}
36
37impl ObjectReference {
38    /// Creates a new object reference from the object's id, version, and
39    /// digest.
40    pub fn new(object_id: ObjectId, version: Version, digest: Digest) -> 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
49    /// referring to.
50    pub fn object_id(&self) -> &ObjectId {
51        &self.object_id
52    }
53
54    /// Returns the version of the object that this ObjectReference is referring
55    /// to.
56    pub fn version(&self) -> Version {
57        self.version
58    }
59
60    /// Returns the digest of the object that this ObjectReference is referring
61    /// to.
62    pub fn digest(&self) -> &Digest {
63        &self.digest
64    }
65
66    /// Returns a 3-tuple containing the object id, version, and digest.
67    pub fn into_parts(self) -> (ObjectId, Version, Digest) {
68        let Self {
69            object_id,
70            version,
71            digest,
72        } = self;
73
74        (object_id, version, digest)
75    }
76}
77
78/// Enum of different types of ownership for an object.
79///
80/// # BCS
81///
82/// The BCS serialized form for this type is defined by the following ABNF:
83///
84/// ```text
85/// owner = owner-address / owner-object / owner-shared / owner-immutable
86///
87/// owner-address   = %x00 address
88/// owner-object    = %x01 object-id
89/// owner-shared    = %x02 u64
90/// owner-immutable = %x03
91/// ```
92#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
93#[cfg_attr(
94    feature = "serde",
95    derive(serde::Serialize, serde::Deserialize),
96    serde(rename_all = "lowercase")
97)]
98#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
99#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
100pub enum Owner {
101    /// Object is exclusively owned by a single address, and is mutable.
102    Address(Address),
103    /// Object is exclusively owned by a single object, and is mutable.
104    Object(ObjectId),
105    /// Object is shared, can be used by any address, and is mutable.
106    Shared(
107        /// The version at which the object became shared
108        #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
109        #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
110        Version,
111    ),
112    /// Object is immutable, and hence ownership doesn't matter.
113    Immutable,
114}
115
116impl Owner {
117    crate::def_is!(Immutable);
118
119    crate::def_is_as_into_opt!(Address, Object(ObjectId), Shared(Version));
120}
121
122impl std::fmt::Display for Owner {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        match self {
125            Owner::Address(address) => write!(f, "Address({address})"),
126            Owner::Object(object_id) => write!(f, "Object({object_id})"),
127            Owner::Shared(version) => write!(f, "Shared({version})"),
128            Owner::Immutable => write!(f, "Immutable"),
129        }
130    }
131}
132
133/// Object data, either a package or struct
134///
135/// # BCS
136///
137/// The BCS serialized form for this type is defined by the following ABNF:
138///
139/// ```text
140/// object-data = object-data-struct / object-data-package
141///
142/// object-data-struct  = %x00 object-move-struct
143/// object-data-package = %x01 object-move-package
144/// ```
145#[derive(Clone, Debug, PartialEq, Eq, Hash)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147#[allow(clippy::large_enum_variant)]
148#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
149// TODO think about hiding this type and not exposing it
150pub enum ObjectData {
151    /// An object whose governing logic lives in a published Move module
152    Struct(MoveStruct),
153    /// Map from each module name to raw serialized Move module bytes
154    Package(MovePackage),
155    // ... IOTA "native" types go here
156}
157
158impl ObjectData {
159    crate::def_is_as_into_opt!(Struct(MoveStruct), Package(MovePackage));
160}
161
162/// A move package
163///
164/// # BCS
165///
166/// The BCS serialized form for this type is defined by the following ABNF:
167///
168/// ```text
169/// object-move-package = object-id u64 move-modules type-origin-table linkage-table
170///
171/// move-modules = map (identifier bytes)
172/// type-origin-table = vector type-origin
173/// linkage-table = map (object-id upgrade-info)
174/// ```
175#[derive(Eq, PartialEq, Debug, Clone, Hash)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
178pub struct MovePackage {
179    /// Address or Id of this package
180    pub id: ObjectId,
181    /// Most move packages are uniquely identified by their ID (i.e. there is
182    /// only one version per ID), but the version is still stored because
183    /// one package may be an upgrade of another (at a different ID), in
184    /// which case its version will be one greater than the version of the
185    /// upgraded package.
186    ///
187    /// Framework packages are an exception to this rule -- all versions of the
188    /// framework packages exist at the same ID, at increasing versions.
189    ///
190    /// In all cases, packages are referred to by move calls using just their
191    /// ID, and they are always loaded at their latest version.
192    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
193    pub version: Version,
194    /// Set of modules defined by this package
195    #[cfg_attr(
196        feature = "serde",
197        serde(with = "::serde_with::As::<BTreeMap<::serde_with::Same, ::serde_with::Bytes>>")
198    )]
199    #[cfg_attr(
200        feature = "proptest",
201        strategy(
202            proptest::collection::btree_map(proptest::arbitrary::any::<Identifier>(), proptest::collection::vec(proptest::arbitrary::any::<u8>(), 0..=1024), 0..=5)
203        )
204    )]
205    pub modules: BTreeMap<Identifier, Vec<u8>>,
206    /// Maps struct/module to a package version where it was first defined,
207    /// stored as a vector for simple serialization and deserialization.
208    pub type_origin_table: Vec<TypeOrigin>,
209    /// For each dependency, maps original package ID to the info about the
210    /// (upgraded) dependency version that this package is using
211    #[cfg_attr(
212        feature = "proptest",
213        strategy(
214            proptest::collection::btree_map(proptest::arbitrary::any::<ObjectId>(), proptest::arbitrary::any::<UpgradeInfo>(), 0..=5)
215        )
216    )]
217    pub linkage_table: BTreeMap<ObjectId, UpgradeInfo>,
218}
219
220/// Identifies a struct and the module it was defined in
221///
222/// # BCS
223///
224/// The BCS serialized form for this type is defined by the following ABNF:
225///
226/// ```text
227/// type-origin = identifier identifier object-id
228/// ```
229#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
231#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
232#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
233pub struct TypeOrigin {
234    pub module_name: Identifier,
235    pub struct_name: Identifier,
236    pub package: ObjectId,
237}
238
239/// Upgraded package info for the linkage table
240///
241/// # BCS
242///
243/// The BCS serialized form for this type is defined by the following ABNF:
244///
245/// ```text
246/// upgrade-info = object-id u64
247/// ```
248#[derive(Eq, PartialEq, Debug, Clone, Hash)]
249#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
250#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
251#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
252pub struct UpgradeInfo {
253    /// Id of the upgraded packages
254    pub upgraded_id: ObjectId,
255    /// Version of the upgraded package
256    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
257    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
258    pub upgraded_version: Version,
259}
260
261/// A move struct
262///
263/// # BCS
264///
265/// The BCS serialized form for this type is defined by the following ABNF:
266///
267/// ```text
268/// object-move-struct = compressed-struct-tag bool u64 object-contents
269///
270/// compressed-struct-tag = other-struct-type / gas-coin-type / staked-iota-type / coin-type
271/// other-struct-type     = %x00 struct-tag
272/// gas-coin-type         = %x01
273/// staked-iota-type      = %x02
274/// coin-type             = %x03 type-tag
275///
276/// ; first 32 bytes of the contents are the object's object-id
277/// object-contents = uleb128 (object-id *OCTET) ; length followed by contents
278/// ```
279#[derive(Eq, PartialEq, Debug, Clone, Hash)]
280// TODO hand-roll a Deserialize impl to enforce that an objectid is present
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
283pub struct MoveStruct {
284    /// The type of this object
285    #[cfg_attr(
286        feature = "serde",
287        serde(with = "::serde_with::As::<serialization::BinaryMoveStructType>")
288    )]
289    pub type_: StructTag,
290    /// Number that increases each time a tx takes this object as a mutable
291    /// input This is a lamport timestamp, not a sequentially increasing
292    /// version
293    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
294    pub version: Version,
295    /// BCS bytes of a Move struct value
296    #[cfg_attr(
297        feature = "serde",
298        serde(with = "::serde_with::As::<::serde_with::Bytes>")
299    )]
300    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(32..=1024).lift()))]
301    pub contents: Vec<u8>,
302}
303
304/// Type of an IOTA object
305#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
306pub enum ObjectType {
307    /// Move package containing one or more bytecode modules
308    Package,
309    /// A Move struct of the given type
310    Struct(StructTag),
311}
312
313impl ObjectType {
314    crate::def_is!(Package);
315
316    crate::def_is_as_into_opt!(Struct(StructTag));
317}
318
319impl std::fmt::Display for ObjectType {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        match self {
322            ObjectType::Package => write!(f, "Package"),
323            ObjectType::Struct(struct_tag) => write!(f, "Struct({struct_tag})"),
324        }
325    }
326}
327
328/// An object on the IOTA blockchain
329///
330/// # BCS
331///
332/// The BCS serialized form for this type is defined by the following ABNF:
333///
334/// ```text
335/// object = object-data owner digest u64
336/// ```
337#[derive(Clone, Debug, PartialEq, Eq)]
338#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
339pub struct Object {
340    /// The meat of the object
341    pub data: ObjectData,
342    /// The owner that unlocks this object
343    pub owner: Owner,
344    /// The digest of the transaction that created or last mutated this object
345    pub previous_transaction: Digest,
346    /// The amount of IOTA we would rebate if this object gets deleted.
347    /// This number is re-calculated each time the object is mutated based on
348    /// the present storage gas price.
349    pub storage_rebate: u64,
350}
351
352impl Object {
353    /// Build an object
354    pub fn new(
355        data: ObjectData,
356        owner: Owner,
357        previous_transaction: Digest,
358        storage_rebate: u64,
359    ) -> Self {
360        Self {
361            data,
362            owner,
363            previous_transaction,
364            storage_rebate,
365        }
366    }
367
368    /// Return this object's id
369    pub fn object_id(&self) -> ObjectId {
370        match &self.data {
371            ObjectData::Struct(struct_) => id_opt(&struct_.contents).unwrap(),
372            ObjectData::Package(package) => package.id,
373        }
374    }
375
376    /// Return this object's reference
377    #[cfg(all(feature = "hash", feature = "serde"))]
378    pub fn object_ref(&self) -> ObjectReference {
379        ObjectReference {
380            object_id: self.object_id(),
381            version: self.version(),
382            digest: self.digest(),
383        }
384    }
385
386    /// Return this object's version
387    pub fn version(&self) -> Version {
388        match &self.data {
389            ObjectData::Struct(struct_) => struct_.version,
390            ObjectData::Package(package) => package.version,
391        }
392    }
393
394    /// Return this object's type
395    pub fn object_type(&self) -> ObjectType {
396        match &self.data {
397            ObjectData::Struct(struct_) => ObjectType::Struct(struct_.type_.clone()),
398            ObjectData::Package(_) => ObjectType::Package,
399        }
400    }
401
402    /// Try to interpret this object as a move struct
403    pub fn as_struct_opt(&self) -> Option<&MoveStruct> {
404        match &self.data {
405            ObjectData::Struct(struct_) => Some(struct_),
406            _ => None,
407        }
408    }
409
410    /// Interpret this object as a move struct
411    pub fn as_struct(&self) -> &MoveStruct {
412        self.as_struct_opt().expect("not a move struct")
413    }
414
415    /// Try to interpret this object as a move package
416    pub fn as_package_opt(&self) -> Option<&MovePackage> {
417        match &self.data {
418            ObjectData::Package(package) => Some(package),
419            _ => None,
420        }
421    }
422
423    /// Interpret this object as a move package
424    pub fn as_package(&self) -> &MovePackage {
425        self.as_package_opt().expect("not a move package")
426    }
427
428    /// Return this object's owner
429    pub fn owner(&self) -> &Owner {
430        &self.owner
431    }
432
433    /// Return this object's data
434    pub fn data(&self) -> &ObjectData {
435        &self.data
436    }
437
438    /// Return the digest of the transaction that last modified this object
439    pub fn previous_transaction(&self) -> Digest {
440        self.previous_transaction
441    }
442
443    /// Return the storage rebate locked in this object
444    ///
445    /// Storage rebates are credited to the gas coin used in a transaction that
446    /// deletes this object.
447    pub fn storage_rebate(&self) -> u64 {
448        self.storage_rebate
449    }
450
451    #[cfg(feature = "serde")]
452    pub fn to_rust<T: serde::de::DeserializeOwned>(&self) -> eyre::Result<T> {
453        use eyre::OptionExt;
454
455        Ok(bcs::from_bytes::<T>(
456            &self.as_struct_opt().ok_or_eyre("not a struct")?.contents,
457        )?)
458    }
459}
460
461fn id_opt(contents: &[u8]) -> Option<ObjectId> {
462    if ObjectId::LENGTH > contents.len() {
463        return None;
464    }
465
466    Some(ObjectId::from(
467        Address::from_bytes(&contents[..ObjectId::LENGTH]).unwrap(),
468    ))
469}
470
471/// An object part of the initial chain state
472///
473/// `GenesisObject`'s are included as a part of genesis, the initial
474/// checkpoint/transaction, that initializes the state of the blockchain.
475///
476/// # BCS
477///
478/// The BCS serialized form for this type is defined by the following ABNF:
479///
480/// ```text
481/// genesis-object = object-data owner
482/// ```
483#[derive(Clone, Debug, PartialEq, Eq)]
484#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
485pub struct GenesisObject {
486    pub data: ObjectData,
487    pub owner: Owner,
488}
489
490impl GenesisObject {
491    pub fn new(data: ObjectData, owner: Owner) -> Self {
492        Self { data, owner }
493    }
494
495    pub fn object_id(&self) -> ObjectId {
496        match &self.data {
497            ObjectData::Struct(struct_) => id_opt(&struct_.contents).unwrap(),
498            ObjectData::Package(package) => package.id,
499        }
500    }
501
502    pub fn version(&self) -> Version {
503        match &self.data {
504            ObjectData::Struct(struct_) => struct_.version,
505            ObjectData::Package(package) => package.version,
506        }
507    }
508
509    pub fn object_type(&self) -> ObjectType {
510        match &self.data {
511            ObjectData::Struct(struct_) => ObjectType::Struct(struct_.type_.clone()),
512            ObjectData::Package(_) => ObjectType::Package,
513        }
514    }
515
516    pub fn owner(&self) -> &Owner {
517        &self.owner
518    }
519
520    pub fn data(&self) -> &ObjectData {
521        &self.data
522    }
523}
524
525// TODO improve ser/de to do borrowing to avoid clones where possible
526#[cfg(feature = "serde")]
527#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
528mod serialization {
529    use std::{borrow::Cow, str::FromStr};
530
531    use serde::{Deserialize, Deserializer, Serialize, Serializer};
532    use serde_with::{DeserializeAs, SerializeAs};
533
534    use super::*;
535    use crate::TypeTag;
536
537    /// Wrapper around StructTag with a space-efficient representation for
538    /// common types like coins The StructTag for a gas coin is 84 bytes, so
539    /// using 1 byte instead is a win. The inner representation is private
540    /// to prevent incorrectly constructing an `Other` instead of one of the
541    /// specialized variants, e.g. `Other(GasCoin::type_())` instead of
542    /// `GasCoin`
543    #[derive(serde::Deserialize)]
544    enum MoveStructType {
545        /// A type that is not `0x2::coin::Coin<T>`
546        Other(StructTag),
547        /// An IOTA coin (i.e., `0x2::coin::Coin<0x2::iota::IOTA>`)
548        GasCoin,
549        /// A record of a staked IOTA coin (i.e.,
550        /// `0x3::staking_pool::StakedIota`)
551        StakedIota,
552        /// A non-IOTA coin type (i.e., `0x2::coin::Coin<T> where T !=
553        /// 0x2::iota::IOTA`)
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::Serialize)]
562    enum MoveStructTypeRef<'a> {
563        /// A type that is not `0x2::coin::Coin<T>`
564        Other(&'a StructTag),
565        /// An IOTA coin (i.e., `0x2::coin::Coin<0x2::iota::IOTA>`)
566        GasCoin,
567        /// A record of a staked IOTA coin (i.e.,
568        /// `0x3::staking_pool::StakedIota`)
569        StakedIota,
570        /// A non-IOTA coin type (i.e., `0x2::coin::Coin<T> where T !=
571        /// 0x2::iota::IOTA`)
572        Coin(&'a TypeTag),
573        // NOTE: if adding a new type here, and there are existing on-chain objects of that
574        // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
575        // to make sure the new type and Other(_) are interpreted consistently.
576    }
577
578    impl MoveStructType {
579        fn into_struct_tag(self) -> StructTag {
580            match self {
581                MoveStructType::Other(tag) => tag,
582                MoveStructType::GasCoin => StructTag::new_gas_coin(),
583                MoveStructType::StakedIota => StructTag::new_staked_iota(),
584                MoveStructType::Coin(type_tag) => StructTag::new_coin(type_tag),
585            }
586        }
587    }
588
589    impl<'a> MoveStructTypeRef<'a> {
590        fn from_struct_tag(s: &'a StructTag) -> Self {
591            let StructTag {
592                address,
593                module,
594                name,
595                type_params,
596            } = s;
597
598            if let Some(coin_type) = s.coin_type_opt() {
599                if let TypeTag::Struct(s_inner) = coin_type {
600                    let StructTag {
601                        address,
602                        module,
603                        name,
604                        type_params,
605                    } = s_inner.as_ref();
606
607                    if address == &Address::FRAMEWORK
608                        && module == "iota"
609                        && name == "IOTA"
610                        && type_params.is_empty()
611                    {
612                        return Self::GasCoin;
613                    }
614                }
615
616                Self::Coin(coin_type)
617            } else if address == &Address::SYSTEM
618                && module == "staking_pool"
619                && name == "StakedIota"
620                && type_params.is_empty()
621            {
622                Self::StakedIota
623            } else {
624                Self::Other(s)
625            }
626        }
627    }
628
629    pub(super) struct BinaryMoveStructType;
630
631    impl SerializeAs<StructTag> for BinaryMoveStructType {
632        fn serialize_as<S>(source: &StructTag, serializer: S) -> Result<S::Ok, S::Error>
633        where
634            S: Serializer,
635        {
636            let move_object_type = MoveStructTypeRef::from_struct_tag(source);
637            move_object_type.serialize(serializer)
638        }
639    }
640
641    impl<'de> DeserializeAs<'de, StructTag> for BinaryMoveStructType {
642        fn deserialize_as<D>(deserializer: D) -> Result<StructTag, D::Error>
643        where
644            D: Deserializer<'de>,
645        {
646            let struct_type = MoveStructType::deserialize(deserializer)?;
647            Ok(struct_type.into_struct_tag())
648        }
649    }
650
651    struct ReadableObjectType;
652
653    impl SerializeAs<ObjectType> for ReadableObjectType {
654        fn serialize_as<S>(source: &ObjectType, serializer: S) -> Result<S::Ok, S::Error>
655        where
656            S: Serializer,
657        {
658            match source {
659                ObjectType::Package => "package".serialize(serializer),
660                ObjectType::Struct(s) => s.serialize(serializer),
661            }
662        }
663    }
664
665    impl<'de> DeserializeAs<'de, ObjectType> for ReadableObjectType {
666        fn deserialize_as<D>(deserializer: D) -> Result<ObjectType, D::Error>
667        where
668            D: Deserializer<'de>,
669        {
670            let s: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
671            if s == "package" {
672                Ok(ObjectType::Package)
673            } else {
674                let struct_tag = StructTag::from_str(&s)
675                    .map_err(|_| serde::de::Error::custom("invalid object type"))?;
676                Ok(ObjectType::Struct(struct_tag))
677            }
678        }
679    }
680
681    #[derive(serde::Serialize, serde::Deserialize)]
682    #[serde(rename = "Object")]
683    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
684    struct ReadableObject {
685        object_id: ObjectId,
686        #[serde(with = "crate::_serde::ReadableDisplay")]
687        #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
688        version: Version,
689        owner: Owner,
690        #[serde(with = "::serde_with::As::<ReadableObjectType>")]
691        #[serde(rename = "type")]
692        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
693        type_: ObjectType,
694        #[serde(flatten)]
695        data: ReadableObjectData,
696        previous_transaction: Digest,
697        #[serde(with = "crate::_serde::ReadableDisplay")]
698        #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
699        storage_rebate: u64,
700    }
701
702    #[cfg(feature = "schemars")]
703    impl schemars::JsonSchema for Object {
704        fn schema_name() -> String {
705            ReadableObject::schema_name()
706        }
707
708        fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
709            ReadableObject::json_schema(gen)
710        }
711    }
712
713    #[derive(serde::Serialize, serde::Deserialize)]
714    #[serde(untagged)]
715    #[cfg_attr(
716        feature = "schemars",
717        derive(schemars::JsonSchema),
718        schemars(rename = "ObjectData")
719    )]
720    enum ReadableObjectData {
721        Move(ReadableMoveStruct),
722        Package(ReadablePackage),
723    }
724
725    #[derive(serde::Serialize, serde::Deserialize)]
726    #[cfg_attr(
727        feature = "schemars",
728        derive(schemars::JsonSchema),
729        schemars(rename = "Package")
730    )]
731    struct ReadablePackage {
732        #[serde(
733            with = "::serde_with::As::<BTreeMap<::serde_with::Same, crate::_serde::Base64Encoded>>"
734        )]
735        #[cfg_attr(
736            feature = "schemars",
737            schemars(with = "BTreeMap<Identifier, crate::_schemars::Base64>")
738        )]
739        modules: BTreeMap<Identifier, Vec<u8>>,
740        type_origin_table: Vec<TypeOrigin>,
741        linkage_table: BTreeMap<ObjectId, UpgradeInfo>,
742    }
743
744    #[derive(serde::Serialize, serde::Deserialize)]
745    #[cfg_attr(
746        feature = "schemars",
747        derive(schemars::JsonSchema),
748        schemars(rename = "MoveStruct")
749    )]
750    struct ReadableMoveStruct {
751        #[serde(with = "::serde_with::As::<crate::_serde::Base64Encoded>")]
752        #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base64"))]
753        contents: Vec<u8>,
754    }
755
756    impl Object {
757        fn readable_object_data(&self) -> ReadableObjectData {
758            match &self.data {
759                ObjectData::Struct(struct_) => ReadableObjectData::Move(ReadableMoveStruct {
760                    contents: struct_.contents.clone(),
761                }),
762                ObjectData::Package(package) => ReadableObjectData::Package(ReadablePackage {
763                    modules: package.modules.clone(),
764                    type_origin_table: package.type_origin_table.clone(),
765                    linkage_table: package.linkage_table.clone(),
766                }),
767            }
768        }
769    }
770
771    impl Serialize for Object {
772        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
773        where
774            S: Serializer,
775        {
776            if serializer.is_human_readable() {
777                let readable = ReadableObject {
778                    object_id: self.object_id(),
779                    version: self.version(),
780                    // digest: todo!(),
781                    owner: self.owner,
782                    previous_transaction: self.previous_transaction,
783                    storage_rebate: self.storage_rebate,
784                    type_: self.object_type(),
785                    data: self.readable_object_data(),
786                };
787                readable.serialize(serializer)
788            } else {
789                let binary = BinaryObject {
790                    data: self.data.clone(),
791                    owner: self.owner,
792                    previous_transaction: self.previous_transaction,
793                    storage_rebate: self.storage_rebate,
794                };
795                binary.serialize(serializer)
796            }
797        }
798    }
799
800    impl<'de> Deserialize<'de> for Object {
801        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
802        where
803            D: Deserializer<'de>,
804        {
805            if deserializer.is_human_readable() {
806                let ReadableObject {
807                    object_id,
808                    version,
809                    owner,
810                    previous_transaction,
811                    storage_rebate,
812                    type_,
813                    data,
814                } = Deserialize::deserialize(deserializer)?;
815
816                // check if package or struct
817                let data = match (type_, data) {
818                    (
819                        ObjectType::Package,
820                        ReadableObjectData::Package(ReadablePackage {
821                            modules,
822                            type_origin_table,
823                            linkage_table,
824                        }),
825                    ) => ObjectData::Package(MovePackage {
826                        id: object_id,
827                        version,
828                        modules,
829                        type_origin_table,
830                        linkage_table,
831                    }),
832                    (
833                        ObjectType::Struct(type_),
834                        ReadableObjectData::Move(ReadableMoveStruct { contents }),
835                    ) => {
836                        // check id matches in contents
837                        if id_opt(&contents).is_none_or(|id| id != object_id) {
838                            return Err(serde::de::Error::custom("id from contents doesn't match"));
839                        }
840
841                        ObjectData::Struct(MoveStruct {
842                            type_,
843                            version,
844                            contents,
845                        })
846                    }
847                    _ => return Err(serde::de::Error::custom("type and data don't match")),
848                };
849
850                Ok(Object {
851                    data,
852                    owner,
853                    previous_transaction,
854                    storage_rebate,
855                })
856            } else {
857                let BinaryObject {
858                    data,
859                    owner,
860                    previous_transaction,
861                    storage_rebate,
862                } = Deserialize::deserialize(deserializer)?;
863
864                Ok(Object {
865                    data,
866                    owner,
867                    previous_transaction,
868                    storage_rebate,
869                })
870            }
871        }
872    }
873
874    #[derive(serde::Serialize, serde::Deserialize)]
875    struct BinaryObject {
876        data: ObjectData,
877        owner: Owner,
878        previous_transaction: Digest,
879        storage_rebate: u64,
880    }
881
882    #[derive(serde::Serialize, serde::Deserialize)]
883    #[serde(rename = "GenesisObject")]
884    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
885    struct ReadableGenesisObject {
886        object_id: ObjectId,
887        #[serde(with = "crate::_serde::ReadableDisplay")]
888        #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
889        version: Version,
890        owner: Owner,
891        #[serde(with = "::serde_with::As::<ReadableObjectType>")]
892        #[serde(rename = "type")]
893        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
894        type_: ObjectType,
895        #[serde(flatten)]
896        data: ReadableObjectData,
897    }
898
899    #[cfg(feature = "schemars")]
900    impl schemars::JsonSchema for GenesisObject {
901        fn schema_name() -> String {
902            ReadableGenesisObject::schema_name()
903        }
904
905        fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
906            ReadableGenesisObject::json_schema(gen)
907        }
908    }
909
910    #[derive(serde::Serialize, serde::Deserialize)]
911    enum BinaryGenesisObject {
912        RawObject { data: ObjectData, owner: Owner },
913    }
914
915    impl GenesisObject {
916        fn readable_object_data(&self) -> ReadableObjectData {
917            match &self.data {
918                ObjectData::Struct(struct_) => ReadableObjectData::Move(ReadableMoveStruct {
919                    contents: struct_.contents.clone(),
920                }),
921                ObjectData::Package(package) => ReadableObjectData::Package(ReadablePackage {
922                    modules: package.modules.clone(),
923                    type_origin_table: package.type_origin_table.clone(),
924                    linkage_table: package.linkage_table.clone(),
925                }),
926            }
927        }
928    }
929
930    impl Serialize for GenesisObject {
931        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
932        where
933            S: Serializer,
934        {
935            if serializer.is_human_readable() {
936                let readable = ReadableGenesisObject {
937                    object_id: self.object_id(),
938                    version: self.version(),
939                    owner: self.owner,
940                    type_: self.object_type(),
941                    data: self.readable_object_data(),
942                };
943                readable.serialize(serializer)
944            } else {
945                let binary = BinaryGenesisObject::RawObject {
946                    data: self.data.clone(),
947                    owner: self.owner,
948                };
949                binary.serialize(serializer)
950            }
951        }
952    }
953
954    impl<'de> Deserialize<'de> for GenesisObject {
955        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
956        where
957            D: Deserializer<'de>,
958        {
959            if deserializer.is_human_readable() {
960                let ReadableGenesisObject {
961                    object_id,
962                    version,
963                    owner,
964                    type_,
965                    data,
966                } = Deserialize::deserialize(deserializer)?;
967
968                // check if package or struct
969                let data = match (type_, data) {
970                    (
971                        ObjectType::Package,
972                        ReadableObjectData::Package(ReadablePackage {
973                            modules,
974                            type_origin_table,
975                            linkage_table,
976                        }),
977                    ) => ObjectData::Package(MovePackage {
978                        id: object_id,
979                        version,
980                        modules,
981                        type_origin_table,
982                        linkage_table,
983                    }),
984                    (
985                        ObjectType::Struct(type_),
986                        ReadableObjectData::Move(ReadableMoveStruct { contents }),
987                    ) => {
988                        // check id matches in contents
989                        if id_opt(&contents).is_none_or(|id| id != object_id) {
990                            return Err(serde::de::Error::custom("id from contents doesn't match"));
991                        }
992
993                        ObjectData::Struct(MoveStruct {
994                            type_,
995                            version,
996                            contents,
997                        })
998                    }
999                    _ => return Err(serde::de::Error::custom("type and data don't match")),
1000                };
1001
1002                Ok(GenesisObject { data, owner })
1003            } else {
1004                let BinaryGenesisObject::RawObject { data, owner } =
1005                    Deserialize::deserialize(deserializer)?;
1006
1007                Ok(GenesisObject { data, owner })
1008            }
1009        }
1010    }
1011
1012    #[cfg(test)]
1013    mod tests {
1014        #[cfg(target_arch = "wasm32")]
1015        use wasm_bindgen_test::wasm_bindgen_test as test;
1016
1017        use super::*;
1018        use crate::object::Object;
1019
1020        #[test]
1021        fn obj() {
1022            let o = Object {
1023                data: ObjectData::Struct(MoveStruct {
1024                    type_: StructTag {
1025                        address: Address::FRAMEWORK,
1026                        module: Identifier::new("bar").unwrap(),
1027                        name: Identifier::new("foo").unwrap(),
1028                        type_params: Vec::new(),
1029                    },
1030                    version: 12,
1031                    contents: ObjectId::ZERO.into(),
1032                }),
1033                // owner: Owner::Address(Address::ZERO),
1034                owner: Owner::Object(ObjectId::ZERO),
1035                // owner: Owner::Immutable,
1036                // owner: Owner::Shared {
1037                //     initial_shared_version: 14,
1038                // },
1039                previous_transaction: Digest::ZERO,
1040                storage_rebate: 100,
1041            };
1042
1043            println!("{}", serde_json::to_string_pretty(&o).unwrap());
1044            println!(
1045                "{}",
1046                serde_json::to_string_pretty(&ObjectReference {
1047                    object_id: ObjectId::ZERO,
1048                    version: 1,
1049                    digest: Digest::ZERO,
1050                })
1051                .unwrap()
1052            );
1053        }
1054
1055        #[test]
1056        fn object_fixture() {
1057            const IOTA_COIN: &[u8] = &[
1058                0, 1, 32, 79, 43, 0, 0, 0, 0, 0, 40, 35, 95, 175, 213, 151, 87, 206, 190, 35, 131,
1059                79, 35, 254, 22, 15, 181, 40, 108, 28, 77, 68, 229, 107, 254, 191, 160, 196, 186,
1060                42, 2, 122, 53, 52, 133, 199, 58, 0, 0, 0, 0, 0, 79, 255, 208, 0, 85, 34, 190, 75,
1061                192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155, 144, 230, 47, 97,
1062                220, 21, 24, 30, 26, 62, 32, 17, 197, 192, 38, 64, 173, 142, 143, 49, 111, 15, 211,
1063                92, 84, 48, 160, 243, 102, 229, 253, 251, 137, 210, 101, 119, 173, 228, 51, 141,
1064                20, 15, 85, 96, 19, 15, 0, 0, 0, 0, 0,
1065            ];
1066
1067            const IOTA_STAKE: &[u8] = &[
1068                0, 2, 154, 1, 52, 5, 0, 0, 0, 0, 80, 3, 112, 71, 231, 166, 234, 205, 164, 99, 237,
1069                29, 56, 97, 170, 21, 96, 105, 158, 227, 122, 22, 251, 60, 162, 12, 97, 151, 218,
1070                71, 253, 231, 239, 116, 138, 12, 233, 128, 195, 128, 77, 33, 38, 122, 77, 53, 154,
1071                197, 198, 75, 212, 12, 182, 163, 224, 42, 82, 123, 69, 248, 40, 207, 143, 211, 13,
1072                106, 1, 0, 0, 0, 0, 0, 0, 59, 81, 183, 246, 112, 0, 0, 0, 0, 79, 255, 208, 0, 85,
1073                34, 190, 75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155, 144,
1074                230, 47, 97, 220, 21, 24, 30, 26, 62, 32, 247, 239, 248, 71, 247, 102, 190, 149,
1075                232, 153, 138, 67, 169, 209, 203, 29, 255, 215, 223, 57, 159, 44, 40, 218, 166, 13,
1076                80, 71, 14, 188, 232, 68, 0, 0, 0, 0, 0, 0, 0, 0,
1077            ];
1078
1079            const NFT: &[u8] = &[
1080                0, 0, 97, 201, 195, 159, 216, 97, 133, 173, 96, 215, 56, 212, 229, 43, 208, 139,
1081                218, 7, 29, 54, 106, 205, 224, 126, 7, 195, 145, 106, 45, 117, 168, 22, 12, 100,
1082                105, 115, 116, 114, 105, 98, 117, 116, 105, 111, 110, 11, 68, 69, 69, 80, 87, 114,
1083                97, 112, 112, 101, 114, 0, 124, 24, 223, 4, 0, 0, 0, 0, 40, 31, 8, 18, 84, 38, 164,
1084                252, 84, 115, 250, 246, 137, 132, 128, 186, 156, 36, 62, 18, 140, 21, 4, 90, 209,
1085                105, 85, 84, 92, 214, 97, 81, 207, 64, 194, 198, 208, 21, 0, 0, 0, 0, 79, 255, 208,
1086                0, 85, 34, 190, 75, 192, 41, 114, 76, 127, 15, 110, 215, 9, 58, 107, 243, 160, 155,
1087                144, 230, 47, 97, 220, 21, 24, 30, 26, 62, 32, 170, 4, 94, 114, 207, 155, 31, 80,
1088                62, 254, 220, 206, 240, 218, 83, 54, 204, 197, 255, 239, 41, 66, 199, 150, 56, 189,
1089                86, 217, 166, 216, 128, 241, 64, 205, 21, 0, 0, 0, 0, 0,
1090            ];
1091
1092            const FUD_COIN: &[u8] = &[
1093                0, 3, 7, 118, 203, 129, 155, 1, 171, 237, 80, 43, 238, 138, 112, 43, 76, 45, 84,
1094                117, 50, 193, 47, 37, 0, 28, 157, 234, 121, 90, 94, 99, 28, 38, 241, 3, 102, 117,
1095                100, 3, 70, 85, 68, 0, 193, 89, 252, 3, 0, 0, 0, 0, 40, 33, 214, 90, 11, 56, 243,
1096                115, 10, 250, 121, 250, 28, 34, 237, 104, 130, 148, 40, 130, 29, 248, 137, 244, 27,
1097                138, 94, 150, 28, 182, 104, 162, 185, 0, 152, 247, 62, 93, 1, 0, 0, 0, 42, 95, 32,
1098                226, 13, 31, 128, 91, 188, 127, 235, 12, 75, 73, 116, 112, 3, 227, 244, 126, 59,
1099                81, 214, 118, 144, 243, 195, 17, 82, 216, 119, 170, 32, 239, 247, 71, 249, 241, 98,
1100                133, 53, 46, 37, 100, 242, 94, 231, 241, 184, 8, 69, 192, 69, 67, 1, 116, 251, 229,
1101                226, 99, 119, 79, 255, 71, 43, 64, 242, 19, 0, 0, 0, 0, 0,
1102            ];
1103
1104            const BULLSHARK_PACKAGE: &[u8] = &[
1105                1, 135, 35, 29, 28, 138, 126, 114, 145, 204, 122, 145, 8, 244, 199, 188, 26, 10,
1106                28, 14, 182, 55, 91, 91, 97, 10, 245, 202, 35, 223, 14, 140, 86, 1, 0, 0, 0, 0, 0,
1107                0, 0, 1, 9, 98, 117, 108, 108, 115, 104, 97, 114, 107, 162, 6, 161, 28, 235, 11, 6,
1108                0, 0, 0, 10, 1, 0, 12, 2, 12, 36, 3, 48, 61, 4, 109, 12, 5, 121, 137, 1, 7, 130, 2,
1109                239, 1, 8, 241, 3, 96, 6, 209, 4, 82, 10, 163, 5, 5, 12, 168, 5, 75, 0, 7, 1, 16,
1110                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,
1111                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,
1112                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,
1113                14, 17, 1, 1, 0, 3, 17, 7, 1, 1, 12, 3, 18, 16, 1, 1, 12, 4, 19, 13, 14, 0, 5, 15,
1114                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,
1115                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,
1116                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,
1117                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,
1118                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,
1119                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,
1120                72, 65, 82, 75, 4, 67, 111, 105, 110, 12, 67, 111, 105, 110, 77, 101, 116, 97, 100,
1121                97, 116, 97, 6, 79, 112, 116, 105, 111, 110, 11, 84, 114, 101, 97, 115, 117, 114,
1122                121, 67, 97, 112, 9, 84, 120, 67, 111, 110, 116, 101, 120, 116, 3, 85, 114, 108, 9,
1123                98, 117, 108, 108, 115, 104, 97, 114, 107, 4, 98, 117, 114, 110, 4, 99, 111, 105,
1124                110, 15, 99, 114, 101, 97, 116, 101, 95, 99, 117, 114, 114, 101, 110, 99, 121, 11,
1125                100, 117, 109, 109, 121, 95, 102, 105, 101, 108, 100, 4, 105, 110, 105, 116, 4,
1126                109, 105, 110, 116, 17, 109, 105, 110, 116, 95, 97, 110, 100, 95, 116, 114, 97,
1127                110, 115, 102, 101, 114, 21, 110, 101, 119, 95, 117, 110, 115, 97, 102, 101, 95,
1128                102, 114, 111, 109, 95, 98, 121, 116, 101, 115, 6, 111, 112, 116, 105, 111, 110,
1129                20, 112, 117, 98, 108, 105, 99, 95, 102, 114, 101, 101, 122, 101, 95, 111, 98, 106,
1130                101, 99, 116, 15, 112, 117, 98, 108, 105, 99, 95, 116, 114, 97, 110, 115, 102, 101,
1131                114, 6, 115, 101, 110, 100, 101, 114, 4, 115, 111, 109, 101, 8, 116, 114, 97, 110,
1132                115, 102, 101, 114, 10, 116, 120, 95, 99, 111, 110, 116, 101, 120, 116, 3, 117,
1133                114, 108, 135, 35, 29, 28, 138, 126, 114, 145, 204, 122, 145, 8, 244, 199, 188, 26,
1134                10, 28, 14, 182, 55, 91, 91, 97, 10, 245, 202, 35, 223, 14, 140, 86, 0, 0, 0, 0, 0,
1135                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,
1136                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,
1137                0, 0, 2, 10, 2, 10, 9, 66, 85, 76, 76, 83, 72, 65, 82, 75, 10, 2, 20, 19, 66, 117,
1138                108, 108, 32, 83, 104, 97, 114, 107, 32, 83, 117, 105, 70, 114, 101, 110, 115, 10,
1139                2, 1, 0, 10, 2, 39, 38, 104, 116, 116, 112, 115, 58, 47, 47, 105, 46, 105, 98, 98,
1140                46, 99, 111, 47, 104, 87, 89, 50, 87, 53, 120, 47, 98, 117, 108, 108, 115, 104, 97,
1141                114, 107, 46, 112, 110, 103, 0, 2, 1, 11, 1, 0, 0, 0, 0, 4, 20, 11, 0, 49, 6, 7, 0,
1142                7, 1, 7, 2, 7, 3, 17, 10, 56, 0, 10, 1, 56, 1, 12, 2, 12, 3, 11, 2, 56, 2, 11, 3,
1143                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,
1144                2, 1, 4, 0, 1, 5, 11, 0, 11, 1, 56, 5, 1, 2, 0, 1, 9, 98, 117, 108, 108, 115, 104,
1145                97, 114, 107, 9, 66, 85, 76, 76, 83, 72, 65, 82, 75, 135, 35, 29, 28, 138, 126,
1146                114, 145, 204, 122, 145, 8, 244, 199, 188, 26, 10, 28, 14, 182, 55, 91, 91, 97, 10,
1147                245, 202, 35, 223, 14, 140, 86, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1148                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,
1149                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,
1150                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,
1151                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,
1152                0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 3, 32, 87, 145, 191, 231, 147, 185,
1153                46, 159, 240, 181, 95, 126, 236, 65, 154, 55, 16, 196, 229, 218, 47, 59, 99, 197,
1154                13, 89, 18, 159, 205, 129, 112, 131, 112, 192, 126, 0, 0, 0, 0, 0,
1155            ];
1156
1157            for fixture in [IOTA_COIN, IOTA_STAKE, NFT, FUD_COIN, BULLSHARK_PACKAGE] {
1158                let object: Object = bcs::from_bytes(fixture).unwrap();
1159                assert_eq!(bcs::to_bytes(&object).unwrap(), fixture);
1160
1161                let json = serde_json::to_string_pretty(&object).unwrap();
1162                println!("{json}");
1163                assert_eq!(object, serde_json::from_str(&json).unwrap());
1164            }
1165        }
1166    }
1167}