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