Zero-knowledge payroll infrastructure on Stellar.
Prove payroll was disbursed correctly. Without the blockchain learning what anyone earns.
Every crypto payroll tool today puts salaries on a public blockchain. The amounts are permanent, searchable, and visible to anyone — employees, competitors, journalists, anyone with a block explorer. Companies that care about privacy don't put payroll on-chain. Companies that put payroll on-chain lose privacy.
ZeroWage resolves this contradiction using zero-knowledge proofs. Instead of publishing salary amounts, an employer publishes a cryptographic proof that the salary math is correct. The Stellar blockchain records the proof — not the numbers that made it valid.
The result: on-chain payroll with off-chain privacy. Verifiable by anyone. Readable by no one.
Employer's browser Stellar Blockchain
────────────────── ──────────────────
salaries[] (private) total: 18,500 USDC
↓ recipients: 5
ZK circuit runs proof_verified: true
↓ proof_hash: e6b361...
Groth16 proof ──────────────▶
(384 bytes) ← salaries never appear here
- The employer enters salaries into the ZeroWage app. The data never leaves their browser.
- A Circom 2 circuit with 1,205 constraints proves three facts about the salary array without revealing it: the sum equals the claimed total, every salary meets the minimum threshold, and this specific batch produced this proof.
- The Groth16 proof is submitted to a Soroban smart contract on Stellar. The contract runs real BN254 elliptic curve pairing verification using Stellar's native host functions. If the pairing equation holds, the proof is accepted.
- A nullifier is stored on-chain, preventing the proof from ever being reused.
- USDC payments are sent to recipient wallets. Both the proof transaction and the payment transaction are recorded together.
The blockchain knows the total was disbursed and the proof is valid. It knows nothing else.
The core protocol implementation. Everything in one monorepo:
| Directory | Contents |
|---|---|
circuits/ |
payroll.circom — 1,205 constraints, Groth16/BN254, Poseidon commitment |
contracts/ |
Soroban verifier in Rust — real BN254 pairing, nullifier system |
app/ |
Next.js 16 frontend — wizard, dashboard, proof explorer, attestation |
Live deployment: https://zerowage-theta.vercel.app
Contracts:
| Network | Address |
|---|---|
| Stellar Testnet | CCOEJ6QC6ZGGA2GIY72IW3MDN6LNJHQSB2XWRZR3WSLE3PVVE6QVUYAP |
payroll.circom is compiled with Circom 2.2.2 and proves with Groth16 over the BN254 curve. Three constraints are enforced:
Constraint 1 — Sum correctness
running[n] === total;The claimed total must equal the actual sum of all salary inputs.
Constraint 2 — Minimum salary floor
active_and_geq[i] <== is_active[i] * geq[i].out;
active_and_geq[i] === is_active[i];Every active recipient's salary must meet the configured minimum. Zero-padded inactive slots are correctly exempted using LessThan components to compute is_active[i] per slot.
Constraint 3 — Poseidon batch commitment
hash.out === commitment;Poseidon(salaries[0..8]) binds the proof permanently to this exact salary batch. A proof from a different payroll cycle cannot be replayed for this one.
Circuit stats: 1,205 non-linear constraints · 2,094 wires · 20 private inputs · 4 public inputs
The contract performs genuine on-chain Groth16 verification using Stellar's native BN254 host functions:
let neg_a = -proof.a;
let vp1 = vec![env, neg_a, vk.alpha, vk_x, proof.c];
let vp2 = vec![env, proof.b, vk.beta, vk.gamma, vk.delta];
Ok(bn.pairing_check(vp1, vp2))This is not a structural check. The pairing equation e(-A,B)·e(α,β)·e(vk_x,γ)·e(C,δ) = 1 is evaluated on-chain. A fabricated or mutated proof fails verification and the transaction reverts.
Every accepted proof's SHA-256 hash is stored in persistent contract storage. Resubmitting the same proof returns ProofAlreadyUsed. Combined with the Poseidon commitment, this makes proofs single-use and batch-specific — the same primitive used in Zcash and Tornado Cash for double-spend prevention.
Before a payroll run can be submitted, a designated CFO wallet must approve it. The approval mechanism requires no backend: the CFO sends a 1 XLM payment with the run ID as a Stellar text memo to the admin's wallet. The admin's browser polls Horizon for the memo. When it appears, submission unlocks. Stellar itself is the coordination layer.
Cryptographic core
- Real Groth16/BN254 verification on Stellar — not a structural check
- On-chain nullifier replay protection
- Poseidon commitment binding each proof to its exact salary batch
- Trusted setup using Hermez Perpetual Powers of Tau (2¹²)
- Browser-side proof generation via snarkjs WASM (~2 seconds)
Product
- 5-step payroll wizard: employees → review → proof → approval → submit
- Manual entry and CSV import with wallet address validation
- Department tagging and filtering
- CFO approval workflow via Stellar memo payments
- Wallet-gated approval: only the configured approver address may sign
- Draft and approved runs persist in a dedicated pending page
- Resume any draft run via
?runId=without restarting
Data and privacy
- Salary data encrypted with AES-GCM (256-bit) before leaving the browser
- Encryption key derived from the admin's Stellar wallet address via PBKDF2
- Encrypted ciphertext stored in Supabase — unreadable by the server
- Falls back to localStorage when Supabase is not configured
Verification and audit
- ZK payslip PDF per employee — proof hash, contract address, privacy notice
- Public attestation page (
/verify/[txHash]) — no authentication required - Proof Explorer with public inputs visible, salary inputs shown as
████████ - Receipt download in
.txtand.jsonformats - Both proof tx and payment tx linked to Stellar Expert
Infrastructure
- GitHub Actions CI: contract tests on every PR (
cargo test) - GitHub Actions CI: Next.js build check on every PR
- Encrypted cross-device storage via Supabase
- Horizon SSE streaming for near-instant approval detection
- Wallet-native auth via Freighter — no email, no password
| Milestone | Status |
|---|---|
| v0.1.0 — Hackathon release | ✅ Complete |
| v0.2.0 — Encrypted storage + circuit fix + streaming | ✅ Complete |
| v0.3.0 — Multi-sig approval + mainnet prep | 🔄 In progress |
| v0.4.0 — Recursive proof aggregation (20+ recipients) | 📋 Planned |
| v0.5.0 — SEP-24 fiat off-ramp integration | 📋 Planned |
| v1.0.0 — Mainnet launch + auditor portal | 📋 Planned |
ZeroWage is actively seeking contributors. We have 40+ open issues across four difficulty tiers:
| Points | Difficulty | Examples |
|---|---|---|
| 100 | Trivial | Documentation, good first issues, small UI fixes |
| 150 | Medium | New features, frontend components, API endpoints |
| 200 | Hard | Circuit changes, contract functions, integrations |
Every issue includes:
- A specific implementation plan
- Exact files to modify
- A screen recording requirement showing the feature working
Browse issues: https://github.com/zerowage-protocol/ZeroWage/issues
Good first issues: https://github.com/zerowage-protocol/ZeroWage/issues?q=label%3A%22good+first+issue%22
git clone https://github.com/zerowage-protocol/ZeroWage
cd ZeroWage/app
pnpm install
cp .env.example .env.local
pnpm devRead CONTRIBUTING.md before opening a PR.
- The issue is referenced in the commit message (
Closes #N) - A screen recording is attached showing the feature working end-to-end
- Existing tests pass (
cargo testfor contract changes) - New tests added for new contract functions
- No salary data appears in logs, exports, or UI elements it shouldn't
Stellar is the only production blockchain where this project is currently possible. The X-Ray protocol upgrade introduced native BN254 host functions — bn254_g1_mul, bn254_g1_add, bn254_pairing_check — making Groth16 proof verification economically viable on-chain. On Ethereum, the equivalent verification costs hundreds of thousands of gas. On Stellar, it costs a fraction of a cent.
Beyond the cryptography: USDC is a first-class asset on Stellar, not a wrapped token. Settlement is 5 seconds. The Stellar Development Foundation's financial inclusion mission is directly aligned with ZeroWage's goal of making compliant, private payroll accessible to any company paying anyone, anywhere.
- Discussions: github.com/zerowage-protocol/ZeroWage/discussions
- Issues: github.com/zerowage-protocol/ZeroWage/issues
- Live app: zerowage-theta.vercel.app
iden3 for Circom and circomlib.
Stellar Development Foundation for Soroban and the BN254 precompiles.
Hermez Network for the Powers of Tau ceremony.
Aztec Protocol for advancing practical ZK systems.
A payroll spreadsheet has always had two columns nobody could reconcile: who gets paid, and who's allowed to know. ZeroWage doesn't pick a side. It proves the first column is correct and makes the second column irrelevant — not by promising to keep a secret, but by never knowing it in the first place.