Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
317 changes: 46 additions & 271 deletions Cargo.lock

Large diffs are not rendered by default.

22 changes: 16 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["uniffi-bindgen","walletkit-core", "walletkit"]
members = ["uniffi-bindgen", "walletkit-core", "walletkit", "wasm"]
resolver = "2"

[workspace.package]
Expand All @@ -8,20 +8,30 @@ license = "MIT"
edition = "2021"
authors = ["World Contributors"]
readme = "./README.md"
homepage = "https://docs.world.org" # TODO: Update to specific WalletKit page
rust-version = "1.86" # MSRV
homepage = "https://docs.world.org" # TODO: Update to specific WalletKit page
rust-version = "1.86" # MSRV
repository = "https://github.com/worldcoin/walletkit"
exclude = ["tests/", "uniffi-bindgen/"]
keywords = ["ZKP", "WorldID", "World", "Identity", "Semaphore"]
categories = ["api-bindings", "cryptography::cryptocurrencies"]


[workspace.dependencies]
alloy-core = { version = "1", default-features = false, features = ["sol-types"] }
alloy-core = { version = "1", default-features = false, features = [
"sol-types",
] }
alloy-primitives = { version = "1", default-features = false }
walletkit-core = { version = "0.1.8", path = "walletkit-core", default-features = false }
uniffi = { version = "0.29", features = ["build", "tokio"] }
world-id-core = { git = "https://github.com/worldcoin/world-id-protocol", rev = "8041ac3", default-features = false, features = ["authenticator"] }
uniffi = { version = "0.29", default-features = false }
world-id-core = { path = "../world-id-protocol/crates/core", default-features = false, features = [
"authenticator",
] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[profile.release]
opt-level = 'z' # Optimize for size.
Expand Down
41 changes: 31 additions & 10 deletions walletkit-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,48 @@ alloy-core = { workspace = true }
alloy-primitives = { workspace = true }
hex = "0.4"
log = "0.4"
reqwest = { version = "0.12", default-features = false, features = [
"json",
"brotli",
"rustls-tls",
] }
ruint = { version = "1.17", default-features = false, features = [
"alloc",
"ark-ff-04",
] }
secrecy = "0.10"
semaphore-rs = { version = "0.5" }
# semaphore-rs uses mmap which doesn't compile on wasm; make it optional and gate on semaphore feature.
semaphore-rs = { version = "0.5", optional = true }
serde = "1"
serde_json = "1"
strum = { version = "0.27", features = ["derive"] }
subtle = "2"
thiserror = "2"
tokio = { version = "1", features = ["sync"] }
uniffi = { workspace = true, features = ["build", "tokio"] }
world-id-core = { workspace = true, optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
reqwest = { version = "0.12", default-features = false, features = ["json"] }
uniffi = { workspace = true, default-features = false }
tokio = { version = "1", default-features = false, features = ["sync"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
reqwest = { version = "0.12", default-features = false, features = [
"json",
"brotli",
"rustls-tls",
] }
uniffi = { workspace = true, features = ["build", "tokio"] }
tokio = { version = "1", features = [
"sync",
"macros",
"rt",
"rt-multi-thread",
"time",
] }

[dev-dependencies]
alloy = { version = "1", default-features = false, features = ["getrandom", "json", "contract", "node-bindings", "signer-local"] }
alloy = { version = "1", default-features = false, features = [
"getrandom",
"json",
"contract",
"node-bindings",
"signer-local",
] }
chrono = "0.4.41"
dotenvy = "0.15.7"
mockito = "1.6"
Expand All @@ -59,7 +79,8 @@ rand = "0.8"
default = ["common-apps", "semaphore", "v4"]
common-apps = []
http-tests = []
semaphore = ["semaphore-rs/depth_30"]
# Semaphore proof generation pulls mmap-rs (not wasm-compatible); require explicit opt-in.
semaphore = ["dep:semaphore-rs", "semaphore-rs/depth_30"]
v4 = ["world-id-core"]

# Before conventions were introduced for external nullifiers with `app_id` & `action`, raw field elements were used.
Expand Down
5 changes: 3 additions & 2 deletions walletkit-core/src/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ use crate::{
};

/// The Authenticator is the main component with which users interact with the World ID Protocol.
#[derive(Debug, uniffi::Object)]
#[derive(Debug)]
#[cfg_attr(not(target_arch = "wasm32"), derive(uniffi::Object))]
pub struct Authenticator(CoreAuthenticator);

#[uniffi::export(async_runtime = "tokio")]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::export(async_runtime = "tokio"))]
impl Authenticator {
/// Initializes a new Authenticator from a seed and with SDK defaults.
///
Expand Down
2 changes: 1 addition & 1 deletion walletkit-core/src/credential_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ use crate::Environment;
Display,
Serialize,
Deserialize,
uniffi::Enum,
)]
#[cfg_attr(not(target_arch = "wasm32"), derive(uniffi::Enum))]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum CredentialType {
Expand Down
4 changes: 3 additions & 1 deletion walletkit-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use thiserror::Error;
use world_id_core::AuthenticatorError;

/// Error outputs from `WalletKit`
#[derive(Debug, Error, uniffi::Error)]
#[derive(Debug, Error)]
#[cfg_attr(not(target_arch = "wasm32"), derive(uniffi::Error))]
pub enum WalletKitError {
/// Invalid input provided (e.g., incorrect length, format, etc.)
#[error("invalid_input_{attribute}")]
Expand Down Expand Up @@ -99,6 +100,7 @@ impl From<reqwest::Error> for WalletKitError {
}
}

#[cfg(feature = "semaphore")]
impl From<semaphore_rs::protocol::ProofError> for WalletKitError {
fn from(error: semaphore_rs::protocol::ProofError) -> Self {
Self::ProofGeneration {
Expand Down
10 changes: 8 additions & 2 deletions walletkit-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use strum::EnumString;
/// Each environment uses different sources of truth for the World ID credentials.
///
/// More information on testing for the World ID Protocol can be found in: `https://docs.world.org/world-id/quick-start/testing`
#[derive(Debug, Clone, PartialEq, Eq, EnumString, uniffi::Enum)]
#[derive(Debug, Clone, PartialEq, Eq, EnumString)]
#[cfg_attr(not(target_arch = "wasm32"), derive(uniffi::Enum))]
#[strum(serialize_all = "lowercase")]
pub enum Environment {
/// For testing purposes ONLY.
Expand Down Expand Up @@ -58,13 +59,15 @@ pub use authenticator::Authenticator;
pub(crate) mod defaults;

////////////////////////////////////////////////////////////////////////////////
// Legacy modules
// Legacy modules (require semaphore feature for proof generation)
////////////////////////////////////////////////////////////////////////////////

/// Contains all components to interact and use a World ID
#[cfg(feature = "semaphore")]
pub mod world_id;

/// This module handles World ID proof generation
#[cfg(feature = "semaphore")]
pub mod proof;

/// This module exposes helper functions to interact with common apps & contracts related to the World ID Protocol.
Expand All @@ -75,7 +78,10 @@ pub mod common_apps;
// Private modules
////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "semaphore")]
mod merkle_tree;
#[cfg(feature = "semaphore")]
mod request;

#[cfg(not(target_arch = "wasm32"))]
uniffi::setup_scaffolding!("walletkit_core");
7 changes: 4 additions & 3 deletions walletkit-core/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use std::sync::{Arc, OnceLock};
/// ```swift
/// setupWalletKitLogger() // Call this only once!!!
/// ```
#[uniffi::export(with_foreign)]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::export(with_foreign))]
pub trait Logger: Sync + Send {
/// Logs a message at the specified log level.
///
Expand All @@ -56,7 +56,8 @@ pub trait Logger: Sync + Send {
/// Enumeration of possible log levels.
///
/// This enum represents the severity levels that can be used when logging messages.
#[derive(Debug, Clone, uniffi::Enum)]
#[derive(Debug, Clone)]
#[cfg_attr(not(target_arch = "wasm32"), derive(uniffi::Enum))]
pub enum LogLevel {
/// Designates very low priority, often extremely detailed messages.
Trace,
Expand Down Expand Up @@ -170,7 +171,7 @@ static LOGGER_INSTANCE: OnceLock<Arc<dyn Logger>> = OnceLock::new();
/// # Note
///
/// If the logger has already been set, this function will print a message and do nothing.
#[uniffi::export]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::export)]
pub fn set_logger(logger: Arc<dyn Logger>) {
match LOGGER_INSTANCE.set(logger) {
Ok(()) => (),
Expand Down
11 changes: 6 additions & 5 deletions walletkit-core/src/u256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use serde::{Deserialize, Serialize};
/// Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD
/// be represented as padded hex strings from Big Endian bytes.
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, uniffi::Object)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(not(target_arch = "wasm32"), derive(uniffi::Object))]
pub struct U256Wrapper(pub U256);

#[uniffi::export]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::export)]
impl U256Wrapper {
/// Outputs a hex string representation of the `U256` value padded to 32 bytes (plus two bytes for the `0x` prefix).
#[must_use]
Expand Down Expand Up @@ -48,7 +49,7 @@ impl U256Wrapper {
///
/// Logically this will only support values up to 64 bits. For larger values a different initialization should be used.
#[must_use]
#[uniffi::constructor]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::constructor)]
pub fn from_u64(value: u64) -> Self {
Self(U256::from(value))
}
Expand All @@ -57,7 +58,7 @@ impl U256Wrapper {
///
/// Logically this will only support values up to 32 bits. For larger values a different initialization should be used.
#[must_use]
#[uniffi::constructor]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::constructor)]
pub fn from_u32(value: u32) -> Self {
Self(U256::from(value))
}
Expand All @@ -69,7 +70,7 @@ impl U256Wrapper {
/// # Errors
///
/// Will return an `Error::InvalidNumber` if the input is not a valid `U256` value.
#[uniffi::constructor]
#[cfg_attr(not(target_arch = "wasm32"), uniffi::constructor)]
pub fn from_limbs(limbs: Vec<u64>) -> Result<Self, WalletKitError> {
let limbs = limbs
.try_into()
Expand Down
27 changes: 27 additions & 0 deletions wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "walletkit-wasm"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description = "WASM bindings for WalletKit"

[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
# Disable semaphore to avoid mmap-rs (not wasm-compatible); only enable v4 authenticator.
walletkit-core = { path = "../walletkit-core", default-features = false, features = [
"v4",
] }
wasm-bindgen = { workspace = true }
wasm-bindgen-futures = { workspace = true }
js-sys = { workspace = true }
web-sys = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde-wasm-bindgen = "0.6"
32 changes: 32 additions & 0 deletions wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# WalletKit WASM bindings

## Builds

Run the helper scripts from the workspace root (they forward additional arguments to `wasm-pack`):

```bash
./walletkit/wasm/scripts/build-web.sh # Builds for browsers
./walletkit/wasm/scripts/build-node.sh # Builds for Node.js (ESM + CJS)
```

Both scripts output artifacts to `walletkit/wasm/pkg/`.

## Usage sketch

```ts
import init, { Authenticator, Environment } from "./pkg/walletkit_wasm.js";
import { readFile } from "node:fs/promises";

const wasm = await readFile("./pkg/walletkit_wasm_bg.wasm");
await init(wasm);

const seed = new Uint8Array(32); // placeholder seed
const authenticator = await Authenticator.initWithDefaults(
seed,
"https://rpc.example.com",
Environment.Staging()
);

console.log(authenticator.onchainAddress());
console.log(await authenticator.getPackedAccountIndexRemote()); // -> bigint
```
7 changes: 7 additions & 0 deletions wasm/scripts/build-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CRATE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"

wasm-pack build "${CRATE_DIR}" --release --target nodejs "$@"
7 changes: 7 additions & 0 deletions wasm/scripts/build-web.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CRATE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"

wasm-pack build "${CRATE_DIR}" --release --target web "$@"
Loading
Loading