From f3ce0d9e7e222f5cc71e9d0fea3afc64f6518863 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 13:08:22 +0000 Subject: [PATCH 1/9] feat: expose authenticator management via UniFFI --- walletkit-core/src/authenticator/mod.rs | 265 +++++++++++++++++++++++- 1 file changed, 263 insertions(+), 2 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 574b25e6..ee58f832 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -9,10 +9,10 @@ use ruint::aliases::U256; use ruint_uniffi::Uint256; use std::sync::Arc; use world_id_core::{ - api_types::{GatewayErrorCode, GatewayRequestState}, + api_types::{GatewayErrorCode, GatewayRequestId, GatewayRequestState}, primitives::{AuthenticatorPublicKeySet, Config}, Authenticator as CoreAuthenticator, AuthenticatorConfig, - Credential as CoreCredential, CredentialInput, + Credential as CoreCredential, CredentialInput, EdDSAPublicKey, InitializingAuthenticator as CoreInitializingAuthenticator, OnchainKeyRepresentable, Signer, }; @@ -339,6 +339,131 @@ impl Authenticator { Ok(request_id.to_string()) } + + /// Inserts a new authenticator into this account. + /// + /// `new_authenticator_pubkey` is the compressed `EdDSA` off-chain public key + /// encoded as the protocol U256 representation. `new_authenticator_address` + /// is the checksummed hex Ethereum address of the new authenticator's + /// on-chain signer. + /// + /// Returns the gateway request ID. Inserting another authenticator changes + /// the account's off-chain signer commitment, but does not change this + /// authenticator's own local `packed_account_data`; callers should poll the + /// returned request ID and initialize the new authenticator after finalization. + /// + /// # Errors + /// Returns [`WalletKitError::InvalidInput`] if either input cannot be + /// parsed, or a network/authenticator error if the request cannot be + /// submitted. + pub async fn insert_authenticator( + &self, + new_authenticator_pubkey: Uint256, + new_authenticator_address: String, + ) -> Result { + let new_authenticator_pubkey = eddsa_public_key_from_uint256( + new_authenticator_pubkey, + "new_authenticator_pubkey", + )?; + let new_authenticator_address = Address::parse_from_ffi( + &new_authenticator_address, + "new_authenticator_address", + )?; + + let request_id = self + .inner + .insert_authenticator(new_authenticator_pubkey, new_authenticator_address) + .await?; + + Ok(request_id.to_string()) + } + + /// Updates an existing authenticator slot with a new authenticator. + /// + /// `pubkey_id` is the authenticator slot index to update. After the gateway + /// request finalizes on-chain, this `Authenticator` may become unusable if + /// it corresponds to the authenticator being updated; callers should poll + /// the request ID and re-initialize the appropriate authenticator as needed. + /// + /// # Errors + /// Returns [`WalletKitError::InvalidInput`] if an address or public key + /// cannot be parsed, or a network/authenticator error if the request cannot + /// be submitted. + pub async fn update_authenticator( + &self, + old_authenticator_address: String, + new_authenticator_address: String, + new_authenticator_pubkey: Uint256, + pubkey_id: u32, + ) -> Result { + let old_authenticator_address = Address::parse_from_ffi( + &old_authenticator_address, + "old_authenticator_address", + )?; + let new_authenticator_address = Address::parse_from_ffi( + &new_authenticator_address, + "new_authenticator_address", + )?; + let new_authenticator_pubkey = eddsa_public_key_from_uint256( + new_authenticator_pubkey, + "new_authenticator_pubkey", + )?; + + let request_id = self + .inner + .update_authenticator( + old_authenticator_address, + new_authenticator_address, + new_authenticator_pubkey, + pubkey_id, + ) + .await?; + + Ok(request_id.to_string()) + } + + /// Removes an authenticator from this account. + /// + /// `pubkey_id` is the authenticator slot index to remove. After the gateway + /// request finalizes on-chain, this `Authenticator` may become unusable if + /// it corresponds to the authenticator being removed; callers should poll + /// the request ID and re-initialize or discard this authenticator as needed. + /// + /// # Errors + /// Returns [`WalletKitError::InvalidInput`] if the address cannot be parsed, + /// or a network/authenticator error if the request cannot be submitted. + pub async fn remove_authenticator( + &self, + authenticator_address: String, + pubkey_id: u32, + ) -> Result { + let authenticator_address = + Address::parse_from_ffi(&authenticator_address, "authenticator_address")?; + + let request_id = self + .inner + .remove_authenticator(authenticator_address, pubkey_id) + .await?; + + Ok(request_id.to_string()) + } + + /// Polls the gateway for a previously submitted account-management request. + /// + /// This can be used with request IDs returned by authenticator management, + /// recovery-agent update, and account-recovery methods. + /// + /// # Errors + /// Returns a network error if the gateway cannot be reached or rejects the + /// request ID. + pub async fn poll_gateway_request_status( + &self, + request_id: String, + ) -> Result { + let request_id = gateway_request_id_from_string(&request_id); + let status = self.inner.poll_status(&request_id).await?; + Ok(status.into()) + } } #[uniffi::export(async_runtime = "tokio")] @@ -591,6 +716,47 @@ impl Authenticator { } } +/// Status for a registry gateway request. +#[derive(Debug, Clone, uniffi::Enum)] +pub enum GatewayRequestStatus { + /// Request queued but not yet batched. + Queued, + /// Request currently being batched. + Batching, + /// Request submitted on-chain. + Submitted { + /// Transaction hash emitted when the request was submitted. + tx_hash: String, + }, + /// Request finalized on-chain. + Finalized { + /// Transaction hash emitted when the request was finalized. + tx_hash: String, + }, + /// Request failed during processing. + Failed { + /// Error message returned by the gateway. + error: String, + /// Specific error code, if available. + error_code: Option, + }, +} + +impl From for GatewayRequestStatus { + fn from(state: GatewayRequestState) -> Self { + match state { + GatewayRequestState::Queued => Self::Queued, + GatewayRequestState::Batching => Self::Batching, + GatewayRequestState::Submitted { tx_hash } => Self::Submitted { tx_hash }, + GatewayRequestState::Finalized { tx_hash } => Self::Finalized { tx_hash }, + GatewayRequestState::Failed { error, error_code } => Self::Failed { + error, + error_code: error_code.map(|c: GatewayErrorCode| c.to_string()), + }, + } + } +} + /// Registration status for a World ID being created through the gateway. #[derive(Debug, Clone, uniffi::Enum)] pub enum RegistrationStatus { @@ -716,6 +882,23 @@ impl InitializingAuthenticator { } } +fn eddsa_public_key_from_uint256( + public_key: Uint256, + attribute: &'static str, +) -> Result { + let public_key: U256 = public_key.into(); + EdDSAPublicKey::from_compressed_bytes(public_key.to_le_bytes()).map_err(|error| { + WalletKitError::InvalidInput { + attribute: attribute.to_string(), + reason: error.to_string(), + } + }) +} + +fn gateway_request_id_from_string(request_id: &str) -> GatewayRequestId { + GatewayRequestId::new(request_id.strip_prefix("gw_").unwrap_or(request_id)) +} + /// The signature and signing nonce returned by /// [`Authenticator::danger_sign_initiate_recovery_agent_update`]. /// @@ -789,10 +972,13 @@ pub fn recovery_data_from_seed(seed: &[u8]) -> Result Date: Wed, 6 May 2026 14:21:10 +0000 Subject: [PATCH 2/9] refactor: use gateway request status for registration --- walletkit-cli/src/commands/auth.rs | 8 +++-- walletkit-core/src/authenticator/mod.rs | 43 +++---------------------- walletkit-core/src/lib.rs | 4 +-- 3 files changed, 11 insertions(+), 44 deletions(-) diff --git a/walletkit-cli/src/commands/auth.rs b/walletkit-cli/src/commands/auth.rs index 1e8f12ba..81a2b15d 100644 --- a/walletkit-cli/src/commands/auth.rs +++ b/walletkit-cli/src/commands/auth.rs @@ -3,7 +3,7 @@ use clap::Subcommand; use eyre::WrapErr as _; use walletkit_core::error::WalletKitError; -use walletkit_core::{InitializingAuthenticator, RecoveryData, RegistrationStatus}; +use walletkit_core::{GatewayRequestStatus, InitializingAuthenticator, RecoveryData}; use crate::output; @@ -61,8 +61,10 @@ pub async fn register_and_poll( let status = init_auth.poll_status().await.wrap_err("poll failed")?; match &status { - RegistrationStatus::Finalized => return Ok(RegisterOutcome::Finalized), - RegistrationStatus::Failed { error, error_code } => { + GatewayRequestStatus::Finalized { .. } => { + return Ok(RegisterOutcome::Finalized) + } + GatewayRequestStatus::Failed { error, error_code } => { eyre::bail!("registration failed: {error} (code: {error_code:?})"); } _ => { diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index ee58f832..9adebe65 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -757,41 +757,6 @@ impl From for GatewayRequestStatus { } } -/// Registration status for a World ID being created through the gateway. -#[derive(Debug, Clone, uniffi::Enum)] -pub enum RegistrationStatus { - /// Request queued but not yet batched. - Queued, - /// Request currently being batched. - Batching, - /// Request submitted on-chain. - Submitted, - /// Request finalized on-chain. The World ID is now registered. - Finalized, - /// Request failed during processing. - Failed { - /// Error message returned by the gateway. - error: String, - /// Specific error code, if available. - error_code: Option, - }, -} - -impl From for RegistrationStatus { - fn from(state: GatewayRequestState) -> Self { - match state { - GatewayRequestState::Queued => Self::Queued, - GatewayRequestState::Batching => Self::Batching, - GatewayRequestState::Submitted { .. } => Self::Submitted, - GatewayRequestState::Finalized { .. } => Self::Finalized, - GatewayRequestState::Failed { error, error_code } => Self::Failed { - error, - error_code: error_code.map(|c: GatewayErrorCode| c.to_string()), - }, - } - } -} - /// Represents an Authenticator in the process of being initialized. /// /// The account is not yet registered in the `WorldIDRegistry` contract. @@ -804,7 +769,7 @@ impl InitializingAuthenticator { /// Registers a new World ID with SDK defaults. /// /// This returns immediately and does not wait for registration to complete. - /// The returned `InitializingAuthenticator` can be used to poll the registration status. + /// The returned `InitializingAuthenticator` can be used to poll the gateway request status. /// /// # Errors /// See `CoreAuthenticator::register` for potential errors. @@ -836,7 +801,7 @@ impl InitializingAuthenticator { /// Registers a new World ID. /// /// This returns immediately and does not wait for registration to complete. - /// The returned `InitializingAuthenticator` can be used to poll the registration status. + /// The returned `InitializingAuthenticator` can be used to poll the gateway request status. /// /// # Errors /// See `CoreAuthenticator::register` for potential errors. @@ -867,7 +832,7 @@ impl InitializingAuthenticator { Ok(Self(initializing_authenticator)) } - /// Polls the registration status from the gateway. + /// Polls the gateway request status for this registration. /// /// # Errors /// Will error if the network request fails or the gateway returns an error. @@ -876,7 +841,7 @@ impl InitializingAuthenticator { name = "gateway_poll", skip_all )] - pub async fn poll_status(&self) -> Result { + pub async fn poll_status(&self) -> Result { let status = self.0.poll_status().await?; Ok(status.into()) } diff --git a/walletkit-core/src/lib.rs b/walletkit-core/src/lib.rs index fd80d63e..6e9ace31 100644 --- a/walletkit-core/src/lib.rs +++ b/walletkit-core/src/lib.rs @@ -117,8 +117,8 @@ pub mod storage; mod authenticator; pub use authenticator::{ - Authenticator, Groth16Materials, InitializingAuthenticator, RecoveryData, - RecoveryUpdateSignature, RegistrationStatus, + Authenticator, GatewayRequestStatus, Groth16Materials, InitializingAuthenticator, + RecoveryData, RecoveryUpdateSignature, }; /// Default configuration values for each [`Environment`]. From 2df47cbe12bd58214421cfab02a59e7ca3b7455b Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:14:37 +0000 Subject: [PATCH 3/9] docs: trim authenticator management comments --- walletkit-core/src/authenticator/mod.rs | 89 +++++-------------------- 1 file changed, 17 insertions(+), 72 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 9adebe65..d1e758ff 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -340,22 +340,9 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Inserts a new authenticator into this account. - /// - /// `new_authenticator_pubkey` is the compressed `EdDSA` off-chain public key - /// encoded as the protocol U256 representation. `new_authenticator_address` - /// is the checksummed hex Ethereum address of the new authenticator's - /// on-chain signer. - /// - /// Returns the gateway request ID. Inserting another authenticator changes - /// the account's off-chain signer commitment, but does not change this - /// authenticator's own local `packed_account_data`; callers should poll the - /// returned request ID and initialize the new authenticator after finalization. - /// - /// # Errors - /// Returns [`WalletKitError::InvalidInput`] if either input cannot be - /// parsed, or a network/authenticator error if the request cannot be - /// submitted. + /// Inserts a new authenticator and returns its gateway request ID. + /// The public key is the compressed `EdDSA` key as a protocol `U256`. + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn insert_authenticator( &self, new_authenticator_pubkey: Uint256, @@ -378,17 +365,9 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Updates an existing authenticator slot with a new authenticator. - /// - /// `pubkey_id` is the authenticator slot index to update. After the gateway - /// request finalizes on-chain, this `Authenticator` may become unusable if - /// it corresponds to the authenticator being updated; callers should poll - /// the request ID and re-initialize the appropriate authenticator as needed. - /// - /// # Errors - /// Returns [`WalletKitError::InvalidInput`] if an address or public key - /// cannot be parsed, or a network/authenticator error if the request cannot - /// be submitted. + /// Updates an authenticator slot and returns its gateway request ID. + /// `pubkey_id` is the authenticator slot index to update. + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn update_authenticator( &self, old_authenticator_address: String, @@ -422,16 +401,9 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Removes an authenticator from this account. - /// - /// `pubkey_id` is the authenticator slot index to remove. After the gateway - /// request finalizes on-chain, this `Authenticator` may become unusable if - /// it corresponds to the authenticator being removed; callers should poll - /// the request ID and re-initialize or discard this authenticator as needed. - /// - /// # Errors - /// Returns [`WalletKitError::InvalidInput`] if the address cannot be parsed, - /// or a network/authenticator error if the request cannot be submitted. + /// Removes an authenticator and returns its gateway request ID. + /// `pubkey_id` is the authenticator slot index to remove. + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn remove_authenticator( &self, authenticator_address: String, @@ -448,14 +420,8 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Polls the gateway for a previously submitted account-management request. - /// - /// This can be used with request IDs returned by authenticator management, - /// recovery-agent update, and account-recovery methods. - /// - /// # Errors - /// Returns a network error if the gateway cannot be reached or rejects the - /// request ID. + /// Polls the gateway status for a previously submitted request. + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn poll_gateway_request_status( &self, request_id: String, @@ -767,18 +733,14 @@ pub struct InitializingAuthenticator(CoreInitializingAuthenticator); #[uniffi::export(async_runtime = "tokio")] impl InitializingAuthenticator { /// Registers a new World ID with SDK defaults. - /// - /// This returns immediately and does not wait for registration to complete. - /// The returned `InitializingAuthenticator` can be used to poll the gateway request status. - /// - /// # Errors - /// See `CoreAuthenticator::register` for potential errors. + /// Returns immediately; use `poll_status` to track the gateway request. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", name = "gateway_register", skip_all )] + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn register_with_defaults( seed: &[u8], rpc_url: Option, @@ -799,18 +761,14 @@ impl InitializingAuthenticator { } /// Registers a new World ID. - /// - /// This returns immediately and does not wait for registration to complete. - /// The returned `InitializingAuthenticator` can be used to poll the gateway request status. - /// - /// # Errors - /// See `CoreAuthenticator::register` for potential errors. + /// Returns immediately; use `poll_status` to track the gateway request. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", name = "gateway_register", skip_all )] + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn register( seed: &[u8], config: &str, @@ -833,14 +791,12 @@ impl InitializingAuthenticator { } /// Polls the gateway request status for this registration. - /// - /// # Errors - /// Will error if the network request fails or the gateway returns an error. #[tracing::instrument( target = "walletkit_latency", name = "gateway_poll", skip_all )] + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn poll_status(&self) -> Result { let status = self.0.poll_status().await?; Ok(status.into()) @@ -898,13 +854,7 @@ pub struct RecoveryData { impl RecoveryData { /// Derives recovery identity material from a 32-byte seed. - /// - /// These values must be submitted on-chain as part of the recovery - /// transaction before the recovered account can be initialised with - /// [`Authenticator::init`] / [`Authenticator::init_with_defaults`]. - /// - /// # Errors - /// Returns [`WalletKitError`] if the seed is invalid or serialization fails. + #[expect(clippy::missing_errors_doc, reason = "FFI")] pub fn from_seed(seed: &[u8]) -> Result { let signer = Signer::from_seed_bytes(seed)?; let authenticator_address = signer.onchain_signer_address().to_checksum(None); @@ -924,11 +874,6 @@ impl RecoveryData { } /// Derives recovery data from a 32-byte seed. -/// -/// This is the foreign-bindings entrypoint for recovery data generation. -/// -/// # Errors -/// Returns [`WalletKitError`] if the seed is invalid or serialization fails. #[uniffi::export] pub fn recovery_data_from_seed(seed: &[u8]) -> Result { RecoveryData::from_seed(seed) From c0ab2060edf74cb036e4dbb630220d88ef9aa3fe Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:15:29 +0000 Subject: [PATCH 4/9] docs: shorten authenticator docs to one line --- walletkit-core/src/authenticator/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index d1e758ff..c0ed4188 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -340,8 +340,7 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Inserts a new authenticator and returns its gateway request ID. - /// The public key is the compressed `EdDSA` key as a protocol `U256`. + /// Inserts a new authenticator into this account. Returns a gateway request ID. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn insert_authenticator( &self, @@ -365,8 +364,7 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Updates an authenticator slot and returns its gateway request ID. - /// `pubkey_id` is the authenticator slot index to update. + /// Updates an existing authenticator slot. Returns a gateway request ID. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn update_authenticator( &self, @@ -401,8 +399,7 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Removes an authenticator and returns its gateway request ID. - /// `pubkey_id` is the authenticator slot index to remove. + /// Removes an authenticator from this account. Returns a gateway request ID. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn remove_authenticator( &self, @@ -732,8 +729,7 @@ pub struct InitializingAuthenticator(CoreInitializingAuthenticator); #[uniffi::export(async_runtime = "tokio")] impl InitializingAuthenticator { - /// Registers a new World ID with SDK defaults. - /// Returns immediately; use `poll_status` to track the gateway request. + /// Registers a new World ID with SDK defaults. Returns immediately; use `poll_status` to wait for finalization. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", @@ -760,8 +756,7 @@ impl InitializingAuthenticator { Ok(Self(initializing_authenticator)) } - /// Registers a new World ID. - /// Returns immediately; use `poll_status` to track the gateway request. + /// Registers a new World ID. Returns immediately; use `poll_status` to wait for finalization. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", From 869ede7a8f27a64511971276c5572f15bdd32290 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:22:05 +0000 Subject: [PATCH 5/9] chore: trim verbose doc comments in authenticator/mod.rs Reduce each doc comment to a single short line. Remove all # Errors sections, multi-paragraph explanations, redundant detail, and # Warning / --- walletkit-core/src/authenticator/mod.rs | 183 ++---------------------- 1 file changed, 14 insertions(+), 169 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index c0ed4188..40d46831 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -39,20 +39,10 @@ impl std::fmt::Debug for Groth16Materials { } /// Constructors that require embedded zkeys compiled into the binary. -/// -/// Enable the `embed-zkeys` Cargo feature to activate these. #[cfg(feature = "embed-zkeys")] #[uniffi::export] impl Groth16Materials { /// Loads Groth16 material from the embedded (compiled-in) zkeys and graphs. - /// - /// Requires the `embed-zkeys` feature. The material is baked into the binary at - /// compile time so no filesystem access is required, and this works on every - /// platform including WASM. - /// - /// # Errors - /// - /// Returns an error if the embedded material cannot be loaded or verified. #[uniffi::constructor] pub fn from_embedded() -> Result { let query = @@ -73,21 +63,10 @@ impl Groth16Materials { } /// Constructors that load Groth16 material from the native filesystem. -/// -/// Not available on WASM targets (no filesystem access). #[cfg(not(target_arch = "wasm32"))] #[uniffi::export] impl Groth16Materials { /// Loads Groth16 material from cached files on disk. - /// - /// Use `storage::cache_embedded_groth16_material` (requires the `embed-zkeys` feature) - /// to populate the cache before calling this. - /// - /// Not available on WASM (no filesystem). - /// - /// # Errors - /// - /// Returns an error if the cached files cannot be read or verified. #[uniffi::constructor] // `Arc` must be taken by value: UniFFI constructors receive // object arguments as owned `Arc`s across the FFI boundary, so passing by @@ -145,35 +124,24 @@ pub struct Authenticator { #[uniffi::export(async_runtime = "tokio")] impl Authenticator { /// Returns the packed account data for the holder's World ID. - /// - /// The packed account data is a 256 bit integer which includes the user's leaf index, their recovery counter, - /// and their pubkey id/commitment. #[must_use] pub fn packed_account_data(&self) -> Uint256 { self.inner.packed_account_data.into() } /// Returns the leaf index for the holder's World ID. - /// - /// This is the index in the Merkle tree where the holder's World ID account is registered. It - /// should only be used inside the authenticator and never shared. #[must_use] pub fn leaf_index(&self) -> u64 { self.inner.leaf_index() } /// Returns the Authenticator's `onchain_address`. - /// - /// See `world_id_core::Authenticator::onchain_address` for more details. #[must_use] pub fn onchain_address(&self) -> String { self.inner.onchain_address().to_string() } - /// Returns the packed account data for the holder's World ID fetching it from the on-chain registry. - /// - /// # Errors - /// Will error if the provided RPC URL is not valid or if there are RPC call failures. + /// Fetches the packed account data for the holder's World ID from the on-chain registry. #[tracing::instrument( target = "walletkit_latency", name = "rpc_account_data", @@ -186,14 +154,7 @@ impl Authenticator { Ok(packed_account_data.into()) } - /// Generates a blinding factor for a Credential sub (through OPRF Nodes). - /// - /// See [`CoreAuthenticator::generate_credential_blinding_factor`] for more details. - /// - /// # Errors - /// - /// - Will generally error if there are network issues or if the OPRF Nodes return an error. - /// - Raises an error if the OPRF Nodes configuration is not correctly set. + /// Generates a blinding factor for a credential sub via OPRF nodes. #[tracing::instrument( target = "walletkit_latency", name = "oprf_blinding_factor", @@ -219,16 +180,7 @@ impl Authenticator { CoreCredential::compute_sub(self.inner.leaf_index(), blinding_factor.0).into() } - /// Signs an arbitrary challenge with the authenticator's on-chain key. - /// - /// # Warning - /// This is considered a dangerous operation because it leaks the user's on-chain key, - /// hence its `leaf_index`. The only acceptable use is to prove the user's `leaf_index` - /// to a Recovery Agent. The Recovery Agent is the only party beyond the user who needs - /// to know the `leaf_index`. - /// - /// # Errors - /// May error if very unexpectedly the signing process fails. Not expected. + /// Signs an arbitrary challenge with the authenticator's on-chain key. Dangerous: leaks `leaf_index`. pub fn danger_sign_challenge( &self, challenge: &[u8], @@ -237,30 +189,7 @@ impl Authenticator { Ok(signature.as_bytes().to_vec()) } - /// Signs the EIP-712 `InitiateRecoveryAgentUpdate` payload and returns the - /// raw signature bytes and signing nonce without submitting anything to the - /// gateway. - /// - /// This is the signing-only counterpart of [`Self::initiate_recovery_agent_update`]. - /// Callers can use the returned bytes to build and submit the gateway request - /// themselves. - /// - /// # Warning - /// This method uses the `onchain_signer` (secp256k1 ECDSA) and produces a - /// recoverable signature. Any holder of the signature together with the - /// EIP-712 parameters can call `ecrecover` to obtain the `onchain_address`, - /// which can then be looked up in the registry to derive the user's - /// `leaf_index`. Only expose the output to trusted parties (e.g. a Recovery - /// Agent). - /// - /// # Arguments - /// * `new_recovery_agent` — the checksummed hex address of the new recovery - /// agent (e.g. `"0x1234…"`). - /// - /// # Errors - /// - Returns [`WalletKitError::InvalidInput`] if `new_recovery_agent` is not - /// a valid address. - /// - Returns an error if the nonce fetch or signing step fails. + /// Signs the EIP-712 `InitiateRecoveryAgentUpdate` payload and returns the raw signature and nonce without submitting. pub async fn danger_sign_initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -277,20 +206,7 @@ impl Authenticator { }) } - /// Initiates a time-locked recovery agent update (14-day cooldown). - /// - /// Signs an EIP-712 `InitiateRecoveryAgentUpdate` payload and submits it to - /// the gateway. Returns the gateway request ID that can be used to poll - /// status. - /// - /// # Arguments - /// * `new_recovery_agent` — the checksummed hex address of the new recovery - /// agent (e.g. `"0x1234…"`). - /// - /// # Errors - /// - Returns [`WalletKitError::InvalidInput`] if `new_recovery_agent` is not - /// a valid address. - /// - Returns a network error if the gateway request fails. + /// Initiates a time-locked recovery agent update (14-day cooldown). Returns a gateway request ID. pub async fn initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -306,17 +222,7 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Executes a pending recovery agent update after the 14-day cooldown has - /// elapsed. - /// - /// This call is **permissionless** — no signature is required. The contract - /// enforces the cooldown and will revert with - /// `RecoveryAgentUpdateStillInCooldown` if called too early. - /// - /// Returns the gateway request ID that can be used to poll status. - /// - /// # Errors - /// Returns a network error if the gateway request fails. + /// Executes a pending recovery agent update after the cooldown has elapsed. Returns a gateway request ID. pub async fn execute_recovery_agent_update( &self, ) -> Result { @@ -325,15 +231,7 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Cancels a pending time-locked recovery agent update before the cooldown - /// expires. - /// - /// Signs an EIP-712 `CancelRecoveryAgentUpdate` payload and submits it to - /// the gateway. Returns the gateway request ID that can be used to poll - /// status. - /// - /// # Errors - /// Returns a network error if the gateway request fails. + /// Cancels a pending time-locked recovery agent update before the cooldown expires. Returns a gateway request ID. pub async fn cancel_recovery_agent_update(&self) -> Result { let request_id = self.inner.cancel_recovery_agent_update().await?; @@ -431,13 +329,7 @@ impl Authenticator { #[uniffi::export(async_runtime = "tokio")] impl Authenticator { - /// Initializes a new Authenticator from a seed and with SDK defaults. - /// - /// The user's World ID must already be registered in the `WorldIDRegistry`, - /// otherwise a [`WalletKitError::AccountDoesNotExist`] error will be returned. - /// - /// # Errors - /// See `CoreAuthenticator::init` for potential errors. + /// Initializes a new Authenticator from a seed with SDK defaults. #[uniffi::constructor] #[tracing::instrument(target = "walletkit_latency", name = "rpc_init", skip_all)] pub async fn init_with_defaults( @@ -462,12 +354,6 @@ impl Authenticator { } /// Initializes a new Authenticator from a seed and config. - /// - /// The user's World ID must already be registered in the `WorldIDRegistry`, - /// otherwise a [`WalletKitError::AccountDoesNotExist`] error will be returned. - /// - /// # Errors - /// Will error if the provided seed is not valid or if the config is not valid. #[uniffi::constructor] #[tracing::instrument(target = "walletkit_latency", name = "rpc_init", skip_all)] pub async fn init( @@ -494,10 +380,7 @@ impl Authenticator { }) } - /// Generates a proof for the given proof request. - /// - /// # Errors - /// Returns an error if proof generation fails. + /// Generates a ZK proof for the given proof request. pub async fn generate_proof( &self, proof_request: &ProofRequest, @@ -615,30 +498,7 @@ impl Authenticator { Ok(result.proof_response.into()) } - /// Generates a WIP-103 Ownership Proof for Issuers. - /// - /// An Ownership Proof lets the user prove they own the credential `sub` - /// associated with a stored credential without revealing their `leaf_index`. - /// - /// # Security-critical usage constraint - /// This method **MUST only** be called as part of a direct - /// **user-initiated** action in the client. Callers **MUST NOT** expose this - /// method to issuer-triggered, backend-triggered, or unauthenticated request - /// flows. - /// - /// # Arguments - /// * `nonce` - A field element provided by the Issuer to prevent replay. - /// * `blinding_factor` - The credential blinding factor previously used to - /// derive the credential `sub`. - /// * `sub` - The credential `sub` (commitment) to prove ownership of. - /// - /// # Errors - /// - Returns [`WalletKitError::InvalidInput`] if `blinding_factor` and - /// `sub` are inconsistent with each other (i.e. `sub` was not derived - /// from this authenticator's leaf index and the provided blinding factor). - /// - Returns a network error if the Merkle inclusion proof cannot be - /// fetched from the indexer. - /// - Returns [`WalletKitError::ProofGeneration`] if the ZK proof fails. + /// Generates a WIP-103 ownership proof for the credential sub. pub async fn prove_credential_sub( &self, nonce: &FieldElement, @@ -720,10 +580,7 @@ impl From for GatewayRequestStatus { } } -/// Represents an Authenticator in the process of being initialized. -/// -/// The account is not yet registered in the `WorldIDRegistry` contract. -/// Use this for non-blocking registration flows where you want to poll the status yourself. +/// An Authenticator in the process of being registered; use `poll_status` to wait for finalization. #[derive(uniffi::Object)] pub struct InitializingAuthenticator(CoreInitializingAuthenticator); @@ -815,28 +672,16 @@ fn gateway_request_id_from_string(request_id: &str) -> GatewayRequestId { GatewayRequestId::new(request_id.strip_prefix("gw_").unwrap_or(request_id)) } -/// The signature and signing nonce returned by -/// [`Authenticator::danger_sign_initiate_recovery_agent_update`]. -/// -/// `UniFFI` does not support returning bare tuples across the FFI boundary, so -/// the two values are bundled in this record type. +/// Signature and nonce returned by `danger_sign_initiate_recovery_agent_update`. #[derive(Debug, Clone, uniffi::Record)] pub struct RecoveryUpdateSignature { - /// Raw bytes of the secp256k1 ECDSA signature over the EIP-712 - /// `InitiateRecoveryAgentUpdate` payload. + /// Raw bytes of the secp256k1 ECDSA signature. pub signature: Vec, - /// The EIP-712 signing nonce that was used; must be included in the - /// gateway request alongside the signature. + /// The EIP-712 signing nonce; must be included in the gateway request alongside the signature. pub nonce: Uint256, } /// Identity material derived from a seed for use during account recovery. -/// -/// During account recovery the user generates new keys from a seed, but those -/// keys do not yet exist on-chain. The three values in this record must be -/// submitted on-chain during the recovery transaction. -/// -/// All fields are hex-encoded strings suitable for direct use in API requests. #[derive(Debug, Clone, uniffi::Record)] pub struct RecoveryData { /// Checksummed hex Ethereum address of the on-chain signer. From 3ca045a65f5dcac3573ccb049833a483a99d99b1 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:23:56 +0000 Subject: [PATCH 6/9] chore: expand doc comments to 2-3 substantive sentences Each doc comment now describes what the method does, its key inputs, and what it returns. Verbose multi-paragraph walls of text and # Errors / # Arguments / # Warning sections remain removed. --- walletkit-core/src/authenticator/mod.rs | 100 ++++++++++++++++++------ 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 40d46831..399c6fd5 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -39,10 +39,13 @@ impl std::fmt::Debug for Groth16Materials { } /// Constructors that require embedded zkeys compiled into the binary. +/// Enable the `embed-zkeys` Cargo feature to activate these. #[cfg(feature = "embed-zkeys")] #[uniffi::export] impl Groth16Materials { /// Loads Groth16 material from the embedded (compiled-in) zkeys and graphs. + /// The material is baked into the binary at compile time, so no filesystem access is required. + /// Requires the `embed-zkeys` Cargo feature. #[uniffi::constructor] pub fn from_embedded() -> Result { let query = @@ -63,10 +66,13 @@ impl Groth16Materials { } /// Constructors that load Groth16 material from the native filesystem. +/// Not available on WASM targets. #[cfg(not(target_arch = "wasm32"))] #[uniffi::export] impl Groth16Materials { - /// Loads Groth16 material from cached files on disk. + /// Loads Groth16 material from previously cached files on disk. + /// Use `storage::cache_embedded_groth16_material` (requires `embed-zkeys`) to populate the cache. + /// Not available on WASM targets. #[uniffi::constructor] // `Arc` must be taken by value: UniFFI constructors receive // object arguments as owned `Arc`s across the FFI boundary, so passing by @@ -124,24 +130,28 @@ pub struct Authenticator { #[uniffi::export(async_runtime = "tokio")] impl Authenticator { /// Returns the packed account data for the holder's World ID. + /// This is a 256-bit integer encoding the leaf index, recovery counter, and pubkey commitment. #[must_use] pub fn packed_account_data(&self) -> Uint256 { self.inner.packed_account_data.into() } - /// Returns the leaf index for the holder's World ID. + /// Returns the Merkle tree leaf index for the holder's World ID. + /// This value is private and should never be shared outside the authenticator. #[must_use] pub fn leaf_index(&self) -> u64 { self.inner.leaf_index() } - /// Returns the Authenticator's `onchain_address`. + /// Returns the on-chain secp256k1 address associated with this authenticator. + /// This is the address used to identify the authenticator in the `WorldIDRegistry`. #[must_use] pub fn onchain_address(&self) -> String { self.inner.onchain_address().to_string() } - /// Fetches the packed account data for the holder's World ID from the on-chain registry. + /// Fetches the packed account data for the holder's World ID directly from the on-chain registry. + /// Use this to get a fresh value when the locally cached `packed_account_data` may be stale. #[tracing::instrument( target = "walletkit_latency", name = "rpc_account_data", @@ -154,7 +164,9 @@ impl Authenticator { Ok(packed_account_data.into()) } - /// Generates a blinding factor for a credential sub via OPRF nodes. + /// Generates a blinding factor for a credential `sub` by querying the OPRF nodes. + /// `issuer_schema_id` identifies the issuer schema for which the blinding factor is derived. + /// The returned value can be passed to `compute_credential_sub` to derive the credential `sub`. #[tracing::instrument( target = "walletkit_latency", name = "oprf_blinding_factor", @@ -180,7 +192,9 @@ impl Authenticator { CoreCredential::compute_sub(self.inner.leaf_index(), blinding_factor.0).into() } - /// Signs an arbitrary challenge with the authenticator's on-chain key. Dangerous: leaks `leaf_index`. + /// Signs an arbitrary challenge with the authenticator's on-chain secp256k1 key. + /// **Dangerous**: the signature reveals the on-chain address, which can be used to derive `leaf_index`. + /// Only use this when proving `leaf_index` to a trusted Recovery Agent. pub fn danger_sign_challenge( &self, challenge: &[u8], @@ -189,7 +203,9 @@ impl Authenticator { Ok(signature.as_bytes().to_vec()) } - /// Signs the EIP-712 `InitiateRecoveryAgentUpdate` payload and returns the raw signature and nonce without submitting. + /// Signs the EIP-712 `InitiateRecoveryAgentUpdate` payload and returns the raw signature bytes and signing nonce. + /// Unlike `initiate_recovery_agent_update`, this does not submit anything to the gateway; + /// callers can use the returned values to build and submit the request themselves. pub async fn danger_sign_initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -206,7 +222,9 @@ impl Authenticator { }) } - /// Initiates a time-locked recovery agent update (14-day cooldown). Returns a gateway request ID. + /// Submits a time-locked request to update the recovery agent; the change takes effect after a 14-day cooldown. + /// `new_recovery_agent` is the checksummed hex Ethereum address of the new agent. + /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. pub async fn initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -222,7 +240,9 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Executes a pending recovery agent update after the cooldown has elapsed. Returns a gateway request ID. + /// Executes a previously initiated recovery agent update once the 14-day cooldown has elapsed. + /// This call is permissionless — no signature is required; the contract enforces the cooldown. + /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. pub async fn execute_recovery_agent_update( &self, ) -> Result { @@ -231,14 +251,17 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Cancels a pending time-locked recovery agent update before the cooldown expires. Returns a gateway request ID. + /// Cancels a pending time-locked recovery agent update before the 14-day cooldown expires. + /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. pub async fn cancel_recovery_agent_update(&self) -> Result { let request_id = self.inner.cancel_recovery_agent_update().await?; Ok(request_id.to_string()) } - /// Inserts a new authenticator into this account. Returns a gateway request ID. + /// Inserts a new authenticator into this account, identified by its EdDSA `pubkey` and on-chain `address`. + /// Poll the returned gateway request ID with `poll_gateway_request_status` to confirm finalization, + /// then initialize the new authenticator using `init` or `init_with_defaults`. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn insert_authenticator( &self, @@ -262,7 +285,9 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Updates an existing authenticator slot. Returns a gateway request ID. + /// Replaces an existing authenticator slot (identified by `pubkey_id`) with a new key pair. + /// `old_authenticator_address` identifies the slot to replace; the new slot is given by `new_authenticator_address` and `new_authenticator_pubkey`. + /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn update_authenticator( &self, @@ -297,7 +322,8 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Removes an authenticator from this account. Returns a gateway request ID. + /// Removes the authenticator at the given slot (identified by `authenticator_address` and `pubkey_id`) from this account. + /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn remove_authenticator( &self, @@ -315,7 +341,9 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Polls the gateway status for a previously submitted request. + /// Polls the gateway for the current status of a previously submitted request. + /// Works with request IDs returned by any of the authenticator-management or recovery methods. + /// Returns a `GatewayRequestStatus` indicating whether the request is queued, submitted, finalized, or failed. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn poll_gateway_request_status( &self, @@ -329,7 +357,9 @@ impl Authenticator { #[uniffi::export(async_runtime = "tokio")] impl Authenticator { - /// Initializes a new Authenticator from a seed with SDK defaults. + /// Initializes a new `Authenticator` from a 32-byte seed using SDK-default config for the given environment. + /// The World ID account must already be registered in the `WorldIDRegistry`; use `InitializingAuthenticator` to register first. + /// `store` is used to cache credentials and inclusion proofs across sessions. #[uniffi::constructor] #[tracing::instrument(target = "walletkit_latency", name = "rpc_init", skip_all)] pub async fn init_with_defaults( @@ -353,7 +383,9 @@ impl Authenticator { }) } - /// Initializes a new Authenticator from a seed and config. + /// Initializes a new `Authenticator` from a 32-byte seed and a JSON-encoded `AuthenticatorConfig`. + /// The World ID account must already be registered in the `WorldIDRegistry`; use `InitializingAuthenticator` to register first. + /// `store` is used to cache credentials and inclusion proofs across sessions. #[uniffi::constructor] #[tracing::instrument(target = "walletkit_latency", name = "rpc_init", skip_all)] pub async fn init( @@ -380,7 +412,9 @@ impl Authenticator { }) } - /// Generates a ZK proof for the given proof request. + /// Generates a ZK proof for the given `ProofRequest`, using stored credentials and a fresh Merkle inclusion proof. + /// `now` is the current Unix timestamp in seconds; on WASM it must be provided by the caller. + /// Returns a `ProofResponse` containing the nullifier and per-credential proofs. pub async fn generate_proof( &self, proof_request: &ProofRequest, @@ -498,7 +532,9 @@ impl Authenticator { Ok(result.proof_response.into()) } - /// Generates a WIP-103 ownership proof for the credential sub. + /// Generates a WIP-103 ownership proof showing that the holder owns the credential `sub` without revealing `leaf_index`. + /// `nonce` is an issuer-provided replay-prevention value; `blinding_factor` and `sub` must be consistent with this authenticator. + /// Not supported on WASM targets. pub async fn prove_credential_sub( &self, nonce: &FieldElement, @@ -580,13 +616,17 @@ impl From for GatewayRequestStatus { } } -/// An Authenticator in the process of being registered; use `poll_status` to wait for finalization. +/// An `Authenticator` in the process of being registered with the `WorldIDRegistry`. +/// Returned by `register` and `register_with_defaults`; call `poll_status` to track the gateway request. +/// Once the registration is finalized, initialize the full `Authenticator` using `init` or `init_with_defaults`. #[derive(uniffi::Object)] pub struct InitializingAuthenticator(CoreInitializingAuthenticator); #[uniffi::export(async_runtime = "tokio")] impl InitializingAuthenticator { - /// Registers a new World ID with SDK defaults. Returns immediately; use `poll_status` to wait for finalization. + /// Submits a new World ID registration using SDK-default config for the given environment. + /// Returns immediately without waiting for finalization; call `poll_status` on the result to track progress. + /// `recovery_address` is an optional checksummed hex Ethereum address to set as the initial recovery agent. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", @@ -613,7 +653,9 @@ impl InitializingAuthenticator { Ok(Self(initializing_authenticator)) } - /// Registers a new World ID. Returns immediately; use `poll_status` to wait for finalization. + /// Submits a new World ID registration using the provided JSON-encoded `AuthenticatorConfig`. + /// Returns immediately without waiting for finalization; call `poll_status` on the result to track progress. + /// `recovery_address` is an optional checksummed hex Ethereum address to set as the initial recovery agent. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", @@ -642,7 +684,8 @@ impl InitializingAuthenticator { Ok(Self(initializing_authenticator)) } - /// Polls the gateway request status for this registration. + /// Polls the gateway for the status of this registration request. + /// Returns a `GatewayRequestStatus` indicating whether the request is queued, submitted, finalized, or failed. #[tracing::instrument( target = "walletkit_latency", name = "gateway_poll", @@ -673,15 +716,18 @@ fn gateway_request_id_from_string(request_id: &str) -> GatewayRequestId { } /// Signature and nonce returned by `danger_sign_initiate_recovery_agent_update`. +/// UniFFI does not support bare tuples across the FFI boundary, so the two values are bundled here. #[derive(Debug, Clone, uniffi::Record)] pub struct RecoveryUpdateSignature { - /// Raw bytes of the secp256k1 ECDSA signature. + /// Raw bytes of the secp256k1 ECDSA signature over the EIP-712 `InitiateRecoveryAgentUpdate` payload. pub signature: Vec, - /// The EIP-712 signing nonce; must be included in the gateway request alongside the signature. + /// The EIP-712 signing nonce used when producing the signature; must be included in the gateway request. pub nonce: Uint256, } /// Identity material derived from a seed for use during account recovery. +/// All three values must be submitted on-chain as part of the recovery transaction +/// before the recovered account can be initialized with `Authenticator::init`. #[derive(Debug, Clone, uniffi::Record)] pub struct RecoveryData { /// Checksummed hex Ethereum address of the on-chain signer. @@ -693,7 +739,8 @@ pub struct RecoveryData { } impl RecoveryData { - /// Derives recovery identity material from a 32-byte seed. + /// Derives the on-chain address, EdDSA public key, and off-chain signer commitment from a 32-byte seed. + /// These values must be submitted on-chain as part of the recovery transaction. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub fn from_seed(seed: &[u8]) -> Result { let signer = Signer::from_seed_bytes(seed)?; @@ -713,7 +760,8 @@ impl RecoveryData { } } -/// Derives recovery data from a 32-byte seed. +/// Derives recovery identity material from a 32-byte seed. +/// This is the FFI entry point for `RecoveryData::from_seed`; see that method for details. #[uniffi::export] pub fn recovery_data_from_seed(seed: &[u8]) -> Result { RecoveryData::from_seed(seed) From def67cf381b57964de51b79e85bfc424d05279e8 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:24:37 +0000 Subject: [PATCH 7/9] docs: expand insert/update/remove/poll doc comments to 2-3 sentences --- walletkit-core/src/authenticator/mod.rs | 29 +++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 399c6fd5..24999d57 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -259,9 +259,11 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Inserts a new authenticator into this account, identified by its EdDSA `pubkey` and on-chain `address`. - /// Poll the returned gateway request ID with `poll_gateway_request_status` to confirm finalization, - /// then initialize the new authenticator using `init` or `init_with_defaults`. + /// Inserts a new authenticator into this account. + /// + /// Accepts the new authenticator's compressed EdDSA public key as a U256 and its on-chain signer + /// address as a hex string. Returns a gateway request ID; poll it with `poll_gateway_request_status` + /// to wait for finalization before initializing the new authenticator. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn insert_authenticator( &self, @@ -285,9 +287,11 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Replaces an existing authenticator slot (identified by `pubkey_id`) with a new key pair. - /// `old_authenticator_address` identifies the slot to replace; the new slot is given by `new_authenticator_address` and `new_authenticator_pubkey`. - /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. + /// Updates an existing authenticator slot. + /// + /// `pubkey_id` identifies the slot to replace; supply the old signer address, the new signer address, + /// and the new compressed EdDSA public key as a U256. Returns a gateway request ID; poll it with + /// `poll_gateway_request_status` to wait for finalization. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn update_authenticator( &self, @@ -322,8 +326,10 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Removes the authenticator at the given slot (identified by `authenticator_address` and `pubkey_id`) from this account. - /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. + /// Removes an authenticator from this account. + /// + /// `authenticator_address` and `pubkey_id` together identify the slot to remove. Returns a gateway + /// request ID; poll it with `poll_gateway_request_status` to wait for finalization. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn remove_authenticator( &self, @@ -341,9 +347,10 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Polls the gateway for the current status of a previously submitted request. - /// Works with request IDs returned by any of the authenticator-management or recovery methods. - /// Returns a `GatewayRequestStatus` indicating whether the request is queued, submitted, finalized, or failed. + /// Polls the gateway for the status of a previously submitted request. + /// + /// Accepts a request ID returned by any authenticator-management or recovery method. Returns a + /// `GatewayRequestStatus` indicating whether the request is queued, submitted, finalized, or failed. #[expect(clippy::missing_errors_doc, reason = "FFI")] pub async fn poll_gateway_request_status( &self, From 2024538ad1d592ffff204b6935b024b932ae0cb7 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:35:58 +0000 Subject: [PATCH 8/9] fix: restore over-eager doc comment changes on pre-existing methods Only the 4 newly introduced methods (insert_authenticator, update_authenticator, remove_authenticator, poll_gateway_request_status) should have had their doc comments modified. Restore all pre-existing method/type doc comments to their main-branch versions. --- walletkit-core/src/authenticator/mod.rs | 270 ++++++++++++++++++------ 1 file changed, 205 insertions(+), 65 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 24999d57..936189df 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -39,13 +39,20 @@ impl std::fmt::Debug for Groth16Materials { } /// Constructors that require embedded zkeys compiled into the binary. +/// /// Enable the `embed-zkeys` Cargo feature to activate these. #[cfg(feature = "embed-zkeys")] #[uniffi::export] impl Groth16Materials { /// Loads Groth16 material from the embedded (compiled-in) zkeys and graphs. - /// The material is baked into the binary at compile time, so no filesystem access is required. - /// Requires the `embed-zkeys` Cargo feature. + /// + /// Requires the `embed-zkeys` feature. The material is baked into the binary at + /// compile time so no filesystem access is required, and this works on every + /// platform including WASM. + /// + /// # Errors + /// + /// Returns an error if the embedded material cannot be loaded or verified. #[uniffi::constructor] pub fn from_embedded() -> Result { let query = @@ -66,13 +73,21 @@ impl Groth16Materials { } /// Constructors that load Groth16 material from the native filesystem. -/// Not available on WASM targets. +/// +/// Not available on WASM targets (no filesystem access). #[cfg(not(target_arch = "wasm32"))] #[uniffi::export] impl Groth16Materials { - /// Loads Groth16 material from previously cached files on disk. - /// Use `storage::cache_embedded_groth16_material` (requires `embed-zkeys`) to populate the cache. - /// Not available on WASM targets. + /// Loads Groth16 material from cached files on disk. + /// + /// Use `storage::cache_embedded_groth16_material` (requires the `embed-zkeys` feature) + /// to populate the cache before calling this. + /// + /// Not available on WASM (no filesystem). + /// + /// # Errors + /// + /// Returns an error if the cached files cannot be read or verified. #[uniffi::constructor] // `Arc` must be taken by value: UniFFI constructors receive // object arguments as owned `Arc`s across the FFI boundary, so passing by @@ -130,28 +145,35 @@ pub struct Authenticator { #[uniffi::export(async_runtime = "tokio")] impl Authenticator { /// Returns the packed account data for the holder's World ID. - /// This is a 256-bit integer encoding the leaf index, recovery counter, and pubkey commitment. + /// + /// The packed account data is a 256 bit integer which includes the user's leaf index, their recovery counter, + /// and their pubkey id/commitment. #[must_use] pub fn packed_account_data(&self) -> Uint256 { self.inner.packed_account_data.into() } - /// Returns the Merkle tree leaf index for the holder's World ID. - /// This value is private and should never be shared outside the authenticator. + /// Returns the leaf index for the holder's World ID. + /// + /// This is the index in the Merkle tree where the holder's World ID account is registered. It + /// should only be used inside the authenticator and never shared. #[must_use] pub fn leaf_index(&self) -> u64 { self.inner.leaf_index() } - /// Returns the on-chain secp256k1 address associated with this authenticator. - /// This is the address used to identify the authenticator in the `WorldIDRegistry`. + /// Returns the Authenticator's `onchain_address`. + /// + /// See `world_id_core::Authenticator::onchain_address` for more details. #[must_use] pub fn onchain_address(&self) -> String { self.inner.onchain_address().to_string() } - /// Fetches the packed account data for the holder's World ID directly from the on-chain registry. - /// Use this to get a fresh value when the locally cached `packed_account_data` may be stale. + /// Returns the packed account data for the holder's World ID fetching it from the on-chain registry. + /// + /// # Errors + /// Will error if the provided RPC URL is not valid or if there are RPC call failures. #[tracing::instrument( target = "walletkit_latency", name = "rpc_account_data", @@ -164,9 +186,14 @@ impl Authenticator { Ok(packed_account_data.into()) } - /// Generates a blinding factor for a credential `sub` by querying the OPRF nodes. - /// `issuer_schema_id` identifies the issuer schema for which the blinding factor is derived. - /// The returned value can be passed to `compute_credential_sub` to derive the credential `sub`. + /// Generates a blinding factor for a Credential sub (through OPRF Nodes). + /// + /// See [`CoreAuthenticator::generate_credential_blinding_factor`] for more details. + /// + /// # Errors + /// + /// - Will generally error if there are network issues or if the OPRF Nodes return an error. + /// - Raises an error if the OPRF Nodes configuration is not correctly set. #[tracing::instrument( target = "walletkit_latency", name = "oprf_blinding_factor", @@ -192,9 +219,16 @@ impl Authenticator { CoreCredential::compute_sub(self.inner.leaf_index(), blinding_factor.0).into() } - /// Signs an arbitrary challenge with the authenticator's on-chain secp256k1 key. - /// **Dangerous**: the signature reveals the on-chain address, which can be used to derive `leaf_index`. - /// Only use this when proving `leaf_index` to a trusted Recovery Agent. + /// Signs an arbitrary challenge with the authenticator's on-chain key. + /// + /// # Warning + /// This is considered a dangerous operation because it leaks the user's on-chain key, + /// hence its `leaf_index`. The only acceptable use is to prove the user's `leaf_index` + /// to a Recovery Agent. The Recovery Agent is the only party beyond the user who needs + /// to know the `leaf_index`. + /// + /// # Errors + /// May error if very unexpectedly the signing process fails. Not expected. pub fn danger_sign_challenge( &self, challenge: &[u8], @@ -203,9 +237,30 @@ impl Authenticator { Ok(signature.as_bytes().to_vec()) } - /// Signs the EIP-712 `InitiateRecoveryAgentUpdate` payload and returns the raw signature bytes and signing nonce. - /// Unlike `initiate_recovery_agent_update`, this does not submit anything to the gateway; - /// callers can use the returned values to build and submit the request themselves. + /// Signs the EIP-712 `InitiateRecoveryAgentUpdate` payload and returns the + /// raw signature bytes and signing nonce without submitting anything to the + /// gateway. + /// + /// This is the signing-only counterpart of [`Self::initiate_recovery_agent_update`]. + /// Callers can use the returned bytes to build and submit the gateway request + /// themselves. + /// + /// # Warning + /// This method uses the `onchain_signer` (secp256k1 ECDSA) and produces a + /// recoverable signature. Any holder of the signature together with the + /// EIP-712 parameters can call `ecrecover` to obtain the `onchain_address`, + /// which can then be looked up in the registry to derive the user's + /// `leaf_index`. Only expose the output to trusted parties (e.g. a Recovery + /// Agent). + /// + /// # Arguments + /// * `new_recovery_agent` — the checksummed hex address of the new recovery + /// agent (e.g. `"0x1234…"`). + /// + /// # Errors + /// - Returns [`WalletKitError::InvalidInput`] if `new_recovery_agent` is not + /// a valid address. + /// - Returns an error if the nonce fetch or signing step fails. pub async fn danger_sign_initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -222,9 +277,20 @@ impl Authenticator { }) } - /// Submits a time-locked request to update the recovery agent; the change takes effect after a 14-day cooldown. - /// `new_recovery_agent` is the checksummed hex Ethereum address of the new agent. - /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. + /// Initiates a time-locked recovery agent update (14-day cooldown). + /// + /// Signs an EIP-712 `InitiateRecoveryAgentUpdate` payload and submits it to + /// the gateway. Returns the gateway request ID that can be used to poll + /// status. + /// + /// # Arguments + /// * `new_recovery_agent` — the checksummed hex address of the new recovery + /// agent (e.g. `"0x1234…"`). + /// + /// # Errors + /// - Returns [`WalletKitError::InvalidInput`] if `new_recovery_agent` is not + /// a valid address. + /// - Returns a network error if the gateway request fails. pub async fn initiate_recovery_agent_update( &self, new_recovery_agent: String, @@ -240,9 +306,17 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Executes a previously initiated recovery agent update once the 14-day cooldown has elapsed. - /// This call is permissionless — no signature is required; the contract enforces the cooldown. - /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. + /// Executes a pending recovery agent update after the 14-day cooldown has + /// elapsed. + /// + /// This call is **permissionless** — no signature is required. The contract + /// enforces the cooldown and will revert with + /// `RecoveryAgentUpdateStillInCooldown` if called too early. + /// + /// Returns the gateway request ID that can be used to poll status. + /// + /// # Errors + /// Returns a network error if the gateway request fails. pub async fn execute_recovery_agent_update( &self, ) -> Result { @@ -251,8 +325,15 @@ impl Authenticator { Ok(request_id.to_string()) } - /// Cancels a pending time-locked recovery agent update before the 14-day cooldown expires. - /// Returns the gateway request ID, which can be polled with `poll_gateway_request_status`. + /// Cancels a pending time-locked recovery agent update before the cooldown + /// expires. + /// + /// Signs an EIP-712 `CancelRecoveryAgentUpdate` payload and submits it to + /// the gateway. Returns the gateway request ID that can be used to poll + /// status. + /// + /// # Errors + /// Returns a network error if the gateway request fails. pub async fn cancel_recovery_agent_update(&self) -> Result { let request_id = self.inner.cancel_recovery_agent_update().await?; @@ -364,9 +445,13 @@ impl Authenticator { #[uniffi::export(async_runtime = "tokio")] impl Authenticator { - /// Initializes a new `Authenticator` from a 32-byte seed using SDK-default config for the given environment. - /// The World ID account must already be registered in the `WorldIDRegistry`; use `InitializingAuthenticator` to register first. - /// `store` is used to cache credentials and inclusion proofs across sessions. + /// Initializes a new Authenticator from a seed and with SDK defaults. + /// + /// The user's World ID must already be registered in the `WorldIDRegistry`, + /// otherwise a [`WalletKitError::AccountDoesNotExist`] error will be returned. + /// + /// # Errors + /// See `CoreAuthenticator::init` for potential errors. #[uniffi::constructor] #[tracing::instrument(target = "walletkit_latency", name = "rpc_init", skip_all)] pub async fn init_with_defaults( @@ -390,9 +475,13 @@ impl Authenticator { }) } - /// Initializes a new `Authenticator` from a 32-byte seed and a JSON-encoded `AuthenticatorConfig`. - /// The World ID account must already be registered in the `WorldIDRegistry`; use `InitializingAuthenticator` to register first. - /// `store` is used to cache credentials and inclusion proofs across sessions. + /// Initializes a new Authenticator from a seed and config. + /// + /// The user's World ID must already be registered in the `WorldIDRegistry`, + /// otherwise a [`WalletKitError::AccountDoesNotExist`] error will be returned. + /// + /// # Errors + /// Will error if the provided seed is not valid or if the config is not valid. #[uniffi::constructor] #[tracing::instrument(target = "walletkit_latency", name = "rpc_init", skip_all)] pub async fn init( @@ -419,9 +508,10 @@ impl Authenticator { }) } - /// Generates a ZK proof for the given `ProofRequest`, using stored credentials and a fresh Merkle inclusion proof. - /// `now` is the current Unix timestamp in seconds; on WASM it must be provided by the caller. - /// Returns a `ProofResponse` containing the nullifier and per-credential proofs. + /// Generates a proof for the given proof request. + /// + /// # Errors + /// Returns an error if proof generation fails. pub async fn generate_proof( &self, proof_request: &ProofRequest, @@ -539,9 +629,30 @@ impl Authenticator { Ok(result.proof_response.into()) } - /// Generates a WIP-103 ownership proof showing that the holder owns the credential `sub` without revealing `leaf_index`. - /// `nonce` is an issuer-provided replay-prevention value; `blinding_factor` and `sub` must be consistent with this authenticator. - /// Not supported on WASM targets. + /// Generates a WIP-103 Ownership Proof for Issuers. + /// + /// An Ownership Proof lets the user prove they own the credential `sub` + /// associated with a stored credential without revealing their `leaf_index`. + /// + /// # Security-critical usage constraint + /// This method **MUST only** be called as part of a direct + /// **user-initiated** action in the client. Callers **MUST NOT** expose this + /// method to issuer-triggered, backend-triggered, or unauthenticated request + /// flows. + /// + /// # Arguments + /// * `nonce` - A field element provided by the Issuer to prevent replay. + /// * `blinding_factor` - The credential blinding factor previously used to + /// derive the credential `sub`. + /// * `sub` - The credential `sub` (commitment) to prove ownership of. + /// + /// # Errors + /// - Returns [`WalletKitError::InvalidInput`] if `blinding_factor` and + /// `sub` are inconsistent with each other (i.e. `sub` was not derived + /// from this authenticator's leaf index and the provided blinding factor). + /// - Returns a network error if the Merkle inclusion proof cannot be + /// fetched from the indexer. + /// - Returns [`WalletKitError::ProofGeneration`] if the ZK proof fails. pub async fn prove_credential_sub( &self, nonce: &FieldElement, @@ -582,7 +693,7 @@ impl Authenticator { } } -/// Status for a registry gateway request. +/// Registration status for a World ID being created through the gateway. #[derive(Debug, Clone, uniffi::Enum)] pub enum GatewayRequestStatus { /// Request queued but not yet batched. @@ -623,17 +734,22 @@ impl From for GatewayRequestStatus { } } -/// An `Authenticator` in the process of being registered with the `WorldIDRegistry`. -/// Returned by `register` and `register_with_defaults`; call `poll_status` to track the gateway request. -/// Once the registration is finalized, initialize the full `Authenticator` using `init` or `init_with_defaults`. +/// Represents an Authenticator in the process of being initialized. +/// +/// The account is not yet registered in the `WorldIDRegistry` contract. +/// Use this for non-blocking registration flows where you want to poll the status yourself. #[derive(uniffi::Object)] pub struct InitializingAuthenticator(CoreInitializingAuthenticator); #[uniffi::export(async_runtime = "tokio")] impl InitializingAuthenticator { - /// Submits a new World ID registration using SDK-default config for the given environment. - /// Returns immediately without waiting for finalization; call `poll_status` on the result to track progress. - /// `recovery_address` is an optional checksummed hex Ethereum address to set as the initial recovery agent. + /// Registers a new World ID with SDK defaults. + /// + /// This returns immediately and does not wait for registration to complete. + /// The returned `InitializingAuthenticator` can be used to poll the registration status. + /// + /// # Errors + /// See `CoreAuthenticator::register` for potential errors. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", @@ -660,9 +776,13 @@ impl InitializingAuthenticator { Ok(Self(initializing_authenticator)) } - /// Submits a new World ID registration using the provided JSON-encoded `AuthenticatorConfig`. - /// Returns immediately without waiting for finalization; call `poll_status` on the result to track progress. - /// `recovery_address` is an optional checksummed hex Ethereum address to set as the initial recovery agent. + /// Registers a new World ID. + /// + /// This returns immediately and does not wait for registration to complete. + /// The returned `InitializingAuthenticator` can be used to poll the registration status. + /// + /// # Errors + /// See `CoreAuthenticator::register` for potential errors. #[uniffi::constructor] #[tracing::instrument( target = "walletkit_latency", @@ -691,8 +811,10 @@ impl InitializingAuthenticator { Ok(Self(initializing_authenticator)) } - /// Polls the gateway for the status of this registration request. - /// Returns a `GatewayRequestStatus` indicating whether the request is queued, submitted, finalized, or failed. + /// Polls the registration status from the gateway. + /// + /// # Errors + /// Will error if the network request fails or the gateway returns an error. #[tracing::instrument( target = "walletkit_latency", name = "gateway_poll", @@ -722,19 +844,28 @@ fn gateway_request_id_from_string(request_id: &str) -> GatewayRequestId { GatewayRequestId::new(request_id.strip_prefix("gw_").unwrap_or(request_id)) } -/// Signature and nonce returned by `danger_sign_initiate_recovery_agent_update`. -/// UniFFI does not support bare tuples across the FFI boundary, so the two values are bundled here. +/// The signature and signing nonce returned by +/// [`Authenticator::danger_sign_initiate_recovery_agent_update`]. +/// +/// `UniFFI` does not support returning bare tuples across the FFI boundary, so +/// the two values are bundled in this record type. #[derive(Debug, Clone, uniffi::Record)] pub struct RecoveryUpdateSignature { - /// Raw bytes of the secp256k1 ECDSA signature over the EIP-712 `InitiateRecoveryAgentUpdate` payload. + /// Raw bytes of the secp256k1 ECDSA signature over the EIP-712 + /// `InitiateRecoveryAgentUpdate` payload. pub signature: Vec, - /// The EIP-712 signing nonce used when producing the signature; must be included in the gateway request. + /// The EIP-712 signing nonce that was used; must be included in the + /// gateway request alongside the signature. pub nonce: Uint256, } /// Identity material derived from a seed for use during account recovery. -/// All three values must be submitted on-chain as part of the recovery transaction -/// before the recovered account can be initialized with `Authenticator::init`. +/// +/// During account recovery the user generates new keys from a seed, but those +/// keys do not yet exist on-chain. The three values in this record must be +/// submitted on-chain during the recovery transaction. +/// +/// All fields are hex-encoded strings suitable for direct use in API requests. #[derive(Debug, Clone, uniffi::Record)] pub struct RecoveryData { /// Checksummed hex Ethereum address of the on-chain signer. @@ -746,9 +877,14 @@ pub struct RecoveryData { } impl RecoveryData { - /// Derives the on-chain address, EdDSA public key, and off-chain signer commitment from a 32-byte seed. - /// These values must be submitted on-chain as part of the recovery transaction. - #[expect(clippy::missing_errors_doc, reason = "FFI")] + /// Derives recovery identity material from a 32-byte seed. + /// + /// These values must be submitted on-chain as part of the recovery + /// transaction before the recovered account can be initialised with + /// [`Authenticator::init`] / [`Authenticator::init_with_defaults`]. + /// + /// # Errors + /// Returns [`WalletKitError`] if the seed is invalid or serialization fails. pub fn from_seed(seed: &[u8]) -> Result { let signer = Signer::from_seed_bytes(seed)?; let authenticator_address = signer.onchain_signer_address().to_checksum(None); @@ -767,8 +903,12 @@ impl RecoveryData { } } -/// Derives recovery identity material from a 32-byte seed. -/// This is the FFI entry point for `RecoveryData::from_seed`; see that method for details. +/// Derives recovery data from a 32-byte seed. +/// +/// This is the foreign-bindings entrypoint for recovery data generation. +/// +/// # Errors +/// Returns [`WalletKitError`] if the seed is invalid or serialization fails. #[uniffi::export] pub fn recovery_data_from_seed(seed: &[u8]) -> Result { RecoveryData::from_seed(seed) From bae07376a6f1b3e868613cf990cc283f95f41556 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 May 2026 16:48:14 +0000 Subject: [PATCH 9/9] docs: replace expect(missing_errors_doc) with # Errors sections on authenticator management methods Replace the #[expect(clippy::missing_errors_doc, reason = "FFI")] suppression attributes on insert_authenticator, update_authenticator, remove_authenticator, and poll_gateway_request_status with proper # Errors doc sections describing the error conditions each method can return. --- walletkit-core/src/authenticator/mod.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/walletkit-core/src/authenticator/mod.rs b/walletkit-core/src/authenticator/mod.rs index 936189df..0f7f0299 100644 --- a/walletkit-core/src/authenticator/mod.rs +++ b/walletkit-core/src/authenticator/mod.rs @@ -345,7 +345,10 @@ impl Authenticator { /// Accepts the new authenticator's compressed EdDSA public key as a U256 and its on-chain signer /// address as a hex string. Returns a gateway request ID; poll it with `poll_gateway_request_status` /// to wait for finalization before initializing the new authenticator. - #[expect(clippy::missing_errors_doc, reason = "FFI")] + /// + /// # Errors + /// Returns [`WalletKitError::InvalidInput`] if the public key or address cannot be parsed. + /// Returns a network or authenticator error if the gateway request fails. pub async fn insert_authenticator( &self, new_authenticator_pubkey: Uint256, @@ -373,7 +376,10 @@ impl Authenticator { /// `pubkey_id` identifies the slot to replace; supply the old signer address, the new signer address, /// and the new compressed EdDSA public key as a U256. Returns a gateway request ID; poll it with /// `poll_gateway_request_status` to wait for finalization. - #[expect(clippy::missing_errors_doc, reason = "FFI")] + /// + /// # Errors + /// Returns [`WalletKitError::InvalidInput`] if any address or the public key cannot be parsed. + /// Returns a network or authenticator error if the gateway request fails. pub async fn update_authenticator( &self, old_authenticator_address: String, @@ -411,7 +417,10 @@ impl Authenticator { /// /// `authenticator_address` and `pubkey_id` together identify the slot to remove. Returns a gateway /// request ID; poll it with `poll_gateway_request_status` to wait for finalization. - #[expect(clippy::missing_errors_doc, reason = "FFI")] + /// + /// # Errors + /// Returns [`WalletKitError::InvalidInput`] if the authenticator address cannot be parsed. + /// Returns a network or authenticator error if the gateway request fails. pub async fn remove_authenticator( &self, authenticator_address: String, @@ -432,7 +441,9 @@ impl Authenticator { /// /// Accepts a request ID returned by any authenticator-management or recovery method. Returns a /// `GatewayRequestStatus` indicating whether the request is queued, submitted, finalized, or failed. - #[expect(clippy::missing_errors_doc, reason = "FFI")] + /// + /// # Errors + /// Returns a network error if the gateway cannot be reached or returns an unexpected response. pub async fn poll_gateway_request_status( &self, request_id: String,