Remote-first startups pay their team in stablecoins to avoid banking friction. However, public ledgers are transparent. If a startup pays its employees from a public treasury:
- Salary Exposure: Anyone can map the treasury wallet and deduce exactly how much every employee is paid, sparking poaching of senior cryptographers and engineers.
- Auditability Gap: Startups must prove compliance and KYC verification to tax authorities and VCs. Currently, they must choose between total transaction exposure (public ledger) or lack of auditability (off-chain opaque flows).
Zebra solves this compliance-vs-privacy paradox by introducing the first ZK-gated payroll engine built natively on Stellar. CFOs upload salary lists, compile ZK proofs demonstrating that (1) every employee is in the registered KYC set and (2) the total payroll amount is mathematically correct, without revealing individual recipient addresses or salaries on-chain. An ECIES-encrypted view key provides selective disclosure for tax compliance.
The Confidential Payout Flow:
- CFO Imports Payroll CSV
$\rightarrow$ Individual records are read, employee names are mapped, and salts are applied.- UltraHonk Proof Generation
$\rightarrow$ A real Noir UltraHonk proof is generated over the salaries (npm run prove:demo; the web UI simulates this step for the demo).- On-chain Verification
$\rightarrow$ The Soroban contract runsverify_proof(andverify_and_releasefor the full flow), accepting the payroll only if the ZK proof verifies.- Selective Auditor Disclosure
$\rightarrow$ Auditors use a Secp256k1 view key to decrypt metadata and audit salaries.
graph TD
subgraph Client [CFO Browser]
CSV[Payroll CSV] -->|Parse & Salt| Prover[bb.js WASM Prover]
Prover -->|Generate ZK Proof| Tx[Freighter Wallet Signer]
CSV -->|ECIES Encrypt| EncMeta[Encrypted IPFS Metadata]
end
subgraph Ledger [Stellar Soroban Contracts]
Tx -->|verify_and_release| Verifier[ZebraVerifier Contract]
Verifier -->|1. bn254_msm verify| Escrow[ZebraTreasury USDC Escrow]
Verifier -->|2. Save key mapping| ViewRegistry[ViewKeyRegistry]
Escrow -->|Disburse total USDC| Payout[Workforce Payouts]
end
subgraph Audit [Auditor Portal]
AuditorKey[Secp256k1 View Key] -->|Decrypt ECIES| Decryptor[Metadata Decryptor]
EncMeta --> Decryptor
Decryptor -->|Reconstruct| PlainCSV[Unmasked Salary Spreadsheet]
end
graph TD
Circuits[Noir payroll_circuit/src/*.nr] -->|nargo compile| Bytecode[circuit.json]
Bytecode -->|bb prove| Proof[proof.bytes]
Bytecode -->|bb prove| Public[public_inputs.bytes]
Bytecode -->|bb write_vk| VKey[vk.bytes]
Proof -->| Freighter / Client Submit | Soroban[Stellar Soroban Contract]
Public -->| Freighter / Client Submit | Soroban
VKey -->| Rust / cargo build | Soroban
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 (App Router), React 19, Tailwind CSS v4, Lucide Icons |
| ZK Circuits | Noir (UltraHonk) compiled with Barretenberg |
| Smart Contracts | Soroban (Rust SDK) optimized for Protocol 26 native MSM |
| Database | Supabase (PostgreSQL) tracking audited ledger hashes |
| Payments | Stellar USDC + Freighter Wallet |
Zebra leverages load-bearing Soroban Protocol 25/26 features to run UltraHonk verification economically:
- Native BN254 host functions: The
rs-soroban-ultrahonkverifier the contract embeds relies on Protocol 26 BN254 multi-scalar-multiplication and scalar-field host functions to do the pairing-based UltraHonk check inside the transaction budget. Without them, a pure-WASM verifier would not fit on-chain. - Poseidon2 commitments: The Noir circuit uses Poseidon2 (via
dep::poseidon) for the KYC Merkle membership β the same hash family Stellar exposes natively on Protocol 25/26, so commitments line up with the on-chain world. - Stablecoin settlement: On a verified proof, the contract releases USDC via the SAC token interface.
Concrete CPU/memory numbers depend on the
bb/verifier version and are not hardcoded here; runnpm run prove:demoand inspect the on-chain transaction (and the contract test'scost_estimateprint) for the real figures.
The load-bearing zero-knowledge in Zebra is a real Noir circuit proven with
Barretenberg UltraHonk and verified on Stellar testnet by a Soroban contract
that embeds the rs-soroban-ultrahonk verifier.
npm run prove:demo
# Compiles the Noir circuit, generates a real UltraHonk proof (14,592 bytes),
# verifies it off-chain (bb), then submits verify_proof on-chain:
# on-chain verify_proof => true
# on-chain verify_proof (tampered) => false (negative control)- Verifier contract (testnet):
CCLTVNPYS5H2AY4OTYIYDU57XYO4S5OZQE435ZZX2TFUVYDAIS6B53N5 - Public signals:
[ total_payroll, treasury_balance, kyc_root ] - Toolchain: Noir
1.0.0-beta.9+ Barretenberg0.87.0 - Compiler Requirements: Target
wasm32v1-none(usingcargo build --target wasm32v1-noneor through Soroban compile tools) under Rust 1.82+ to support native BN254 host functions.
The contract ZebraPayrollContract exposes the following functions:
initialize(env: Env, admin: Address, token: Address): Set admin and USDC token addresses.set_verification_key(env: Env, admin: Address, vk_bytes: Bytes): Set ZK verification key (admin authorization required).set_compliance_provider(env: Env, admin: Address, provider: Address): Set trusted compliance provider for KYC checks (admin auth required).verify_and_release(env: Env, prover: Address, proof: Bytes, total_payroll: u128, treasury_balance: u128, kyc_root: BytesN<32>, metadata_uri: Bytes, encrypted_key: Bytes, tx_hash: BytesN<32>) -> bool: Verify ZK proof, check KYC root compliance, check duplication, and transfer payroll USDC from CFO to escrow pool (CFO auth required).verify_proof(env: Env, proof: Bytes, public_inputs: Bytes) -> bool: Read-only verifier of UltraHonk proofs.get_view_key(env: Env, tx_hash: BytesN<32>) -> Option<Bytes>: Retrieve encrypted metadata view key for audits.get_audit_record(env: Env, tx_hash: BytesN<32>) -> Option<PayrollAuditRecord>: Retrieve total amount, KYC root, encrypted key, and metadata URI.
Honest status: the v1 circuit proves exactly three public signals β
[total_payroll, treasury_balance, kyc_root](salary-sum = total, solvency, KYC membership). The v3 multi-currency payroll ships as a separate, dedicated contract with its own UltraHonk VK (circuitpayroll_circuit_mc), verified on Stellar testnet and reproducible vianpm run prove:demo:mc. It is not wired into the hosted demo web app, which demos the v1 flow only.
release_payroll_v3(...)[v3, shipped] β Multi-currency payroll (USDC/EURC/MXNB) with per-jurisdiction tax withholding and ZK-verified FX conversion, distributing withholdings to N tax authorities in one atomic transaction, against a dedicated multi-currency VK on testnet contractCBXJ75PDFAMMGL2VHEXBJX2UT3GSWPHEZZULOFZWUZLN57V6UUBOL6NB. Reproduce:npm run prove:demo:mc.
Honest status: the hosted web app is a demo sandbox β it visualizes the CFO/Auditor flow with local crypto simulations for instant UX. The real cryptography (proof generation + on-chain verification) is the
npm run prove:demopipeline and the deployed contract above. The browser does not yet run the bb.js prover in-page (roadmap item).
| Area | Status |
|---|---|
| ZK circuit | Real Noir 1.0 + Poseidon2; nargo test passes |
| Proof system | Barretenberg UltraHonk (keccak oracle), proof verifies off-chain (bb verify) |
| On-chain verification | verify_proof returns true on testnet; tampered inputs return false |
| Contract tests | cargo test (payroll flow + duplicate-nullifier guard) pass |
| App test harness | scripts/run_tests.js unit suite + Playwright E2E (demo mode) |
| CI Pipeline | 6-stage (Quality β Security β Build β E2E β Perf β Deploy) |
The web app's per-test counts and Lighthouse numbers come from the harness in
scripts/; the headline cryptographic claim β a real UltraHonk proof verified on Stellar β is reproducible vianpm run prove:demo.
- Node.js
$\ge$ 20 - npm
- Clone the repository:
git clone https://github.com/edycutjong/zebra.git cd zebra - Install dependencies:
npm install
- Configure environment variables:
cp .env.example .env
- Run the local development server:
npm run dev
Zebra features a comprehensive 6-stage CI/CD pipeline and a unit testing harness containing 107 tests.
# ββ Code Quality ββββββββββββββββββββββββββββ
make quality # Run Prettier format & ESLint checks
make typecheck # Run TypeScript compiler checks
make test # Run 107 core payroll cryptographic unit tests
# ββ E2E & Audits ββββββββββββββββββββββββββββ
make e2e # Run Playwright E2E browser tests (Demo mode)
make lighthouse # Run Lighthouse CI performance/accessibility audit
make security-scan # Run npm audit & license compliance scandorahacks-stellarzh-zebra/
βββ .github/ # GitHub Actions CI/CD workflows
βββ contracts/ # Soroban smart contract source code
βββ db/ # Database migration schemas & seeds
βββ docs/ # Design assets (logo, banner, defense)
βββ e2e/ # Playwright E2E browser test suites
βββ payroll_circuit/ # Noir ZK circuit code
βββ scripts/ # Unit tests, readiness checks, benchmarks
βββ src/
β βββ app/ # Next.js pages & API routes
β βββ lib/ # Shared client libs & payroll engine
βββ Makefile # Harness automation commands
βββ README.md # You are here
Zebra verifies payroll proofs on Stellar using the BN254 host functions added in Protocol 26
(through the rs-soroban-ultrahonk verifier). The Soroban test harness prints the real CPU/memory
budget via env.cost_estimate() β see the === ZK CRYPTO BENCHMARK (ZEBRA) === line in
cargo test -- --nocapture. The contract unit test exercises the escrow/auditor flow with the ZK
check stubbed (#[cfg(test)]); the full UltraHonk verification cost is what you observe when you
run npm run prove:demo against the deployed contract on testnet.
We deliberately don't pin a single instruction count in this README because it moves with the
bband verifier versions. Reproduce it rather than trust a number.
- Phase 1: Core Noir circuit implementation
- Phase 2: Soroban
verify_and_releaseescrow contract - Phase 3: Client-side CSV parser and ECIES metadata encryption
- Phase 4: Freighter wallet integration and Next.js portal
- Phase 5: 6-stage engineering harness (Quality β Security β Build β E2E β Perf β Deploy)
- Phase 6: Multi-currency payroll with cross-jurisdiction tax withholding (v3) β shipped & verified on-chain. Real
payroll_circuit_mcNoir circuit (private FX conversion + per-employee tax withholding + Poseidon2 KYC membership) β UltraHonk proof β on-chainverify_mc_proof/release_payroll_v3against a dedicated multi-currency VK on testnet contractCBXJ75PDFAMMGL2VHEXBJX2UT3GSWPHEZZULOFZWUZLN57V6UUBOL6NB. Reproduce:npm run prove:demo:mc(real proof verifies βtrue, tampered inputs βfalse). Covered by contract unit teststest_release_payroll_v3_*. - Phase 7: Hosted/decentralized prover network (e.g. Sindri) scaling for larger employee batches β blocked on external infra: requires a third-party proving account + API key, which is not available in this environment. The proving pipeline (
prove:demo:mc) is the integration point; plugging a remote prover in is a credentialed config change, not new protocol work. Not deployed β left honest rather than stubbed.
- GitHub Repository: https://github.com/edycutjong/zebra
- Live App URL: https://zebra.edycu.dev
- Pitch Deck: https://zebra.edycu.dev/pitch.html
MIT Β© 2026 Edy Cu

