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