1use 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::borrow::Cow;
80use std::cmp::max;
81use std::convert::{TryFrom, TryInto};
82use std::fmt;
83use std::str::FromStr;
84use sui_protocol_config::ProtocolConfig;
85
86#[cfg(test)]
87#[path = "unit_tests/base_types_tests.rs"]
88mod base_types_tests;
89
90#[cfg(test)]
91#[path = "unit_tests/accumulator_types_tests.rs"]
92mod accumulator_types_tests;
93
94#[derive(
95 Eq,
96 PartialEq,
97 Ord,
98 PartialOrd,
99 Copy,
100 Clone,
101 Hash,
102 Default,
103 Debug,
104 Serialize,
105 Deserialize,
106 JsonSchema,
107)]
108#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
109pub struct SequenceNumber(u64);
110
111impl SequenceNumber {
112 pub fn one_before(&self) -> Option<SequenceNumber> {
113 if self.0 == 0 {
114 None
115 } else {
116 Some(SequenceNumber(self.0 - 1))
117 }
118 }
119
120 pub fn next(&self) -> SequenceNumber {
121 SequenceNumber(self.0 + 1)
122 }
123}
124
125pub type TxSequenceNumber = u64;
126
127impl fmt::Display for SequenceNumber {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 write!(f, "{:#x}", self.0)
130 }
131}
132
133pub type VersionNumber = SequenceNumber;
134
135#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Default, Debug, Serialize, Deserialize)]
136pub struct UserData(pub Option<[u8; 32]>);
137
138pub type AuthorityName = AuthorityPublicKeyBytes;
139
140pub trait ConciseableName<'a> {
141 type ConciseTypeRef: std::fmt::Debug;
142 type ConciseType: std::fmt::Debug;
143
144 fn concise(&'a self) -> Self::ConciseTypeRef;
145 fn concise_owned(&self) -> Self::ConciseType;
146}
147
148#[serde_as]
149#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)]
150pub struct ObjectID(
151 #[schemars(with = "Hex")]
152 #[serde_as(as = "Readable<HexAccountAddress, _>")]
153 AccountAddress,
154);
155
156#[serde_as]
157#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
158pub enum FullObjectID {
159 Fastpath(ObjectID),
160 Consensus(ConsensusObjectSequenceKey),
161}
162
163impl FullObjectID {
164 pub fn new(object_id: ObjectID, start_version: Option<SequenceNumber>) -> Self {
165 if let Some(start_version) = start_version {
166 Self::Consensus((object_id, start_version))
167 } else {
168 Self::Fastpath(object_id)
169 }
170 }
171
172 pub fn id(&self) -> ObjectID {
173 match &self {
174 FullObjectID::Fastpath(object_id) => *object_id,
175 FullObjectID::Consensus(consensus_object_sequence_key) => {
176 consensus_object_sequence_key.0
177 }
178 }
179 }
180}
181
182pub type VersionDigest = (SequenceNumber, ObjectDigest);
183
184pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest);
185
186pub fn random_object_ref() -> ObjectRef {
187 (
188 ObjectID::random(),
189 SequenceNumber::new(),
190 ObjectDigest::new([0; 32]),
191 )
192}
193
194pub fn update_object_ref_for_testing(object_ref: ObjectRef) -> ObjectRef {
195 (
196 object_ref.0,
197 object_ref.1.next(),
198 ObjectDigest::new([0; 32]),
199 )
200}
201
202#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
203pub struct FullObjectRef(pub FullObjectID, pub SequenceNumber, pub ObjectDigest);
204
205impl FullObjectRef {
206 pub fn from_fastpath_ref(object_ref: ObjectRef) -> Self {
207 Self(
208 FullObjectID::Fastpath(object_ref.0),
209 object_ref.1,
210 object_ref.2,
211 )
212 }
213
214 pub fn from_object_ref_and_owner(object_ref: ObjectRef, owner: &Owner) -> Self {
215 let full_id = if let Some(start_version) = owner.start_version() {
216 FullObjectID::Consensus((object_ref.0, start_version))
217 } else {
218 FullObjectID::Fastpath(object_ref.0)
219 };
220 Self(full_id, object_ref.1, object_ref.2)
221 }
222
223 pub fn as_object_ref(&self) -> ObjectRef {
224 (self.0.id(), self.1, self.2)
225 }
226}
227pub type ConsensusObjectSequenceKey = (ObjectID, SequenceNumber);
230
231#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
236pub struct MoveObjectType(MoveObjectType_);
237
238#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
241pub enum MoveObjectType_ {
242 Other(StructTag),
244 GasCoin,
246 StakedSui,
248 Coin(TypeTag),
250 SuiBalanceAccumulatorField,
253 BalanceAccumulatorField(TypeTag),
257 }
261
262impl MoveObjectType {
263 pub fn gas_coin() -> Self {
264 Self(MoveObjectType_::GasCoin)
265 }
266
267 pub fn is_efficient_representation(&self) -> bool {
268 !matches!(self.0, MoveObjectType_::Other(_))
269 }
270
271 pub fn coin(coin_type: TypeTag) -> Self {
272 Self(if GAS::is_gas_type(&coin_type) {
273 MoveObjectType_::GasCoin
274 } else {
275 MoveObjectType_::Coin(coin_type)
276 })
277 }
278
279 pub fn staked_sui() -> Self {
280 Self(MoveObjectType_::StakedSui)
281 }
282
283 pub fn address(&self) -> AccountAddress {
284 match &self.0 {
285 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => SUI_FRAMEWORK_ADDRESS,
286 MoveObjectType_::StakedSui => SUI_SYSTEM_ADDRESS,
287 MoveObjectType_::SuiBalanceAccumulatorField
288 | MoveObjectType_::BalanceAccumulatorField(_) => SUI_FRAMEWORK_ADDRESS,
289 MoveObjectType_::Other(s) => s.address,
290 }
291 }
292
293 pub fn module(&self) -> &IdentStr {
294 match &self.0 {
295 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME,
296 MoveObjectType_::StakedSui => STAKING_POOL_MODULE_NAME,
297 MoveObjectType_::SuiBalanceAccumulatorField
298 | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_MODULE_NAME,
299 MoveObjectType_::Other(s) => &s.module,
300 }
301 }
302
303 pub fn name(&self) -> &IdentStr {
304 match &self.0 {
305 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME,
306 MoveObjectType_::StakedSui => STAKED_SUI_STRUCT_NAME,
307 MoveObjectType_::SuiBalanceAccumulatorField
308 | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_FIELD_STRUCT_NAME,
309 MoveObjectType_::Other(s) => &s.name,
310 }
311 }
312
313 pub fn type_params(&self) -> Vec<Cow<'_, TypeTag>> {
314 match &self.0 {
315 MoveObjectType_::GasCoin => vec![Cow::Owned(GAS::type_tag())],
316 MoveObjectType_::StakedSui => vec![],
317 MoveObjectType_::Coin(inner) => vec![Cow::Borrowed(inner)],
318 MoveObjectType_::SuiBalanceAccumulatorField => {
319 Self::balance_accumulator_field_type_params(GAS::type_tag())
320 .into_iter()
321 .map(Cow::Owned)
322 .collect()
323 }
324 MoveObjectType_::BalanceAccumulatorField(inner) => {
325 Self::balance_accumulator_field_type_params(inner.clone())
326 .into_iter()
327 .map(Cow::Owned)
328 .collect()
329 }
330 MoveObjectType_::Other(s) => s.type_params.iter().map(Cow::Borrowed).collect(),
331 }
332 }
333
334 pub fn into_type_params(self) -> Vec<TypeTag> {
335 match self.0 {
336 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
337 MoveObjectType_::StakedSui => vec![],
338 MoveObjectType_::Coin(inner) => vec![inner],
339 MoveObjectType_::SuiBalanceAccumulatorField => {
340 Self::balance_accumulator_field_type_params(GAS::type_tag())
341 }
342 MoveObjectType_::BalanceAccumulatorField(inner) => {
343 Self::balance_accumulator_field_type_params(inner)
344 }
345 MoveObjectType_::Other(s) => s.type_params,
346 }
347 }
348
349 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
350 match &self.0 {
351 MoveObjectType_::GasCoin => Some(GAS::type_tag()),
352 MoveObjectType_::Coin(inner) => Some(inner.clone()),
353 MoveObjectType_::StakedSui => None,
354 MoveObjectType_::SuiBalanceAccumulatorField => None,
355 MoveObjectType_::BalanceAccumulatorField(_) => None,
356 MoveObjectType_::Other(_) => None,
357 }
358 }
359
360 pub fn balance_accumulator_field_type_maybe(&self) -> Option<TypeTag> {
361 match &self.0 {
362 MoveObjectType_::SuiBalanceAccumulatorField => Some(GAS::type_tag()),
363 MoveObjectType_::BalanceAccumulatorField(inner) => Some(inner.clone()),
364 _ => None,
365 }
366 }
367
368 pub fn is_balance_accumulator_field(&self) -> bool {
369 matches!(
370 self.0,
371 MoveObjectType_::SuiBalanceAccumulatorField
372 | MoveObjectType_::BalanceAccumulatorField(_)
373 )
374 }
375
376 pub fn is_sui_balance_accumulator_field(&self) -> bool {
377 matches!(self.0, MoveObjectType_::SuiBalanceAccumulatorField)
378 }
379
380 pub fn module_id(&self) -> ModuleId {
381 ModuleId::new(self.address(), self.module().to_owned())
382 }
383
384 pub fn size_for_gas_metering(&self) -> usize {
385 match &self.0 {
387 MoveObjectType_::GasCoin => 1,
388 MoveObjectType_::StakedSui => 1,
389 MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
390 MoveObjectType_::SuiBalanceAccumulatorField => 1,
391 MoveObjectType_::BalanceAccumulatorField(inner) => {
392 bcs::serialized_size(inner).unwrap() + 1
393 }
394 MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
395 }
396 }
397
398 pub fn is_coin(&self) -> bool {
400 match &self.0 {
401 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
402 MoveObjectType_::StakedSui
403 | MoveObjectType_::SuiBalanceAccumulatorField
404 | MoveObjectType_::BalanceAccumulatorField(_)
405 | MoveObjectType_::Other(_) => false,
406 }
407 }
408
409 pub fn is_gas_coin(&self) -> bool {
411 match &self.0 {
412 MoveObjectType_::GasCoin => true,
413 MoveObjectType_::StakedSui
414 | MoveObjectType_::Coin(_)
415 | MoveObjectType_::SuiBalanceAccumulatorField
416 | MoveObjectType_::BalanceAccumulatorField(_)
417 | MoveObjectType_::Other(_) => false,
418 }
419 }
420
421 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
423 match &self.0 {
424 MoveObjectType_::GasCoin => GAS::is_gas_type(t),
425 MoveObjectType_::Coin(c) => t == c,
426 MoveObjectType_::StakedSui
427 | MoveObjectType_::SuiBalanceAccumulatorField
428 | MoveObjectType_::BalanceAccumulatorField(_)
429 | MoveObjectType_::Other(_) => false,
430 }
431 }
432
433 pub fn is_staked_sui(&self) -> bool {
434 match &self.0 {
435 MoveObjectType_::StakedSui => true,
436 MoveObjectType_::GasCoin
437 | MoveObjectType_::Coin(_)
438 | MoveObjectType_::SuiBalanceAccumulatorField
439 | MoveObjectType_::BalanceAccumulatorField(_)
440 | MoveObjectType_::Other(_) => false,
441 }
442 }
443
444 pub fn is_coin_metadata(&self) -> bool {
445 match &self.0 {
446 MoveObjectType_::GasCoin
447 | MoveObjectType_::StakedSui
448 | MoveObjectType_::Coin(_)
449 | MoveObjectType_::SuiBalanceAccumulatorField
450 | MoveObjectType_::BalanceAccumulatorField(_) => false,
451 MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s),
452 }
453 }
454
455 pub fn is_currency(&self) -> bool {
456 match &self.0 {
457 MoveObjectType_::GasCoin
458 | MoveObjectType_::StakedSui
459 | MoveObjectType_::Coin(_)
460 | MoveObjectType_::SuiBalanceAccumulatorField
461 | MoveObjectType_::BalanceAccumulatorField(_) => false,
462 MoveObjectType_::Other(s) => Currency::is_currency(s),
463 }
464 }
465
466 pub fn is_treasury_cap(&self) -> bool {
467 match &self.0 {
468 MoveObjectType_::GasCoin
469 | MoveObjectType_::StakedSui
470 | MoveObjectType_::Coin(_)
471 | MoveObjectType_::SuiBalanceAccumulatorField
472 | MoveObjectType_::BalanceAccumulatorField(_) => false,
473 MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
474 }
475 }
476
477 pub fn is_upgrade_cap(&self) -> bool {
478 self.address() == SUI_FRAMEWORK_ADDRESS
479 && self.module().as_str() == "package"
480 && self.name().as_str() == "UpgradeCap"
481 }
482
483 pub fn is_regulated_coin_metadata(&self) -> bool {
484 self.address() == SUI_FRAMEWORK_ADDRESS
485 && self.module().as_str() == "coin"
486 && self.name().as_str() == "RegulatedCoinMetadata"
487 }
488
489 pub fn is_coin_deny_cap(&self) -> bool {
490 self.address() == SUI_FRAMEWORK_ADDRESS
491 && self.module().as_str() == "coin"
492 && self.name().as_str() == "DenyCap"
493 }
494
495 pub fn is_coin_deny_cap_v2(&self) -> bool {
496 self.address() == SUI_FRAMEWORK_ADDRESS
497 && self.module().as_str() == "coin"
498 && self.name().as_str() == "DenyCapV2"
499 }
500
501 pub fn is_dynamic_field(&self) -> bool {
502 match &self.0 {
503 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
504 false
505 }
506 MoveObjectType_::SuiBalanceAccumulatorField
507 | MoveObjectType_::BalanceAccumulatorField(_) => {
508 true }
510 MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
511 }
512 }
513
514 pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> SuiResult<TypeTag> {
515 match &self.0 {
516 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
517 Err(SuiErrorKind::ObjectDeserializationError {
518 error: "Error extracting dynamic object name from specialized object type"
519 .to_string(),
520 }
521 .into())
522 }
523 MoveObjectType_::SuiBalanceAccumulatorField
524 | MoveObjectType_::BalanceAccumulatorField(_) => {
525 let struct_tag: StructTag = self.clone().into();
526 DynamicFieldInfo::try_extract_field_name(&struct_tag, type_)
527 }
528 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
529 }
530 }
531
532 pub fn try_extract_field_value(&self) -> SuiResult<TypeTag> {
533 match &self.0 {
534 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
535 Err(SuiErrorKind::ObjectDeserializationError {
536 error: "Error extracting dynamic object value from specialized object type"
537 .to_string(),
538 }
539 .into())
540 }
541 MoveObjectType_::SuiBalanceAccumulatorField
542 | MoveObjectType_::BalanceAccumulatorField(_) => {
543 let struct_tag: StructTag = self.clone().into();
544 DynamicFieldInfo::try_extract_field_value(&struct_tag)
545 }
546 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
547 }
548 }
549
550 pub fn is(&self, s: &StructTag) -> bool {
551 match &self.0 {
552 MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
553 MoveObjectType_::StakedSui => StakedSui::is_staked_sui(s),
554 MoveObjectType_::Coin(inner) => {
555 Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
556 }
557 MoveObjectType_::SuiBalanceAccumulatorField => {
558 is_balance_accumulator_field(s)
559 && extract_balance_type_from_field(s)
560 .map(|t| GAS::is_gas_type(&t))
561 .unwrap_or(false)
562 }
563 MoveObjectType_::BalanceAccumulatorField(inner) => {
564 is_balance_accumulator_field(s)
565 && extract_balance_type_from_field(s)
566 .map(|t| &t == inner)
567 .unwrap_or(false)
568 }
569 MoveObjectType_::Other(o) => s == o,
570 }
571 }
572
573 pub fn other(&self) -> Option<&StructTag> {
574 if let MoveObjectType_::Other(s) = &self.0 {
575 Some(s)
576 } else {
577 None
578 }
579 }
580
581 pub fn to_canonical_string(&self, with_prefix: bool) -> String {
583 StructTag::from(self.clone()).to_canonical_string(with_prefix)
584 }
585
586 fn balance_accumulator_field_type_params(inner_type: TypeTag) -> Vec<TypeTag> {
589 use crate::accumulator_root::{AccumulatorKey, U128};
590 let balance_type = Balance::type_tag(inner_type);
591 let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
592 let u128_type = U128::get_type_tag();
593 vec![key_type, u128_type]
594 }
595
596 fn balance_accumulator_field_struct_tag(inner_type: TypeTag) -> StructTag {
598 use crate::accumulator_root::{AccumulatorKey, U128};
599 let balance_type = Balance::type_tag(inner_type);
600 let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
601 let u128_type = U128::get_type_tag();
602 DynamicFieldInfo::dynamic_field_type(key_type, u128_type)
603 }
604}
605
606impl From<StructTag> for MoveObjectType {
607 fn from(mut s: StructTag) -> Self {
608 Self(if GasCoin::is_gas_coin(&s) {
609 MoveObjectType_::GasCoin
610 } else if Coin::is_coin(&s) {
611 MoveObjectType_::Coin(s.type_params.pop().unwrap())
613 } else if StakedSui::is_staked_sui(&s) {
614 MoveObjectType_::StakedSui
615 } else if is_balance_accumulator_field(&s) {
616 if let Some(balance_type) = extract_balance_type_from_field(&s) {
617 if GAS::is_gas_type(&balance_type) {
618 MoveObjectType_::SuiBalanceAccumulatorField
619 } else {
620 MoveObjectType_::BalanceAccumulatorField(balance_type)
621 }
622 } else {
623 MoveObjectType_::Other(s)
624 }
625 } else {
626 MoveObjectType_::Other(s)
627 })
628 }
629}
630
631impl From<MoveObjectType> for StructTag {
632 fn from(t: MoveObjectType) -> Self {
633 match t.0 {
634 MoveObjectType_::GasCoin => GasCoin::type_(),
635 MoveObjectType_::StakedSui => StakedSui::type_(),
636 MoveObjectType_::Coin(inner) => Coin::type_(inner),
637 MoveObjectType_::SuiBalanceAccumulatorField => {
638 MoveObjectType::balance_accumulator_field_struct_tag(GAS::type_tag())
639 }
640 MoveObjectType_::BalanceAccumulatorField(inner) => {
641 MoveObjectType::balance_accumulator_field_struct_tag(inner)
642 }
643 MoveObjectType_::Other(s) => s,
644 }
645 }
646}
647
648impl From<MoveObjectType> for TypeTag {
649 fn from(o: MoveObjectType) -> TypeTag {
650 let s: StructTag = o.into();
651 TypeTag::Struct(Box::new(s))
652 }
653}
654
655pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
657 use TypeTag as T;
658
659 match t {
660 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
661 T::Vector(inner) => is_primitive_type_tag(inner),
662 T::Struct(st) => {
663 let StructTag {
664 address,
665 module,
666 name,
667 type_params: type_args,
668 } = &**st;
669 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
670 if resolved_struct == RESOLVED_SUI_ID {
672 return true;
673 }
674 if resolved_struct == RESOLVED_UTF8_STR {
676 return true;
677 }
678 if resolved_struct == RESOLVED_ASCII_STR {
680 return true;
681 }
682 resolved_struct == RESOLVED_STD_OPTION
684 && type_args.len() == 1
685 && is_primitive_type_tag(&type_args[0])
686 }
687 T::Signer => false,
688 }
689}
690
691#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
693pub enum ObjectType {
694 Package,
696 Struct(MoveObjectType),
698}
699
700impl From<&Object> for ObjectType {
701 fn from(o: &Object) -> Self {
702 o.data
703 .type_()
704 .map(|t| ObjectType::Struct(t.clone()))
705 .unwrap_or(ObjectType::Package)
706 }
707}
708
709impl TryFrom<ObjectType> for StructTag {
710 type Error = anyhow::Error;
711
712 fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
713 match o {
714 ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
715 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
716 }
717 }
718}
719
720impl FromStr for ObjectType {
721 type Err = anyhow::Error;
722
723 fn from_str(s: &str) -> Result<Self, Self::Err> {
724 if s.to_lowercase() == PACKAGE {
725 Ok(ObjectType::Package)
726 } else {
727 let tag = parse_sui_struct_tag(s)?;
728 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
729 }
730 }
731}
732
733#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
734pub struct ObjectInfo {
735 pub object_id: ObjectID,
736 pub version: SequenceNumber,
737 pub digest: ObjectDigest,
738 pub type_: ObjectType,
739 pub owner: Owner,
740 pub previous_transaction: TransactionDigest,
741}
742
743impl ObjectInfo {
744 pub fn new(oref: &ObjectRef, o: &Object) -> Self {
745 let (object_id, version, digest) = *oref;
746 Self {
747 object_id,
748 version,
749 digest,
750 type_: o.into(),
751 owner: o.owner.clone(),
752 previous_transaction: o.previous_transaction,
753 }
754 }
755
756 pub fn from_object(object: &Object) -> Self {
757 Self {
758 object_id: object.id(),
759 version: object.version(),
760 digest: object.digest(),
761 type_: object.into(),
762 owner: object.owner.clone(),
763 previous_transaction: object.previous_transaction,
764 }
765 }
766}
767const PACKAGE: &str = "package";
768impl ObjectType {
769 pub fn is_gas_coin(&self) -> bool {
770 matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
771 }
772
773 pub fn is_coin(&self) -> bool {
774 matches!(self, ObjectType::Struct(s) if s.is_coin())
775 }
776
777 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
779 matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
780 }
781
782 pub fn is_package(&self) -> bool {
783 matches!(self, ObjectType::Package)
784 }
785}
786
787impl From<ObjectInfo> for ObjectRef {
788 fn from(info: ObjectInfo) -> Self {
789 (info.object_id, info.version, info.digest)
790 }
791}
792
793impl From<&ObjectInfo> for ObjectRef {
794 fn from(info: &ObjectInfo) -> Self {
795 (info.object_id, info.version, info.digest)
796 }
797}
798
799pub const SUI_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
800
801#[serde_as]
802#[derive(
803 Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
804)]
805#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
806pub struct SuiAddress(
807 #[schemars(with = "Hex")]
808 #[serde_as(as = "Readable<Hex, _>")]
809 [u8; SUI_ADDRESS_LENGTH],
810);
811
812impl SuiAddress {
813 pub const ZERO: Self = Self([0u8; SUI_ADDRESS_LENGTH]);
814
815 pub fn to_vec(&self) -> Vec<u8> {
817 self.0.to_vec()
818 }
819
820 pub fn random_for_testing_only() -> Self {
822 AccountAddress::random().into()
823 }
824
825 pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
826 let buf: [u8; SUI_ADDRESS_LENGTH] = rng.r#gen();
827 Self(buf)
828 }
829
830 pub fn optional_address_as_hex<S>(
832 key: &Option<SuiAddress>,
833 serializer: S,
834 ) -> Result<S::Ok, S::Error>
835 where
836 S: serde::ser::Serializer,
837 {
838 serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
839 }
840
841 pub fn optional_address_from_hex<'de, D>(
843 deserializer: D,
844 ) -> Result<Option<SuiAddress>, D::Error>
845 where
846 D: serde::de::Deserializer<'de>,
847 {
848 let s = String::deserialize(deserializer)?;
849 let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
850 Ok(Some(value))
851 }
852
853 pub fn to_inner(self) -> [u8; SUI_ADDRESS_LENGTH] {
855 self.0
856 }
857
858 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, SuiError> {
860 <[u8; SUI_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
861 .map_err(|_| SuiErrorKind::InvalidAddress.into())
862 .map(SuiAddress)
863 }
864
865 pub fn try_from_padded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
869 Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
870 }
871
872 pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
874 let mut hasher = DefaultHash::default();
875 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
876 let iss_bytes = inputs.get_iss().as_bytes();
877 hasher.update([iss_bytes.len() as u8]);
878 hasher.update(iss_bytes);
879 hasher.update(inputs.get_address_seed().unpadded());
880 Ok(SuiAddress(hasher.finalize().digest))
881 }
882}
883
884impl From<ObjectID> for SuiAddress {
885 fn from(object_id: ObjectID) -> SuiAddress {
886 Self(object_id.into_bytes())
887 }
888}
889
890impl From<AccountAddress> for SuiAddress {
891 fn from(address: AccountAddress) -> SuiAddress {
892 Self(address.into_bytes())
893 }
894}
895
896impl TryFrom<&[u8]> for SuiAddress {
897 type Error = SuiError;
898
899 fn try_from(bytes: &[u8]) -> Result<Self, SuiError> {
901 Self::from_bytes(bytes)
902 }
903}
904
905impl TryFrom<Vec<u8>> for SuiAddress {
906 type Error = SuiError;
907
908 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
910 Self::from_bytes(bytes)
911 }
912}
913
914impl AsRef<[u8]> for SuiAddress {
915 fn as_ref(&self) -> &[u8] {
916 &self.0[..]
917 }
918}
919
920impl FromStr for SuiAddress {
921 type Err = anyhow::Error;
922 fn from_str(s: &str) -> Result<Self, Self::Err> {
923 decode_bytes_hex(s).map_err(|e| anyhow!(e))
924 }
925}
926
927impl<T: SuiPublicKey> From<&T> for SuiAddress {
928 fn from(pk: &T) -> Self {
929 let mut hasher = DefaultHash::default();
930 hasher.update([T::SIGNATURE_SCHEME.flag()]);
931 hasher.update(pk);
932 let g_arr = hasher.finalize();
933 SuiAddress(g_arr.digest)
934 }
935}
936
937impl From<&PublicKey> for SuiAddress {
938 fn from(pk: &PublicKey) -> Self {
939 let mut hasher = DefaultHash::default();
940 hasher.update([pk.flag()]);
941 hasher.update(pk);
942 let g_arr = hasher.finalize();
943 SuiAddress(g_arr.digest)
944 }
945}
946
947impl From<&MultiSigPublicKey> for SuiAddress {
948 fn from(multisig_pk: &MultiSigPublicKey) -> Self {
957 let mut hasher = DefaultHash::default();
958 hasher.update([SignatureScheme::MultiSig.flag()]);
959 hasher.update(multisig_pk.threshold().to_le_bytes());
960 multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
961 hasher.update([pk.flag()]);
962 hasher.update(pk.as_ref());
963 hasher.update(w.to_le_bytes());
964 });
965 SuiAddress(hasher.finalize().digest)
966 }
967}
968
969impl TryFrom<&ZkLoginAuthenticator> for SuiAddress {
972 type Error = SuiError;
973 fn try_from(authenticator: &ZkLoginAuthenticator) -> SuiResult<Self> {
974 SuiAddress::try_from_unpadded(&authenticator.inputs)
975 }
976}
977
978impl TryFrom<&GenericSignature> for SuiAddress {
979 type Error = SuiError;
980 fn try_from(sig: &GenericSignature) -> SuiResult<Self> {
982 match sig {
983 GenericSignature::Signature(sig) => {
984 let scheme = sig.scheme();
985 let pub_key_bytes = sig.public_key_bytes();
986 let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
987 SuiErrorKind::InvalidSignature {
988 error: "Cannot parse pubkey".to_string(),
989 }
990 })?;
991 Ok(SuiAddress::from(&pub_key))
992 }
993 GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
994 GenericSignature::MultiSigLegacy(ms) => {
995 Ok(crate::multisig::MultiSig::try_from(ms.clone())
996 .map_err(|_| SuiErrorKind::InvalidSignature {
997 error: "Invalid legacy multisig".to_string(),
998 })?
999 .get_pk()
1000 .into())
1001 }
1002 GenericSignature::ZkLoginAuthenticator(zklogin) => {
1003 SuiAddress::try_from_unpadded(&zklogin.inputs)
1004 }
1005 GenericSignature::PasskeyAuthenticator(s) => Ok(SuiAddress::from(&s.get_pk()?)),
1006 }
1007 }
1008}
1009
1010impl fmt::Display for SuiAddress {
1011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012 write!(f, "0x{}", Hex::encode(self.0))
1013 }
1014}
1015
1016impl fmt::Debug for SuiAddress {
1017 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1018 write!(f, "0x{}", Hex::encode(self.0))
1019 }
1020}
1021
1022pub fn dbg_addr(name: u8) -> SuiAddress {
1024 let addr = [name; SUI_ADDRESS_LENGTH];
1025 SuiAddress(addr)
1026}
1027
1028#[derive(
1029 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
1030)]
1031pub struct ExecutionDigests {
1032 pub transaction: TransactionDigest,
1033 pub effects: TransactionEffectsDigest,
1034}
1035
1036impl ExecutionDigests {
1037 pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
1038 Self {
1039 transaction,
1040 effects,
1041 }
1042 }
1043
1044 pub fn random() -> Self {
1045 Self {
1046 transaction: TransactionDigest::random(),
1047 effects: TransactionEffectsDigest::random(),
1048 }
1049 }
1050}
1051
1052#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
1053pub struct ExecutionData {
1054 pub transaction: Transaction,
1055 pub effects: TransactionEffects,
1056}
1057
1058impl ExecutionData {
1059 pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
1060 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1061 Self {
1062 transaction,
1063 effects,
1064 }
1065 }
1066
1067 pub fn digests(&self) -> ExecutionDigests {
1068 self.effects.execution_digests()
1069 }
1070}
1071
1072#[derive(Clone, Eq, PartialEq, Debug)]
1073pub struct VerifiedExecutionData {
1074 pub transaction: VerifiedTransaction,
1075 pub effects: TransactionEffects,
1076}
1077
1078impl VerifiedExecutionData {
1079 pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
1080 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1081 Self {
1082 transaction,
1083 effects,
1084 }
1085 }
1086
1087 pub fn new_unchecked(data: ExecutionData) -> Self {
1088 Self {
1089 transaction: VerifiedTransaction::new_unchecked(data.transaction),
1090 effects: data.effects,
1091 }
1092 }
1093
1094 pub fn into_inner(self) -> ExecutionData {
1095 ExecutionData {
1096 transaction: self.transaction.into_inner(),
1097 effects: self.effects,
1098 }
1099 }
1100
1101 pub fn digests(&self) -> ExecutionDigests {
1102 self.effects.execution_digests()
1103 }
1104}
1105
1106pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
1107pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
1108pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
1109 &MOVE_STDLIB_ADDRESS,
1110 STD_OPTION_MODULE_NAME,
1111 STD_OPTION_STRUCT_NAME,
1112);
1113
1114pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
1115pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
1116pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1117 &MOVE_STDLIB_ADDRESS,
1118 STD_ASCII_MODULE_NAME,
1119 STD_ASCII_STRUCT_NAME,
1120);
1121
1122pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
1123pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
1124pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1125 &MOVE_STDLIB_ADDRESS,
1126 STD_UTF8_MODULE_NAME,
1127 STD_UTF8_STRUCT_NAME,
1128);
1129
1130pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
1131pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
1132pub const RESOLVED_TX_CONTEXT: (&AccountAddress, &IdentStr, &IdentStr) = (
1133 &SUI_FRAMEWORK_ADDRESS,
1134 TX_CONTEXT_MODULE_NAME,
1135 TX_CONTEXT_STRUCT_NAME,
1136);
1137
1138pub const URL_MODULE_NAME: &IdentStr = ident_str!("url");
1139pub const URL_STRUCT_NAME: &IdentStr = ident_str!("Url");
1140
1141pub const VEC_MAP_MODULE_NAME: &IdentStr = ident_str!("vec_map");
1142pub const VEC_MAP_STRUCT_NAME: &IdentStr = ident_str!("VecMap");
1143pub const VEC_MAP_ENTRY_STRUCT_NAME: &IdentStr = ident_str!("Entry");
1144
1145pub fn move_ascii_str_layout() -> A::MoveStructLayout {
1146 A::MoveStructLayout {
1147 type_: StructTag {
1148 address: MOVE_STDLIB_ADDRESS,
1149 module: STD_ASCII_MODULE_NAME.to_owned(),
1150 name: STD_ASCII_STRUCT_NAME.to_owned(),
1151 type_params: vec![],
1152 },
1153 fields: vec![A::MoveFieldLayout::new(
1154 ident_str!("bytes").into(),
1155 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1156 )],
1157 }
1158}
1159
1160pub fn move_utf8_str_layout() -> A::MoveStructLayout {
1161 A::MoveStructLayout {
1162 type_: StructTag {
1163 address: MOVE_STDLIB_ADDRESS,
1164 module: STD_UTF8_MODULE_NAME.to_owned(),
1165 name: STD_UTF8_STRUCT_NAME.to_owned(),
1166 type_params: vec![],
1167 },
1168 fields: vec![A::MoveFieldLayout::new(
1169 ident_str!("bytes").into(),
1170 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1171 )],
1172 }
1173}
1174
1175pub fn url_layout() -> A::MoveStructLayout {
1176 A::MoveStructLayout {
1177 type_: StructTag {
1178 address: SUI_FRAMEWORK_ADDRESS,
1179 module: URL_MODULE_NAME.to_owned(),
1180 name: URL_STRUCT_NAME.to_owned(),
1181 type_params: vec![],
1182 },
1183 fields: vec![A::MoveFieldLayout::new(
1184 ident_str!("url").to_owned(),
1185 A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1186 )],
1187 }
1188}
1189
1190#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1200pub struct MoveLegacyTxContext {
1201 sender: AccountAddress,
1203 digest: Vec<u8>,
1205 epoch: EpochId,
1207 epoch_timestamp_ms: CheckpointTimestamp,
1209 ids_created: u64,
1211}
1212
1213impl From<&TxContext> for MoveLegacyTxContext {
1214 fn from(tx_context: &TxContext) -> Self {
1215 Self {
1216 sender: tx_context.sender,
1217 digest: tx_context.digest.clone(),
1218 epoch: tx_context.epoch,
1219 epoch_timestamp_ms: tx_context.epoch_timestamp_ms,
1220 ids_created: tx_context.ids_created,
1221 }
1222 }
1223}
1224
1225#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1228pub struct TxContext {
1229 sender: AccountAddress,
1231 digest: Vec<u8>,
1233 epoch: EpochId,
1235 epoch_timestamp_ms: CheckpointTimestamp,
1237 ids_created: u64,
1239 rgp: u64,
1241 gas_price: u64,
1243 gas_budget: u64,
1245 sponsor: Option<AccountAddress>,
1247 is_native: bool,
1250}
1251
1252#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1253pub enum TxContextKind {
1254 None,
1256 Mutable,
1258 Immutable,
1260}
1261
1262impl TxContext {
1263 pub fn new(
1264 sender: &SuiAddress,
1265 digest: &TransactionDigest,
1266 epoch_data: &EpochData,
1267 rgp: u64,
1268 gas_price: u64,
1269 gas_budget: u64,
1270 sponsor: Option<SuiAddress>,
1271 protocol_config: &ProtocolConfig,
1272 ) -> Self {
1273 Self::new_from_components(
1274 sender,
1275 digest,
1276 &epoch_data.epoch_id(),
1277 epoch_data.epoch_start_timestamp(),
1278 rgp,
1279 gas_price,
1280 gas_budget,
1281 sponsor,
1282 protocol_config,
1283 )
1284 }
1285
1286 pub fn new_from_components(
1287 sender: &SuiAddress,
1288 digest: &TransactionDigest,
1289 epoch_id: &EpochId,
1290 epoch_timestamp_ms: u64,
1291 rgp: u64,
1292 gas_price: u64,
1293 gas_budget: u64,
1294 sponsor: Option<SuiAddress>,
1295 protocol_config: &ProtocolConfig,
1296 ) -> Self {
1297 Self {
1298 sender: AccountAddress::new(sender.0),
1299 digest: digest.into_inner().to_vec(),
1300 epoch: *epoch_id,
1301 epoch_timestamp_ms,
1302 ids_created: 0,
1303 rgp,
1304 gas_price,
1305 gas_budget,
1306 sponsor: sponsor.map(|s| s.into()),
1307 is_native: protocol_config.move_native_context(),
1308 }
1309 }
1310
1311 pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1313 use SignatureToken as S;
1314 let (kind, s) = match s {
1315 S::MutableReference(s) => (TxContextKind::Mutable, s),
1316 S::Reference(s) => (TxContextKind::Immutable, s),
1317 _ => return TxContextKind::None,
1318 };
1319
1320 let S::Datatype(idx) = &**s else {
1321 return TxContextKind::None;
1322 };
1323
1324 if resolve_struct(view, *idx) == RESOLVED_TX_CONTEXT {
1325 kind
1326 } else {
1327 TxContextKind::None
1328 }
1329 }
1330
1331 pub fn type_() -> StructTag {
1332 StructTag {
1333 address: SUI_FRAMEWORK_ADDRESS,
1334 module: TX_CONTEXT_MODULE_NAME.to_owned(),
1335 name: TX_CONTEXT_STRUCT_NAME.to_owned(),
1336 type_params: vec![],
1337 }
1338 }
1339
1340 pub fn epoch(&self) -> EpochId {
1341 self.epoch
1342 }
1343
1344 pub fn sender(&self) -> SuiAddress {
1345 self.sender.into()
1346 }
1347
1348 pub fn epoch_timestamp_ms(&self) -> u64 {
1349 self.epoch_timestamp_ms
1350 }
1351
1352 pub fn digest(&self) -> TransactionDigest {
1354 TransactionDigest::new(self.digest.clone().try_into().unwrap())
1355 }
1356
1357 pub fn sponsor(&self) -> Option<SuiAddress> {
1358 self.sponsor.map(SuiAddress::from)
1359 }
1360
1361 pub fn rgp(&self) -> u64 {
1362 self.rgp
1363 }
1364
1365 pub fn gas_price(&self) -> u64 {
1366 self.gas_price
1367 }
1368
1369 pub fn gas_budget(&self) -> u64 {
1370 self.gas_budget
1371 }
1372
1373 pub fn ids_created(&self) -> u64 {
1374 self.ids_created
1375 }
1376
1377 pub fn fresh_id(&mut self) -> ObjectID {
1379 let id = ObjectID::derive_id(self.digest(), self.ids_created);
1380
1381 self.ids_created += 1;
1382 id
1383 }
1384
1385 pub fn to_bcs_legacy_context(&self) -> Vec<u8> {
1386 let move_context: MoveLegacyTxContext = if self.is_native {
1387 let tx_context = &TxContext {
1388 sender: AccountAddress::ZERO,
1389 digest: self.digest.clone(),
1390 epoch: 0,
1391 epoch_timestamp_ms: 0,
1392 ids_created: 0,
1393 rgp: 0,
1394 gas_price: 0,
1395 gas_budget: 0,
1396 sponsor: None,
1397 is_native: true,
1398 };
1399 tx_context.into()
1400 } else {
1401 self.into()
1402 };
1403 bcs::to_bytes(&move_context).unwrap()
1404 }
1405
1406 pub fn to_vec(&self) -> Vec<u8> {
1407 bcs::to_bytes(&self).unwrap()
1408 }
1409
1410 pub fn update_state(&mut self, other: MoveLegacyTxContext) -> Result<(), ExecutionError> {
1415 if !self.is_native {
1416 if self.sender != other.sender
1417 || self.digest != other.digest
1418 || other.ids_created < self.ids_created
1419 {
1420 return Err(ExecutionError::new_with_source(
1421 ExecutionErrorKind::InvariantViolation,
1422 "Immutable fields for TxContext changed",
1423 ));
1424 }
1425 self.ids_created = other.ids_created;
1426 }
1427 Ok(())
1428 }
1429
1430 pub fn replace(
1434 &mut self,
1435 sender: AccountAddress,
1436 tx_hash: Vec<u8>,
1437 epoch: u64,
1438 epoch_timestamp_ms: u64,
1439 ids_created: u64,
1440 rgp: u64,
1441 gas_price: u64,
1442 gas_budget: u64,
1443 sponsor: Option<AccountAddress>,
1444 ) {
1445 self.sender = sender;
1446 self.digest = tx_hash;
1447 self.epoch = epoch;
1448 self.epoch_timestamp_ms = epoch_timestamp_ms;
1449 self.ids_created = ids_created;
1450 self.rgp = rgp;
1451 self.gas_price = gas_price;
1452 self.gas_budget = gas_budget;
1453 self.sponsor = sponsor;
1454 }
1455}
1456
1457impl SequenceNumber {
1459 pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1460 pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1461 pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1462 pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1463 pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1464 SequenceNumber(SequenceNumber::MAX.value() + 3);
1465 pub const UNKNOWN: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 4);
1468
1469 pub const fn new() -> Self {
1470 SequenceNumber(0)
1471 }
1472
1473 pub const fn value(&self) -> u64 {
1474 self.0
1475 }
1476
1477 pub const fn from_u64(u: u64) -> Self {
1478 SequenceNumber(u)
1479 }
1480
1481 pub fn increment(&mut self) {
1482 assert_ne!(self.0, u64::MAX);
1483 self.0 += 1;
1484 }
1485
1486 pub fn increment_to(&mut self, next: SequenceNumber) {
1487 debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1488 *self = next;
1489 }
1490
1491 pub fn decrement(&mut self) {
1492 assert_ne!(self.0, 0);
1493 self.0 -= 1;
1494 }
1495
1496 pub fn decrement_to(&mut self, prev: SequenceNumber) {
1497 debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1498 *self = prev;
1499 }
1500
1501 #[must_use]
1504 pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1505 let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1506
1507 assert_ne!(max_input.0, u64::MAX);
1512
1513 SequenceNumber(max_input.0 + 1)
1514 }
1515
1516 pub fn is_cancelled(&self) -> bool {
1517 self == &SequenceNumber::CANCELLED_READ
1518 || self == &SequenceNumber::CONGESTED
1519 || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1520 }
1521
1522 pub fn is_valid(&self) -> bool {
1523 self < &SequenceNumber::MAX
1524 }
1525}
1526
1527impl From<SequenceNumber> for u64 {
1528 fn from(val: SequenceNumber) -> Self {
1529 val.0
1530 }
1531}
1532
1533impl From<u64> for SequenceNumber {
1534 fn from(value: u64) -> Self {
1535 SequenceNumber(value)
1536 }
1537}
1538
1539impl From<SequenceNumber> for usize {
1540 fn from(value: SequenceNumber) -> Self {
1541 value.0 as usize
1542 }
1543}
1544
1545impl ObjectID {
1546 pub const LENGTH: usize = AccountAddress::LENGTH;
1548 pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1550 pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1551 pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1553 Self(AccountAddress::new(obj_id))
1554 }
1555
1556 pub const fn from_address(addr: AccountAddress) -> Self {
1558 Self(addr)
1559 }
1560
1561 pub fn random() -> Self {
1563 Self::from(AccountAddress::random())
1564 }
1565
1566 pub fn random_from_rng<R>(rng: &mut R) -> Self
1568 where
1569 R: AllowedRng,
1570 {
1571 let buf: [u8; Self::LENGTH] = rng.r#gen();
1572 ObjectID::new(buf)
1573 }
1574
1575 pub fn to_vec(&self) -> Vec<u8> {
1577 self.0.to_vec()
1578 }
1579
1580 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1582 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1583 .map_err(|_| ObjectIDParseError::TryFromSliceError)
1584 .map(ObjectID::new)
1585 }
1586
1587 pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1589 self.0.into_bytes()
1590 }
1591
1592 pub const fn from_single_byte(byte: u8) -> ObjectID {
1594 let mut bytes = [0u8; Self::LENGTH];
1595 bytes[Self::LENGTH - 1] = byte;
1596 ObjectID::new(bytes)
1597 }
1598
1599 pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1602 if !literal.starts_with("0x") {
1603 return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1604 }
1605
1606 let hex_len = literal.len() - 2;
1607
1608 if hex_len < Self::LENGTH * 2 {
1610 let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1611 for _ in 0..Self::LENGTH * 2 - hex_len {
1612 hex_str.push('0');
1613 }
1614 hex_str.push_str(&literal[2..]);
1615 Self::from_str(&hex_str)
1616 } else {
1617 Self::from_str(&literal[2..])
1618 }
1619 }
1620
1621 pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1624 let mut hasher = DefaultHash::default();
1625 hasher.update([HashingIntentScope::RegularObjectId as u8]);
1626 hasher.update(digest);
1627 hasher.update(creation_num.to_le_bytes());
1628 let hash = hasher.finalize();
1629
1630 ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1633 }
1634
1635 pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1637 let mut curr_vec = self.to_vec();
1638 let mut step_copy = step;
1639
1640 let mut carry = 0;
1641 for idx in (0..Self::LENGTH).rev() {
1642 if step_copy == 0 {
1643 break;
1645 }
1646 let g = (step_copy % 0x100) as u16;
1648 step_copy >>= 8;
1650 let mut val = curr_vec[idx] as u16;
1651 (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1652 curr_vec[idx] = val as u8;
1653 }
1654
1655 if carry > 0 {
1656 return Err(anyhow!("Increment will cause overflow"));
1657 }
1658 ObjectID::try_from(curr_vec).map_err(|w| w.into())
1659 }
1660
1661 pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1663 let mut prev_val = self.to_vec();
1664 let mx = [0xFF; Self::LENGTH];
1665
1666 if prev_val == mx {
1667 return Err(anyhow!("Increment will cause overflow"));
1668 }
1669
1670 for idx in (0..Self::LENGTH).rev() {
1672 if prev_val[idx] == 0xFF {
1673 prev_val[idx] = 0;
1674 } else {
1675 prev_val[idx] += 1;
1676 break;
1677 };
1678 }
1679 ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1680 }
1681
1682 pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1684 let mut ret = Vec::new();
1685 let mut prev = offset;
1686 for o in 0..count {
1687 if o != 0 {
1688 prev = prev.next_increment()?;
1689 }
1690 ret.push(prev);
1691 }
1692 Ok(ret)
1693 }
1694
1695 pub fn to_hex_uncompressed(&self) -> String {
1698 format!("{self}")
1699 }
1700
1701 pub fn is_clock(&self) -> bool {
1702 *self == SUI_CLOCK_OBJECT_ID
1703 }
1704}
1705
1706impl From<SuiAddress> for ObjectID {
1707 fn from(address: SuiAddress) -> ObjectID {
1708 let tmp: AccountAddress = address.into();
1709 tmp.into()
1710 }
1711}
1712
1713impl From<AccountAddress> for ObjectID {
1714 fn from(address: AccountAddress) -> Self {
1715 Self(address)
1716 }
1717}
1718
1719impl fmt::Display for ObjectID {
1720 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1721 write!(f, "0x{}", Hex::encode(self.0))
1722 }
1723}
1724
1725impl fmt::Debug for ObjectID {
1726 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1727 write!(f, "0x{}", Hex::encode(self.0))
1728 }
1729}
1730
1731impl AsRef<[u8]> for ObjectID {
1732 fn as_ref(&self) -> &[u8] {
1733 self.0.as_slice()
1734 }
1735}
1736
1737impl TryFrom<&[u8]> for ObjectID {
1738 type Error = ObjectIDParseError;
1739
1740 fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1742 Self::from_bytes(bytes)
1743 }
1744}
1745
1746impl TryFrom<Vec<u8>> for ObjectID {
1747 type Error = ObjectIDParseError;
1748
1749 fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1751 Self::from_bytes(bytes)
1752 }
1753}
1754
1755impl FromStr for ObjectID {
1756 type Err = ObjectIDParseError;
1757
1758 fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1760 decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1761 }
1762}
1763
1764impl std::ops::Deref for ObjectID {
1765 type Target = AccountAddress;
1766
1767 fn deref(&self) -> &Self::Target {
1768 &self.0
1769 }
1770}
1771
1772pub fn dbg_object_id(name: u8) -> ObjectID {
1774 ObjectID::new([name; ObjectID::LENGTH])
1775}
1776
1777#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1778pub enum ObjectIDParseError {
1779 #[error("ObjectID hex literal must start with 0x")]
1780 HexLiteralPrefixMissing,
1781
1782 #[error("Could not convert from bytes slice")]
1783 TryFromSliceError,
1784}
1785
1786impl From<ObjectID> for AccountAddress {
1787 fn from(obj_id: ObjectID) -> Self {
1788 obj_id.0
1789 }
1790}
1791
1792impl From<SuiAddress> for AccountAddress {
1793 fn from(address: SuiAddress) -> Self {
1794 Self::new(address.0)
1795 }
1796}
1797
1798struct HexAccountAddress;
1800
1801impl SerializeAs<AccountAddress> for HexAccountAddress {
1802 fn serialize_as<S>(value: &AccountAddress, serializer: S) -> Result<S::Ok, S::Error>
1803 where
1804 S: Serializer,
1805 {
1806 Hex::serialize_as(value, serializer)
1807 }
1808}
1809
1810impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress {
1811 fn deserialize_as<D>(deserializer: D) -> Result<AccountAddress, D::Error>
1812 where
1813 D: Deserializer<'de>,
1814 {
1815 let s = String::deserialize(deserializer)?;
1816 if s.starts_with("0x") {
1817 AccountAddress::from_hex_literal(&s)
1818 } else {
1819 AccountAddress::from_hex(&s)
1820 }
1821 .map_err(to_custom_deser_error::<'de, D, _>)
1822 }
1823}
1824
1825impl fmt::Display for MoveObjectType {
1826 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1827 let s: StructTag = self.clone().into();
1828 write!(
1829 f,
1830 "{}",
1831 to_sui_struct_tag_string(&s).map_err(fmt::Error::custom)?
1832 )
1833 }
1834}
1835
1836impl fmt::Display for ObjectType {
1837 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1838 match self {
1839 ObjectType::Package => write!(f, "{}", PACKAGE),
1840 ObjectType::Struct(t) => write!(f, "{}", t),
1841 }
1842 }
1843}
1844
1845#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1851#[serde(try_from = "Vec<T>")]
1852pub struct SizeOneVec<T> {
1853 e: T,
1854}
1855
1856impl<T> SizeOneVec<T> {
1857 pub fn new(e: T) -> Self {
1858 Self { e }
1859 }
1860
1861 pub fn element(&self) -> &T {
1862 &self.e
1863 }
1864
1865 pub fn element_mut(&mut self) -> &mut T {
1866 &mut self.e
1867 }
1868
1869 pub fn into_inner(self) -> T {
1870 self.e
1871 }
1872
1873 pub fn iter(&self) -> std::iter::Once<&T> {
1874 std::iter::once(&self.e)
1875 }
1876}
1877
1878impl<T> Serialize for SizeOneVec<T>
1879where
1880 T: Serialize,
1881{
1882 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1883 where
1884 S: Serializer,
1885 {
1886 let mut seq = serializer.serialize_seq(Some(1))?;
1887 seq.serialize_element(&self.e)?;
1888 seq.end()
1889 }
1890}
1891
1892impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1893 type Error = anyhow::Error;
1894
1895 fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1896 if v.len() != 1 {
1897 Err(anyhow!("Expected a vec of size 1"))
1898 } else {
1899 Ok(SizeOneVec {
1900 e: v.pop().unwrap(),
1901 })
1902 }
1903 }
1904}
1905
1906#[test]
1907fn test_size_one_vec_is_transparent() {
1908 let regular = vec![42u8];
1909 let size_one = SizeOneVec::new(42u8);
1910
1911 let regular_ser = bcs::to_bytes(®ular).unwrap();
1913 let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(®ular_ser).unwrap();
1914 assert_eq!(size_one, size_one_deser);
1915
1916 let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1918 let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1919 assert_eq!(regular_deser, vec![43u8]);
1920
1921 let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1923 bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1924
1925 let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1926 bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1927}