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