Skip to main content

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