Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7af3d89
feat: Add Move Authenticator support
DaughterOfMars Dec 2, 2025
171e915
cleanup
DaughterOfMars Dec 2, 2025
e6d3857
improvements
DaughterOfMars Dec 2, 2025
5cfd1e5
rename signature schemes
DaughterOfMars Dec 2, 2025
56cf83b
build bindings
DaughterOfMars Dec 2, 2025
2f40ea7
Add bindings fns
DaughterOfMars Dec 2, 2025
9e74ab7
clippy
DaughterOfMars Dec 2, 2025
0156060
fix tests
DaughterOfMars Dec 2, 2025
c6cce0f
use Vec<String> for type args
DaughterOfMars Dec 2, 2025
d38ae8e
fix features
DaughterOfMars Dec 2, 2025
96b5b61
another one
DaughterOfMars Dec 2, 2025
6066a06
update bindings
DaughterOfMars Dec 2, 2025
8ceee72
add more suffixes
DaughterOfMars Dec 3, 2025
97d69ea
refactor flow
DaughterOfMars Dec 3, 2025
fabaab7
tiny cleanup
DaughterOfMars Dec 3, 2025
72033cb
delete example for now
DaughterOfMars Dec 4, 2025
a39b9c8
fmt
DaughterOfMars Dec 4, 2025
496e142
expect with message
DaughterOfMars Dec 5, 2025
917af55
Merge branch 'develop' into feat/move-auth
DaughterOfMars Dec 8, 2025
e610c0d
bindings
DaughterOfMars Dec 8, 2025
48cab0d
remove error
DaughterOfMars Dec 9, 2025
f0eeff8
Merge branch 'develop' into feat/move-auth
thibault-martinez Dec 15, 2025
70967ae
verifier variables name consistency
thibault-martinez Dec 16, 2025
2facb0c
remove "new" from doc
thibault-martinez Dec 16, 2025
2242ed2
Merge branch 'develop' into feat/move-auth
thibault-martinez Dec 16, 2025
ff38492
doc nit
thibault-martinez Dec 17, 2025
6da4116
Merge branch 'develop' into feat/move-auth
thibault-martinez Dec 17, 2025
e9b223d
add move-auth-flag to doc
thibault-martinez Dec 17, 2025
a11b66e
remove outdated commented tests
thibault-martinez Dec 17, 2025
5dd9fa1
rename to new_shared
thibault-martinez Dec 17, 2025
d02b502
remove MoveAuthenticator::new
thibault-martinez Dec 18, 2025
e87ee41
MoveAuthenticatorArgs
thibault-martinez Dec 18, 2025
f2ed2dc
remove mutable getters
thibault-martinez Dec 18, 2025
e54bd5e
make it work
thibault-martinez Dec 18, 2025
b4d9c10
clean the example
thibault-martinez Dec 19, 2025
7b79e05
Add InvalidMoveAuthAccount
thibault-martinez Dec 19, 2025
ba342b1
undo a rename
thibault-martinez Dec 19, 2025
330fb8a
MoveAuthenticator cleanup
thibault-martinez Dec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ go: ## Build Go bindings
@printf "Building Go bindings...\n"
@$(build_binding) \
uniffi-bindgen-go --library target/release/libiota_sdk_ffi$${LIB_EXT} --out-dir bindings/go --no-format --config bindings/go/uniffi.toml || exit $$?
# TODO: For some reason only the .h file is renamed, not the .go file
@# TODO: For some reason only the .h file is renamed, not the .go file
@mv bindings/go/iota_sdk/iota_sdk_ffi.go bindings/go/iota_sdk/iota_sdk.go
@sed -i.bak "s/^package iota_sdk_ffi$$/package iota_sdk/" bindings/go/iota_sdk/iota_sdk.go && rm bindings/go/iota_sdk/iota_sdk.go.bak

Expand Down
453 changes: 395 additions & 58 deletions bindings/go/iota_sdk/iota_sdk.go

Large diffs are not rendered by default.

227 changes: 179 additions & 48 deletions bindings/go/iota_sdk/iota_sdk.h

Large diffs are not rendered by default.

656 changes: 598 additions & 58 deletions bindings/kotlin/lib/iota_sdk/iota_sdk_ffi.kt

Large diffs are not rendered by default.

568 changes: 488 additions & 80 deletions bindings/python/lib/iota_sdk.py

Large diffs are not rendered by default.

