From 15875ce78dd58b9d330bbeb7807cb4d3351abd4f Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Fri, 8 Aug 2025 11:54:05 +0200 Subject: [PATCH 1/9] feat(ffi): TypeTag and StructTag --- crates/iota-sdk-ffi/src/types/mod.rs | 1 + crates/iota-sdk-ffi/src/types/struct_tag.rs | 55 ++++++ crates/iota-sdk-ffi/src/types/type_tag.rs | 197 ++++++++++++++++++++ crates/iota-sdk-types/src/type_tag/mod.rs | 14 +- 4 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 crates/iota-sdk-ffi/src/types/struct_tag.rs diff --git a/crates/iota-sdk-ffi/src/types/mod.rs b/crates/iota-sdk-ffi/src/types/mod.rs index f374c259d..d3341aa19 100644 --- a/crates/iota-sdk-ffi/src/types/mod.rs +++ b/crates/iota-sdk-ffi/src/types/mod.rs @@ -10,5 +10,6 @@ pub mod gas; pub mod graphql; pub mod object; pub mod signature; +pub mod struct_tag; pub mod transaction; pub mod type_tag; diff --git a/crates/iota-sdk-ffi/src/types/struct_tag.rs b/crates/iota-sdk-ffi/src/types/struct_tag.rs new file mode 100644 index 000000000..389176cc1 --- /dev/null +++ b/crates/iota-sdk-ffi/src/types/struct_tag.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::Arc; + +/// Type information for a move struct +/// +/// # BCS +/// +/// The BCS serialized form for this type is defined by the following ABNF: +/// +/// ```text +/// struct-tag = address ; address of the package +/// identifier ; name of the module +/// identifier ; name of the type +/// (vector type-tag) ; type parameters +/// ``` +#[derive(Clone, Debug, derive_more::From, uniffi::Object)] +pub struct StructTag(pub iota_types::StructTag); + +#[uniffi::export] +impl StructTag { + #[uniffi::constructor] + pub fn coin(type_tag: &super::type_tag::TypeTag) -> Self { + Self(iota_types::StructTag::coin(type_tag.0.clone())) + } + + /// Checks if this is a Coin type + pub fn coin_type_opt(&self) -> Option> { + self.0 + .coin_type_opt() + .cloned() + .map(Into::into) + .map(Arc::new) + } + + /// Checks if this is a Coin type + pub fn coin_type(&self) -> super::type_tag::TypeTag { + self.0.coin_type().clone().into() + } + + #[uniffi::constructor] + pub fn gas_coin() -> Self { + Self(iota_types::StructTag::gas_coin()) + } + + #[uniffi::constructor] + pub fn staked_iota() -> Self { + Self(iota_types::StructTag::staked_iota()) + } + + pub fn address(&self) -> super::address::Address { + self.0.address().into() + } +} diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index 3f472c0b7..103c77009 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -1,5 +1,202 @@ // Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::sync::Arc; + +/// Type of a move value +/// +/// # BCS +/// +/// The BCS serialized form for this type is defined by the following ABNF: +/// +/// ```text +/// type-tag = type-tag-u8 \ +/// type-tag-u16 \ +/// type-tag-u32 \ +/// type-tag-u64 \ +/// type-tag-u128 \ +/// type-tag-u256 \ +/// type-tag-bool \ +/// type-tag-address \ +/// type-tag-signer \ +/// type-tag-vector \ +/// type-tag-struct +/// +/// type-tag-u8 = %x01 +/// type-tag-u16 = %x08 +/// type-tag-u32 = %x09 +/// type-tag-u64 = %x02 +/// type-tag-u128 = %x03 +/// type-tag-u256 = %x0a +/// type-tag-bool = %x00 +/// type-tag-address = %x04 +/// type-tag-signer = %x05 +/// type-tag-vector = %x06 type-tag +/// type-tag-struct = %x07 struct-tag +/// ``` #[derive(Clone, Debug, derive_more::From, uniffi::Object)] pub struct TypeTag(pub iota_types::TypeTag); + +#[uniffi::export] +impl TypeTag { + #[inline] + pub fn is_u8(&self) -> bool { + self.0.is_u8() + } + + #[inline] + pub fn is_u16(&self) -> bool { + self.0.is_u16() + } + + #[inline] + pub fn is_u32(&self) -> bool { + self.0.is_u32() + } + + #[inline] + pub fn is_u64(&self) -> bool { + self.0.is_u64() + } + + #[inline] + pub fn is_u128(&self) -> bool { + self.0.is_u128() + } + + #[inline] + pub fn is_u256(&self) -> bool { + self.0.is_u256() + } + + #[inline] + pub fn is_bool(&self) -> bool { + self.0.is_bool() + } + + #[inline] + pub fn is_address(&self) -> bool { + self.0.is_address() + } + + #[inline] + pub fn is_signer(&self) -> bool { + self.0.is_signer() + } + + #[inline] + pub fn is_vector(&self) -> bool { + self.0.is_vector() + } + + #[inline] + pub fn as_vector_type_tag_opt(&self) -> Option> { + self.0 + .as_vector_type_tag_opt() + .cloned() + .map(Into::into) + .map(Arc::new) + } + + #[inline] + pub fn as_vector_type_tag(&self) -> TypeTag { + self.0.as_vector_type_tag().clone().into() + } + + // TODO cannot take self? + #[inline] + pub fn into_vector_type_tag_opt(&self) -> Option> { + self.clone() + .0 + .into_vector_type_tag_opt() + .map(Into::into) + .map(Arc::new) + } + + // TODO cannot take self? + #[inline] + pub fn into_vector_type_tag(&self) -> TypeTag { + self.clone().0.into_vector_type_tag().into() + } + + #[inline] + pub fn is_struct(&self) -> bool { + self.0.is_struct() + } + + #[inline] + pub fn as_struct_tag_opt(&self) -> Option> { + self.0 + .as_struct_tag_opt() + .cloned() + .map(Into::into) + .map(Arc::new) + } + + #[inline] + pub fn as_struct_tag(&self) -> super::struct_tag::StructTag { + self.0.as_struct_tag().clone().into() + } + + // TODO cannot take self? + #[inline] + pub fn into_struct_tag_opt(&self) -> Option> { + self.clone() + .0 + .into_struct_tag_opt() + .clone() + .map(Into::into) + .map(Arc::new) + } + + // TODO cannot take self? + #[inline] + pub fn into_struct_tag(&self) -> super::struct_tag::StructTag { + self.clone().0.into_struct_tag().into() + } + + #[uniffi::constructor] + pub fn u8() -> Self { + Self(iota_types::TypeTag::U8) + } + + #[uniffi::constructor] + pub fn u16() -> Self { + Self(iota_types::TypeTag::U16) + } + + #[uniffi::constructor] + pub fn u32() -> Self { + Self(iota_types::TypeTag::U32) + } + + #[uniffi::constructor] + pub fn u64() -> Self { + Self(iota_types::TypeTag::U64) + } + + #[uniffi::constructor] + pub fn u128() -> Self { + Self(iota_types::TypeTag::U128) + } + + #[uniffi::constructor] + pub fn u256() -> Self { + Self(iota_types::TypeTag::U256) + } + + #[uniffi::constructor] + pub fn bool() -> Self { + Self(iota_types::TypeTag::Bool) + } + + #[uniffi::constructor] + pub fn address() -> Self { + Self(iota_types::TypeTag::Address) + } + + #[uniffi::constructor] + pub fn signer() -> Self { + Self(iota_types::TypeTag::Signer) + } +} diff --git a/crates/iota-sdk-types/src/type_tag/mod.rs b/crates/iota-sdk-types/src/type_tag/mod.rs index 9b3e513b0..cf7c1cf92 100644 --- a/crates/iota-sdk-types/src/type_tag/mod.rs +++ b/crates/iota-sdk-types/src/type_tag/mod.rs @@ -61,11 +61,11 @@ pub enum TypeTag { impl TypeTag { crate::def_is!(U8, U16, U32, U64, U128, U256, Bool, Address, Signer); - pub fn is_vector_type(&self) -> bool { + pub fn is_vector(&self) -> bool { matches!(self, Self::Vector(_)) } - pub fn as_vector_type_opt(&self) -> Option<&TypeTag> { + pub fn as_vector_type_tag_opt(&self) -> Option<&TypeTag> { if let Self::Vector(inner) = self { Some(inner) } else { @@ -73,11 +73,11 @@ impl TypeTag { } } - pub fn as_vector_type(&self) -> &TypeTag { - self.as_vector_type_opt().expect("not a Vector") + pub fn as_vector_type_tag(&self) -> &TypeTag { + self.as_vector_type_tag_opt().expect("not a Vector") } - pub fn into_vector_type_opt(self) -> Option { + pub fn into_vector_type_tag_opt(self) -> Option { if let Self::Vector(inner) = self { Some(*inner) } else { @@ -85,8 +85,8 @@ impl TypeTag { } } - pub fn into_vector_type(self) -> TypeTag { - self.into_vector_type_opt().expect("not a Vector type") + pub fn into_vector_type_tag(self) -> TypeTag { + self.into_vector_type_tag_opt().expect("not a Vector") } pub fn is_struct(&self) -> bool { From bc065f87c059a243d62beee0250114535c63bbd9 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 09:16:55 +0200 Subject: [PATCH 2/9] remove TODOs --- crates/iota-sdk-ffi/src/types/type_tag.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index 103c77009..64e926b25 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -103,7 +103,6 @@ impl TypeTag { self.0.as_vector_type_tag().clone().into() } - // TODO cannot take self? #[inline] pub fn into_vector_type_tag_opt(&self) -> Option> { self.clone() @@ -113,7 +112,6 @@ impl TypeTag { .map(Arc::new) } - // TODO cannot take self? #[inline] pub fn into_vector_type_tag(&self) -> TypeTag { self.clone().0.into_vector_type_tag().into() @@ -138,7 +136,6 @@ impl TypeTag { self.0.as_struct_tag().clone().into() } - // TODO cannot take self? #[inline] pub fn into_struct_tag_opt(&self) -> Option> { self.clone() @@ -149,7 +146,6 @@ impl TypeTag { .map(Arc::new) } - // TODO cannot take self? #[inline] pub fn into_struct_tag(&self) -> super::struct_tag::StructTag { self.clone().0.into_struct_tag().into() From a303b3e6715f72f41fc89a11c3eb3a16490d609d Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 09:50:32 +0200 Subject: [PATCH 3/9] remove duplicate methods --- crates/iota-sdk-ffi/src/types/type_tag.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index 64e926b25..0cabc81f5 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -103,15 +103,6 @@ impl TypeTag { self.0.as_vector_type_tag().clone().into() } - #[inline] - pub fn into_vector_type_tag_opt(&self) -> Option> { - self.clone() - .0 - .into_vector_type_tag_opt() - .map(Into::into) - .map(Arc::new) - } - #[inline] pub fn into_vector_type_tag(&self) -> TypeTag { self.clone().0.into_vector_type_tag().into() @@ -136,16 +127,6 @@ impl TypeTag { self.0.as_struct_tag().clone().into() } - #[inline] - pub fn into_struct_tag_opt(&self) -> Option> { - self.clone() - .0 - .into_struct_tag_opt() - .clone() - .map(Into::into) - .map(Arc::new) - } - #[inline] pub fn into_struct_tag(&self) -> super::struct_tag::StructTag { self.clone().0.into_struct_tag().into() From c16f8f726b9678529274b784400317767baeb0c9 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 11:25:38 +0200 Subject: [PATCH 4/9] remove into_ --- crates/iota-sdk-ffi/src/types/type_tag.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index 0cabc81f5..eb143b2f1 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -103,11 +103,6 @@ impl TypeTag { self.0.as_vector_type_tag().clone().into() } - #[inline] - pub fn into_vector_type_tag(&self) -> TypeTag { - self.clone().0.into_vector_type_tag().into() - } - #[inline] pub fn is_struct(&self) -> bool { self.0.is_struct() @@ -127,11 +122,6 @@ impl TypeTag { self.0.as_struct_tag().clone().into() } - #[inline] - pub fn into_struct_tag(&self) -> super::struct_tag::StructTag { - self.clone().0.into_struct_tag().into() - } - #[uniffi::constructor] pub fn u8() -> Self { Self(iota_types::TypeTag::U8) From a81bba850d0ac58c799450c373595f009bc72106 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 11:31:48 +0200 Subject: [PATCH 5/9] vector/struct constructors --- crates/iota-sdk-ffi/src/types/type_tag.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index eb143b2f1..2dea44445 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -3,6 +3,8 @@ use std::sync::Arc; +use iota_types::StructTag; + /// Type of a move value /// /// # BCS @@ -166,4 +168,14 @@ impl TypeTag { pub fn signer() -> Self { Self(iota_types::TypeTag::Signer) } + + #[uniffi::constructor] + pub fn vector(type_tag: &TypeTag) -> Self { + Self(iota_types::TypeTag::Vector(Box::new(type_tag.0.clone()))) + } + + #[uniffi::constructor] + pub fn struct_tag(struct_tag: &super::struct_tag::StructTag) -> Self { + Self(iota_types::TypeTag::Struct(Box::new(struct_tag.0.clone()))) + } } From d2f3a8bfff68f682cafb66ab58896a5de493165f Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 11:49:09 +0200 Subject: [PATCH 6/9] remove unused import --- crates/iota-sdk-ffi/src/types/type_tag.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index 2dea44445..304152a58 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -3,8 +3,6 @@ use std::sync::Arc; -use iota_types::StructTag; - /// Type of a move value /// /// # BCS From 265ba3de8063580fd85344a8aaa4d68c336c11d1 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 12:11:10 +0200 Subject: [PATCH 7/9] Add StructTag::new --- crates/iota-sdk-ffi/src/types/struct_tag.rs | 80 +++++++++++++++++++-- crates/iota-sdk-types/src/type_tag/mod.rs | 2 +- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/struct_tag.rs b/crates/iota-sdk-ffi/src/types/struct_tag.rs index 389176cc1..0a5fda732 100644 --- a/crates/iota-sdk-ffi/src/types/struct_tag.rs +++ b/crates/iota-sdk-ffi/src/types/struct_tag.rs @@ -3,6 +3,60 @@ use std::sync::Arc; +use crate::types::{address::Address, type_tag::TypeTag}; + +#[derive(Clone, Debug, derive_more::From, uniffi::Record)] +pub struct TypeParseError { + source: String, +} + +impl From for iota_types::TypeParseError { + fn from(value: TypeParseError) -> Self { + iota_types::TypeParseError { + source: value.source, + } + } +} + +impl From for TypeParseError { + fn from(value: iota_types::TypeParseError) -> Self { + TypeParseError { + source: value.source, + } + } +} + +/// A move identifier +/// +/// # BCS +/// +/// The BCS serialized form for this type is defined by the following ABNF: +/// +/// ```text +/// identifier = %x01-80 ; length of the identifier +/// (ALPHA *127(ALPHA / DIGIT / UNDERSCORE)) / +/// (UNDERSCORE 1*127(ALPHA / DIGIT / UNDERSCORE)) +/// +/// UNDERSCORE = %x95 +/// ``` +#[derive(Clone, Debug, derive_more::From, uniffi::Object)] +pub struct Identifier(iota_types::Identifier); + +impl Identifier { + #[uniffi::constructor] + pub fn new(identifier: impl AsRef) -> Result { + Ok(Self(iota_types::Identifier::new(identifier)?)) + } + + pub fn into_inner(self) -> Box { + self.0.into_inner() + } + + pub fn as_str(&self) -> &str { + &self.0.as_str() + } +} + /// Type information for a move struct /// /// # BCS @@ -21,12 +75,30 @@ pub struct StructTag(pub iota_types::StructTag); #[uniffi::export] impl StructTag { #[uniffi::constructor] - pub fn coin(type_tag: &super::type_tag::TypeTag) -> Self { + pub fn new( + address: &Address, + module: &Identifier, + name: &Identifier, + type_params: Vec>, + ) -> Self { + Self(iota_types::StructTag { + address: address.0.clone(), + module: module.0.clone(), + name: name.0.clone(), + type_params: type_params + .iter() + .map(|type_tag| type_tag.0.clone()) + .collect(), + }) + } + + #[uniffi::constructor] + pub fn coin(type_tag: &TypeTag) -> Self { Self(iota_types::StructTag::coin(type_tag.0.clone())) } /// Checks if this is a Coin type - pub fn coin_type_opt(&self) -> Option> { + pub fn coin_type_opt(&self) -> Option> { self.0 .coin_type_opt() .cloned() @@ -35,7 +107,7 @@ impl StructTag { } /// Checks if this is a Coin type - pub fn coin_type(&self) -> super::type_tag::TypeTag { + pub fn coin_type(&self) -> TypeTag { self.0.coin_type().clone().into() } @@ -49,7 +121,7 @@ impl StructTag { Self(iota_types::StructTag::staked_iota()) } - pub fn address(&self) -> super::address::Address { + pub fn address(&self) -> Address { self.0.address().into() } } diff --git a/crates/iota-sdk-types/src/type_tag/mod.rs b/crates/iota-sdk-types/src/type_tag/mod.rs index cf7c1cf92..839e90b1b 100644 --- a/crates/iota-sdk-types/src/type_tag/mod.rs +++ b/crates/iota-sdk-types/src/type_tag/mod.rs @@ -190,7 +190,7 @@ impl From for TypeTag { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct TypeParseError { - source: String, + pub source: String, } impl std::fmt::Display for TypeParseError { From dd42fbb0cff9f95ea705db1c21e180dfe587c4ae Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 12:12:38 +0200 Subject: [PATCH 8/9] import StructTag --- crates/iota-sdk-ffi/src/types/type_tag.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/type_tag.rs b/crates/iota-sdk-ffi/src/types/type_tag.rs index 304152a58..9a154a4ce 100644 --- a/crates/iota-sdk-ffi/src/types/type_tag.rs +++ b/crates/iota-sdk-ffi/src/types/type_tag.rs @@ -3,6 +3,8 @@ use std::sync::Arc; +use crate::types::struct_tag::StructTag; + /// Type of a move value /// /// # BCS @@ -109,7 +111,7 @@ impl TypeTag { } #[inline] - pub fn as_struct_tag_opt(&self) -> Option> { + pub fn as_struct_tag_opt(&self) -> Option> { self.0 .as_struct_tag_opt() .cloned() @@ -118,7 +120,7 @@ impl TypeTag { } #[inline] - pub fn as_struct_tag(&self) -> super::struct_tag::StructTag { + pub fn as_struct_tag(&self) -> StructTag { self.0.as_struct_tag().clone().into() } @@ -173,7 +175,7 @@ impl TypeTag { } #[uniffi::constructor] - pub fn struct_tag(struct_tag: &super::struct_tag::StructTag) -> Self { + pub fn struct_tag(struct_tag: &StructTag) -> Self { Self(iota_types::TypeTag::Struct(Box::new(struct_tag.0.clone()))) } } From 62379d001684bdb4e8b5123596852608fb7c904b Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 11 Aug 2025 13:14:27 +0200 Subject: [PATCH 9/9] clippy --- crates/iota-sdk-ffi/src/types/struct_tag.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/iota-sdk-ffi/src/types/struct_tag.rs b/crates/iota-sdk-ffi/src/types/struct_tag.rs index 0a5fda732..4741016cf 100644 --- a/crates/iota-sdk-ffi/src/types/struct_tag.rs +++ b/crates/iota-sdk-ffi/src/types/struct_tag.rs @@ -53,7 +53,7 @@ impl Identifier { } pub fn as_str(&self) -> &str { - &self.0.as_str() + self.0.as_str() } } @@ -82,7 +82,7 @@ impl StructTag { type_params: Vec>, ) -> Self { Self(iota_types::StructTag { - address: address.0.clone(), + address: address.0, module: module.0.clone(), name: name.0.clone(), type_params: type_params