Skip to main content

sui_types/
base_types.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::MOVE_STDLIB_ADDRESS;
6use crate::MoveTypeTagTrait;
7use crate::MoveTypeTagTraitGeneric;
8use crate::SUI_CLOCK_OBJECT_ID;
9use crate::SUI_FRAMEWORK_ADDRESS;
10use crate::SUI_SYSTEM_ADDRESS;
11use crate::accumulator_root::accumulator_value_balance_type_maybe;
12use crate::balance::Balance;
13use crate::coin::COIN_MODULE_NAME;
14use crate::coin::COIN_STRUCT_NAME;
15use crate::coin::Coin;
16use crate::coin::CoinMetadata;
17use crate::coin::TreasuryCap;
18use crate::coin_registry::Currency;
19pub use crate::committee::EpochId;
20use crate::crypto::{
21    AuthorityPublicKeyBytes, DefaultHash, PublicKey, SignatureScheme, SuiPublicKey, SuiSignature,
22};
23pub use crate::digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest};
24use crate::dynamic_field::DynamicFieldInfo;
25use crate::dynamic_field::DynamicFieldType;
26use crate::dynamic_field::{DYNAMIC_FIELD_FIELD_STRUCT_NAME, DYNAMIC_FIELD_MODULE_NAME};
27use crate::effects::TransactionEffects;
28use crate::effects::TransactionEffectsAPI;
29use crate::epoch_data::EpochData;
30use crate::error::SuiError;
31use crate::error::SuiErrorKind;
32use crate::error::{ExecutionError, SuiResult};
33use crate::execution_status::ExecutionErrorKind;
34use crate::gas_coin::GAS;
35use crate::gas_coin::GasCoin;
36use crate::governance::STAKED_SUI_STRUCT_NAME;
37use crate::governance::STAKING_POOL_MODULE_NAME;
38use crate::governance::StakedSui;
39use crate::id::RESOLVED_SUI_ID;
40use crate::messages_checkpoint::CheckpointTimestamp;
41use crate::multisig::MultiSigPublicKey;
42use crate::object::{Object, Owner};
43use crate::parse_sui_struct_tag;
44use crate::signature::GenericSignature;
45use crate::sui_serde::Readable;
46use crate::sui_serde::to_custom_deser_error;
47use crate::sui_serde::to_sui_struct_tag_string;
48use crate::transaction::Transaction;
49use crate::transaction::VerifiedTransaction;
50use crate::zk_login_authenticator::ZkLoginAuthenticator;
51use anyhow::anyhow;
52use fastcrypto::encoding::decode_bytes_hex;
53use fastcrypto::encoding::{Encoding, Hex};
54use fastcrypto::hash::HashFunction;
55use fastcrypto::traits::AllowedRng;
56use fastcrypto_zkp::bn254::zk_login::ZkLoginInputs;
57use move_binary_format::CompiledModule;
58use move_binary_format::file_format::SignatureToken;
59use move_bytecode_utils::resolve_struct;
60use move_core_types::account_address::AccountAddress;
61use move_core_types::annotated_value as A;
62use move_core_types::ident_str;
63use move_core_types::identifier::IdentStr;
64use move_core_types::language_storage::ModuleId;
65use move_core_types::language_storage::StructTag;
66use move_core_types::language_storage::TypeTag;
67use rand::Rng;
68use schemars::JsonSchema;
69use serde::Deserializer;
70use serde::Serializer;
71use serde::ser::Error;
72use serde::ser::SerializeSeq;
73use serde::{Deserialize, Serialize};
74use serde_with::DeserializeAs;
75use serde_with::SerializeAs;
76use serde_with::serde_as;
77use shared_crypto::intent::HashingIntentScope;
78use std::borrow::Cow;
79use std::cmp::max;
80use std::collections::BTreeMap;
81use std::convert::{TryFrom, TryInto};
82use std::fmt;
83use std::str::FromStr;
84use sui_protocol_config::ProtocolConfig;
85
86#[cfg(test)]
87#[path = "unit_tests/base_types_tests.rs"]
88mod base_types_tests;
89
90#[cfg(test)]
91#[path = "unit_tests/accumulator_types_tests.rs"]
92mod accumulator_types_tests;
93
94#[derive(
95    Eq,
96    PartialEq,
97    Ord,
98    PartialOrd,
99    Copy,
100    Clone,
101    Hash,
102    Default,
103    Debug,
104    Serialize,
105    Deserialize,
106    JsonSchema,
107)]
108#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
109pub struct SequenceNumber(u64);
110
111impl SequenceNumber {
112    pub fn one_before(&self) -> Option<SequenceNumber> {
113        if self.0 == 0 {
114            None
115        } else {
116            Some(SequenceNumber(self.0 - 1))
117        }
118    }
119
120    pub fn next(&self) -> SequenceNumber {
121        SequenceNumber(self.0 + 1)
122    }
123}
124
125pub type TxSequenceNumber = u64;
126
127impl fmt::Display for SequenceNumber {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(f, "{:#x}", self.0)
130    }
131}
132
133pub type VersionNumber = SequenceNumber;
134
135#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Default, Debug, Serialize, Deserialize)]
136pub struct UserData(pub Option<[u8; 32]>);
137
138pub type AuthorityName = AuthorityPublicKeyBytes;
139
140pub trait ConciseableName<'a> {
141    type ConciseTypeRef: std::fmt::Debug;
142    type ConciseType: std::fmt::Debug;
143
144    fn concise(&'a self) -> Self::ConciseTypeRef;
145    fn concise_owned(&self) -> Self::ConciseType;
146}
147
148#[serde_as]
149#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
150pub struct ObjectID(
151    #[schemars(with = "Hex")]
152    #[serde_as(as = "Readable<HexAccountAddress, _>")]
153    AccountAddress,
154);
155
156#[serde_as]
157#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
158pub enum FullObjectID {
159    Fastpath(ObjectID),
160    Consensus(ConsensusObjectSequenceKey),
161}
162
163impl FullObjectID {
164    pub fn new(object_id: ObjectID, start_version: Option<SequenceNumber>) -> Self {
165        if let Some(start_version) = start_version {
166            Self::Consensus((object_id, start_version))
167        } else {
168            Self::Fastpath(object_id)
169        }
170    }
171
172    pub fn id(&self) -> ObjectID {
173        match &self {
174            FullObjectID::Fastpath(object_id) => *object_id,
175            FullObjectID::Consensus(consensus_object_sequence_key) => {
176                consensus_object_sequence_key.0
177            }
178        }
179    }
180}
181
182pub type VersionDigest = (SequenceNumber, ObjectDigest);
183
184pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest);
185
186pub fn random_object_ref() -> ObjectRef {
187    (
188        ObjectID::random(),
189        SequenceNumber::new(),
190        ObjectDigest::new([0; 32]),
191    )
192}
193
194pub fn update_object_ref_for_testing(object_ref: ObjectRef) -> ObjectRef {
195    (
196        object_ref.0,
197        object_ref.1.next(),
198        ObjectDigest::new([0; 32]),
199    )
200}
201
202#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
203pub struct FullObjectRef(pub FullObjectID, pub SequenceNumber, pub ObjectDigest);
204
205impl FullObjectRef {
206    pub fn from_fastpath_ref(object_ref: ObjectRef) -> Self {
207        Self(
208            FullObjectID::Fastpath(object_ref.0),
209            object_ref.1,
210            object_ref.2,
211        )
212    }
213
214    pub fn from_object_ref_and_owner(object_ref: ObjectRef, owner: &Owner) -> Self {
215        let full_id = if let Some(start_version) = owner.start_version() {
216            FullObjectID::Consensus((object_ref.0, start_version))
217        } else {
218            FullObjectID::Fastpath(object_ref.0)
219        };
220        Self(full_id, object_ref.1, object_ref.2)
221    }
222
223    pub fn as_object_ref(&self) -> ObjectRef {
224        (self.0.id(), self.1, self.2)
225    }
226}
227/// Represents an distinct stream of object versions for a consensus object,
228/// based on the object ID and start version.
229pub type ConsensusObjectSequenceKey = (ObjectID, SequenceNumber);
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub struct ConsensusObjectVersion {
233    pub initial_shared_version: SequenceNumber,
234    pub version: SequenceNumber,
235}
236
237/// Versions of the implicitly read system objects pinned for one transaction, keyed by object
238/// ID. Only members of `IMPLICITLY_READ_SYSTEM_OBJECTS` may appear.
239#[derive(Debug, Clone, Default, PartialEq, Eq)]
240pub struct SystemObjectVersions(BTreeMap<ObjectID, ConsensusObjectVersion>);
241
242impl SystemObjectVersions {
243    pub fn empty() -> Self {
244        Self::default()
245    }
246
247    pub fn from_map(versions: BTreeMap<ObjectID, ConsensusObjectVersion>) -> Self {
248        for object_id in versions.keys() {
249            Self::assert_implicitly_read(object_id);
250        }
251        Self(versions)
252    }
253
254    pub fn get(&self, object_id: &ObjectID) -> Option<ConsensusObjectVersion> {
255        Self::assert_implicitly_read(object_id);
256        self.0.get(object_id).copied()
257    }
258
259    pub fn insert_for_testing(&mut self, object_id: ObjectID, version: ConsensusObjectVersion) {
260        Self::assert_implicitly_read(&object_id);
261        self.0.insert(object_id, version);
262    }
263
264    fn assert_implicitly_read(object_id: &ObjectID) {
265        assert!(
266            object_id.is_implicitly_read_system_object(),
267            "{object_id} is not an implicitly read system object"
268        );
269    }
270}
271
272/// Wrapper around StructTag with a space-efficient representation for common types like coins
273/// The StructTag for a gas coin is 84 bytes, so using 1 byte instead is a win.
274/// The inner representation is private to prevent incorrectly constructing an `Other` instead of
275/// one of the specialized variants, e.g. `Other(GasCoin::type_())` instead of `GasCoin`
276#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
277pub struct MoveObjectType(MoveObjectType_);
278
279/// Even though it is declared public, it is the "private", internal representation for
280/// `MoveObjectType`
281#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
282pub enum MoveObjectType_ {
283    /// A type that is not `0x2::coin::Coin<T>`
284    Other(StructTag),
285    /// A SUI coin (i.e., `0x2::coin::Coin<0x2::sui::SUI>`)
286    GasCoin,
287    /// A record of a staked SUI coin (i.e., `0x3::staking_pool::StakedSui`)
288    StakedSui,
289    /// A non-SUI coin type (i.e., `0x2::coin::Coin<T> where T != 0x2::sui::SUI`)
290    Coin(TypeTag),
291    /// A SUI balance accumulator field
292    /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<0x2::sui::SUI>>, 0x2::accumulator::U128>`)
293    SuiBalanceAccumulatorField,
294    /// A non-SUI balance accumulator field
295    /// (i.e., `0x2::dynamic_field::Field<0x2::accumulator::Key<0x2::balance::Balance<T>>, 0x2::accumulator::U128>`
296    /// where T != 0x2::sui::SUI)
297    BalanceAccumulatorField(TypeTag),
298    // NOTE: if adding a new type here, and there are existing on-chain objects of that
299    // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash
300    // to make sure the new type and Other(_) are interpreted consistently.
301}
302
303impl MoveObjectType {
304    pub fn gas_coin() -> Self {
305        Self(MoveObjectType_::GasCoin)
306    }
307
308    pub fn is_efficient_representation(&self) -> bool {
309        !matches!(self.0, MoveObjectType_::Other(_))
310    }
311
312    pub fn coin(coin_type: TypeTag) -> Self {
313        Self(if GAS::is_gas_type(&coin_type) {
314            MoveObjectType_::GasCoin
315        } else {
316            MoveObjectType_::Coin(coin_type)
317        })
318    }
319
320    pub fn staked_sui() -> Self {
321        Self(MoveObjectType_::StakedSui)
322    }
323
324    pub fn address(&self) -> AccountAddress {
325        match &self.0 {
326            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => SUI_FRAMEWORK_ADDRESS,
327            MoveObjectType_::StakedSui => SUI_SYSTEM_ADDRESS,
328            MoveObjectType_::SuiBalanceAccumulatorField
329            | MoveObjectType_::BalanceAccumulatorField(_) => SUI_FRAMEWORK_ADDRESS,
330            MoveObjectType_::Other(s) => s.address,
331        }
332    }
333
334    pub fn module(&self) -> &IdentStr {
335        match &self.0 {
336            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME,
337            MoveObjectType_::StakedSui => STAKING_POOL_MODULE_NAME,
338            MoveObjectType_::SuiBalanceAccumulatorField
339            | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_MODULE_NAME,
340            MoveObjectType_::Other(s) => &s.module,
341        }
342    }
343
344    pub fn name(&self) -> &IdentStr {
345        match &self.0 {
346            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME,
347            MoveObjectType_::StakedSui => STAKED_SUI_STRUCT_NAME,
348            MoveObjectType_::SuiBalanceAccumulatorField
349            | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_FIELD_STRUCT_NAME,
350            MoveObjectType_::Other(s) => &s.name,
351        }
352    }
353
354    pub fn type_params(&self) -> Vec<Cow<'_, TypeTag>> {
355        match &self.0 {
356            MoveObjectType_::GasCoin => vec![Cow::Owned(GAS::type_tag())],
357            MoveObjectType_::StakedSui => vec![],
358            MoveObjectType_::Coin(inner) => vec![Cow::Borrowed(inner)],
359            MoveObjectType_::SuiBalanceAccumulatorField => {
360                Self::balance_accumulator_field_type_params(GAS::type_tag())
361                    .into_iter()
362                    .map(Cow::Owned)
363                    .collect()
364            }
365            MoveObjectType_::BalanceAccumulatorField(inner) => {
366                Self::balance_accumulator_field_type_params(inner.clone())
367                    .into_iter()
368                    .map(Cow::Owned)
369                    .collect()
370            }
371            MoveObjectType_::Other(s) => s.type_params.iter().map(Cow::Borrowed).collect(),
372        }
373    }
374
375    pub fn into_type_params(self) -> Vec<TypeTag> {
376        match self.0 {
377            MoveObjectType_::GasCoin => vec![GAS::type_tag()],
378            MoveObjectType_::StakedSui => vec![],
379            MoveObjectType_::Coin(inner) => vec![inner],
380            MoveObjectType_::SuiBalanceAccumulatorField => {
381                Self::balance_accumulator_field_type_params(GAS::type_tag())
382            }
383            MoveObjectType_::BalanceAccumulatorField(inner) => {
384                Self::balance_accumulator_field_type_params(inner)
385            }
386            MoveObjectType_::Other(s) => s.type_params,
387        }
388    }
389
390    pub fn coin_type_maybe(&self) -> Option<TypeTag> {
391        match &self.0 {
392            MoveObjectType_::GasCoin => Some(GAS::type_tag()),
393            MoveObjectType_::Coin(inner) => Some(inner.clone()),
394            MoveObjectType_::StakedSui => None,
395            MoveObjectType_::SuiBalanceAccumulatorField => None,
396            MoveObjectType_::BalanceAccumulatorField(_) => None,
397            MoveObjectType_::Other(_) => None,
398        }
399    }
400
401    pub fn balance_accumulator_field_type_maybe(&self) -> Option<TypeTag> {
402        match &self.0 {
403            MoveObjectType_::SuiBalanceAccumulatorField => Some(GAS::type_tag()),
404            MoveObjectType_::BalanceAccumulatorField(inner) => Some(inner.clone()),
405            _ => None,
406        }
407    }
408
409    pub fn is_balance_accumulator_field(&self) -> bool {
410        matches!(
411            self.0,
412            MoveObjectType_::SuiBalanceAccumulatorField
413                | MoveObjectType_::BalanceAccumulatorField(_)
414        )
415    }
416
417    pub fn is_sui_balance_accumulator_field(&self) -> bool {
418        matches!(self.0, MoveObjectType_::SuiBalanceAccumulatorField)
419    }
420
421    pub fn module_id(&self) -> ModuleId {
422        ModuleId::new(self.address(), self.module().to_owned())
423    }
424
425    pub fn size_for_gas_metering(&self) -> usize {
426        // unwraps safe because a `StructTag` cannot fail to serialize
427        match &self.0 {
428            MoveObjectType_::GasCoin => 1,
429            MoveObjectType_::StakedSui => 1,
430            MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
431            MoveObjectType_::SuiBalanceAccumulatorField => 1,
432            MoveObjectType_::BalanceAccumulatorField(inner) => {
433                bcs::serialized_size(inner).unwrap() + 1
434            }
435            MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
436        }
437    }
438
439    /// Return true if `self` is `0x2::coin::Coin<T>` for some T (note: T can be SUI)
440    pub fn is_coin(&self) -> bool {
441        match &self.0 {
442            MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
443            MoveObjectType_::StakedSui
444            | MoveObjectType_::SuiBalanceAccumulatorField
445            | MoveObjectType_::BalanceAccumulatorField(_)
446            | MoveObjectType_::Other(_) => false,
447        }
448    }
449
450    /// Return true if `self` is 0x2::coin::Coin<0x2::sui::SUI>
451    pub fn is_gas_coin(&self) -> bool {
452        match &self.0 {
453            MoveObjectType_::GasCoin => true,
454            MoveObjectType_::StakedSui
455            | MoveObjectType_::Coin(_)
456            | MoveObjectType_::SuiBalanceAccumulatorField
457            | MoveObjectType_::BalanceAccumulatorField(_)
458            | MoveObjectType_::Other(_) => false,
459        }
460    }
461
462    /// Return true if `self` is `0x2::coin::Coin<t>`
463    pub fn is_coin_t(&self, t: &TypeTag) -> bool {
464        match &self.0 {
465            MoveObjectType_::GasCoin => GAS::is_gas_type(t),
466            MoveObjectType_::Coin(c) => t == c,
467            MoveObjectType_::StakedSui
468            | MoveObjectType_::SuiBalanceAccumulatorField
469            | MoveObjectType_::BalanceAccumulatorField(_)
470            | MoveObjectType_::Other(_) => false,
471        }
472    }
473
474    pub fn is_staked_sui(&self) -> bool {
475        match &self.0 {
476            MoveObjectType_::StakedSui => true,
477            MoveObjectType_::GasCoin
478            | MoveObjectType_::Coin(_)
479            | MoveObjectType_::SuiBalanceAccumulatorField
480            | MoveObjectType_::BalanceAccumulatorField(_)
481            | MoveObjectType_::Other(_) => false,
482        }
483    }
484
485    pub fn is_coin_metadata(&self) -> bool {
486        match &self.0 {
487            MoveObjectType_::GasCoin
488            | MoveObjectType_::StakedSui
489            | MoveObjectType_::Coin(_)
490            | MoveObjectType_::SuiBalanceAccumulatorField
491            | MoveObjectType_::BalanceAccumulatorField(_) => false,
492            MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s),
493        }
494    }
495
496    pub fn is_currency(&self) -> bool {
497        match &self.0 {
498            MoveObjectType_::GasCoin
499            | MoveObjectType_::StakedSui
500            | MoveObjectType_::Coin(_)
501            | MoveObjectType_::SuiBalanceAccumulatorField
502            | MoveObjectType_::BalanceAccumulatorField(_) => false,
503            MoveObjectType_::Other(s) => Currency::is_currency(s),
504        }
505    }
506
507    pub fn is_treasury_cap(&self) -> bool {
508        match &self.0 {
509            MoveObjectType_::GasCoin
510            | MoveObjectType_::StakedSui
511            | MoveObjectType_::Coin(_)
512            | MoveObjectType_::SuiBalanceAccumulatorField
513            | MoveObjectType_::BalanceAccumulatorField(_) => false,
514            MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
515        }
516    }
517
518    pub fn is_upgrade_cap(&self) -> bool {
519        self.address() == SUI_FRAMEWORK_ADDRESS
520            && self.module().as_str() == "package"
521            && self.name().as_str() == "UpgradeCap"
522    }
523
524    pub fn is_regulated_coin_metadata(&self) -> bool {
525        self.address() == SUI_FRAMEWORK_ADDRESS
526            && self.module().as_str() == "coin"
527            && self.name().as_str() == "RegulatedCoinMetadata"
528    }
529
530    pub fn is_coin_deny_cap(&self) -> bool {
531        self.address() == SUI_FRAMEWORK_ADDRESS
532            && self.module().as_str() == "coin"
533            && self.name().as_str() == "DenyCap"
534    }
535
536    pub fn is_coin_deny_cap_v2(&self) -> bool {
537        self.address() == SUI_FRAMEWORK_ADDRESS
538            && self.module().as_str() == "coin"
539            && self.name().as_str() == "DenyCapV2"
540    }
541
542    pub fn is_dynamic_field(&self) -> bool {
543        match &self.0 {
544            MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
545                false
546            }
547            MoveObjectType_::SuiBalanceAccumulatorField
548            | MoveObjectType_::BalanceAccumulatorField(_) => true, // These are dynamic fields
549            MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
550        }
551    }
552
553    pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> SuiResult<TypeTag> {
554        match &self.0 {
555            MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
556                Err(SuiErrorKind::ObjectDeserializationError {
557                    error: "Error extracting dynamic object name from specialized object type"
558                        .to_string(),
559                }
560                .into())
561            }
562            MoveObjectType_::SuiBalanceAccumulatorField
563            | MoveObjectType_::BalanceAccumulatorField(_) => {
564                let struct_tag: StructTag = self.clone().into();
565                DynamicFieldInfo::try_extract_field_name(&struct_tag, type_)
566            }
567            MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
568        }
569    }
570
571    pub fn try_extract_field_value(&self) -> SuiResult<TypeTag> {
572        match &self.0 {
573            MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
574                Err(SuiErrorKind::ObjectDeserializationError {
575                    error: "Error extracting dynamic object value from specialized object type"
576                        .to_string(),
577                }
578                .into())
579            }
580            MoveObjectType_::SuiBalanceAccumulatorField
581            | MoveObjectType_::BalanceAccumulatorField(_) => {
582                let struct_tag: StructTag = self.clone().into();
583                DynamicFieldInfo::try_extract_field_value(&struct_tag)
584            }
585            MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
586        }
587    }
588
589    pub fn is(&self, s: &StructTag) -> bool {
590        match &self.0 {
591            MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
592            MoveObjectType_::StakedSui => StakedSui::is_staked_sui(s),
593            MoveObjectType_::Coin(inner) => {
594                Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
595            }
596            MoveObjectType_::SuiBalanceAccumulatorField => accumulator_value_balance_type_maybe(s)
597                .map(|t| GAS::is_gas_type(&t))
598                .unwrap_or(false),
599            MoveObjectType_::BalanceAccumulatorField(inner) => {
600                accumulator_value_balance_type_maybe(s)
601                    .map(|t| &t == inner)
602                    .unwrap_or(false)
603            }
604            MoveObjectType_::Other(o) => s == o,
605        }
606    }
607
608    pub fn other(&self) -> Option<&StructTag> {
609        if let MoveObjectType_::Other(s) = &self.0 {
610            Some(s)
611        } else {
612            None
613        }
614    }
615
616    /// Returns the string representation of this object's type using the canonical display.
617    pub fn to_canonical_string(&self, with_prefix: bool) -> String {
618        StructTag::from(self.clone()).to_canonical_string(with_prefix)
619    }
620
621    /// Helper function to construct type parameters for balance accumulator fields
622    /// Field<Key<Balance<T>>, U128> has two type params
623    fn balance_accumulator_field_type_params(inner_type: TypeTag) -> Vec<TypeTag> {
624        use crate::accumulator_root::{AccumulatorKey, U128};
625        let balance_type = Balance::type_tag(inner_type);
626        let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
627        let u128_type = U128::get_type_tag();
628        vec![key_type, u128_type]
629    }
630
631    /// Map from T to Field<AccumulatorKey<Balance<T>>, U128>
632    fn balance_accumulator_field_struct_tag(inner_type: TypeTag) -> StructTag {
633        use crate::accumulator_root::{AccumulatorKey, U128};
634        let balance_type = Balance::type_tag(inner_type);
635        let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
636        let u128_type = U128::get_type_tag();
637        DynamicFieldInfo::dynamic_field_type(key_type, u128_type)
638    }
639}
640
641impl From<StructTag> for MoveObjectType {
642    fn from(mut s: StructTag) -> Self {
643        Self(if GasCoin::is_gas_coin(&s) {
644            MoveObjectType_::GasCoin
645        } else if Coin::is_coin(&s) {
646            // unwrap safe because a coin has exactly one type parameter
647            MoveObjectType_::Coin(s.type_params.pop().unwrap())
648        } else if StakedSui::is_staked_sui(&s) {
649            MoveObjectType_::StakedSui
650        } else if let Some(balance_type) = accumulator_value_balance_type_maybe(&s) {
651            if GAS::is_gas_type(&balance_type) {
652                MoveObjectType_::SuiBalanceAccumulatorField
653            } else {
654                MoveObjectType_::BalanceAccumulatorField(balance_type)
655            }
656        } else {
657            MoveObjectType_::Other(s)
658        })
659    }
660}
661
662impl From<MoveObjectType> for StructTag {
663    fn from(t: MoveObjectType) -> Self {
664        match t.0 {
665            MoveObjectType_::GasCoin => GasCoin::type_(),
666            MoveObjectType_::StakedSui => StakedSui::type_(),
667            MoveObjectType_::Coin(inner) => Coin::type_(inner),
668            MoveObjectType_::SuiBalanceAccumulatorField => {
669                MoveObjectType::balance_accumulator_field_struct_tag(GAS::type_tag())
670            }
671            MoveObjectType_::BalanceAccumulatorField(inner) => {
672                MoveObjectType::balance_accumulator_field_struct_tag(inner)
673            }
674            MoveObjectType_::Other(s) => s,
675        }
676    }
677}
678
679impl From<MoveObjectType> for TypeTag {
680    fn from(o: MoveObjectType) -> TypeTag {
681        let s: StructTag = o.into();
682        TypeTag::Struct(Box::new(s))
683    }
684}
685
686/// Whether this type is valid as a primitive (pure) transaction input.
687pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
688    use TypeTag as T;
689
690    match t {
691        T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
692        T::Vector(inner) => is_primitive_type_tag(inner),
693        T::Struct(st) => {
694            let StructTag {
695                address,
696                module,
697                name,
698                type_params: type_args,
699            } = &**st;
700            let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
701            // is id or..
702            if resolved_struct == RESOLVED_SUI_ID {
703                return true;
704            }
705            // is utf8 string
706            if resolved_struct == RESOLVED_UTF8_STR {
707                return true;
708            }
709            // is ascii string
710            if resolved_struct == RESOLVED_ASCII_STR {
711                return true;
712            }
713            // is option of a primitive
714            resolved_struct == RESOLVED_STD_OPTION
715                && type_args.len() == 1
716                && is_primitive_type_tag(&type_args[0])
717        }
718        T::Signer => false,
719    }
720}
721
722/// Type of a Sui object
723#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
724pub enum ObjectType {
725    /// Move package containing one or more bytecode modules
726    Package,
727    /// A Move struct of the given type
728    Struct(MoveObjectType),
729}
730
731impl From<&Object> for ObjectType {
732    fn from(o: &Object) -> Self {
733        o.data
734            .type_()
735            .map(|t| ObjectType::Struct(t.clone()))
736            .unwrap_or(ObjectType::Package)
737    }
738}
739
740impl TryFrom<ObjectType> for StructTag {
741    type Error = anyhow::Error;
742
743    fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
744        match o {
745            ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
746            ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
747        }
748    }
749}
750
751impl FromStr for ObjectType {
752    type Err = anyhow::Error;
753
754    fn from_str(s: &str) -> Result<Self, Self::Err> {
755        if s.to_lowercase() == PACKAGE {
756            Ok(ObjectType::Package)
757        } else {
758            let tag = parse_sui_struct_tag(s)?;
759            Ok(ObjectType::Struct(MoveObjectType::from(tag)))
760        }
761    }
762}
763
764#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
765pub struct ObjectInfo {
766    pub object_id: ObjectID,
767    pub version: SequenceNumber,
768    pub digest: ObjectDigest,
769    pub type_: ObjectType,
770    pub owner: Owner,
771    pub previous_transaction: TransactionDigest,
772}
773
774impl ObjectInfo {
775    pub fn new(oref: &ObjectRef, o: &Object) -> Self {
776        let (object_id, version, digest) = *oref;
777        Self {
778            object_id,
779            version,
780            digest,
781            type_: o.into(),
782            owner: o.owner.clone(),
783            previous_transaction: o.previous_transaction,
784        }
785    }
786
787    pub fn from_object(object: &Object) -> Self {
788        Self {
789            object_id: object.id(),
790            version: object.version(),
791            digest: object.digest(),
792            type_: object.into(),
793            owner: object.owner.clone(),
794            previous_transaction: object.previous_transaction,
795        }
796    }
797}
798const PACKAGE: &str = "package";
799impl ObjectType {
800    pub fn is_gas_coin(&self) -> bool {
801        matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
802    }
803
804    pub fn is_coin(&self) -> bool {
805        matches!(self, ObjectType::Struct(s) if s.is_coin())
806    }
807
808    /// Return true if `self` is `0x2::coin::Coin<t>`
809    pub fn is_coin_t(&self, t: &TypeTag) -> bool {
810        matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
811    }
812
813    pub fn is_package(&self) -> bool {
814        matches!(self, ObjectType::Package)
815    }
816}
817
818impl From<ObjectInfo> for ObjectRef {
819    fn from(info: ObjectInfo) -> Self {
820        (info.object_id, info.version, info.digest)
821    }
822}
823
824impl From<&ObjectInfo> for ObjectRef {
825    fn from(info: &ObjectInfo) -> Self {
826        (info.object_id, info.version, info.digest)
827    }
828}
829
830pub const SUI_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
831
832#[serde_as]
833#[derive(
834    Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
835)]
836#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
837pub struct SuiAddress(
838    #[schemars(with = "Hex")]
839    #[serde_as(as = "Readable<Hex, _>")]
840    [u8; SUI_ADDRESS_LENGTH],
841);
842
843impl SuiAddress {
844    pub const ZERO: Self = Self([0u8; SUI_ADDRESS_LENGTH]);
845
846    /// Convert the address to a byte buffer.
847    pub fn to_vec(&self) -> Vec<u8> {
848        self.0.to_vec()
849    }
850
851    /// Return a random SuiAddress.
852    pub fn random_for_testing_only() -> Self {
853        AccountAddress::random().into()
854    }
855
856    pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
857        let buf: [u8; SUI_ADDRESS_LENGTH] = rng.r#gen();
858        Self(buf)
859    }
860
861    /// Serialize an `Option<SuiAddress>` in Hex.
862    pub fn optional_address_as_hex<S>(
863        key: &Option<SuiAddress>,
864        serializer: S,
865    ) -> Result<S::Ok, S::Error>
866    where
867        S: serde::ser::Serializer,
868    {
869        serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
870    }
871
872    /// Deserialize into an `Option<SuiAddress>`.
873    pub fn optional_address_from_hex<'de, D>(
874        deserializer: D,
875    ) -> Result<Option<SuiAddress>, D::Error>
876    where
877        D: serde::de::Deserializer<'de>,
878    {
879        let s = String::deserialize(deserializer)?;
880        let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
881        Ok(Some(value))
882    }
883
884    /// Return the underlying byte array of a SuiAddress.
885    pub fn to_inner(self) -> [u8; SUI_ADDRESS_LENGTH] {
886        self.0
887    }
888
889    /// Parse a SuiAddress from a byte array or buffer.
890    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, SuiError> {
891        <[u8; SUI_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
892            .map_err(|_| SuiErrorKind::InvalidAddress.into())
893            .map(SuiAddress)
894    }
895
896    /// This derives a zkLogin address by parsing the iss and address_seed from [struct ZkLoginAuthenticator].
897    /// Define as iss_bytes_len || iss_bytes || padded_32_byte_address_seed. This is to be differentiated with
898    /// try_from_unpadded defined below.
899    pub fn try_from_padded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
900        Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
901    }
902
903    /// Define as iss_bytes_len || iss_bytes || unpadded_32_byte_address_seed.
904    pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
905        let mut hasher = DefaultHash::default();
906        hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
907        let iss_bytes = inputs.get_iss().as_bytes();
908        hasher.update([iss_bytes.len() as u8]);
909        hasher.update(iss_bytes);
910        hasher.update(inputs.get_address_seed().unpadded());
911        Ok(SuiAddress(hasher.finalize().digest))
912    }
913}
914
915impl From<ObjectID> for SuiAddress {
916    fn from(object_id: ObjectID) -> SuiAddress {
917        Self(object_id.into_bytes())
918    }
919}
920
921impl From<AccountAddress> for SuiAddress {
922    fn from(address: AccountAddress) -> SuiAddress {
923        Self(address.into_bytes())
924    }
925}
926
927impl TryFrom<&[u8]> for SuiAddress {
928    type Error = SuiError;
929
930    /// Tries to convert the provided byte array into a SuiAddress.
931    fn try_from(bytes: &[u8]) -> Result<Self, SuiError> {
932        Self::from_bytes(bytes)
933    }
934}
935
936impl TryFrom<Vec<u8>> for SuiAddress {
937    type Error = SuiError;
938
939    /// Tries to convert the provided byte buffer into a SuiAddress.
940    fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
941        Self::from_bytes(bytes)
942    }
943}
944
945impl AsRef<[u8]> for SuiAddress {
946    fn as_ref(&self) -> &[u8] {
947        &self.0[..]
948    }
949}
950
951impl FromStr for SuiAddress {
952    type Err = anyhow::Error;
953    fn from_str(s: &str) -> Result<Self, Self::Err> {
954        decode_bytes_hex(s).map_err(|e| anyhow!(e))
955    }
956}
957
958impl<T: SuiPublicKey> From<&T> for SuiAddress {
959    fn from(pk: &T) -> Self {
960        let mut hasher = DefaultHash::default();
961        hasher.update([T::SIGNATURE_SCHEME.flag()]);
962        hasher.update(pk);
963        let g_arr = hasher.finalize();
964        SuiAddress(g_arr.digest)
965    }
966}
967
968impl From<&PublicKey> for SuiAddress {
969    fn from(pk: &PublicKey) -> Self {
970        let mut hasher = DefaultHash::default();
971        hasher.update([pk.flag()]);
972        hasher.update(pk);
973        let g_arr = hasher.finalize();
974        SuiAddress(g_arr.digest)
975    }
976}
977
978impl From<&MultiSigPublicKey> for SuiAddress {
979    /// Derive a SuiAddress from [struct MultiSigPublicKey]. A MultiSig address
980    /// is defined as the 32-byte Blake2b hash of serializing the flag, the
981    /// threshold, concatenation of all n flag, public keys and
982    /// its weight. `flag_MultiSig || threshold || flag_1 || pk_1 || weight_1
983    /// || ... || flag_n || pk_n || weight_n`.
984    ///
985    /// When flag_i is ZkLogin, pk_i refers to [struct ZkLoginPublicIdentifier]
986    /// derived from padded address seed in bytes and iss.
987    fn from(multisig_pk: &MultiSigPublicKey) -> Self {
988        let mut hasher = DefaultHash::default();
989        hasher.update([SignatureScheme::MultiSig.flag()]);
990        hasher.update(multisig_pk.threshold().to_le_bytes());
991        multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
992            hasher.update([pk.flag()]);
993            hasher.update(pk.as_ref());
994            hasher.update(w.to_le_bytes());
995        });
996        SuiAddress(hasher.finalize().digest)
997    }
998}
999
1000/// Sui address for [struct ZkLoginAuthenticator] is defined as the black2b hash of
1001/// [zklogin_flag || iss_bytes_length || iss_bytes || unpadded_address_seed_in_bytes].
1002impl TryFrom<&ZkLoginAuthenticator> for SuiAddress {
1003    type Error = SuiError;
1004    fn try_from(authenticator: &ZkLoginAuthenticator) -> SuiResult<Self> {
1005        SuiAddress::try_from_unpadded(&authenticator.inputs)
1006    }
1007}
1008
1009impl TryFrom<&GenericSignature> for SuiAddress {
1010    type Error = SuiError;
1011    /// Derive a SuiAddress from a serialized signature in Sui [GenericSignature].
1012    fn try_from(sig: &GenericSignature) -> SuiResult<Self> {
1013        match sig {
1014            GenericSignature::Signature(sig) => {
1015                let scheme = sig.scheme();
1016                let pub_key_bytes = sig.public_key_bytes();
1017                let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
1018                    SuiErrorKind::InvalidSignature {
1019                        error: "Cannot parse pubkey".to_string(),
1020                    }
1021                })?;
1022                Ok(SuiAddress::from(&pub_key))
1023            }
1024            GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
1025            GenericSignature::MultiSigLegacy(ms) => {
1026                Ok(crate::multisig::MultiSig::try_from(ms.clone())
1027                    .map_err(|_| SuiErrorKind::InvalidSignature {
1028                        error: "Invalid legacy multisig".to_string(),
1029                    })?
1030                    .get_pk()
1031                    .into())
1032            }
1033            GenericSignature::ZkLoginAuthenticator(zklogin) => {
1034                SuiAddress::try_from_unpadded(&zklogin.inputs)
1035            }
1036            GenericSignature::PasskeyAuthenticator(s) => Ok(SuiAddress::from(&s.get_pk()?)),
1037        }
1038    }
1039}
1040
1041impl fmt::Display for SuiAddress {
1042    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043        write!(f, "0x{}", Hex::encode(self.0))
1044    }
1045}
1046
1047impl fmt::Debug for SuiAddress {
1048    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1049        write!(f, "0x{}", Hex::encode(self.0))
1050    }
1051}
1052
1053/// Generate a fake SuiAddress with repeated one byte.
1054pub fn dbg_addr(name: u8) -> SuiAddress {
1055    let addr = [name; SUI_ADDRESS_LENGTH];
1056    SuiAddress(addr)
1057}
1058
1059#[derive(
1060    Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
1061)]
1062pub struct ExecutionDigests {
1063    pub transaction: TransactionDigest,
1064    pub effects: TransactionEffectsDigest,
1065}
1066
1067impl ExecutionDigests {
1068    pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
1069        Self {
1070            transaction,
1071            effects,
1072        }
1073    }
1074
1075    pub fn random() -> Self {
1076        Self {
1077            transaction: TransactionDigest::random(),
1078            effects: TransactionEffectsDigest::random(),
1079        }
1080    }
1081}
1082
1083#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
1084pub struct ExecutionData {
1085    pub transaction: Transaction,
1086    pub effects: TransactionEffects,
1087}
1088
1089impl ExecutionData {
1090    pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
1091        debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1092        Self {
1093            transaction,
1094            effects,
1095        }
1096    }
1097
1098    pub fn digests(&self) -> ExecutionDigests {
1099        self.effects.execution_digests()
1100    }
1101}
1102
1103#[derive(Clone, Eq, PartialEq, Debug)]
1104pub struct VerifiedExecutionData {
1105    pub transaction: VerifiedTransaction,
1106    pub effects: TransactionEffects,
1107}
1108
1109impl VerifiedExecutionData {
1110    pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
1111        debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1112        Self {
1113            transaction,
1114            effects,
1115        }
1116    }
1117
1118    pub fn new_unchecked(data: ExecutionData) -> Self {
1119        Self {
1120            transaction: VerifiedTransaction::new_unchecked(data.transaction),
1121            effects: data.effects,
1122        }
1123    }
1124
1125    pub fn into_inner(self) -> ExecutionData {
1126        ExecutionData {
1127            transaction: self.transaction.into_inner(),
1128            effects: self.effects,
1129        }
1130    }
1131
1132    pub fn digests(&self) -> ExecutionDigests {
1133        self.effects.execution_digests()
1134    }
1135}
1136
1137pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
1138pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
1139pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
1140    &MOVE_STDLIB_ADDRESS,
1141    STD_OPTION_MODULE_NAME,
1142    STD_OPTION_STRUCT_NAME,
1143);
1144
1145pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
1146pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
1147pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1148    &MOVE_STDLIB_ADDRESS,
1149    STD_ASCII_MODULE_NAME,
1150    STD_ASCII_STRUCT_NAME,
1151);
1152
1153pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
1154pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
1155pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1156    &MOVE_STDLIB_ADDRESS,
1157    STD_UTF8_MODULE_NAME,
1158    STD_UTF8_STRUCT_NAME,
1159);
1160
1161pub const STD_TYPE_NAME_MODULE_NAME: &IdentStr = ident_str!("type_name");
1162pub const STD_TYPE_NAME_STRUCT_NAME: &IdentStr = ident_str!("TypeName");
1163pub const RESOLVED_STD_TYPE_NAME: (&AccountAddress, &IdentStr, &IdentStr) = (
1164    &MOVE_STDLIB_ADDRESS,
1165    STD_TYPE_NAME_MODULE_NAME,
1166    STD_TYPE_NAME_STRUCT_NAME,
1167);
1168
1169pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
1170pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
1171pub const RESOLVED_TX_CONTEXT: (&AccountAddress, &IdentStr, &IdentStr) = (
1172    &SUI_FRAMEWORK_ADDRESS,
1173    TX_CONTEXT_MODULE_NAME,
1174    TX_CONTEXT_STRUCT_NAME,
1175);
1176
1177pub const URL_MODULE_NAME: &IdentStr = ident_str!("url");
1178pub const URL_STRUCT_NAME: &IdentStr = ident_str!("Url");
1179
1180pub const VEC_MAP_MODULE_NAME: &IdentStr = ident_str!("vec_map");
1181pub const VEC_MAP_STRUCT_NAME: &IdentStr = ident_str!("VecMap");
1182pub const VEC_MAP_ENTRY_STRUCT_NAME: &IdentStr = ident_str!("Entry");
1183
1184pub fn move_ascii_str_layout() -> A::MoveStructLayout {
1185    A::MoveStructLayout {
1186        type_: StructTag {
1187            address: MOVE_STDLIB_ADDRESS,
1188            module: STD_ASCII_MODULE_NAME.to_owned(),
1189            name: STD_ASCII_STRUCT_NAME.to_owned(),
1190            type_params: vec![],
1191        },
1192        fields: vec![A::MoveFieldLayout::new(
1193            ident_str!("bytes").into(),
1194            A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1195        )],
1196    }
1197}
1198
1199pub fn move_utf8_str_layout() -> A::MoveStructLayout {
1200    A::MoveStructLayout {
1201        type_: StructTag {
1202            address: MOVE_STDLIB_ADDRESS,
1203            module: STD_UTF8_MODULE_NAME.to_owned(),
1204            name: STD_UTF8_STRUCT_NAME.to_owned(),
1205            type_params: vec![],
1206        },
1207        fields: vec![A::MoveFieldLayout::new(
1208            ident_str!("bytes").into(),
1209            A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1210        )],
1211    }
1212}
1213
1214pub fn url_layout() -> A::MoveStructLayout {
1215    A::MoveStructLayout {
1216        type_: StructTag {
1217            address: SUI_FRAMEWORK_ADDRESS,
1218            module: URL_MODULE_NAME.to_owned(),
1219            name: URL_STRUCT_NAME.to_owned(),
1220            type_params: vec![],
1221        },
1222        fields: vec![A::MoveFieldLayout::new(
1223            ident_str!("url").to_owned(),
1224            A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1225        )],
1226    }
1227}
1228
1229pub fn type_name_layout() -> A::MoveStructLayout {
1230    A::MoveStructLayout {
1231        type_: StructTag {
1232            address: MOVE_STDLIB_ADDRESS,
1233            module: STD_TYPE_NAME_MODULE_NAME.to_owned(),
1234            name: STD_TYPE_NAME_STRUCT_NAME.to_owned(),
1235            type_params: vec![],
1236        },
1237        fields: vec![A::MoveFieldLayout::new(
1238            ident_str!("name").into(),
1239            A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1240        )],
1241    }
1242}
1243
1244// The Rust representation of the Move `TxContext`.
1245// This struct must be kept in sync with the Move `TxContext` definition.
1246// Moving forward we are going to zero all fields of the Move `TxContext`
1247// and use native functions to retrieve info about the transaction.
1248// However we cannot remove the Move type and so this struct is going to
1249// be the Rust equivalent to the Move `TxContext` for legacy usages.
1250//
1251// `TxContext` in Rust (see below) is going to be purely used in Rust and can
1252// evolve as needed without worrying any compatibility with Move.
1253#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1254pub struct MoveLegacyTxContext {
1255    // Signer/sender of the transaction
1256    sender: AccountAddress,
1257    // Digest of the current transaction
1258    digest: Vec<u8>,
1259    // The current epoch number
1260    epoch: EpochId,
1261    // Timestamp that the epoch started at
1262    epoch_timestamp_ms: CheckpointTimestamp,
1263    // Number of `ObjectID`'s generated during execution of the current transaction
1264    ids_created: u64,
1265}
1266
1267impl From<&TxContext> for MoveLegacyTxContext {
1268    fn from(tx_context: &TxContext) -> Self {
1269        Self {
1270            sender: tx_context.sender,
1271            digest: tx_context.digest.clone(),
1272            epoch: tx_context.epoch,
1273            epoch_timestamp_ms: tx_context.epoch_timestamp_ms,
1274            ids_created: tx_context.ids_created,
1275        }
1276    }
1277}
1278
1279// Information about the transaction context.
1280// This struct is not related to Move and can evolve as needed/required.
1281#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1282pub struct TxContext {
1283    /// Sender of the transaction
1284    sender: AccountAddress,
1285    /// Digest of the current transaction
1286    digest: Vec<u8>,
1287    /// The current epoch number
1288    epoch: EpochId,
1289    /// Timestamp that the epoch started at
1290    epoch_timestamp_ms: CheckpointTimestamp,
1291    /// Number of `ObjectID`'s generated during execution of the current transaction
1292    ids_created: u64,
1293    // Reference gas price
1294    rgp: u64,
1295    // gas price passed to transaction as input
1296    gas_price: u64,
1297    // gas budget passed to transaction as input
1298    gas_budget: u64,
1299    // address of the sponsor if any
1300    sponsor: Option<AccountAddress>,
1301    // whether the `TxContext` is native or not
1302    // (TODO: once we version execution we could drop this field)
1303    is_native: bool,
1304}
1305
1306#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1307pub enum TxContextKind {
1308    // No TxContext
1309    None,
1310    // &mut TxContext
1311    Mutable,
1312    // &TxContext
1313    Immutable,
1314}
1315
1316impl TxContext {
1317    pub fn new(
1318        sender: &SuiAddress,
1319        digest: &TransactionDigest,
1320        epoch_data: &EpochData,
1321        rgp: u64,
1322        gas_price: u64,
1323        gas_budget: u64,
1324        sponsor: Option<SuiAddress>,
1325        protocol_config: &ProtocolConfig,
1326    ) -> Self {
1327        Self::new_from_components(
1328            sender,
1329            digest,
1330            &epoch_data.epoch_id(),
1331            epoch_data.epoch_start_timestamp(),
1332            rgp,
1333            gas_price,
1334            gas_budget,
1335            sponsor,
1336            protocol_config,
1337        )
1338    }
1339
1340    pub fn new_from_components(
1341        sender: &SuiAddress,
1342        digest: &TransactionDigest,
1343        epoch_id: &EpochId,
1344        epoch_timestamp_ms: u64,
1345        rgp: u64,
1346        gas_price: u64,
1347        gas_budget: u64,
1348        sponsor: Option<SuiAddress>,
1349        protocol_config: &ProtocolConfig,
1350    ) -> Self {
1351        Self {
1352            sender: AccountAddress::new(sender.0),
1353            digest: digest.into_inner().to_vec(),
1354            epoch: *epoch_id,
1355            epoch_timestamp_ms,
1356            ids_created: 0,
1357            rgp,
1358            gas_price,
1359            gas_budget,
1360            sponsor: sponsor.map(|s| s.into()),
1361            is_native: protocol_config.move_native_context(),
1362        }
1363    }
1364
1365    /// Returns whether the type signature is &mut TxContext, &TxContext, or none of the above.
1366    pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1367        use SignatureToken as S;
1368        let (kind, s) = match s {
1369            S::MutableReference(s) => (TxContextKind::Mutable, s),
1370            S::Reference(s) => (TxContextKind::Immutable, s),
1371            _ => return TxContextKind::None,
1372        };
1373
1374        let S::Datatype(idx) = &**s else {
1375            return TxContextKind::None;
1376        };
1377
1378        if resolve_struct(view, *idx) == RESOLVED_TX_CONTEXT {
1379            kind
1380        } else {
1381            TxContextKind::None
1382        }
1383    }
1384
1385    pub fn type_() -> StructTag {
1386        StructTag {
1387            address: SUI_FRAMEWORK_ADDRESS,
1388            module: TX_CONTEXT_MODULE_NAME.to_owned(),
1389            name: TX_CONTEXT_STRUCT_NAME.to_owned(),
1390            type_params: vec![],
1391        }
1392    }
1393
1394    pub fn epoch(&self) -> EpochId {
1395        self.epoch
1396    }
1397
1398    pub fn sender(&self) -> SuiAddress {
1399        self.sender.into()
1400    }
1401
1402    pub fn epoch_timestamp_ms(&self) -> u64 {
1403        self.epoch_timestamp_ms
1404    }
1405
1406    /// Return the transaction digest, to include in new objects
1407    pub fn digest(&self) -> TransactionDigest {
1408        TransactionDigest::new(self.digest.clone().try_into().unwrap())
1409    }
1410
1411    pub fn sponsor(&self) -> Option<SuiAddress> {
1412        self.sponsor.map(SuiAddress::from)
1413    }
1414
1415    pub fn rgp(&self) -> u64 {
1416        self.rgp
1417    }
1418
1419    pub fn gas_price(&self) -> u64 {
1420        self.gas_price
1421    }
1422
1423    pub fn gas_budget(&self) -> u64 {
1424        self.gas_budget
1425    }
1426
1427    pub fn ids_created(&self) -> u64 {
1428        self.ids_created
1429    }
1430
1431    /// Derive a globally unique object ID by hashing self.digest | self.ids_created
1432    pub fn fresh_id(&mut self) -> ObjectID {
1433        let id = ObjectID::derive_id(self.digest(), self.ids_created);
1434
1435        self.ids_created += 1;
1436        id
1437    }
1438
1439    pub fn to_bcs_legacy_context(&self) -> Vec<u8> {
1440        let move_context: MoveLegacyTxContext = if self.is_native {
1441            let tx_context = &TxContext {
1442                sender: AccountAddress::ZERO,
1443                digest: self.digest.clone(),
1444                epoch: 0,
1445                epoch_timestamp_ms: 0,
1446                ids_created: 0,
1447                rgp: 0,
1448                gas_price: 0,
1449                gas_budget: 0,
1450                sponsor: None,
1451                is_native: true,
1452            };
1453            tx_context.into()
1454        } else {
1455            self.into()
1456        };
1457        bcs::to_bytes(&move_context).unwrap()
1458    }
1459
1460    pub fn to_vec(&self) -> Vec<u8> {
1461        bcs::to_bytes(&self).unwrap()
1462    }
1463
1464    /// Updates state of the context instance. It's intended to use
1465    /// when mutable context is passed over some boundary via
1466    /// serialize/deserialize and this is the reason why this method
1467    /// consumes the other context..
1468    pub fn update_state(&mut self, other: MoveLegacyTxContext) -> Result<(), ExecutionError> {
1469        if !self.is_native {
1470            if self.sender != other.sender
1471                || self.digest != other.digest
1472                || other.ids_created < self.ids_created
1473            {
1474                return Err(ExecutionError::new_with_source(
1475                    ExecutionErrorKind::InvariantViolation,
1476                    "Immutable fields for TxContext changed",
1477                ));
1478            }
1479            self.ids_created = other.ids_created;
1480        }
1481        Ok(())
1482    }
1483
1484    //
1485    // Move test only API
1486    //
1487    pub fn replace(
1488        &mut self,
1489        sender: AccountAddress,
1490        tx_hash: Vec<u8>,
1491        epoch: u64,
1492        epoch_timestamp_ms: u64,
1493        ids_created: u64,
1494        rgp: u64,
1495        gas_price: u64,
1496        gas_budget: u64,
1497        sponsor: Option<AccountAddress>,
1498    ) {
1499        self.sender = sender;
1500        self.digest = tx_hash;
1501        self.epoch = epoch;
1502        self.epoch_timestamp_ms = epoch_timestamp_ms;
1503        self.ids_created = ids_created;
1504        self.rgp = rgp;
1505        self.gas_price = gas_price;
1506        self.gas_budget = gas_budget;
1507        self.sponsor = sponsor;
1508    }
1509}
1510
1511// TODO: rename to version
1512impl SequenceNumber {
1513    pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1514    pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1515    pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1516    pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1517    pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1518        SequenceNumber(SequenceNumber::MAX.value() + 3);
1519    // Used to represent a sequence number whose value is unknown.
1520    // For internal use only. This should never appear on chain.
1521    pub const UNKNOWN: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 4);
1522
1523    pub const fn new() -> Self {
1524        SequenceNumber(0)
1525    }
1526
1527    pub const fn value(&self) -> u64 {
1528        self.0
1529    }
1530
1531    pub const fn from_u64(u: u64) -> Self {
1532        SequenceNumber(u)
1533    }
1534
1535    pub fn increment(&mut self) {
1536        assert_ne!(self.0, u64::MAX);
1537        self.0 += 1;
1538    }
1539
1540    pub fn increment_to(&mut self, next: SequenceNumber) {
1541        debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1542        *self = next;
1543    }
1544
1545    pub fn decrement(&mut self) {
1546        assert_ne!(self.0, 0);
1547        self.0 -= 1;
1548    }
1549
1550    pub fn decrement_to(&mut self, prev: SequenceNumber) {
1551        debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1552        *self = prev;
1553    }
1554
1555    /// Returns a new sequence number that is greater than all `SequenceNumber`s in `inputs`,
1556    /// assuming this operation will not overflow.
1557    #[must_use]
1558    pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1559        let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1560
1561        // TODO: Ensure this never overflows.
1562        // Option 1: Freeze the object when sequence number reaches MAX.
1563        // Option 2: Reject tx with MAX sequence number.
1564        // Issue #182.
1565        assert_ne!(max_input.0, u64::MAX);
1566
1567        SequenceNumber(max_input.0 + 1)
1568    }
1569
1570    pub fn is_cancelled(&self) -> bool {
1571        self == &SequenceNumber::CANCELLED_READ
1572            || self == &SequenceNumber::CONGESTED
1573            || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1574    }
1575
1576    pub fn is_valid(&self) -> bool {
1577        self < &SequenceNumber::MAX
1578    }
1579}
1580
1581impl From<SequenceNumber> for u64 {
1582    fn from(val: SequenceNumber) -> Self {
1583        val.0
1584    }
1585}
1586
1587impl From<u64> for SequenceNumber {
1588    fn from(value: u64) -> Self {
1589        SequenceNumber(value)
1590    }
1591}
1592
1593impl From<SequenceNumber> for usize {
1594    fn from(value: SequenceNumber) -> Self {
1595        value.0 as usize
1596    }
1597}
1598
1599impl ObjectID {
1600    /// The number of bytes in an address.
1601    pub const LENGTH: usize = AccountAddress::LENGTH;
1602    /// Hex address: 0x0
1603    pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1604    pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1605    /// Create a new ObjectID
1606    pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1607        Self(AccountAddress::new(obj_id))
1608    }
1609
1610    /// Const fn variant of `<ObjectID as From<AccountAddress>>::from`
1611    pub const fn from_address(addr: AccountAddress) -> Self {
1612        Self(addr)
1613    }
1614
1615    /// Return a random ObjectID.
1616    pub fn random() -> Self {
1617        Self::from(AccountAddress::random())
1618    }
1619
1620    /// Return a random ObjectID from a given RNG.
1621    pub fn random_from_rng<R>(rng: &mut R) -> Self
1622    where
1623        R: AllowedRng,
1624    {
1625        let buf: [u8; Self::LENGTH] = rng.r#gen();
1626        ObjectID::new(buf)
1627    }
1628
1629    /// Return the underlying bytes buffer of the ObjectID.
1630    pub fn to_vec(&self) -> Vec<u8> {
1631        self.0.to_vec()
1632    }
1633
1634    /// Parse the ObjectID from byte array or buffer.
1635    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1636        <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1637            .map_err(|_| ObjectIDParseError::TryFromSliceError)
1638            .map(ObjectID::new)
1639    }
1640
1641    /// Return the underlying bytes array of the ObjectID.
1642    pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1643        self.0.into_bytes()
1644    }
1645
1646    /// Make an ObjectID with padding 0s before the single byte.
1647    pub const fn from_single_byte(byte: u8) -> ObjectID {
1648        let mut bytes = [0u8; Self::LENGTH];
1649        bytes[Self::LENGTH - 1] = byte;
1650        ObjectID::new(bytes)
1651    }
1652
1653    /// System objects have IDs that fit in the last 8 bytes (24 leading zero bytes).
1654    pub fn is_system_object(&self) -> bool {
1655        self.0.as_ref()[..24] == [0u8; 24]
1656    }
1657
1658    /// Convert from hex string to ObjectID where the string is prefixed with 0x
1659    /// Padding 0s if the string is too short.
1660    pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1661        if !literal.starts_with("0x") {
1662            return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1663        }
1664
1665        let hex_len = literal.len() - 2;
1666
1667        // If the string is too short, pad it
1668        if hex_len < Self::LENGTH * 2 {
1669            let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1670            for _ in 0..Self::LENGTH * 2 - hex_len {
1671                hex_str.push('0');
1672            }
1673            hex_str.push_str(&literal[2..]);
1674            Self::from_str(&hex_str)
1675        } else {
1676            Self::from_str(&literal[2..])
1677        }
1678    }
1679
1680    /// Create an ObjectID from `TransactionDigest` and `creation_num`.
1681    /// Caller is responsible for ensuring that `creation_num` is fresh
1682    pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1683        let mut hasher = DefaultHash::default();
1684        hasher.update([HashingIntentScope::RegularObjectId as u8]);
1685        hasher.update(digest);
1686        hasher.update(creation_num.to_le_bytes());
1687        let hash = hasher.finalize();
1688
1689        // truncate into an ObjectID.
1690        // OK to access slice because digest should never be shorter than ObjectID::LENGTH.
1691        ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1692    }
1693
1694    /// Incremenent the ObjectID by usize IDs, assuming the ObjectID hex is a number represented as an array of bytes
1695    pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1696        let mut curr_vec = self.to_vec();
1697        let mut step_copy = step;
1698
1699        let mut carry = 0;
1700        for idx in (0..Self::LENGTH).rev() {
1701            if step_copy == 0 {
1702                // Nothing else to do
1703                break;
1704            }
1705            // Extract the relevant part
1706            let g = (step_copy % 0x100) as u16;
1707            // Shift to next group
1708            step_copy >>= 8;
1709            let mut val = curr_vec[idx] as u16;
1710            (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1711            curr_vec[idx] = val as u8;
1712        }
1713
1714        if carry > 0 {
1715            return Err(anyhow!("Increment will cause overflow"));
1716        }
1717        ObjectID::try_from(curr_vec).map_err(|w| w.into())
1718    }
1719
1720    /// Increment the ObjectID by one, assuming the ObjectID hex is a number represented as an array of bytes
1721    pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1722        let mut prev_val = self.to_vec();
1723        let mx = [0xFF; Self::LENGTH];
1724
1725        if prev_val == mx {
1726            return Err(anyhow!("Increment will cause overflow"));
1727        }
1728
1729        // This logic increments the integer representation of an ObjectID u8 array
1730        for idx in (0..Self::LENGTH).rev() {
1731            if prev_val[idx] == 0xFF {
1732                prev_val[idx] = 0;
1733            } else {
1734                prev_val[idx] += 1;
1735                break;
1736            };
1737        }
1738        ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1739    }
1740
1741    /// Create `count` object IDs starting with one at `offset`
1742    pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1743        let mut ret = Vec::new();
1744        let mut prev = offset;
1745        for o in 0..count {
1746            if o != 0 {
1747                prev = prev.next_increment()?;
1748            }
1749            ret.push(prev);
1750        }
1751        Ok(ret)
1752    }
1753
1754    /// Return the full hex string with 0x prefix without removing trailing 0s. Prefer this
1755    /// over [fn to_hex_literal] if the string needs to be fully preserved.
1756    pub fn to_hex_uncompressed(&self) -> String {
1757        format!("{self}")
1758    }
1759
1760    pub fn is_clock(&self) -> bool {
1761        *self == SUI_CLOCK_OBJECT_ID
1762    }
1763
1764    pub fn is_implicitly_read_system_object(&self) -> bool {
1765        crate::IMPLICITLY_READ_SYSTEM_OBJECTS.contains(self)
1766    }
1767}
1768
1769impl From<SuiAddress> for ObjectID {
1770    fn from(address: SuiAddress) -> ObjectID {
1771        let tmp: AccountAddress = address.into();
1772        tmp.into()
1773    }
1774}
1775
1776impl From<AccountAddress> for ObjectID {
1777    fn from(address: AccountAddress) -> Self {
1778        Self(address)
1779    }
1780}
1781
1782impl fmt::Display for ObjectID {
1783    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1784        write!(f, "0x{}", Hex::encode(self.0))
1785    }
1786}
1787
1788impl fmt::Debug for ObjectID {
1789    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1790        write!(f, "0x{}", Hex::encode(self.0))
1791    }
1792}
1793
1794impl AsRef<[u8]> for ObjectID {
1795    fn as_ref(&self) -> &[u8] {
1796        self.0.as_slice()
1797    }
1798}
1799
1800impl TryFrom<&[u8]> for ObjectID {
1801    type Error = ObjectIDParseError;
1802
1803    /// Tries to convert the provided byte array into ObjectID.
1804    fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1805        Self::from_bytes(bytes)
1806    }
1807}
1808
1809impl TryFrom<Vec<u8>> for ObjectID {
1810    type Error = ObjectIDParseError;
1811
1812    /// Tries to convert the provided byte buffer into ObjectID.
1813    fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1814        Self::from_bytes(bytes)
1815    }
1816}
1817
1818impl FromStr for ObjectID {
1819    type Err = ObjectIDParseError;
1820
1821    /// Parse ObjectID from hex string with or without 0x prefix, pad with 0s if needed.
1822    fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1823        decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1824    }
1825}
1826
1827impl std::ops::Deref for ObjectID {
1828    type Target = AccountAddress;
1829
1830    fn deref(&self) -> &Self::Target {
1831        &self.0
1832    }
1833}
1834
1835/// Generate a fake ObjectID with repeated one byte.
1836pub fn dbg_object_id(name: u8) -> ObjectID {
1837    ObjectID::new([name; ObjectID::LENGTH])
1838}
1839
1840#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1841pub enum ObjectIDParseError {
1842    #[error("ObjectID hex literal must start with 0x")]
1843    HexLiteralPrefixMissing,
1844
1845    #[error("Could not convert from bytes slice")]
1846    TryFromSliceError,
1847}
1848
1849impl From<ObjectID> for AccountAddress {
1850    fn from(obj_id: ObjectID) -> Self {
1851        obj_id.0
1852    }
1853}
1854
1855impl From<SuiAddress> for AccountAddress {
1856    fn from(address: SuiAddress) -> Self {
1857        Self::new(address.0)
1858    }
1859}
1860
1861/// Hex serde for AccountAddress
1862struct HexAccountAddress;
1863
1864impl SerializeAs<AccountAddress> for HexAccountAddress {
1865    fn serialize_as<S>(value: &AccountAddress, serializer: S) -> Result<S::Ok, S::Error>
1866    where
1867        S: Serializer,
1868    {
1869        Hex::serialize_as(value, serializer)
1870    }
1871}
1872
1873impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress {
1874    fn deserialize_as<D>(deserializer: D) -> Result<AccountAddress, D::Error>
1875    where
1876        D: Deserializer<'de>,
1877    {
1878        let s = String::deserialize(deserializer)?;
1879        if s.starts_with("0x") {
1880            AccountAddress::from_hex_literal(&s)
1881        } else {
1882            AccountAddress::from_hex(&s)
1883        }
1884        .map_err(to_custom_deser_error::<'de, D, _>)
1885    }
1886}
1887
1888impl fmt::Display for MoveObjectType {
1889    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1890        let s: StructTag = self.clone().into();
1891        write!(
1892            f,
1893            "{}",
1894            to_sui_struct_tag_string(&s).map_err(fmt::Error::custom)?
1895        )
1896    }
1897}
1898
1899impl fmt::Display for ObjectType {
1900    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1901        match self {
1902            ObjectType::Package => write!(f, "{}", PACKAGE),
1903            ObjectType::Struct(t) => write!(f, "{}", t),
1904        }
1905    }
1906}
1907
1908// SizeOneVec is a wrapper around Vec<T> that enforces the size of the vec to be 1.
1909// This seems pointless, but it allows us to have fields in protocol messages that are
1910// current enforced to be of size 1, but might later allow other sizes, and to have
1911// that constraint enforced in the serialization/deserialization layer, instead of
1912// requiring manual input validation.
1913#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1914#[serde(try_from = "Vec<T>")]
1915pub struct SizeOneVec<T> {
1916    e: T,
1917}
1918
1919impl<T> SizeOneVec<T> {
1920    pub fn new(e: T) -> Self {
1921        Self { e }
1922    }
1923
1924    pub fn element(&self) -> &T {
1925        &self.e
1926    }
1927
1928    pub fn element_mut(&mut self) -> &mut T {
1929        &mut self.e
1930    }
1931
1932    pub fn into_inner(self) -> T {
1933        self.e
1934    }
1935
1936    pub fn iter(&self) -> std::iter::Once<&T> {
1937        std::iter::once(&self.e)
1938    }
1939}
1940
1941impl<T> Serialize for SizeOneVec<T>
1942where
1943    T: Serialize,
1944{
1945    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1946    where
1947        S: Serializer,
1948    {
1949        let mut seq = serializer.serialize_seq(Some(1))?;
1950        seq.serialize_element(&self.e)?;
1951        seq.end()
1952    }
1953}
1954
1955impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1956    type Error = anyhow::Error;
1957
1958    fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1959        if v.len() != 1 {
1960            Err(anyhow!("Expected a vec of size 1"))
1961        } else {
1962            Ok(SizeOneVec {
1963                e: v.pop().unwrap(),
1964            })
1965        }
1966    }
1967}
1968
1969#[test]
1970fn test_size_one_vec_is_transparent() {
1971    let regular = vec![42u8];
1972    let size_one = SizeOneVec::new(42u8);
1973
1974    // Vec -> SizeOneVec serialization is transparent
1975    let regular_ser = bcs::to_bytes(&regular).unwrap();
1976    let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(&regular_ser).unwrap();
1977    assert_eq!(size_one, size_one_deser);
1978
1979    // other direction works too
1980    let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1981    let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1982    assert_eq!(regular_deser, vec![43u8]);
1983
1984    // we get a deserialize error when deserializing a vec with size != 1
1985    let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1986    bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1987
1988    let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1989    bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1990}