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