Skip to main content

iota_types/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2026 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5#![warn(
6    future_incompatible,
7    nonstandard_style,
8    rust_2018_idioms,
9    rust_2021_compatibility
10)]
11
12use iota_sdk_types::{Address, ObjectId, StructTag, TypeTag, Version};
13use move_binary_format::{
14    CompiledModule,
15    file_format::{AbilitySet, SignatureToken},
16};
17use move_bytecode_utils::resolve_struct;
18use move_core_types::{account_address::AccountAddress, language_storage::ModuleId};
19use object::OBJECT_START_VERSION;
20
21use crate::{
22    base_types::{RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR},
23    id::RESOLVED_IOTA_ID,
24    iota_sdk_types_conversions::{struct_tag_core_to_sdk, type_tag_core_to_sdk},
25};
26
27#[macro_use]
28pub mod error;
29
30pub mod account_abstraction;
31pub mod auth_context;
32pub mod balance;
33pub mod base_types;
34pub mod clock;
35pub mod coin;
36pub mod coin_manager;
37pub mod collection_types;
38pub mod committee;
39pub mod config;
40pub mod crypto;
41pub mod deny_list_v1;
42pub mod deny_rule_governance;
43pub mod digests;
44pub mod display;
45pub mod dynamic_field;
46pub mod effects;
47pub mod epoch_data;
48pub mod event;
49pub mod executable_transaction;
50pub mod execution;
51pub mod execution_config_utils;
52pub mod full_checkpoint_content;
53pub mod gas;
54pub mod gas_coin;
55pub mod gas_model;
56pub mod global_state_hash;
57pub mod governance;
58pub mod id;
59pub mod in_memory_storage;
60pub mod inner_temporary_store;
61pub mod iota_sdk_types_conversions;
62pub mod iota_serde;
63pub mod iota_system_state;
64pub mod layout_resolver;
65pub mod message_envelope;
66pub mod messages_checkpoint;
67// Consensus message types (and the gRPC API types that carry them) are
68// node-only and pull in fastcrypto-tbls / tonic, which don't build on wasm32.
69#[cfg(not(target_arch = "wasm32"))]
70pub mod messages_consensus;
71#[cfg(not(target_arch = "wasm32"))]
72pub mod messages_grpc;
73pub mod messages_safe_client;
74pub mod metrics;
75pub mod mock_checkpoint_builder;
76pub mod move_authenticator;
77pub mod move_package;
78pub mod multisig;
79pub mod object;
80pub mod passkey_authenticator;
81pub mod programmable_transaction_builder;
82pub mod proto_value;
83pub mod ptb_trace;
84pub mod quorum_driver_types;
85pub mod randomness_state;
86pub mod signature;
87pub mod signature_verification;
88pub mod stardust;
89pub mod storage;
90pub mod supported_protocol_versions;
91pub mod test_checkpoint_data_builder;
92pub mod timelock;
93pub mod traffic_control;
94pub mod transaction;
95pub mod transaction_deny_rules;
96pub mod transaction_driver_types;
97pub mod transaction_executor;
98pub mod transfer;
99
100#[path = "./unit_tests/utils.rs"]
101pub mod utils;
102
103macro_rules! built_in_ids {
104    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
105        $(
106            pub const $addr: AccountAddress = builtin_address($init);
107            pub const $id: ObjectId = ObjectId::new($addr.into_bytes());
108        )*
109    }
110}
111
112macro_rules! built_in_pkgs {
113    ($($addr:ident / $id:ident = $init:expr);* $(;)?) => {
114        built_in_ids! { $($addr / $id = $init;)* }
115    }
116}
117
118built_in_pkgs! {
119    MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1;
120    IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2;
121    IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3;
122    GENESIS_BRIDGE_ADDRESS / GENESIS_BRIDGE_PACKAGE_ID = 0xb;
123    STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a;
124}
125
126built_in_ids! {
127    IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5;
128    IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6;
129    IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7;
130    IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8;
131    GENESIS_IOTA_BRIDGE_ADDRESS / GENESIS_IOTA_BRIDGE_OBJECT_ID = 0x9;
132    IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403;
133    IOTA_TRANSACTION_DENY_RULES_ADDRESS / IOTA_TRANSACTION_DENY_RULES_OBJECT_ID = 0xde9;
134}
135
136pub const SYSTEM_PACKAGE_ADDRESSES: [Address; 5] = [
137    Address::STD,
138    Address::FRAMEWORK,
139    Address::SYSTEM,
140    Address::GENESIS_BRIDGE,
141    Address::STARDUST,
142];
143
144pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: Version = OBJECT_START_VERSION;
145pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: Version = OBJECT_START_VERSION;
146
147const fn builtin_address(suffix: u16) -> AccountAddress {
148    let mut addr = [0u8; AccountAddress::LENGTH];
149    let [hi, lo] = suffix.to_be_bytes();
150    addr[AccountAddress::LENGTH - 2] = hi;
151    addr[AccountAddress::LENGTH - 1] = lo;
152    AccountAddress::new(addr)
153}
154
155pub fn iota_framework_address_concat_string(suffix: &str) -> String {
156    format!("{}{suffix}", Address::FRAMEWORK.to_short_hex())
157}
158
159/// Parses `s` as an address. Valid formats for addresses are:
160///
161/// - A 256bit number, encoded in decimal, or hexadecimal with a leading "0x"
162///   prefix.
163/// - One of a number of pre-defined named addresses: std, iota, iota_system,
164///   stardust.
165///
166/// Parsing succeeds if and only if `s` matches one of these formats exactly,
167/// with no remaining suffix. This function is intended for use within the
168/// authority codebases.
169pub fn parse_iota_address(s: &str) -> anyhow::Result<Address> {
170    use move_core_types::parsing::address::ParsedAddress;
171    Ok(Address::new(
172        ParsedAddress::parse(s)?
173            .into_account_address(&resolve_address)?
174            .into_bytes(),
175    ))
176}
177
178/// Parse `s` as a Module ID: An address (see `parse_iota_address`), followed by
179/// `::`, and then a module name (an identifier). Parsing succeeds if and only
180/// if `s` matches this format exactly, with no remaining input. This function
181/// is intended for use within the authority codebases.
182pub fn parse_iota_module_id(s: &str) -> anyhow::Result<ModuleId> {
183    use move_core_types::parsing::types::ParsedModuleId;
184    ParsedModuleId::parse(s)?.into_module_id(&resolve_address)
185}
186
187/// Parse `s` as a fully-qualified name: A Module ID (see
188/// `parse_iota_module_id`), followed by `::`, and then an identifier (for the
189/// module member). Parsing succeeds if and only if `s` matches this
190/// format exactly, with no remaining input. This function is intended for use
191/// within the authority codebases.
192pub fn parse_iota_fq_name(s: &str) -> anyhow::Result<(ModuleId, String)> {
193    use move_core_types::parsing::types::ParsedFqName;
194    ParsedFqName::parse(s)?.into_fq_name(&resolve_address)
195}
196
197/// Parse `s` as a struct type: A fully-qualified name, optionally followed by a
198/// list of type parameters (types -- see `parse_iota_type_tag`, separated by
199/// commas, surrounded by angle brackets). Parsing succeeds if and only if `s`
200/// matches this format exactly, with no remaining input. This function is
201/// intended for use within the authority codebase.
202pub fn parse_iota_struct_tag(s: &str) -> anyhow::Result<StructTag> {
203    use move_core_types::parsing::types::ParsedStructType;
204    ParsedStructType::parse(s)?
205        .into_struct_tag(&resolve_address)
206        .map(|s| struct_tag_core_to_sdk(&s))
207}
208
209/// Parse `s` as a type: Either a struct type (see `parse_iota_struct_tag`), a
210/// primitive type, or a vector with a type parameter. Parsing succeeds if and
211/// only if `s` matches this format exactly, with no remaining input. This
212/// function is intended for use within the authority codebase.
213pub fn parse_iota_type_tag(s: &str) -> anyhow::Result<TypeTag> {
214    use move_core_types::parsing::types::ParsedType;
215    ParsedType::parse(s)?
216        .into_type_tag(&resolve_address)
217        .map(|s| type_tag_core_to_sdk(&s))
218}
219
220/// Resolve well-known named addresses into numeric addresses.
221pub fn resolve_address(addr: &str) -> Option<AccountAddress> {
222    match addr {
223        "std" => Some(Address::STD),
224        "iota" => Some(Address::FRAMEWORK),
225        "iota_system" => Some(Address::SYSTEM),
226        "stardust" => Some(Address::STARDUST),
227        _ => None,
228    }
229    .map(|addr| AccountAddress::new(addr.into_bytes()))
230}
231
232pub trait MoveTypeTagTrait {
233    fn get_type_tag() -> TypeTag;
234}
235
236impl MoveTypeTagTrait for u8 {
237    fn get_type_tag() -> TypeTag {
238        TypeTag::U8
239    }
240}
241
242impl MoveTypeTagTrait for u64 {
243    fn get_type_tag() -> TypeTag {
244        TypeTag::U64
245    }
246}
247
248impl MoveTypeTagTrait for String {
249    fn get_type_tag() -> TypeTag {
250        TypeTag::Struct(Box::new(StructTag::new_string()))
251    }
252}
253
254impl MoveTypeTagTrait for ObjectId {
255    fn get_type_tag() -> TypeTag {
256        TypeTag::Address
257    }
258}
259
260impl MoveTypeTagTrait for Address {
261    fn get_type_tag() -> TypeTag {
262        TypeTag::Address
263    }
264}
265
266impl MoveTypeTagTrait for iota_sdk_move_types::iota_framework::object::ID {
267    fn get_type_tag() -> TypeTag {
268        TypeTag::Struct(Box::new(StructTag::new_id()))
269    }
270}
271
272impl<N: MoveTypeTagTrait> MoveTypeTagTrait
273    for iota_sdk_move_types::iota_framework::dynamic_object_field::Wrapper<N>
274{
275    fn get_type_tag() -> TypeTag {
276        TypeTag::Struct(Box::new(
277            dynamic_field::DynamicFieldInfo::dynamic_object_field_wrapper(N::get_type_tag()),
278        ))
279    }
280}
281
282impl MoveTypeTagTrait for iota_sdk_move_types::iota_framework::deny_list::ConfigKey {
283    fn get_type_tag() -> TypeTag {
284        TypeTag::Struct(Box::new(StructTag::new_deny_list_config_key()))
285    }
286}
287
288impl MoveTypeTagTrait for iota_sdk_move_types::iota_framework::deny_list::AddressKey {
289    fn get_type_tag() -> TypeTag {
290        TypeTag::Struct(Box::new(StructTag::new_deny_list_address_key()))
291    }
292}
293
294impl MoveTypeTagTrait for iota_sdk_move_types::iota_framework::deny_list::GlobalPauseKey {
295    fn get_type_tag() -> TypeTag {
296        TypeTag::Struct(Box::new(StructTag::new_deny_list_global_pause_key()))
297    }
298}
299
300impl<T: MoveTypeTagTrait> MoveTypeTagTrait for Vec<T> {
301    fn get_type_tag() -> TypeTag {
302        TypeTag::Vector(Box::new(T::get_type_tag()))
303    }
304}
305
306/// Check if a type is a primitive type in optimistic mode. It invokes the inner
307/// function with is_strict = false.
308pub fn is_primitive(
309    view: &CompiledModule,
310    function_type_args: &[AbilitySet],
311    s: &SignatureToken,
312) -> bool {
313    is_primitive_inner(view, function_type_args, s, false)
314}
315
316/// Check if a type is a primitive type in strict mode. It invokes the inner
317/// function with is_strict = true.
318pub fn is_primitive_strict(
319    view: &CompiledModule,
320    function_type_args: &[AbilitySet],
321    s: &SignatureToken,
322) -> bool {
323    is_primitive_inner(view, function_type_args, s, true)
324}
325
326/// Check if a type is a primitive type.
327/// In optimistic mode (is_strict = false), a type parameter is considered
328/// primitive if it has no key ability. In strict mode (is_strict = true), a
329/// type parameter is considered primitive if it has at least copy or drop
330/// ability.
331pub fn is_primitive_inner(
332    view: &CompiledModule,
333    function_type_args: &[AbilitySet],
334    s: &SignatureToken,
335    is_strict: bool,
336) -> bool {
337    use SignatureToken as S;
338    match s {
339        S::Bool | S::U8 | S::U16 | S::U32 | S::U64 | S::U128 | S::U256 | S::Address => true,
340        S::Signer => false,
341        // optimistic -> no primitive has key
342        // strict -> all primitives have at least copy or drop
343        S::TypeParameter(idx) => {
344            if !is_strict {
345                // optimistic: has no key
346                !function_type_args[*idx as usize].has_key()
347            } else {
348                // strict: has at least one of: copy or drop (or store and one of the others).
349                // copy or drop abilities always imply having no key, but here we double check
350                let abilities = function_type_args[*idx as usize];
351                !abilities.has_key() && (abilities.has_copy() || abilities.has_drop())
352            }
353        }
354
355        S::Datatype(idx) => [RESOLVED_IOTA_ID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR]
356            .contains(&resolve_struct(view, *idx)),
357
358        S::DatatypeInstantiation(inst) => {
359            let (idx, targs) = &**inst;
360            let resolved_struct = resolve_struct(view, *idx);
361            // option is a primitive
362            resolved_struct == RESOLVED_STD_OPTION
363                && targs.len() == 1
364                && is_primitive_inner(view, function_type_args, &targs[0], is_strict)
365        }
366
367        S::Vector(inner) => is_primitive_inner(view, function_type_args, inner, is_strict),
368        S::Reference(_) | S::MutableReference(_) => false,
369    }
370}
371
372pub fn is_object(
373    view: &CompiledModule,
374    function_type_args: &[AbilitySet],
375    t: &SignatureToken,
376) -> Result<bool, String> {
377    use SignatureToken as S;
378    match t {
379        S::Reference(inner) | S::MutableReference(inner) => {
380            is_object(view, function_type_args, inner)
381        }
382        _ => is_object_struct(view, function_type_args, t),
383    }
384}
385
386pub fn is_object_vector(
387    view: &CompiledModule,
388    function_type_args: &[AbilitySet],
389    t: &SignatureToken,
390) -> Result<bool, String> {
391    use SignatureToken as S;
392    match t {
393        S::Vector(inner) => is_object_struct(view, function_type_args, inner),
394        _ => is_object_struct(view, function_type_args, t),
395    }
396}
397
398pub fn is_object_struct(
399    view: &CompiledModule,
400    function_type_args: &[AbilitySet],
401    s: &SignatureToken,
402) -> Result<bool, String> {
403    use SignatureToken as S;
404    match s {
405        S::Bool
406        | S::U8
407        | S::U16
408        | S::U32
409        | S::U64
410        | S::U128
411        | S::U256
412        | S::Address
413        | S::Signer
414        | S::Vector(_)
415        | S::Reference(_)
416        | S::MutableReference(_) => Ok(false),
417        S::TypeParameter(idx) => Ok(function_type_args
418            .get(*idx as usize)
419            .map(|abs| abs.has_key())
420            .unwrap_or(false)),
421        S::Datatype(_) | S::DatatypeInstantiation(_) => {
422            let abilities = view
423                .abilities(s, function_type_args)
424                .map_err(|vm_err| vm_err.to_string())?;
425            Ok(abilities.has_key())
426        }
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use expect_test::expect;
433
434    use super::*;
435
436    #[test]
437    fn test_parse_iota_numeric_address() {
438        let result = parse_iota_address("0x2").expect("should not error");
439
440        let expected =
441            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
442        expected.assert_eq(&result.to_string());
443    }
444
445    #[test]
446    fn test_parse_iota_named_address() {
447        let result = parse_iota_address("iota").expect("should not error");
448
449        let expected =
450            expect!["0x0000000000000000000000000000000000000000000000000000000000000002"];
451        expected.assert_eq(&result.to_string());
452    }
453
454    #[test]
455    fn test_parse_iota_module_id() {
456        let result = parse_iota_module_id("0x2::iota").expect("should not error");
457        let expected =
458            expect!["0x0000000000000000000000000000000000000000000000000000000000000002::iota"];
459        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
460    }
461
462    #[test]
463    fn test_parse_iota_fq_name() {
464        let (module, name) = parse_iota_fq_name("0x2::object::new").expect("should not error");
465        let expected = expect![
466            "0x0000000000000000000000000000000000000000000000000000000000000002::object::new"
467        ];
468        expected.assert_eq(&format!(
469            "{}::{name}",
470            module.to_canonical_display(/* with_prefix */ true)
471        ));
472    }
473
474    #[test]
475    fn test_parse_iota_struct_tag_short_account_addr() {
476        let result = parse_iota_struct_tag("0x2::iota::IOTA").expect("should not error");
477
478        let expected = expect!["0x2::iota::IOTA"];
479        expected.assert_eq(&result.to_string());
480
481        let expected = expect![
482            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
483        ];
484        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
485    }
486
487    #[test]
488    fn test_parse_iota_struct_tag_long_account_addr() {
489        let result = parse_iota_struct_tag(
490            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA",
491        )
492        .expect("should not error");
493
494        let expected = expect!["0x2::iota::IOTA"];
495        expected.assert_eq(&result.to_string());
496
497        let expected = expect![
498            "0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA"
499        ];
500        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
501    }
502
503    #[test]
504    fn test_parse_iota_struct_with_type_param_short_addr() {
505        let result =
506            parse_iota_struct_tag("0x2::coin::COIN<0x2::iota::IOTA>").expect("should not error");
507
508        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
509        expected.assert_eq(&result.to_string());
510
511        let expected = expect![
512            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
513        ];
514        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
515    }
516
517    #[test]
518    fn test_parse_iota_struct_with_type_param_long_addr() {
519        let result = parse_iota_struct_tag("0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>")
520            .expect("should not error");
521
522        let expected = expect!["0x2::coin::COIN<0x2::iota::IOTA>"];
523        expected.assert_eq(&result.to_string());
524
525        let expected = expect![
526            "0x0000000000000000000000000000000000000000000000000000000000000002::coin::COIN<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>"
527        ];
528        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
529    }
530
531    #[test]
532    fn test_complex_struct_tag_with_short_addr() {
533        let result = parse_iota_struct_tag(
534            "0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>",
535        )
536        .expect("should not error");
537
538        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
539        expected.assert_eq(&result.to_string());
540
541        let expected = expect![
542            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
543        ];
544        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
545    }
546
547    #[test]
548    fn test_complex_struct_tag_with_long_addr() {
549        let result = parse_iota_struct_tag("0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>")
550            .expect("should not error");
551
552        let expected = expect!["0xe7::vec_coin::VecCoin<vector<0x2::coin::Coin<0x2::iota::IOTA>>>"];
553        expected.assert_eq(&result.to_string());
554
555        let expected = expect![
556            "0x00000000000000000000000000000000000000000000000000000000000000e7::vec_coin::VecCoin<vector<0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<0x0000000000000000000000000000000000000000000000000000000000000002::iota::IOTA>>>"
557        ];
558        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
559    }
560
561    #[test]
562    fn test_dynamic_field_short_addr() {
563        let result = parse_iota_struct_tag(
564            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
565        )
566        .expect("should not error");
567
568        let expected =
569            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
570        expected.assert_eq(&result.to_string());
571
572        let expected = expect![
573            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
574        ];
575        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
576    }
577
578    #[test]
579    fn test_dynamic_field_long_addr() {
580        let result = parse_iota_struct_tag(
581            "0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>",
582        )
583        .expect("should not error");
584
585        let expected =
586            expect!["0x2::dynamic_field::Field<address, 0x2::balance::Balance<0x234::coin::COIN>>"];
587        expected.assert_eq(&result.to_string());
588
589        let expected = expect![
590            "0x0000000000000000000000000000000000000000000000000000000000000002::dynamic_field::Field<address,0x0000000000000000000000000000000000000000000000000000000000000002::balance::Balance<0x0000000000000000000000000000000000000000000000000000000000000234::coin::COIN>>"
591        ];
592        expected.assert_eq(&result.to_canonical_string(/* with_prefix */ true));
593    }
594}