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::collections::BTreeMap;
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(Debug, Clone, Copy, PartialEq, Eq)]
232pub struct ConsensusObjectVersion {
233 pub initial_shared_version: SequenceNumber,
234 pub version: SequenceNumber,
235}
236
237#[derive(Debug, Clone, Default, PartialEq, Eq)]
240pub struct SystemObjectVersions(BTreeMap<ObjectID, ConsensusObjectVersion>);
241
242impl SystemObjectVersions {
243 pub fn empty() -> Self {
244 Self::default()
245 }
246
247 pub fn from_map(versions: BTreeMap<ObjectID, ConsensusObjectVersion>) -> Self {
248 for object_id in versions.keys() {
249 Self::assert_implicitly_read(object_id);
250 }
251 Self(versions)
252 }
253
254 pub fn get(&self, object_id: &ObjectID) -> Option<ConsensusObjectVersion> {
255 Self::assert_implicitly_read(object_id);
256 self.0.get(object_id).copied()
257 }
258
259 pub fn insert_for_testing(&mut self, object_id: ObjectID, version: ConsensusObjectVersion) {
260 Self::assert_implicitly_read(&object_id);
261 self.0.insert(object_id, version);
262 }
263
264 fn assert_implicitly_read(object_id: &ObjectID) {
265 assert!(
266 object_id.is_implicitly_read_system_object(),
267 "{object_id} is not an implicitly read system object"
268 );
269 }
270}
271
272#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
277pub struct MoveObjectType(MoveObjectType_);
278
279#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)]
282pub enum MoveObjectType_ {
283 Other(StructTag),
285 GasCoin,
287 StakedSui,
289 Coin(TypeTag),
291 SuiBalanceAccumulatorField,
294 BalanceAccumulatorField(TypeTag),
298 }
302
303impl MoveObjectType {
304 pub fn gas_coin() -> Self {
305 Self(MoveObjectType_::GasCoin)
306 }
307
308 pub fn is_efficient_representation(&self) -> bool {
309 !matches!(self.0, MoveObjectType_::Other(_))
310 }
311
312 pub fn coin(coin_type: TypeTag) -> Self {
313 Self(if GAS::is_gas_type(&coin_type) {
314 MoveObjectType_::GasCoin
315 } else {
316 MoveObjectType_::Coin(coin_type)
317 })
318 }
319
320 pub fn staked_sui() -> Self {
321 Self(MoveObjectType_::StakedSui)
322 }
323
324 pub fn address(&self) -> AccountAddress {
325 match &self.0 {
326 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => SUI_FRAMEWORK_ADDRESS,
327 MoveObjectType_::StakedSui => SUI_SYSTEM_ADDRESS,
328 MoveObjectType_::SuiBalanceAccumulatorField
329 | MoveObjectType_::BalanceAccumulatorField(_) => SUI_FRAMEWORK_ADDRESS,
330 MoveObjectType_::Other(s) => s.address,
331 }
332 }
333
334 pub fn module(&self) -> &IdentStr {
335 match &self.0 {
336 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME,
337 MoveObjectType_::StakedSui => STAKING_POOL_MODULE_NAME,
338 MoveObjectType_::SuiBalanceAccumulatorField
339 | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_MODULE_NAME,
340 MoveObjectType_::Other(s) => &s.module,
341 }
342 }
343
344 pub fn name(&self) -> &IdentStr {
345 match &self.0 {
346 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME,
347 MoveObjectType_::StakedSui => STAKED_SUI_STRUCT_NAME,
348 MoveObjectType_::SuiBalanceAccumulatorField
349 | MoveObjectType_::BalanceAccumulatorField(_) => DYNAMIC_FIELD_FIELD_STRUCT_NAME,
350 MoveObjectType_::Other(s) => &s.name,
351 }
352 }
353
354 pub fn type_params(&self) -> Vec<Cow<'_, TypeTag>> {
355 match &self.0 {
356 MoveObjectType_::GasCoin => vec![Cow::Owned(GAS::type_tag())],
357 MoveObjectType_::StakedSui => vec![],
358 MoveObjectType_::Coin(inner) => vec![Cow::Borrowed(inner)],
359 MoveObjectType_::SuiBalanceAccumulatorField => {
360 Self::balance_accumulator_field_type_params(GAS::type_tag())
361 .into_iter()
362 .map(Cow::Owned)
363 .collect()
364 }
365 MoveObjectType_::BalanceAccumulatorField(inner) => {
366 Self::balance_accumulator_field_type_params(inner.clone())
367 .into_iter()
368 .map(Cow::Owned)
369 .collect()
370 }
371 MoveObjectType_::Other(s) => s.type_params.iter().map(Cow::Borrowed).collect(),
372 }
373 }
374
375 pub fn into_type_params(self) -> Vec<TypeTag> {
376 match self.0 {
377 MoveObjectType_::GasCoin => vec![GAS::type_tag()],
378 MoveObjectType_::StakedSui => vec![],
379 MoveObjectType_::Coin(inner) => vec![inner],
380 MoveObjectType_::SuiBalanceAccumulatorField => {
381 Self::balance_accumulator_field_type_params(GAS::type_tag())
382 }
383 MoveObjectType_::BalanceAccumulatorField(inner) => {
384 Self::balance_accumulator_field_type_params(inner)
385 }
386 MoveObjectType_::Other(s) => s.type_params,
387 }
388 }
389
390 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
391 match &self.0 {
392 MoveObjectType_::GasCoin => Some(GAS::type_tag()),
393 MoveObjectType_::Coin(inner) => Some(inner.clone()),
394 MoveObjectType_::StakedSui => None,
395 MoveObjectType_::SuiBalanceAccumulatorField => None,
396 MoveObjectType_::BalanceAccumulatorField(_) => None,
397 MoveObjectType_::Other(_) => None,
398 }
399 }
400
401 pub fn balance_accumulator_field_type_maybe(&self) -> Option<TypeTag> {
402 match &self.0 {
403 MoveObjectType_::SuiBalanceAccumulatorField => Some(GAS::type_tag()),
404 MoveObjectType_::BalanceAccumulatorField(inner) => Some(inner.clone()),
405 _ => None,
406 }
407 }
408
409 pub fn is_balance_accumulator_field(&self) -> bool {
410 matches!(
411 self.0,
412 MoveObjectType_::SuiBalanceAccumulatorField
413 | MoveObjectType_::BalanceAccumulatorField(_)
414 )
415 }
416
417 pub fn is_sui_balance_accumulator_field(&self) -> bool {
418 matches!(self.0, MoveObjectType_::SuiBalanceAccumulatorField)
419 }
420
421 pub fn module_id(&self) -> ModuleId {
422 ModuleId::new(self.address(), self.module().to_owned())
423 }
424
425 pub fn size_for_gas_metering(&self) -> usize {
426 match &self.0 {
428 MoveObjectType_::GasCoin => 1,
429 MoveObjectType_::StakedSui => 1,
430 MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1,
431 MoveObjectType_::SuiBalanceAccumulatorField => 1,
432 MoveObjectType_::BalanceAccumulatorField(inner) => {
433 bcs::serialized_size(inner).unwrap() + 1
434 }
435 MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1,
436 }
437 }
438
439 pub fn is_coin(&self) -> bool {
441 match &self.0 {
442 MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true,
443 MoveObjectType_::StakedSui
444 | MoveObjectType_::SuiBalanceAccumulatorField
445 | MoveObjectType_::BalanceAccumulatorField(_)
446 | MoveObjectType_::Other(_) => false,
447 }
448 }
449
450 pub fn is_gas_coin(&self) -> bool {
452 match &self.0 {
453 MoveObjectType_::GasCoin => true,
454 MoveObjectType_::StakedSui
455 | MoveObjectType_::Coin(_)
456 | MoveObjectType_::SuiBalanceAccumulatorField
457 | MoveObjectType_::BalanceAccumulatorField(_)
458 | MoveObjectType_::Other(_) => false,
459 }
460 }
461
462 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
464 match &self.0 {
465 MoveObjectType_::GasCoin => GAS::is_gas_type(t),
466 MoveObjectType_::Coin(c) => t == c,
467 MoveObjectType_::StakedSui
468 | MoveObjectType_::SuiBalanceAccumulatorField
469 | MoveObjectType_::BalanceAccumulatorField(_)
470 | MoveObjectType_::Other(_) => false,
471 }
472 }
473
474 pub fn is_staked_sui(&self) -> bool {
475 match &self.0 {
476 MoveObjectType_::StakedSui => true,
477 MoveObjectType_::GasCoin
478 | MoveObjectType_::Coin(_)
479 | MoveObjectType_::SuiBalanceAccumulatorField
480 | MoveObjectType_::BalanceAccumulatorField(_)
481 | MoveObjectType_::Other(_) => false,
482 }
483 }
484
485 pub fn is_coin_metadata(&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) => CoinMetadata::is_coin_metadata(s),
493 }
494 }
495
496 pub fn is_currency(&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) => Currency::is_currency(s),
504 }
505 }
506
507 pub fn is_treasury_cap(&self) -> bool {
508 match &self.0 {
509 MoveObjectType_::GasCoin
510 | MoveObjectType_::StakedSui
511 | MoveObjectType_::Coin(_)
512 | MoveObjectType_::SuiBalanceAccumulatorField
513 | MoveObjectType_::BalanceAccumulatorField(_) => false,
514 MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s),
515 }
516 }
517
518 pub fn is_upgrade_cap(&self) -> bool {
519 self.address() == SUI_FRAMEWORK_ADDRESS
520 && self.module().as_str() == "package"
521 && self.name().as_str() == "UpgradeCap"
522 }
523
524 pub fn is_regulated_coin_metadata(&self) -> bool {
525 self.address() == SUI_FRAMEWORK_ADDRESS
526 && self.module().as_str() == "coin"
527 && self.name().as_str() == "RegulatedCoinMetadata"
528 }
529
530 pub fn is_coin_deny_cap(&self) -> bool {
531 self.address() == SUI_FRAMEWORK_ADDRESS
532 && self.module().as_str() == "coin"
533 && self.name().as_str() == "DenyCap"
534 }
535
536 pub fn is_coin_deny_cap_v2(&self) -> bool {
537 self.address() == SUI_FRAMEWORK_ADDRESS
538 && self.module().as_str() == "coin"
539 && self.name().as_str() == "DenyCapV2"
540 }
541
542 pub fn is_dynamic_field(&self) -> bool {
543 match &self.0 {
544 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
545 false
546 }
547 MoveObjectType_::SuiBalanceAccumulatorField
548 | MoveObjectType_::BalanceAccumulatorField(_) => true, MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s),
550 }
551 }
552
553 pub fn try_extract_field_name(&self, type_: &DynamicFieldType) -> SuiResult<TypeTag> {
554 match &self.0 {
555 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
556 Err(SuiErrorKind::ObjectDeserializationError {
557 error: "Error extracting dynamic object name from specialized object type"
558 .to_string(),
559 }
560 .into())
561 }
562 MoveObjectType_::SuiBalanceAccumulatorField
563 | MoveObjectType_::BalanceAccumulatorField(_) => {
564 let struct_tag: StructTag = self.clone().into();
565 DynamicFieldInfo::try_extract_field_name(&struct_tag, type_)
566 }
567 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_name(s, type_),
568 }
569 }
570
571 pub fn try_extract_field_value(&self) -> SuiResult<TypeTag> {
572 match &self.0 {
573 MoveObjectType_::GasCoin | MoveObjectType_::StakedSui | MoveObjectType_::Coin(_) => {
574 Err(SuiErrorKind::ObjectDeserializationError {
575 error: "Error extracting dynamic object value from specialized object type"
576 .to_string(),
577 }
578 .into())
579 }
580 MoveObjectType_::SuiBalanceAccumulatorField
581 | MoveObjectType_::BalanceAccumulatorField(_) => {
582 let struct_tag: StructTag = self.clone().into();
583 DynamicFieldInfo::try_extract_field_value(&struct_tag)
584 }
585 MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s),
586 }
587 }
588
589 pub fn is(&self, s: &StructTag) -> bool {
590 match &self.0 {
591 MoveObjectType_::GasCoin => GasCoin::is_gas_coin(s),
592 MoveObjectType_::StakedSui => StakedSui::is_staked_sui(s),
593 MoveObjectType_::Coin(inner) => {
594 Coin::is_coin(s) && s.type_params.len() == 1 && inner == &s.type_params[0]
595 }
596 MoveObjectType_::SuiBalanceAccumulatorField => accumulator_value_balance_type_maybe(s)
597 .map(|t| GAS::is_gas_type(&t))
598 .unwrap_or(false),
599 MoveObjectType_::BalanceAccumulatorField(inner) => {
600 accumulator_value_balance_type_maybe(s)
601 .map(|t| &t == inner)
602 .unwrap_or(false)
603 }
604 MoveObjectType_::Other(o) => s == o,
605 }
606 }
607
608 pub fn other(&self) -> Option<&StructTag> {
609 if let MoveObjectType_::Other(s) = &self.0 {
610 Some(s)
611 } else {
612 None
613 }
614 }
615
616 pub fn to_canonical_string(&self, with_prefix: bool) -> String {
618 StructTag::from(self.clone()).to_canonical_string(with_prefix)
619 }
620
621 fn balance_accumulator_field_type_params(inner_type: TypeTag) -> Vec<TypeTag> {
624 use crate::accumulator_root::{AccumulatorKey, U128};
625 let balance_type = Balance::type_tag(inner_type);
626 let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
627 let u128_type = U128::get_type_tag();
628 vec![key_type, u128_type]
629 }
630
631 fn balance_accumulator_field_struct_tag(inner_type: TypeTag) -> StructTag {
633 use crate::accumulator_root::{AccumulatorKey, U128};
634 let balance_type = Balance::type_tag(inner_type);
635 let key_type = AccumulatorKey::get_type_tag(&[balance_type]);
636 let u128_type = U128::get_type_tag();
637 DynamicFieldInfo::dynamic_field_type(key_type, u128_type)
638 }
639}
640
641impl From<StructTag> for MoveObjectType {
642 fn from(mut s: StructTag) -> Self {
643 Self(if GasCoin::is_gas_coin(&s) {
644 MoveObjectType_::GasCoin
645 } else if Coin::is_coin(&s) {
646 MoveObjectType_::Coin(s.type_params.pop().unwrap())
648 } else if StakedSui::is_staked_sui(&s) {
649 MoveObjectType_::StakedSui
650 } else if let Some(balance_type) = accumulator_value_balance_type_maybe(&s) {
651 if GAS::is_gas_type(&balance_type) {
652 MoveObjectType_::SuiBalanceAccumulatorField
653 } else {
654 MoveObjectType_::BalanceAccumulatorField(balance_type)
655 }
656 } else {
657 MoveObjectType_::Other(s)
658 })
659 }
660}
661
662impl From<MoveObjectType> for StructTag {
663 fn from(t: MoveObjectType) -> Self {
664 match t.0 {
665 MoveObjectType_::GasCoin => GasCoin::type_(),
666 MoveObjectType_::StakedSui => StakedSui::type_(),
667 MoveObjectType_::Coin(inner) => Coin::type_(inner),
668 MoveObjectType_::SuiBalanceAccumulatorField => {
669 MoveObjectType::balance_accumulator_field_struct_tag(GAS::type_tag())
670 }
671 MoveObjectType_::BalanceAccumulatorField(inner) => {
672 MoveObjectType::balance_accumulator_field_struct_tag(inner)
673 }
674 MoveObjectType_::Other(s) => s,
675 }
676 }
677}
678
679impl From<MoveObjectType> for TypeTag {
680 fn from(o: MoveObjectType) -> TypeTag {
681 let s: StructTag = o.into();
682 TypeTag::Struct(Box::new(s))
683 }
684}
685
686pub fn is_primitive_type_tag(t: &TypeTag) -> bool {
688 use TypeTag as T;
689
690 match t {
691 T::Bool | T::U8 | T::U16 | T::U32 | T::U64 | T::U128 | T::U256 | T::Address => true,
692 T::Vector(inner) => is_primitive_type_tag(inner),
693 T::Struct(st) => {
694 let StructTag {
695 address,
696 module,
697 name,
698 type_params: type_args,
699 } = &**st;
700 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
701 if resolved_struct == RESOLVED_SUI_ID {
703 return true;
704 }
705 if resolved_struct == RESOLVED_UTF8_STR {
707 return true;
708 }
709 if resolved_struct == RESOLVED_ASCII_STR {
711 return true;
712 }
713 resolved_struct == RESOLVED_STD_OPTION
715 && type_args.len() == 1
716 && is_primitive_type_tag(&type_args[0])
717 }
718 T::Signer => false,
719 }
720}
721
722#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
724pub enum ObjectType {
725 Package,
727 Struct(MoveObjectType),
729}
730
731impl From<&Object> for ObjectType {
732 fn from(o: &Object) -> Self {
733 o.data
734 .type_()
735 .map(|t| ObjectType::Struct(t.clone()))
736 .unwrap_or(ObjectType::Package)
737 }
738}
739
740impl TryFrom<ObjectType> for StructTag {
741 type Error = anyhow::Error;
742
743 fn try_from(o: ObjectType) -> Result<Self, anyhow::Error> {
744 match o {
745 ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")),
746 ObjectType::Struct(move_object_type) => Ok(move_object_type.into()),
747 }
748 }
749}
750
751impl FromStr for ObjectType {
752 type Err = anyhow::Error;
753
754 fn from_str(s: &str) -> Result<Self, Self::Err> {
755 if s.to_lowercase() == PACKAGE {
756 Ok(ObjectType::Package)
757 } else {
758 let tag = parse_sui_struct_tag(s)?;
759 Ok(ObjectType::Struct(MoveObjectType::from(tag)))
760 }
761 }
762}
763
764#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)]
765pub struct ObjectInfo {
766 pub object_id: ObjectID,
767 pub version: SequenceNumber,
768 pub digest: ObjectDigest,
769 pub type_: ObjectType,
770 pub owner: Owner,
771 pub previous_transaction: TransactionDigest,
772}
773
774impl ObjectInfo {
775 pub fn new(oref: &ObjectRef, o: &Object) -> Self {
776 let (object_id, version, digest) = *oref;
777 Self {
778 object_id,
779 version,
780 digest,
781 type_: o.into(),
782 owner: o.owner.clone(),
783 previous_transaction: o.previous_transaction,
784 }
785 }
786
787 pub fn from_object(object: &Object) -> Self {
788 Self {
789 object_id: object.id(),
790 version: object.version(),
791 digest: object.digest(),
792 type_: object.into(),
793 owner: object.owner.clone(),
794 previous_transaction: object.previous_transaction,
795 }
796 }
797}
798const PACKAGE: &str = "package";
799impl ObjectType {
800 pub fn is_gas_coin(&self) -> bool {
801 matches!(self, ObjectType::Struct(s) if s.is_gas_coin())
802 }
803
804 pub fn is_coin(&self) -> bool {
805 matches!(self, ObjectType::Struct(s) if s.is_coin())
806 }
807
808 pub fn is_coin_t(&self, t: &TypeTag) -> bool {
810 matches!(self, ObjectType::Struct(s) if s.is_coin_t(t))
811 }
812
813 pub fn is_package(&self) -> bool {
814 matches!(self, ObjectType::Package)
815 }
816}
817
818impl From<ObjectInfo> for ObjectRef {
819 fn from(info: ObjectInfo) -> Self {
820 (info.object_id, info.version, info.digest)
821 }
822}
823
824impl From<&ObjectInfo> for ObjectRef {
825 fn from(info: &ObjectInfo) -> Self {
826 (info.object_id, info.version, info.digest)
827 }
828}
829
830pub const SUI_ADDRESS_LENGTH: usize = ObjectID::LENGTH;
831
832#[serde_as]
833#[derive(
834 Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema,
835)]
836#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
837pub struct SuiAddress(
838 #[schemars(with = "Hex")]
839 #[serde_as(as = "Readable<Hex, _>")]
840 [u8; SUI_ADDRESS_LENGTH],
841);
842
843impl SuiAddress {
844 pub const ZERO: Self = Self([0u8; SUI_ADDRESS_LENGTH]);
845
846 pub fn to_vec(&self) -> Vec<u8> {
848 self.0.to_vec()
849 }
850
851 pub fn random_for_testing_only() -> Self {
853 AccountAddress::random().into()
854 }
855
856 pub fn generate<R: rand::RngCore + rand::CryptoRng>(mut rng: R) -> Self {
857 let buf: [u8; SUI_ADDRESS_LENGTH] = rng.r#gen();
858 Self(buf)
859 }
860
861 pub fn optional_address_as_hex<S>(
863 key: &Option<SuiAddress>,
864 serializer: S,
865 ) -> Result<S::Ok, S::Error>
866 where
867 S: serde::ser::Serializer,
868 {
869 serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default())
870 }
871
872 pub fn optional_address_from_hex<'de, D>(
874 deserializer: D,
875 ) -> Result<Option<SuiAddress>, D::Error>
876 where
877 D: serde::de::Deserializer<'de>,
878 {
879 let s = String::deserialize(deserializer)?;
880 let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?;
881 Ok(Some(value))
882 }
883
884 pub fn to_inner(self) -> [u8; SUI_ADDRESS_LENGTH] {
886 self.0
887 }
888
889 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, SuiError> {
891 <[u8; SUI_ADDRESS_LENGTH]>::try_from(bytes.as_ref())
892 .map_err(|_| SuiErrorKind::InvalidAddress.into())
893 .map(SuiAddress)
894 }
895
896 pub fn try_from_padded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
900 Ok((&PublicKey::from_zklogin_inputs(inputs)?).into())
901 }
902
903 pub fn try_from_unpadded(inputs: &ZkLoginInputs) -> SuiResult<Self> {
905 let mut hasher = DefaultHash::default();
906 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
907 let iss_bytes = inputs.get_iss().as_bytes();
908 hasher.update([iss_bytes.len() as u8]);
909 hasher.update(iss_bytes);
910 hasher.update(inputs.get_address_seed().unpadded());
911 Ok(SuiAddress(hasher.finalize().digest))
912 }
913}
914
915impl From<ObjectID> for SuiAddress {
916 fn from(object_id: ObjectID) -> SuiAddress {
917 Self(object_id.into_bytes())
918 }
919}
920
921impl From<AccountAddress> for SuiAddress {
922 fn from(address: AccountAddress) -> SuiAddress {
923 Self(address.into_bytes())
924 }
925}
926
927impl TryFrom<&[u8]> for SuiAddress {
928 type Error = SuiError;
929
930 fn try_from(bytes: &[u8]) -> Result<Self, SuiError> {
932 Self::from_bytes(bytes)
933 }
934}
935
936impl TryFrom<Vec<u8>> for SuiAddress {
937 type Error = SuiError;
938
939 fn try_from(bytes: Vec<u8>) -> Result<Self, SuiError> {
941 Self::from_bytes(bytes)
942 }
943}
944
945impl AsRef<[u8]> for SuiAddress {
946 fn as_ref(&self) -> &[u8] {
947 &self.0[..]
948 }
949}
950
951impl FromStr for SuiAddress {
952 type Err = anyhow::Error;
953 fn from_str(s: &str) -> Result<Self, Self::Err> {
954 decode_bytes_hex(s).map_err(|e| anyhow!(e))
955 }
956}
957
958impl<T: SuiPublicKey> From<&T> for SuiAddress {
959 fn from(pk: &T) -> Self {
960 let mut hasher = DefaultHash::default();
961 hasher.update([T::SIGNATURE_SCHEME.flag()]);
962 hasher.update(pk);
963 let g_arr = hasher.finalize();
964 SuiAddress(g_arr.digest)
965 }
966}
967
968impl From<&PublicKey> for SuiAddress {
969 fn from(pk: &PublicKey) -> Self {
970 let mut hasher = DefaultHash::default();
971 hasher.update([pk.flag()]);
972 hasher.update(pk);
973 let g_arr = hasher.finalize();
974 SuiAddress(g_arr.digest)
975 }
976}
977
978impl From<&MultiSigPublicKey> for SuiAddress {
979 fn from(multisig_pk: &MultiSigPublicKey) -> Self {
988 let mut hasher = DefaultHash::default();
989 hasher.update([SignatureScheme::MultiSig.flag()]);
990 hasher.update(multisig_pk.threshold().to_le_bytes());
991 multisig_pk.pubkeys().iter().for_each(|(pk, w)| {
992 hasher.update([pk.flag()]);
993 hasher.update(pk.as_ref());
994 hasher.update(w.to_le_bytes());
995 });
996 SuiAddress(hasher.finalize().digest)
997 }
998}
999
1000impl TryFrom<&ZkLoginAuthenticator> for SuiAddress {
1003 type Error = SuiError;
1004 fn try_from(authenticator: &ZkLoginAuthenticator) -> SuiResult<Self> {
1005 SuiAddress::try_from_unpadded(&authenticator.inputs)
1006 }
1007}
1008
1009impl TryFrom<&GenericSignature> for SuiAddress {
1010 type Error = SuiError;
1011 fn try_from(sig: &GenericSignature) -> SuiResult<Self> {
1013 match sig {
1014 GenericSignature::Signature(sig) => {
1015 let scheme = sig.scheme();
1016 let pub_key_bytes = sig.public_key_bytes();
1017 let pub_key = PublicKey::try_from_bytes(scheme, pub_key_bytes).map_err(|_| {
1018 SuiErrorKind::InvalidSignature {
1019 error: "Cannot parse pubkey".to_string(),
1020 }
1021 })?;
1022 Ok(SuiAddress::from(&pub_key))
1023 }
1024 GenericSignature::MultiSig(ms) => Ok(ms.get_pk().into()),
1025 GenericSignature::MultiSigLegacy(ms) => {
1026 Ok(crate::multisig::MultiSig::try_from(ms.clone())
1027 .map_err(|_| SuiErrorKind::InvalidSignature {
1028 error: "Invalid legacy multisig".to_string(),
1029 })?
1030 .get_pk()
1031 .into())
1032 }
1033 GenericSignature::ZkLoginAuthenticator(zklogin) => {
1034 SuiAddress::try_from_unpadded(&zklogin.inputs)
1035 }
1036 GenericSignature::PasskeyAuthenticator(s) => Ok(SuiAddress::from(&s.get_pk()?)),
1037 }
1038 }
1039}
1040
1041impl fmt::Display for SuiAddress {
1042 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043 write!(f, "0x{}", Hex::encode(self.0))
1044 }
1045}
1046
1047impl fmt::Debug for SuiAddress {
1048 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1049 write!(f, "0x{}", Hex::encode(self.0))
1050 }
1051}
1052
1053pub fn dbg_addr(name: u8) -> SuiAddress {
1055 let addr = [name; SUI_ADDRESS_LENGTH];
1056 SuiAddress(addr)
1057}
1058
1059#[derive(
1060 Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema, Debug,
1061)]
1062pub struct ExecutionDigests {
1063 pub transaction: TransactionDigest,
1064 pub effects: TransactionEffectsDigest,
1065}
1066
1067impl ExecutionDigests {
1068 pub fn new(transaction: TransactionDigest, effects: TransactionEffectsDigest) -> Self {
1069 Self {
1070 transaction,
1071 effects,
1072 }
1073 }
1074
1075 pub fn random() -> Self {
1076 Self {
1077 transaction: TransactionDigest::random(),
1078 effects: TransactionEffectsDigest::random(),
1079 }
1080 }
1081}
1082
1083#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
1084pub struct ExecutionData {
1085 pub transaction: Transaction,
1086 pub effects: TransactionEffects,
1087}
1088
1089impl ExecutionData {
1090 pub fn new(transaction: Transaction, effects: TransactionEffects) -> ExecutionData {
1091 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1092 Self {
1093 transaction,
1094 effects,
1095 }
1096 }
1097
1098 pub fn digests(&self) -> ExecutionDigests {
1099 self.effects.execution_digests()
1100 }
1101}
1102
1103#[derive(Clone, Eq, PartialEq, Debug)]
1104pub struct VerifiedExecutionData {
1105 pub transaction: VerifiedTransaction,
1106 pub effects: TransactionEffects,
1107}
1108
1109impl VerifiedExecutionData {
1110 pub fn new(transaction: VerifiedTransaction, effects: TransactionEffects) -> Self {
1111 debug_assert_eq!(transaction.digest(), effects.transaction_digest());
1112 Self {
1113 transaction,
1114 effects,
1115 }
1116 }
1117
1118 pub fn new_unchecked(data: ExecutionData) -> Self {
1119 Self {
1120 transaction: VerifiedTransaction::new_unchecked(data.transaction),
1121 effects: data.effects,
1122 }
1123 }
1124
1125 pub fn into_inner(self) -> ExecutionData {
1126 ExecutionData {
1127 transaction: self.transaction.into_inner(),
1128 effects: self.effects,
1129 }
1130 }
1131
1132 pub fn digests(&self) -> ExecutionDigests {
1133 self.effects.execution_digests()
1134 }
1135}
1136
1137pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option");
1138pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option");
1139pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = (
1140 &MOVE_STDLIB_ADDRESS,
1141 STD_OPTION_MODULE_NAME,
1142 STD_OPTION_STRUCT_NAME,
1143);
1144
1145pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii");
1146pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String");
1147pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1148 &MOVE_STDLIB_ADDRESS,
1149 STD_ASCII_MODULE_NAME,
1150 STD_ASCII_STRUCT_NAME,
1151);
1152
1153pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string");
1154pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String");
1155pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = (
1156 &MOVE_STDLIB_ADDRESS,
1157 STD_UTF8_MODULE_NAME,
1158 STD_UTF8_STRUCT_NAME,
1159);
1160
1161pub const STD_TYPE_NAME_MODULE_NAME: &IdentStr = ident_str!("type_name");
1162pub const STD_TYPE_NAME_STRUCT_NAME: &IdentStr = ident_str!("TypeName");
1163pub const RESOLVED_STD_TYPE_NAME: (&AccountAddress, &IdentStr, &IdentStr) = (
1164 &MOVE_STDLIB_ADDRESS,
1165 STD_TYPE_NAME_MODULE_NAME,
1166 STD_TYPE_NAME_STRUCT_NAME,
1167);
1168
1169pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context");
1170pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext");
1171pub const RESOLVED_TX_CONTEXT: (&AccountAddress, &IdentStr, &IdentStr) = (
1172 &SUI_FRAMEWORK_ADDRESS,
1173 TX_CONTEXT_MODULE_NAME,
1174 TX_CONTEXT_STRUCT_NAME,
1175);
1176
1177pub const URL_MODULE_NAME: &IdentStr = ident_str!("url");
1178pub const URL_STRUCT_NAME: &IdentStr = ident_str!("Url");
1179
1180pub const VEC_MAP_MODULE_NAME: &IdentStr = ident_str!("vec_map");
1181pub const VEC_MAP_STRUCT_NAME: &IdentStr = ident_str!("VecMap");
1182pub const VEC_MAP_ENTRY_STRUCT_NAME: &IdentStr = ident_str!("Entry");
1183
1184pub fn move_ascii_str_layout() -> A::MoveStructLayout {
1185 A::MoveStructLayout {
1186 type_: StructTag {
1187 address: MOVE_STDLIB_ADDRESS,
1188 module: STD_ASCII_MODULE_NAME.to_owned(),
1189 name: STD_ASCII_STRUCT_NAME.to_owned(),
1190 type_params: vec![],
1191 },
1192 fields: vec![A::MoveFieldLayout::new(
1193 ident_str!("bytes").into(),
1194 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1195 )],
1196 }
1197}
1198
1199pub fn move_utf8_str_layout() -> A::MoveStructLayout {
1200 A::MoveStructLayout {
1201 type_: StructTag {
1202 address: MOVE_STDLIB_ADDRESS,
1203 module: STD_UTF8_MODULE_NAME.to_owned(),
1204 name: STD_UTF8_STRUCT_NAME.to_owned(),
1205 type_params: vec![],
1206 },
1207 fields: vec![A::MoveFieldLayout::new(
1208 ident_str!("bytes").into(),
1209 A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)),
1210 )],
1211 }
1212}
1213
1214pub fn url_layout() -> A::MoveStructLayout {
1215 A::MoveStructLayout {
1216 type_: StructTag {
1217 address: SUI_FRAMEWORK_ADDRESS,
1218 module: URL_MODULE_NAME.to_owned(),
1219 name: URL_STRUCT_NAME.to_owned(),
1220 type_params: vec![],
1221 },
1222 fields: vec![A::MoveFieldLayout::new(
1223 ident_str!("url").to_owned(),
1224 A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1225 )],
1226 }
1227}
1228
1229pub fn type_name_layout() -> A::MoveStructLayout {
1230 A::MoveStructLayout {
1231 type_: StructTag {
1232 address: MOVE_STDLIB_ADDRESS,
1233 module: STD_TYPE_NAME_MODULE_NAME.to_owned(),
1234 name: STD_TYPE_NAME_STRUCT_NAME.to_owned(),
1235 type_params: vec![],
1236 },
1237 fields: vec![A::MoveFieldLayout::new(
1238 ident_str!("name").into(),
1239 A::MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())),
1240 )],
1241 }
1242}
1243
1244#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1254pub struct MoveLegacyTxContext {
1255 sender: AccountAddress,
1257 digest: Vec<u8>,
1259 epoch: EpochId,
1261 epoch_timestamp_ms: CheckpointTimestamp,
1263 ids_created: u64,
1265}
1266
1267impl From<&TxContext> for MoveLegacyTxContext {
1268 fn from(tx_context: &TxContext) -> Self {
1269 Self {
1270 sender: tx_context.sender,
1271 digest: tx_context.digest.clone(),
1272 epoch: tx_context.epoch,
1273 epoch_timestamp_ms: tx_context.epoch_timestamp_ms,
1274 ids_created: tx_context.ids_created,
1275 }
1276 }
1277}
1278
1279#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1282pub struct TxContext {
1283 sender: AccountAddress,
1285 digest: Vec<u8>,
1287 epoch: EpochId,
1289 epoch_timestamp_ms: CheckpointTimestamp,
1291 ids_created: u64,
1293 rgp: u64,
1295 gas_price: u64,
1297 gas_budget: u64,
1299 sponsor: Option<AccountAddress>,
1301 is_native: bool,
1304}
1305
1306#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1307pub enum TxContextKind {
1308 None,
1310 Mutable,
1312 Immutable,
1314}
1315
1316impl TxContext {
1317 pub fn new(
1318 sender: &SuiAddress,
1319 digest: &TransactionDigest,
1320 epoch_data: &EpochData,
1321 rgp: u64,
1322 gas_price: u64,
1323 gas_budget: u64,
1324 sponsor: Option<SuiAddress>,
1325 protocol_config: &ProtocolConfig,
1326 ) -> Self {
1327 Self::new_from_components(
1328 sender,
1329 digest,
1330 &epoch_data.epoch_id(),
1331 epoch_data.epoch_start_timestamp(),
1332 rgp,
1333 gas_price,
1334 gas_budget,
1335 sponsor,
1336 protocol_config,
1337 )
1338 }
1339
1340 pub fn new_from_components(
1341 sender: &SuiAddress,
1342 digest: &TransactionDigest,
1343 epoch_id: &EpochId,
1344 epoch_timestamp_ms: u64,
1345 rgp: u64,
1346 gas_price: u64,
1347 gas_budget: u64,
1348 sponsor: Option<SuiAddress>,
1349 protocol_config: &ProtocolConfig,
1350 ) -> Self {
1351 Self {
1352 sender: AccountAddress::new(sender.0),
1353 digest: digest.into_inner().to_vec(),
1354 epoch: *epoch_id,
1355 epoch_timestamp_ms,
1356 ids_created: 0,
1357 rgp,
1358 gas_price,
1359 gas_budget,
1360 sponsor: sponsor.map(|s| s.into()),
1361 is_native: protocol_config.move_native_context(),
1362 }
1363 }
1364
1365 pub fn kind(view: &CompiledModule, s: &SignatureToken) -> TxContextKind {
1367 use SignatureToken as S;
1368 let (kind, s) = match s {
1369 S::MutableReference(s) => (TxContextKind::Mutable, s),
1370 S::Reference(s) => (TxContextKind::Immutable, s),
1371 _ => return TxContextKind::None,
1372 };
1373
1374 let S::Datatype(idx) = &**s else {
1375 return TxContextKind::None;
1376 };
1377
1378 if resolve_struct(view, *idx) == RESOLVED_TX_CONTEXT {
1379 kind
1380 } else {
1381 TxContextKind::None
1382 }
1383 }
1384
1385 pub fn type_() -> StructTag {
1386 StructTag {
1387 address: SUI_FRAMEWORK_ADDRESS,
1388 module: TX_CONTEXT_MODULE_NAME.to_owned(),
1389 name: TX_CONTEXT_STRUCT_NAME.to_owned(),
1390 type_params: vec![],
1391 }
1392 }
1393
1394 pub fn epoch(&self) -> EpochId {
1395 self.epoch
1396 }
1397
1398 pub fn sender(&self) -> SuiAddress {
1399 self.sender.into()
1400 }
1401
1402 pub fn epoch_timestamp_ms(&self) -> u64 {
1403 self.epoch_timestamp_ms
1404 }
1405
1406 pub fn digest(&self) -> TransactionDigest {
1408 TransactionDigest::new(self.digest.clone().try_into().unwrap())
1409 }
1410
1411 pub fn sponsor(&self) -> Option<SuiAddress> {
1412 self.sponsor.map(SuiAddress::from)
1413 }
1414
1415 pub fn rgp(&self) -> u64 {
1416 self.rgp
1417 }
1418
1419 pub fn gas_price(&self) -> u64 {
1420 self.gas_price
1421 }
1422
1423 pub fn gas_budget(&self) -> u64 {
1424 self.gas_budget
1425 }
1426
1427 pub fn ids_created(&self) -> u64 {
1428 self.ids_created
1429 }
1430
1431 pub fn fresh_id(&mut self) -> ObjectID {
1433 let id = ObjectID::derive_id(self.digest(), self.ids_created);
1434
1435 self.ids_created += 1;
1436 id
1437 }
1438
1439 pub fn to_bcs_legacy_context(&self) -> Vec<u8> {
1440 let move_context: MoveLegacyTxContext = if self.is_native {
1441 let tx_context = &TxContext {
1442 sender: AccountAddress::ZERO,
1443 digest: self.digest.clone(),
1444 epoch: 0,
1445 epoch_timestamp_ms: 0,
1446 ids_created: 0,
1447 rgp: 0,
1448 gas_price: 0,
1449 gas_budget: 0,
1450 sponsor: None,
1451 is_native: true,
1452 };
1453 tx_context.into()
1454 } else {
1455 self.into()
1456 };
1457 bcs::to_bytes(&move_context).unwrap()
1458 }
1459
1460 pub fn to_vec(&self) -> Vec<u8> {
1461 bcs::to_bytes(&self).unwrap()
1462 }
1463
1464 pub fn update_state(&mut self, other: MoveLegacyTxContext) -> Result<(), ExecutionError> {
1469 if !self.is_native {
1470 if self.sender != other.sender
1471 || self.digest != other.digest
1472 || other.ids_created < self.ids_created
1473 {
1474 return Err(ExecutionError::new_with_source(
1475 ExecutionErrorKind::InvariantViolation,
1476 "Immutable fields for TxContext changed",
1477 ));
1478 }
1479 self.ids_created = other.ids_created;
1480 }
1481 Ok(())
1482 }
1483
1484 pub fn replace(
1488 &mut self,
1489 sender: AccountAddress,
1490 tx_hash: Vec<u8>,
1491 epoch: u64,
1492 epoch_timestamp_ms: u64,
1493 ids_created: u64,
1494 rgp: u64,
1495 gas_price: u64,
1496 gas_budget: u64,
1497 sponsor: Option<AccountAddress>,
1498 ) {
1499 self.sender = sender;
1500 self.digest = tx_hash;
1501 self.epoch = epoch;
1502 self.epoch_timestamp_ms = epoch_timestamp_ms;
1503 self.ids_created = ids_created;
1504 self.rgp = rgp;
1505 self.gas_price = gas_price;
1506 self.gas_budget = gas_budget;
1507 self.sponsor = sponsor;
1508 }
1509}
1510
1511impl SequenceNumber {
1513 pub const MIN: SequenceNumber = SequenceNumber(u64::MIN);
1514 pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff);
1515 pub const CANCELLED_READ: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 1);
1516 pub const CONGESTED: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 2);
1517 pub const RANDOMNESS_UNAVAILABLE: SequenceNumber =
1518 SequenceNumber(SequenceNumber::MAX.value() + 3);
1519 pub const UNKNOWN: SequenceNumber = SequenceNumber(SequenceNumber::MAX.value() + 4);
1522
1523 pub const fn new() -> Self {
1524 SequenceNumber(0)
1525 }
1526
1527 pub const fn value(&self) -> u64 {
1528 self.0
1529 }
1530
1531 pub const fn from_u64(u: u64) -> Self {
1532 SequenceNumber(u)
1533 }
1534
1535 pub fn increment(&mut self) {
1536 assert_ne!(self.0, u64::MAX);
1537 self.0 += 1;
1538 }
1539
1540 pub fn increment_to(&mut self, next: SequenceNumber) {
1541 debug_assert!(*self < next, "Not an increment: {} to {}", self, next);
1542 *self = next;
1543 }
1544
1545 pub fn decrement(&mut self) {
1546 assert_ne!(self.0, 0);
1547 self.0 -= 1;
1548 }
1549
1550 pub fn decrement_to(&mut self, prev: SequenceNumber) {
1551 debug_assert!(prev < *self, "Not a decrement: {} to {}", self, prev);
1552 *self = prev;
1553 }
1554
1555 #[must_use]
1558 pub fn lamport_increment(inputs: impl IntoIterator<Item = SequenceNumber>) -> SequenceNumber {
1559 let max_input = inputs.into_iter().fold(SequenceNumber::new(), max);
1560
1561 assert_ne!(max_input.0, u64::MAX);
1566
1567 SequenceNumber(max_input.0 + 1)
1568 }
1569
1570 pub fn is_cancelled(&self) -> bool {
1571 self == &SequenceNumber::CANCELLED_READ
1572 || self == &SequenceNumber::CONGESTED
1573 || self == &SequenceNumber::RANDOMNESS_UNAVAILABLE
1574 }
1575
1576 pub fn is_valid(&self) -> bool {
1577 self < &SequenceNumber::MAX
1578 }
1579}
1580
1581impl From<SequenceNumber> for u64 {
1582 fn from(val: SequenceNumber) -> Self {
1583 val.0
1584 }
1585}
1586
1587impl From<u64> for SequenceNumber {
1588 fn from(value: u64) -> Self {
1589 SequenceNumber(value)
1590 }
1591}
1592
1593impl From<SequenceNumber> for usize {
1594 fn from(value: SequenceNumber) -> Self {
1595 value.0 as usize
1596 }
1597}
1598
1599impl ObjectID {
1600 pub const LENGTH: usize = AccountAddress::LENGTH;
1602 pub const ZERO: Self = Self::new([0u8; Self::LENGTH]);
1604 pub const MAX: Self = Self::new([0xff; Self::LENGTH]);
1605 pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self {
1607 Self(AccountAddress::new(obj_id))
1608 }
1609
1610 pub const fn from_address(addr: AccountAddress) -> Self {
1612 Self(addr)
1613 }
1614
1615 pub fn random() -> Self {
1617 Self::from(AccountAddress::random())
1618 }
1619
1620 pub fn random_from_rng<R>(rng: &mut R) -> Self
1622 where
1623 R: AllowedRng,
1624 {
1625 let buf: [u8; Self::LENGTH] = rng.r#gen();
1626 ObjectID::new(buf)
1627 }
1628
1629 pub fn to_vec(&self) -> Vec<u8> {
1631 self.0.to_vec()
1632 }
1633
1634 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, ObjectIDParseError> {
1636 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
1637 .map_err(|_| ObjectIDParseError::TryFromSliceError)
1638 .map(ObjectID::new)
1639 }
1640
1641 pub fn into_bytes(self) -> [u8; Self::LENGTH] {
1643 self.0.into_bytes()
1644 }
1645
1646 pub const fn from_single_byte(byte: u8) -> ObjectID {
1648 let mut bytes = [0u8; Self::LENGTH];
1649 bytes[Self::LENGTH - 1] = byte;
1650 ObjectID::new(bytes)
1651 }
1652
1653 pub fn is_system_object(&self) -> bool {
1655 self.0.as_ref()[..24] == [0u8; 24]
1656 }
1657
1658 pub fn from_hex_literal(literal: &str) -> Result<Self, ObjectIDParseError> {
1661 if !literal.starts_with("0x") {
1662 return Err(ObjectIDParseError::HexLiteralPrefixMissing);
1663 }
1664
1665 let hex_len = literal.len() - 2;
1666
1667 if hex_len < Self::LENGTH * 2 {
1669 let mut hex_str = String::with_capacity(Self::LENGTH * 2);
1670 for _ in 0..Self::LENGTH * 2 - hex_len {
1671 hex_str.push('0');
1672 }
1673 hex_str.push_str(&literal[2..]);
1674 Self::from_str(&hex_str)
1675 } else {
1676 Self::from_str(&literal[2..])
1677 }
1678 }
1679
1680 pub fn derive_id(digest: TransactionDigest, creation_num: u64) -> Self {
1683 let mut hasher = DefaultHash::default();
1684 hasher.update([HashingIntentScope::RegularObjectId as u8]);
1685 hasher.update(digest);
1686 hasher.update(creation_num.to_le_bytes());
1687 let hash = hasher.finalize();
1688
1689 ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap()
1692 }
1693
1694 pub fn advance(&self, step: usize) -> Result<ObjectID, anyhow::Error> {
1696 let mut curr_vec = self.to_vec();
1697 let mut step_copy = step;
1698
1699 let mut carry = 0;
1700 for idx in (0..Self::LENGTH).rev() {
1701 if step_copy == 0 {
1702 break;
1704 }
1705 let g = (step_copy % 0x100) as u16;
1707 step_copy >>= 8;
1709 let mut val = curr_vec[idx] as u16;
1710 (carry, val) = ((val + carry + g) / 0x100, (val + carry + g) % 0x100);
1711 curr_vec[idx] = val as u8;
1712 }
1713
1714 if carry > 0 {
1715 return Err(anyhow!("Increment will cause overflow"));
1716 }
1717 ObjectID::try_from(curr_vec).map_err(|w| w.into())
1718 }
1719
1720 pub fn next_increment(&self) -> Result<ObjectID, anyhow::Error> {
1722 let mut prev_val = self.to_vec();
1723 let mx = [0xFF; Self::LENGTH];
1724
1725 if prev_val == mx {
1726 return Err(anyhow!("Increment will cause overflow"));
1727 }
1728
1729 for idx in (0..Self::LENGTH).rev() {
1731 if prev_val[idx] == 0xFF {
1732 prev_val[idx] = 0;
1733 } else {
1734 prev_val[idx] += 1;
1735 break;
1736 };
1737 }
1738 ObjectID::try_from(prev_val.clone()).map_err(|w| w.into())
1739 }
1740
1741 pub fn in_range(offset: ObjectID, count: u64) -> Result<Vec<ObjectID>, anyhow::Error> {
1743 let mut ret = Vec::new();
1744 let mut prev = offset;
1745 for o in 0..count {
1746 if o != 0 {
1747 prev = prev.next_increment()?;
1748 }
1749 ret.push(prev);
1750 }
1751 Ok(ret)
1752 }
1753
1754 pub fn to_hex_uncompressed(&self) -> String {
1757 format!("{self}")
1758 }
1759
1760 pub fn is_clock(&self) -> bool {
1761 *self == SUI_CLOCK_OBJECT_ID
1762 }
1763
1764 pub fn is_implicitly_read_system_object(&self) -> bool {
1765 crate::IMPLICITLY_READ_SYSTEM_OBJECTS.contains(self)
1766 }
1767}
1768
1769impl From<SuiAddress> for ObjectID {
1770 fn from(address: SuiAddress) -> ObjectID {
1771 let tmp: AccountAddress = address.into();
1772 tmp.into()
1773 }
1774}
1775
1776impl From<AccountAddress> for ObjectID {
1777 fn from(address: AccountAddress) -> Self {
1778 Self(address)
1779 }
1780}
1781
1782impl fmt::Display for ObjectID {
1783 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1784 write!(f, "0x{}", Hex::encode(self.0))
1785 }
1786}
1787
1788impl fmt::Debug for ObjectID {
1789 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1790 write!(f, "0x{}", Hex::encode(self.0))
1791 }
1792}
1793
1794impl AsRef<[u8]> for ObjectID {
1795 fn as_ref(&self) -> &[u8] {
1796 self.0.as_slice()
1797 }
1798}
1799
1800impl TryFrom<&[u8]> for ObjectID {
1801 type Error = ObjectIDParseError;
1802
1803 fn try_from(bytes: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
1805 Self::from_bytes(bytes)
1806 }
1807}
1808
1809impl TryFrom<Vec<u8>> for ObjectID {
1810 type Error = ObjectIDParseError;
1811
1812 fn try_from(bytes: Vec<u8>) -> Result<ObjectID, ObjectIDParseError> {
1814 Self::from_bytes(bytes)
1815 }
1816}
1817
1818impl FromStr for ObjectID {
1819 type Err = ObjectIDParseError;
1820
1821 fn from_str(s: &str) -> Result<Self, ObjectIDParseError> {
1823 decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s))
1824 }
1825}
1826
1827impl std::ops::Deref for ObjectID {
1828 type Target = AccountAddress;
1829
1830 fn deref(&self) -> &Self::Target {
1831 &self.0
1832 }
1833}
1834
1835pub fn dbg_object_id(name: u8) -> ObjectID {
1837 ObjectID::new([name; ObjectID::LENGTH])
1838}
1839
1840#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
1841pub enum ObjectIDParseError {
1842 #[error("ObjectID hex literal must start with 0x")]
1843 HexLiteralPrefixMissing,
1844
1845 #[error("Could not convert from bytes slice")]
1846 TryFromSliceError,
1847}
1848
1849impl From<ObjectID> for AccountAddress {
1850 fn from(obj_id: ObjectID) -> Self {
1851 obj_id.0
1852 }
1853}
1854
1855impl From<SuiAddress> for AccountAddress {
1856 fn from(address: SuiAddress) -> Self {
1857 Self::new(address.0)
1858 }
1859}
1860
1861struct HexAccountAddress;
1863
1864impl SerializeAs<AccountAddress> for HexAccountAddress {
1865 fn serialize_as<S>(value: &AccountAddress, serializer: S) -> Result<S::Ok, S::Error>
1866 where
1867 S: Serializer,
1868 {
1869 Hex::serialize_as(value, serializer)
1870 }
1871}
1872
1873impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress {
1874 fn deserialize_as<D>(deserializer: D) -> Result<AccountAddress, D::Error>
1875 where
1876 D: Deserializer<'de>,
1877 {
1878 let s = String::deserialize(deserializer)?;
1879 if s.starts_with("0x") {
1880 AccountAddress::from_hex_literal(&s)
1881 } else {
1882 AccountAddress::from_hex(&s)
1883 }
1884 .map_err(to_custom_deser_error::<'de, D, _>)
1885 }
1886}
1887
1888impl fmt::Display for MoveObjectType {
1889 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1890 let s: StructTag = self.clone().into();
1891 write!(
1892 f,
1893 "{}",
1894 to_sui_struct_tag_string(&s).map_err(fmt::Error::custom)?
1895 )
1896 }
1897}
1898
1899impl fmt::Display for ObjectType {
1900 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1901 match self {
1902 ObjectType::Package => write!(f, "{}", PACKAGE),
1903 ObjectType::Struct(t) => write!(f, "{}", t),
1904 }
1905 }
1906}
1907
1908#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1914#[serde(try_from = "Vec<T>")]
1915pub struct SizeOneVec<T> {
1916 e: T,
1917}
1918
1919impl<T> SizeOneVec<T> {
1920 pub fn new(e: T) -> Self {
1921 Self { e }
1922 }
1923
1924 pub fn element(&self) -> &T {
1925 &self.e
1926 }
1927
1928 pub fn element_mut(&mut self) -> &mut T {
1929 &mut self.e
1930 }
1931
1932 pub fn into_inner(self) -> T {
1933 self.e
1934 }
1935
1936 pub fn iter(&self) -> std::iter::Once<&T> {
1937 std::iter::once(&self.e)
1938 }
1939}
1940
1941impl<T> Serialize for SizeOneVec<T>
1942where
1943 T: Serialize,
1944{
1945 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1946 where
1947 S: Serializer,
1948 {
1949 let mut seq = serializer.serialize_seq(Some(1))?;
1950 seq.serialize_element(&self.e)?;
1951 seq.end()
1952 }
1953}
1954
1955impl<T> TryFrom<Vec<T>> for SizeOneVec<T> {
1956 type Error = anyhow::Error;
1957
1958 fn try_from(mut v: Vec<T>) -> Result<Self, Self::Error> {
1959 if v.len() != 1 {
1960 Err(anyhow!("Expected a vec of size 1"))
1961 } else {
1962 Ok(SizeOneVec {
1963 e: v.pop().unwrap(),
1964 })
1965 }
1966 }
1967}
1968
1969#[test]
1970fn test_size_one_vec_is_transparent() {
1971 let regular = vec![42u8];
1972 let size_one = SizeOneVec::new(42u8);
1973
1974 let regular_ser = bcs::to_bytes(®ular).unwrap();
1976 let size_one_deser = bcs::from_bytes::<SizeOneVec<u8>>(®ular_ser).unwrap();
1977 assert_eq!(size_one, size_one_deser);
1978
1979 let size_one_ser = bcs::to_bytes(&SizeOneVec::new(43u8)).unwrap();
1981 let regular_deser = bcs::from_bytes::<Vec<u8>>(&size_one_ser).unwrap();
1982 assert_eq!(regular_deser, vec![43u8]);
1983
1984 let empty_ser = bcs::to_bytes(&Vec::<u8>::new()).unwrap();
1986 bcs::from_bytes::<SizeOneVec<u8>>(&empty_ser).unwrap_err();
1987
1988 let size_greater_than_one_ser = bcs::to_bytes(&vec![1u8, 2u8]).unwrap();
1989 bcs::from_bytes::<SizeOneVec<u8>>(&size_greater_than_one_ser).unwrap_err();
1990}