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