Bridge SDK¶
Role¶
The bridge-sdk is the Mina-side counterpart to bridge-head, the Ethereum-side bridge contracts, and the TypeScript integration layer that ties them together. Bridge-head produces an SP1 consensus-MPT transition proof from an Ethereum block; proof-conversion turns that into an o1js- verifiable Plonk proof; bridge-sdk is what verifies that proof on Mina, mints/burns the bridged token, locks/unlocks on Ethereum, and provides the worker, attestation, and integrity-anchor machinery a client needs to drive the end-to-end flow. Every on-chain action on either side of the bridge ultimately runs through code in this repo.
Source¶
- Repository: https://github.com/Nori-zk/nori-bridge-sdk (branch:
develop) - Languages: TypeScript (workspaces, o1js programs, tasks) and Solidity ^0.8.28 (Ethereum contracts)
- License: Apache-2.0 at repo root (the
contracts/ethereumworkspace declares MIT in itspackage.json; the Solidity files themselves carry per-file SPDX headers — reconciliation is tracked as a pre-mainnet open item) - npm scope:
@nori-zk/. Four workspaces publish publicly toregistry.npmjs.org:@nori-zk/workers,@nori-zk/o1js-zk-utils,@nori-zk/ethereum-token-bridge, and@nori-zk/mina-token-bridge. Two workspaces are marked"private": "true"and are not published —@nori-zk/cache-serverandnori-client-minimal— though their source remains public in this repo.
Architecture¶
| Workspace | npm name | Language(s) | Role |
|---|---|---|---|
workers |
@nori-zk/workers |
TypeScript | Node/browser worker abstraction for off-main-thread proof compilation and execution. |
o1js-zk-utils |
@nori-zk/o1js-zk-utils |
TypeScript / o1js | Deposit attestation, merkle attestor, byte/Field helpers, integrity anchors. |
contracts/ethereum |
@nori-zk/ethereum-token-bridge |
Solidity, TypeScript | NoriTokenBridge.sol (lock/unlock + fee accounting), Hardhat tasks, OZ TimelockController integration. |
contracts/mina |
@nori-zk/mina-token-bridge |
TypeScript / o1js | NoriTokenBridge zkApp, storage zkApp, proof submitter, operator CLIs. |
cache-server |
@nori-zk/cache-server |
TypeScript | uWebSockets.js static server that delivers pre-compiled o1js VK / circuit cache layouts to clients. Not published to npm. |
minimal-client |
nori-client-minimal |
TypeScript | Puppeteer-driven browser end-to-end harness that exercises the full ETH→Mina flow. Not published to npm. |
All four o1js-consuming workspaces pin the same peer:
o1js@3.0.0-mesa.698ca.
Public API¶
@nori-zk/workers exports the worker plumbing:
createWorker, createProxy, WorkerParentBase, WorkerChildBase, and
the parent/child interfaces. Subpath exports expose node and browser
entry points (./node/parent, ./node/child, ./browser/parent,
./browser/child). The pattern is "build a proxy class on the parent
side that mirrors the worker class on the child side and forwards calls
across a JSON message channel"; this is what contracts/mina uses to
run tokenBridgeWorker (off-main-thread Mina prover) and
tokenBridgeTester (test harness worker).
@nori-zk/o1js-zk-utils is the o1js helper library. The
re-exports of interest to an integrator (see o1js-zk-utils/src/index.ts)
are the merkle attestor helpers (buildMerkleTree,
computeMerkleRootFromPath, foldMerkleLeft,
getMerklePathFromLeaves, merkleLeafAttestorGenerator), the deposit
attestor (ContractDepositAttestor, ContractDepositAttestorInput,
ContractDepositAttestorProof, buildContractDepositLeaves,
getContractDepositWitness), the EthInput struct + byte/Field
utilities, the three integrity-anchor re-exports
(bridgeHeadNoriSP1HeliosProgramPi0,
proofConversionSP1ToPlonkPO2, proofConversionSP1ToPlonkVkData),
and an EthVerifier ZkProgram (o1js-zk-utils/src/ethVerifier.ts)
kept as a legacy reference. The live, deployed verification path is
the inline ethVerify method on the Mina-side zkApp.
@nori-zk/mina-token-bridge exports the NoriTokenBridge
zkApp class (contracts/mina/src/NoriTokenBridge.ts). The deployed
contract exposes one verification entry point — @method update(input,
proof, oldestAction) at NoriTokenBridge.ts:348 — plus three admin
rotation entry points at NoriTokenBridge.ts:643-655:
updateVerificationKey, updateNoriHeliosProgramPi0, and
updateProofConversionPO2 (with updateStoreHash immediately
following).
@nori-zk/ethereum-token-bridge exposes the Solidity
NoriTokenBridge contract plus a set of Hardhat tasks for lock,
fee management, and operator rotation (test:lock, get-fee-info,
set-fee-rate, set-fee-recipient, withdraw-fees,
set-bridge-operator).
Build & test¶
Test files follow the pattern documented in
README.TEST-CONVENTIONS.md:
*.unit.spec.ts are deterministic and offline,
*.integration.spec.ts exercise multiple components or ZK programs
together (and may require lightnet), and *.e2e.spec.ts drive full
ETH→Mina workflows (*.devnet.e2e.spec.ts,
*.lightnet.e2e.spec.ts). Each test file runs in its own Node process
via a bash for-loop in the workspace's test:unit / test:integration /
test:e2e scripts, which contains o1js memory growth across files.
The canonical CI entry point is npm run test-ci (not
npm run test): each workspace's test-ci script selects a deliberately
bounded subset designed for the CI runtime budget. Use npm run test
locally when you want the full suite for a workspace.
A cross-language Merkle root reproducibility harness lives at
o1js-zk-utils/test/cross-reference-roots.sh
(see o1js-zk-utils/test/CROSS-REFERENCE-ROOTS.md) and validates
Rust ↔ TS non-provable ↔ TS provable Merkle root agreement.
Trust anchors¶
For the cross-component story of how pi0, vkData, and PO2 flow
between bridge-head, proof-conversion, and the Mina-side zkApp, see
Trust anchors. This section
documents the SDK-specific mechanics: where the integrity files live,
which Mina-side state slots consume them, and which operator commands
rotate them.
The Mina-side bridge does not bake its trust-anchor values into the
circuit verification key. Instead, three values are kept as updatable
on-chain state, with the SDK shipping the canonical values as JSON files
that operators can rotate as upstream programs evolve. The three integrity
files all live under o1js-zk-utils/src/integrity/:
File (under o1js-zk-utils/src/integrity/) |
Anchors | Mina-side consumer |
|---|---|---|
nori-sp1-helios-program.pi0.json |
SP1 program identifier (Nori SP1-Helios ELF) | @state(FrC) noriHeliosProgramPi0 |
ProofConversion.sp1ToPlonk.po2.json |
proof-conversion public output digest | @state(Field) proofConversionPO2 |
ProofConversion.sp1ToPlonk.vkData.json |
proof-conversion verification key data + hash | inlined VerificationKey consumed by ethVerify() |
pi0 and po2 are first-class @state slots set at deploy and rotated
via dedicated admin methods (updateNoriHeliosProgramPi0,
updateProofConversionPO2). The proof-conversion vkData is a circuit
constant — it is read at proof-build time from the integrity JSON and
becomes part of the deployed VK; rotating it requires a re-bake plus an
updateVerificationKey transaction.
sequenceDiagram
autonumber
participant BH as nori-bridge-head
participant PC as proof-conversion
participant SDK as o1js-zk-utils/integrity/
participant OP as Operator (CLIs)
participant ZK as NoriTokenBridge zkApp (Mina)
BH->>SDK: pi0.json (per release)
PC->>SDK: po2.json + vkData.json
OP->>ZK: deploy() — sets noriHeliosProgramPi0, proofConversionPO2, vkData baked into VK
Note over OP,ZK: Subsequent rotations
OP->>ZK: update:pi0 (admin tx) — bridge-head release
OP->>ZK: update:po2 (admin tx) — SP1 major upgrade
OP->>ZK: bake-vk-hashes + update:vk — proof-conversion VK change
Operations¶
The operator-facing scripts live in
contracts/mina/src/bin/ and are exposed as npm scripts on the
@nori-zk/mina-token-bridge workspace. The full set:
# Deploy / pre-deploy
npm run derive-token-id --workspace=@nori-zk/mina-token-bridge
npm run deploy --workspace=@nori-zk/mina-token-bridge
npm run deploy-with-keys --workspace=@nori-zk/mina-token-bridge # testing only
# Integrity rotation
npm run update:pi0 --workspace=@nori-zk/mina-token-bridge
npm run update:po2 --workspace=@nori-zk/mina-token-bridge
npm run update:integrity-params --workspace=@nori-zk/mina-token-bridge
npm run update:store-hash --workspace=@nori-zk/mina-token-bridge
npm run update:vk --workspace=@nori-zk/mina-token-bridge
npm run update:vk-non-provable --workspace=@nori-zk/mina-token-bridge
# VK artefact maintenance
npm run bake-vk-hashes --workspaces --if-present
npm run migrate-vk-to-tag --workspace=@nori-zk/mina-token-bridge
# Cache / ops
npm run build:cache-layouts --workspace=@nori-zk/mina-token-bridge
npm run poll-deposits-root --workspace=@nori-zk/mina-token-bridge
npm run prove-and-submit --workspace=@nori-zk/mina-token-bridge
npm run load-runner --workspace=@nori-zk/mina-token-bridge
The end-to-end production sequence — FROST 3/4 threshold keys,
pre-deploy npm run pre-deploy/derive-token-id, deploy, then
bake-vk-hashes followed by initial integrity-params posting — is
documented in contracts/DEPLOYMENT.md.
The Ethereum-side deploy is driven from the contracts/ethereum
workspace with npm run pre-deploy (fetches the genesis validators
root into .env.nori-eth-pre-deploy), npm run deploy, and
npm run deploy-timelock for the governance TimelockController.
Quirks¶
- o1js peer-dep churn.
o1jsis pinned at a tagged pre-release (3.0.0-mesa.698ca); a diagnostic at the repo root,determine-o1js-breaking-changes.js, helps surface breakage when the peer-dep moves. - Cache layouts dance.
contracts/minaproduces the VK cache vianpm run build:cache-layouts;@nori-zk/cache-serverthen serves the resulting layout directory over HTTP (uWebSockets.js) for browser clients that need pre-compiled circuit data. Thecache-serverworkspace exposes a conveniencenpm run build:cachethat chains into thecontracts/minabuild. - minimal-client visibility. The repo as a whole is public on
GitHub, but
minimal-client/package.jsonhas"private": "true"so the package is not published to npm. Treat it as "source-public, npm-private" — use it as a worked example of the full browser e2e wiring, not as a published dependency. - Node engine.
o1js-zk-utilspins Node>=22.0.0; the other workspaces declare>=18.14.0. A fresh checkout on Node 18 will silently fail to buildo1js-zk-utils; use Node 22 or newer for the full repo.