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