paritytech / paritytech/verifiablejs

Enable React Native (iOS/Android) support — evaluate native `verifiable` crate via UniFFI Turbo Modules

Open
#25 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement question
Dominant language
Rust
Stars
1
Forks
1
Avg merge
1h 59m
Merged PRs (30d)
4

Description

Goal

Make verifiablejs usable from React Native to build real iOS/Android apps. This issue captures the full investigation, the blockers, every approach considered, and a recommended plan so it can be picked up directly.

TL;DR: You cannot drop the current WASM package into React Native today. The most durable path is not to ship WASM to the device at all — compile the verifiable crate to a native library and bridge it to JS as a Turbo Module, ideally via UniFFI, which can also regenerate the web/Node builds from the same source. Start with a time-boxed spike to de-risk the mobile toolchain.


Why the current package doesn't work in React Native

The package ships two wasm-pack targets; both hard-depend on things Hermes (RN's default engine) doesn't provide:

  1. No WebAssembly runtime in Hermes.
    • pkg-bundler/verifiablejs.jsimport * as wasm from "./verifiablejs_bg.wasm" (needs bundler WASM-ESM + a WebAssembly global).
    • pkg-nodejs/verifiablejs.jsrequire('fs').readFileSync(...) + new WebAssembly.Module(...) (needs Node fs/path + WebAssembly).
    • Hermes historically has no global.WebAssembly (facebook/hermes#429). Native WASM is only just landing in Hermes v1 / React Native 0.84 — bleeding edge, and wasm-bindgen-glue compatibility is unproven.
  2. Metro doesn't resolve .wasm imports or Node built-ins out of the box.
  3. getrandom needs crypto.getRandomValues (Cargo.toml uses getrandom = { features = ["js"] }); RN has no global crypto (would need react-native-get-random-values).
  4. TextEncoder/TextDecoder globals need polyfilling on older RN.
  5. ~7.3 MB .wasm — cold-start / bundle cost on-device.

Approaches considered

⭐ A. Native verifiable crate via UniFFI Turbo Module — recommended

Don't ship WASM. Compile the Rust crate natively for mobile and bridge via uniffi-bindgen-react-native (renaming to uniffi-bindgen-javascript).

Why it's actually simpler than the WASM route:

  • getrandom gets simpler — native targets use the OS RNG directly; drop the js feature and the polyfill.
  • No Metro .wasm handling, no WebAssembly global, no 7.3 MB blob at cold start.
  • Native speed for the heavy ring-VRF proving, on all RN versions (no waiting for RN 0.84).
  • verifiable is pure-Rust arkworks crypto, no system deps → clean cross-compile.

Bonus: from one UniFFI annotation set, uniffi-bindgen-react-native emits iOS/Android (Turbo Modules), web (still WASM, generated via wasm-bindgen), and Node/Bun (N-API). So this is a plausible single-toolchain future covering web + mobile, not a web-vs-mobile fork.

Cost / risk:

  • Re-express the API (one_shot, validate, validate_with_commitment, sign, verify_signature, key mgmt — see packages/verifiablejs/src/lib.rs) in UniFFI annotations. API is mostly Uint8Array in/out → maps cleanly to UniFFI bytes/records.
  • New Rust→mobile build & CI: cargo-ndk (Android), .xcframework (iOS).
  • uniffi-bindgen-react-native is relatively young for the JS/RN target.
B. Polygen (AOT wasm2c → C → JSI) — callstackincubator/polygen

Reuses the existing .wasm, AOT-compiled so it runs on iOS (no JIT). Near-native speed.

  • ⚠️ Runs the module, not the wasm-bindgen JS glue — still must wire crypto.getRandomValues/TextEncoder. "Under active development, use at your own risk." RN 0.75+, new arch only.
  • Good as a fast de-risking spike if we want to prove the existing binary can run at all, but weaker as the long-term answer than A.
C. react-native-webassembly (wasm3 interpreter via JSI) — cawfree/react-native-webassembly

Runs universal .wasm but interpreted → slow. Ring-VRF proving is heavy; likely too slow. Low-priority fallback.

D. Native Hermes WASM (RN 0.84 / Hermes v1)

Cleanest reuse of the existing package eventually, but bleeding-edge; still needs Metro .wasm handling + polyfills, and wasm-bindgen glue on Hermes v1 is unverified. Revisit once 0.84 is stable.


Background: what is UniFFI, and how does it relate to wasm-pack?

UniFFI is Mozilla's multi-language bindings generator for Rust (built for Firefox to share Rust across Swift/Kotlin/Python). You annotate a thin Rust wrapper; it generates idiomatic bindings + C-ABI glue per target.

UniFFI vs wasm-pack:

wasm-pack (today) UniFFI + rn/js bindgen
Targets Web + Node (WASM only) Web (WASM) + Node + iOS + Android native
Setup one command, mature more setup, mobile build/CI, newer JS/RN tooling
JS API control full wasm-bindgen power constrained to UniFFI's cross-language type system
  • Keep wasm-pack while web/Node is the whole story — simpler, mature, full control of the JS API shape. Right tool today.
  • Reach for UniFFI once mobile is a real target — one annotation set → web + node + iOS + Android, and the web output is still WASM, so we lose ~nothing on web while gaining native mobile.

Proposed plan (for a fresh agent)

Phase 0 — Decision spike (time-boxed):

  • Confirm verifiable cross-compiles to aarch64-apple-ios and aarch64-linux-android cleanly (pure Rust, expected yes). Note any feature-flag/no_std/getrandom adjustments needed.
  • Stand up a minimal uniffi-bindgen-react-native proof-of-concept wrapping one function (one_shot) end-to-end into a bare RN app on a simulator; measure APK/IPA size delta and proving latency.
  • (Optional, parallel) Quick Polygen spike to see if the existing .wasm runs at all — useful data point, not the deliverable.

Phase 1 — Full binding crate (if spike is green):

  • Author the UniFFI wrapper covering the full API surface in packages/verifiablejs/src/lib.rs.
  • Wire mobile builds: cargo-ndk (Android), .xcframework (iOS); integrate into CI.
  • Publish an RN-consumable package (Turbo Module + TS types).
  • Evaluate whether to migrate the web/Node builds onto the same UniFFI source of truth or keep wasm-pack in parallel.

Phase 2 — Docs:

  • Add a "React Native / mobile" section to the README Platform Support with setup + status.

Open questions

  • Do we commit to UniFFI as the single toolchain (web+node+mobile), or keep wasm-pack for web and add UniFFI only for mobile?
  • Minimum RN version / new-architecture requirement we're willing to require.
  • Any API-shape constraints from UniFFI's type system that would change the current JS ergonomics (current API is byte-array-centric, so likely minimal).

References

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/verifiablejs/src/lib.rs and the one_shot API, then verify native cross-compilation for aarch64-apple-ios and aarch64-linux-android. Build a minimal uniffi-bindgen-react-native proof of concept in a bare React Native app and measure proving latency and package size. Done means the spike has documented toolchain blockers and a decision on whether to proceed with the full binding plan.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, ios, react-native, rust
Domain
build-system, mobile-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.