PaySlip
Private payroll. Provable income. Zero disclosure.
PaySlip is a Midnight dApp that turns a confidential payroll record into a reusable zero-knowledge income credential. Its core idea is simple: the payment is the credential. An employee can prove that they meet an income threshold for a particular month without disclosing their salary, employer payroll data, transaction history, or bank statements.
Inspiration
On-chain payroll creates a painful privacy trade-off. Stablecoins can make international payroll fast, programmable, and accessible, but a transparent ledger can expose every employee's pay and allow competitors, coworkers, or strangers to reconstruct a company's compensation structure. That is a serious blocker to real-world adoption of blockchain payroll.
Employees face the mirror image of the same problem. Renting an apartment, applying for a loan, or completing an onboarding check often means sending a landlord or lender an entire bank statement or payslip simply to establish one fact: my income is at least this amount. The verifier receives far more data than they need, and the employee loses control over where that data goes next.
We built PaySlip around the insight that these are not two separate problems. A private payment record can become a privacy-preserving credential. This directly explores the intersection of Midnight's confidential smart-contract model with the confidential payroll and proof-of-income use cases highlighted in Midnight's Request for Startups, while keeping the hackathon MVP focused on a single, demonstrable proof: one payslip proves income meets a requested threshold for one pay period.
What it does
PaySlip supports the three people involved in an income-verification flow:
- Employers register an organization and run payroll. The public ledger receives only an opaque payslip commitment per employee; amounts, employee identity, period details, and salts remain private.
- Employees hold their own payslip preimage locally and can view their private payslip history. When asked to verify income, they generate a proof from their local credential rather than upload a document.
- Verifiers such as landlords or lenders create a request containing a threshold, period, and label. When the employee proves eligibility, the verifier sees an unambiguous
INCOME VERIFIEDresult—without a salary number or a revealed payslip.
The proof checks four meaningful facts: the employee controls the private key bound to the credential; the private payslip hashes to a commitment in the contract's historical Merkle tree; the payslip belongs to the requested period; and the private amount is at least the public threshold. The contract writes only a successful boolean result. A failed or below-threshold attempt cannot produce a proof, so no negative salary information is published either.
The app also includes a read-only ledger explorer designed for the demo. It makes the privacy model visible: observers can inspect commitments, requests, and verification results, but never find names, amounts, or payslip preimages on-chain.
For a reliable walkthrough, ?demo=1 starts deterministic demo state with Acme Robotics, three employees, June and July 2026 pay runs, and an open $1,500 July income request. Ada can satisfy that request; Kofi is deliberately below the threshold, which demonstrates the graceful failure path: no proof is generated and nothing is revealed.
How we built it
Privacy-first contract model
The heart of PaySlip is a Compact contract in contracts/payslip.compact. It separates public ledger state from private witness data by design:
| Public ledger state | Private witness state |
|---|---|
| Registered employer identifiers and public names | Employer and employee secret keys |
Opaque payslip commitments in an append-only HistoricMerkleTree |
Payslip preimage: employer ID, employee ID, amount, period, and random salt |
| Public verification request: threshold, period, label, and status | Merkle path proving the commitment is in the tree |
A successful verification result (true) |
The actual salary and the commitment leaf used |
Employers self-register using a hash derived from a private key. Registered employers can append a persistentHash commitment of a payslip to the historic Merkle tree, with a public commitment index preventing duplicates. The proveIncome circuit validates private membership, employee ownership, period equality, and amount >= threshold, then marks the request verified. This MVP intentionally proves a single payslip rather than pretending to support a fragile multi-payslip aggregation flow.
Typed service layer and Midnight integration
The frontend talks to one typed ChainService interface. That boundary keeps the React UI independent of whether it is running against the demo provider or Midnight:
MidnightChainServiceuses the Midnight dApp Connector, Midnight.js contract bindings, a ZK configuration provider, an HTTP proof provider, and indexer public-data queries. It deploys or reconnects to the contract through a connected wallet and keeps the credential's private state in browser-local storage for the MVP.MockChainServiceimplements the same interface for a zero-dependency, deterministic demo. It uses real browser SHA-256 commitments, models realistic transaction/proof delays, persists state in local storage, and enforces the same threshold failure semantics.
The repository includes Compact build artifacts, browser-served prover/verifier assets, local network scripts, Docker Compose configuration for a node, indexer, and proof server, plus scripts for compilation, local setup, deployment, balance checks, and end-to-end connectivity checks. This makes the real Midnight path reviewable without making a demo dependent on faucet timing or infrastructure availability.
Product and UX
We built the client as a React, TypeScript, and Vite single-page app with customized Material UI rather than a default component-library interface. It includes:
- A landing page that explains the privacy problem and the three-step flow: pay privately, hold the credential, prove without revealing.
- An employer dashboard for registration, payroll-vault status, employee selection, pay-run history, and per-employee progress.
- An employee wallet that exposes private payslip information only to its owner and guides proof generation through Building witness → Generating ZK proof → Submitting on-chain.
- A verifier portal for creating threshold requests, sharing a request ID/link or QR code, and seeing the final verification state.
- A ledger page for inspecting the intentionally limited public footprint.
The visual system centers on PaySlip green (#15994E) with a dark, black-background mode as the default and a fully themed light mode. We paired Space Grotesk with Inter, use tabular figures for money and identifiers, provide copy-friendly truncated hashes, respect reduced-motion preferences, and use a reusable payslip presentation to emphasize the public commitment/private detail split. The app also handles wallet absence and rejected connections with human-readable messages instead of raw errors. Neon Auth adds optional email/password session flows without storing salary data, witnesses, or wallet keys in Neon.
Challenges we ran into
The central challenge was designing something that is both technically credible as a zero-knowledge application and dependable enough to demonstrate in a hackathon window. Proof generation is inherently measured in seconds, not milliseconds, and local nodes, indexers, wallets, funding, and proof servers add several points of failure. We addressed this by building the same user flow behind a shared interface and giving the demo provider real commitments, deliberate proof stages, persistence, and a genuine below-threshold rejection—not a cosmetic success animation.
Compact and Midnight tooling also require disciplined versioning. Contract language syntax and SDK conventions evolve quickly, so the contract, generated artifacts, browser proof assets, and local infrastructure have to agree. We kept the contract scope narrow, pinned the toolchain path in the project scripts, and included local setup and diagnostics rather than hiding the operational pieces.
Privacy UX was another difficult balance. A proof system is valuable only if non-technical users can understand what it reveals and what it does not. PaySlip therefore makes the data boundary explicit in the interface: public commitments are visible in the ledger, private fields are visible in the owner's wallet, and the verifier result deliberately omits the salary. The failure message is equally important: “proof could not be generated” communicates that no proof was possible without turning a failed eligibility check into an additional public disclosure.
Finally, we had to preserve scope discipline. The original product vision can grow into shielded settlement, multiple employers, aggregation, auditing, and compliance controls. The hackathon version focuses on the smallest end-to-end claim that demonstrates Midnight's value: a registered employer posts an opaque commitment and an employee privately proves a monthly threshold against it.
Accomplishments that we're proud of
- We turned a familiar but privacy-invasive document workflow into a cryptographic credential flow with a clear product thesis: the payment is the credential.
- We built a Compact contract that uses an employer-gated historical Merkle tree and private witnesses to verify income without putting the salary, employee, or payslip details on the public ledger.
- We designed the proof result to disclose only the minimum useful outcome. A verifier learns one bit—whether the threshold is met—not the amount that produced it.
- We delivered all three role journeys and a ledger explorer, so the privacy property is demonstrated from employer action through employee proof to verifier result rather than described only in a diagram.
- We built resilient demo infrastructure: a deterministic seeded experience, real SHA-256 commitments, local persistence, realistic proof stages, and a deliberate failure case let reviewers see both success and rejection without external dependencies.
- We treated visual polish and accessibility as part of execution: a deliberate dark/light design system, custom typography, responsive role-based screens, loading/error/success states, reduced-motion support, and friendly wallet errors make a sophisticated technical flow feel approachable.
- We kept the project reviewable with a compact contract, typed domain models, a single chain-service seam, local-development scripts, Docker configuration, and developer-facing documentation that explains the public/private boundary.
What we learned
We learned that privacy is not simply a feature that can be added after a payment flow is designed. The public/private boundary has to be the first architectural decision: what is committed, what is witnessed, what must be public for a verifier to express a request, and what result is truly necessary to write back to the ledger.
We also learned that zero-knowledge proof UX needs to be designed around uncertainty and time. A user should understand that the device is building a witness and generating a proof, while also knowing why a proof can fail. Explicit stages and an honest failure state are not just polish; they are essential to making cryptographic guarantees legible.
The project reinforced the value of a strict integration seam. The same ChainService contract lets us prove out the entire product flow in a controlled demo while retaining a real Midnight implementation path. That separation is especially useful when wallet, prover, testnet, or faucet conditions are variable, because it protects the product experience without obscuring what is mocked.
Finally, we learned that an intentionally narrow proof can be more persuasive than a broad, unfinished protocol. A single-payslip threshold proof is easy to explain, easy to verify, and already solves a meaningful real-world problem. It establishes a secure foundation for aggregation and richer disclosure policies later.
What's next for PaySlip
Our next milestone is production-grade confidential settlement: connect employer-funded shielded-token vaults to payroll execution so the value transfer and credential issuance are part of one private workflow. We will then deploy the verified contract through a funded Midnight wallet and publish the contract address and transaction references once the public-network transaction is complete.
From there, we plan to expand the proof model carefully:
- Aggregate multiple payslips across months, employers, or income sources while revealing only that their sum meets a threshold.
- Add employer signatures and richer credential issuance/delivery flows, with secure recovery and device migration for private witnesses.
- Introduce selective-disclosure options for auditors and compliance teams, so users can prove approved facts without exposing their entire financial history.
- Build expiration, revocation, and verifier-policy controls for reusable income credentials.
- Move private state beyond the MVP browser-local model into a production-grade, user-controlled encrypted storage and backup experience.
- Complete public-network deployment, broaden wallet coverage, and add end-to-end tests across the wallet, proof server, indexer, and contract lifecycle.
PaySlip's long-term opportunity is larger than payroll. It is a blueprint for a world where financial activity can stay private by default but become provable on demand. For employers, that means confidential global payroll; for employees, reusable proof instead of document oversharing; and for verifiers, a cryptographically strong answer to the only question they actually need to ask.
Built With
- compact
- confidential-smart-contracts
- credentials
- dapp-connector
- defi
- docker
- historic-merkle-trees
- localstorage
- material-ui
- midnight-network
- midnight.js
- neon-auth
- privacy-preserving-payroll
- qr-codes
- react
- sha-256
- typescript
- vite
- web3
- zero-knowledge-proofs
- zk
Log in or sign up for Devpost to join the conversation.