React Native Setup
This tutorial will guide you through integrating the iOS bindings and Android bindings into an React Native) project. Before you begin, make sure you’ve completed the "Getting Started - 3. Mopro build" process with selecting iOS platform and Android platform and have the MoproiOSBindings and MoproAndroidBindings folder ready:
React Native is a JavaScript framework that enables developers to build native apps for multiple platforms with a single codebase.
In this tutorial, you will learn how to create a native Mopro module on both Android and iOS simulators/devices.


In this example, we use Circom circuits and their corresponding .zkey files. The process is similar for other provers.
0. Initialize an React Native project
First let's create a new React Native project. If you already have a React Native project you can skip this step.
-
Getting started with React Native: Official documentation
infoThe Expo framework is recommended by the React Native community. (Last updated on Apr 14, 2025)
We will use the Expo framework throughout this documentation.
Ref: Start a new React Native project with Expo -
After creating a React Native project, you should be able to run with a commands like
npm run iosfor iOS simulators. And
npm run androidfor Android emulators.
1. Creating a Native Module
-
Creating a native module by the command
npx create-expo-module --local moproIt will create a native module named
moproin themodules/moprofolder with the structure like this├── modules
│ └── mopro
│ ├── android
│ ├── expo-module.config.json
│ ├── index.ts
│ ├── ios
│ └── src
2. Implement the module on iOS
Please refer to react-native-app to see the latest update.
2-1 Use a framework
-
Get the
MoproiOSBindingsfrommopro build.infoSee Getting Started
-
Copy the
MoproiOSBindingsdirectory tomodules/mopro/ios -
Bundle the bindings in
Mopro.podspec/modules/mopro/ios/Mopro.podspec...
s.dependency 'ExpoModulesCore'
s.vendored_frameworks = 'MoproiOSBindings/MoproBindings.xcframework'
...
2-2 Create convertible types for Javascript library with swift.
-
Create a new file called
MoproType.swiftin the following folder:modules/mopro/iosFull
/modules/mopro/ios/MoproType.swift/modules/mopro/ios/MoproType.swiftimport ExpoModulesCore
struct ExpoG1: Record {
@Field
var x: String?
@Field
var y: String?
@Field
var z: String?
}
struct ExpoG2: Record {
@Field
var x: [String]?
@Field
var y: [String]?
@Field
var z: [String]?
}
struct ExpoProof: Record {
@Field
var a: ExpoG1?
@Field
var b: ExpoG2?
@Field
var c: ExpoG1?
@Field
var `protocol`: String?
@Field
var curve: String?
}
struct ExpoCircomProofResult: Record {
@Field
var inputs: [String]?
@Field
var proof: ExpoProof?
}
enum ProofLibOption: Int, Enumerable {
case arkworks
case rapidsnark
}
struct ExpoCircomProofLib: Record {
@Field
var proofLib: ProofLibOption = .arkworks
}
2-3. Create native module implementation in MoproModule.swift
-
Define helper functions to bridge types between the Mopro bindings and the Expo framework:
/modules/mopro/ios/MoproModule.swifthelpers/modules/mopro/ios/MoproModule.swiftimport ExpoModulesCore
import moproFFI
// convert the mopro proofs to be exposed to Expo framework
func convertCircomProof(proof: CircomProof) -> ExpoProof {
let a = ExpoG1()
a.x = proof.a.x
a.y = proof.a.y
a.z = proof.a.z
let b = ExpoG2()
b.x = proof.b.x
b.y = proof.b.y
b.z = proof.b.z
let c = ExpoG1()
c.x = proof.c.x
c.y = proof.c.y
c.z = proof.c.z
let expoProof = ExpoProof()
expoProof.a = a
expoProof.b = b
expoProof.c = c
expoProof.protocol = proof.protocol
expoProof.curve = proof.curve
return expoProof
}
// convert the Expo proofs to be used in mopro bindings
func convertCircomProofResult(proofResult: ExpoCircomProofResult) -> CircomProofResult {
guard let proof = proofResult.proof,
let a = proof.a,
let b = proof.b,
let c = proof.c,
let inputs = proofResult.inputs,
let `protocol` = proof.protocol,
let curve = proof.curve
else {
fatalError("Invalid proof result")
}
let g1a = G1(x: a.x ?? "0", y: a.y ?? "0", z: a.z ?? "1")
let g2b = G2(x: b.x ?? ["1", "0"], y: b.y ?? ["1", "0"], z: b.z ?? ["1", "0"])
let g1c = G1(x: c.x ?? "0", y: c.y ?? "0", z: c.z ?? "1")
let circomProof = CircomProof(
a: g1a, b: g2b, c: g1c, protocol: `protocol`, curve: curve)
let circomProofResult = CircomProofResult(proof: circomProof, inputs: inputs)
return circomProofResult
}
enum CircomError: Error {
case circomProofGenerationFailed(String)
case circomProofVerificationFailed(String)
} -
Define the native module API. See the Module API Reference for details.
modules/mopro/ios/MoproModule.swiftpublic class MoproModule: Module {
// Each module class must implement the definition function. The definition consists of components
// that describes the module's functionality and behavior.
// See https://docs.expo.dev/modules/module-api for more details about available components.
public func definition() -> ModuleDefinition {
// Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
// Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
// The module will be accessible from `requireNativeModule('Mopro')` in JavaScript.
Name("Mopro")
// ...
AsyncFunction("generateCircomProof") {
(zkeyPath: String, circuitInputs: String, expoProofLib: ExpoCircomProofLib) -> ExpoCircomProofResult in
do {
let proofLib = expoProofLib.proofLib == ProofLibOption.arkworks ? ProofLib.arkworks : ProofLib.rapidsnark
let res = try generateCircomProof(
zkeyPath: zkeyPath, circuitInputs: circuitInputs, proofLib: proofLib)
let result = ExpoCircomProofResult()
result.inputs = res.inputs
result.proof = convertCircomProof(proof: res.proof)
return result
} catch {
throw CircomError.circomProofGenerationFailed(error.localizedDescription)
}
}
AsyncFunction("verifyCircomProof") {
(zkeyPath: String, proofResult: ExpoCircomProofResult, proofLib: ExpoCircomProofLib) -> Bool in
do {
let proofLib = proofLib.proofLib == .arkworks ? ProofLib.arkworks : ProofLib.rapidsnark
let isValid = try verifyCircomProof(
zkeyPath: zkeyPath,
proofResult: convertCircomProofResult(proofResult: proofResult),
proofLib: ProofLib.arkworks
)
return isValid
} catch {
throw CircomError.circomProofVerificationFailed(error.localizedDescription)
}
}
// ...
}
3. Implement the module on Android
Please refer to react-native-app to see the latest update.
3-1. Add dependency for jna in the file build.gradle.
dependencies {
implementation("net.java.dev.jna:jna:5.13.0@aar")
}
3-2. Include Mopro bindings in the native Android module
- Get the
MoproAndroidBindingsfrommopro build.infoSee Getting Started
- Move the
jniLibsdirectory tomodules/mopro/android/src/main/.
And moveuniffidirectory tomodules/mopro/android/src/main/java/.
The folder structure should be as follows:modules/mopro/android/src/main
├── AndroidManifest.xml
├── assets
├── java
│ ├── expo
│ │ └── modules
│ │ └── mopro
│ │ ├── MoproModule.kt
│ │ └── MoproView.kt