ZeroPay is a privacy-preserving payroll and fund distribution dApp built on the Midnight Network using Compact Zero-Knowledge (ZK) circuits and Next.js.
It enables Web3 organizations, DAOs, and global enterprises to execute verifiable, batch-funded payroll distributions without exposing employee salaries, private wallet addresses, or compensation structures on a public ledger.
| Resource | Link / Identifier |
|---|---|
| Live Preprod dApp | https://zeropay-midnight.vercel.app |
| Preprod Contract Address | 6f678977ce5a7fbe124870356149edabcf99e43e4b8d593953227988eb877e94 |
| Product X (Twitter) Profile | @ZeroPayZK |
| Demo Walkthrough Video | Watch Demo on YouTube |
Traditional blockchains expose all financial payouts publicly. ZeroPay utilizes Midnight's dual-state computational model to decouple private execution from on-chain state verification:
+-------------------------------------------------------------------------+
| LOCAL CLIENT (Browser + Lace) |
| |
| [ Private Witness Inputs ] |
| - secretKey |
| - payoutAmount |
| - Merkle Proof Vector |
| |
| │ |
| ▼ |
| [ Compact Proof Engine ] ──► Computes: Nullifier = H(secretKey || ID) |
| Generates: ZK-Proof (valid membership) |
+------------------------------------+------------------------------------+
│
│ Submits Proof & Nullifier
▼
+-------------------------------------------------------------------------+
| MIDNIGHT PREPROD TESTNET (On-Chain) |
| |
| [ Public Ledger State ] |
| - vaultTotal: Global deposit balance |
| - payoutRoot: 32-Byte Merkle commitment of all authorized salaries |
| - nullifierSet: Map<Bytes<32>, Boolean> (Enforces single-claim) |
| |
| [ Circuit Verification ] |
| - Verifies ZK-Proof matches payoutRoot |
| - Asserts !nullifierSet.member(nullifier) |
| - Decrements vaultTotal & inserts nullifier |
+-------------------------------------------------------------------------+
| Property | Visibility | Location | Description |
|---|---|---|---|
| Employee Secret Key | 🔒 Private | Client Device | Private witness used to derive leaf hashes. Never broadcast. |
| Individual Salary | 🔒 Private | Client Device | Kept confidential off-chain; verified via ZK-SNARK. |
| Merkle Sibling Path | 🔒 Private | Client Device | Proves membership in payout batch without disclosing peer nodes. |
| Public Vault Balance | 🌐 Public | On-Chain Ledger | Total tokens locked in contract to back aggregate disbursements. |
| Payout Merkle Root | 🌐 Public | On-Chain Ledger | Cryptographic commitment representing all valid employee splits. |
| Claim Nullifier | 🌐 Public | On-Chain Ledger | Unique hash registered upon claim to prevent double-spending. |
├── .github/
│ └── workflows/
│ └── ci.yml # Automated GitHub Actions test & build pipeline
├── contract/
│ ├── src/
│ │ ├── zeropay.compact # Compact smart contract defining circuits & state
│ │ └── types.ts # TypeScript circuit interfaces
│ └── test/
│ └── zeropay.test.ts # Vitest unit test suite (proof, claims, nullifiers)
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx # ZeroPay dashboard (Claim, Deposit, Privacy audit)
│ │ │ └── layout.tsx
│ │ └── hooks/
│ │ └── useLaceWallet.ts # Midnight Lace DApp Connector hook
│ ├── package.json
│ └── tsconfig.json
└── README.md
- Node.js v20.x or higher
- npm v10.x or higher
- Midnight Lace Wallet Extension configured for Preprod Testnet
git clone https://github.com/adewolescott/midnight-level1.git zeropay cd zeropay
cd frontend npm install --legacy-peer-deps
npx vitest run
npm run dev
Open http://localhost:3000 in your browser to interact with the dApp.
The contract test suite (contract/test/zeropay.test.ts) verifies the following operational criteria:
- Vault Initialization: Confirms batch deposits increment vaultTotal and store payoutRoot.
- Confidential Claim Verification: Validates that a valid private witness (secretKey, amount, proof) authorizes payment.
- Double-Claim Prevention: Asserts contract rejection when the same nullifier is submitted twice.
- Unauthorized Witness Rejection: Validates that altered secret keys or incorrect salary amounts fail circuit assertions.
-
depositPayroll(depositAmount: Uint<64>, newPayoutRoot: Bytes<32>): Void
Locks batch payroll funds intovaultTotaland updates the activepayoutRoot. -
claimPayout(publicNullifier: Bytes<32>, payoutAmount: Uint<64>, witness secretKey: Bytes<32>, witness proof: Vector<4, Bytes<32>>, witness indices: Vector<4, Boolean>): Void
Verifies the private witness proof againstpayoutRoot, writespublicNullifiertonullifierSet, and debitspayoutAmountfromvaultTotal.
This project is licensed under the MIT License.