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