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