29 changes: 17 additions & 12 deletions crates/iota-sdk-crypto/src/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,30 +223,33 @@ impl UserSignatureVerifier {
impl Verifier<UserSignature> for UserSignatureVerifier {
fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
match signature {
UserSignature::Simple(simple_signature) => {
crate::simple::SimpleVerifier.verify(message, simple_signature)
UserSignature::Simple(signature) => {
crate::simple::SimpleVerifier.verify(message, signature)
}
UserSignature::Multisig(multisig) => self.inner.verify(message, multisig),
UserSignature::Multisig(signature) => self.inner.verify(message, signature),
#[cfg(not(feature = "zklogin"))]
UserSignature::ZkLogin(_) => Err(SignatureError::from_source(
UserSignature::ZkLoginAuthenticator(_) => Err(SignatureError::from_source(
"support for zklogin is not enabled",
)),
#[cfg(feature = "zklogin")]
UserSignature::ZkLogin(zklogin_authenticator) => {
UserSignature::ZkLoginAuthenticator(authenticator) => {
let zklogin_verifier = self
.zklogin_verifier()
.ok_or_else(|| SignatureError::from_source("no zklogin verifier provided"))?;

zklogin_verifier.verify(message, zklogin_authenticator.as_ref())
zklogin_verifier.verify(message, authenticator.as_ref())
}
#[cfg(not(feature = "passkey"))]
UserSignature::Passkey(_) => Err(SignatureError::from_source(
UserSignature::PasskeyAuthenticator(_) => Err(SignatureError::from_source(
"support for passkey is not enabled",
)),
#[cfg(feature = "passkey")]
UserSignature::Passkey(passkey_authenticator) => {
crate::passkey::PasskeyVerifier::default().verify(message, passkey_authenticator)
UserSignature::PasskeyAuthenticator(authenticator) => {
crate::passkey::PasskeyVerifier::default().verify(message, authenticator)
}
UserSignature::MoveAuthenticator(_) => Err(SignatureError::from_source(
"move authenticators cannot be verified",
)),
}
}
}
Expand Down Expand Up @@ -380,19 +383,21 @@ fn multisig_pubkey_and_signature_from_user_signature(
MultisigMemberSignature::Secp256r1(signature),
)),
#[cfg(not(feature = "zklogin"))]
UserSignature::ZkLogin(_) => Err(SignatureError::from_source(
UserSignature::ZkLoginAuthenticator(_) => Err(SignatureError::from_source(
"support for zklogin is not enabled",
)),
#[cfg(feature = "zklogin")]
UserSignature::ZkLogin(zklogin_authenticator) => {
UserSignature::ZkLoginAuthenticator(zklogin_authenticator) => {
let zklogin_identifier = zklogin_authenticator.inputs.public_identifier().to_owned();
Ok((
MultisigMemberPublicKey::ZkLogin(zklogin_identifier),
MultisigMemberSignature::ZkLogin(zklogin_authenticator),
))
}

UserSignature::Multisig(_) | UserSignature::Passkey(_) => {
UserSignature::Multisig(_)
| UserSignature::PasskeyAuthenticator(_)
| UserSignature::MoveAuthenticator(_) => {
Err(SignatureError::from_source("invalid signature scheme"))
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/iota-sdk-crypto/src/passkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Verifier<PasskeyAuthenticator> for PasskeyVerifier {

impl Verifier<UserSignature> for PasskeyVerifier {
fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
let UserSignature::Passkey(authenticator) = signature else {
let UserSignature::PasskeyAuthenticator(authenticator) = signature else {
return Err(SignatureError::from_source("not a passkey authenticator"));
};

Expand Down
6 changes: 3 additions & 3 deletions crates/iota-sdk-crypto/src/zklogin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ impl Verifier<ZkLoginAuthenticator> for ZkloginVerifier {

impl Verifier<UserSignature> for ZkloginVerifier {
fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
let UserSignature::ZkLogin(zklogin_authenticator) = signature else {
return Err(SignatureError::from_source("not a zklogin signature"));
let UserSignature::ZkLoginAuthenticator(authenticator) = signature else {
return Err(SignatureError::from_source("not a zklogin authenticator"));
};

self.verify(message, zklogin_authenticator.as_ref())
self.verify(message, authenticator.as_ref())
}
}
2 changes: 1 addition & 1 deletion crates/iota-sdk-crypto/src/zklogin/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn zklogin_sign_personal_message() {
.verify(&message.signing_digest(), &zklogin_authenticator)
.unwrap();

let user_signature = UserSignature::ZkLogin(zklogin_authenticator.into());
let user_signature = UserSignature::ZkLoginAuthenticator(zklogin_authenticator.into());
verifier
.verify_personal_message(&message, &user_signature)
.unwrap();
Expand Down
25 changes: 24 additions & 1 deletion crates/iota-sdk-ffi/src/transaction_builder/client_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use std::{
time::Duration,
};

use iota_sdk::{graphql_client::WaitForTx, types::Input};
use iota_sdk::{
graphql_client::WaitForTx, transaction_builder::MoveAuthenticatorArgs, types::Input,
};

use crate::{
crypto::simple::SimpleKeypair,
Expand Down Expand Up @@ -368,4 +370,25 @@ impl ClientTransactionBuilder {
.await?
.into())
}

/// Execute the transaction with the provided move authenticator call data
/// and optionally wait for finalization.
#[uniffi::method(default(inputs = [], type_args = [], wait_for = None))]
pub async fn execute_with_move_authenticator(
&self,
inputs: Vec<Arc<PTBArgument>>,
type_args: Vec<Arc<TypeTag>>,
wait_for: Option<WaitForTx>,
) -> Result<TransactionEffects> {
Ok(self
.read(|builder| {
builder.clone().execute_with_move_authenticator(
MoveAuthenticatorArgs::inputs(inputs)
.type_tags(type_args.into_iter().map(|v| v.0.clone())),
wait_for,
)
})
.await?
.into())
}
}
13 changes: 13 additions & 0 deletions crates/iota-sdk-ffi/src/transaction_builder/ptb_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,4 +422,17 @@ impl iota_sdk::transaction_builder::PTBArgument for &PTBArgument {
PTBArgument::Gas => iota_sdk::transaction_builder::unresolved::Argument::Gas,
}
}

fn input(self) -> iota_sdk::transaction_builder::unresolved::InputKind {
match self {
PTBArgument::ObjectId(object_id) => object_id.input(),
PTBArgument::ObjectRef(obj_ref) => obj_ref.clone().input(),
PTBArgument::Move(arg) => arg.input(),
PTBArgument::Res(res) => res.input(),
PTBArgument::Shared(shared) => shared.input(),
PTBArgument::SharedMut(shared_mut) => shared_mut.input(),
PTBArgument::Receiving(receiving) => receiving.input(),
_ => panic!(),
}
}
}
1 change: 1 addition & 0 deletions crates/iota-sdk-ffi/src/types/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2025 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

pub mod move_authenticator;
pub mod multisig;
pub mod passkey;
pub mod zklogin;
Expand Down
80 changes: 80 additions & 0 deletions crates/iota-sdk-ffi/src/types/crypto/move_authenticator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) 2025 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::sync::Arc;

use crate::types::{
address::Address,
digest::Digest,
object::{ObjectId, ObjectReference},
transaction::Input,
type_tag::TypeTag,
};

/// MoveAuthenticator is a signature variant that enables a method of
/// authentication through Move code. This function represents the data received
/// by the Move authenticate function during the Account Abstraction
/// authentication flow.
#[derive(derive_more::From, uniffi::Object)]
pub struct MoveAuthenticator(pub iota_sdk::types::MoveAuthenticator);

#[uniffi::export]
impl MoveAuthenticator {
/// Create a new move authenticator from an immutable object.
#[uniffi::constructor]
pub fn new_immutable(
call_args: Vec<Arc<Input>>,
type_args: Vec<Arc<TypeTag>>,
object_to_authenticate: ObjectReference,
) -> Self {
Self(iota_sdk::types::MoveAuthenticator::new_immutable(
call_args.into_iter().map(|v| v.0.clone()).collect(),
type_args.into_iter().map(|v| v.0.clone()).collect(),
object_to_authenticate.into(),
))
}

/// Create a new move authenticator from a shared object.
#[uniffi::constructor]
pub fn new_shared(
call_args: Vec<Arc<Input>>,
type_args: Vec<Arc<TypeTag>>,
object_to_authenticate: &ObjectId,
initial_shared_version: u64,
) -> Self {
Self(iota_sdk::types::MoveAuthenticator::new_shared(
call_args.into_iter().map(|v| v.0.clone()).collect(),
type_args.into_iter().map(|v| v.0.clone()).collect(),
**object_to_authenticate,
initial_shared_version,
))
}

pub fn address(&self) -> Address {
self.0.address().into()
}

pub fn call_args(&self) -> Vec<Arc<Input>> {
self.0
.call_args()
.iter()
.cloned()
.map(Into::into)
.map(Arc::new)
.collect()
}

pub fn type_args(&self) -> Vec<Arc<TypeTag>> {
self.0
.type_args()
.iter()
.cloned()
.map(Into::into)
.map(Arc::new)
.collect()
}

pub fn object_to_authenticate(&self) -> Input {
self.0.object_to_authenticate().clone().into()
}
}
Loading
Loading