Inspiration
Payroll, grants, and business disbursements are broken in Web3. Every on-chain payment leaks: who got paid, how much, and the full payment graph. Tesseract fixes this at the protocol level — a ZK-powered batch payment rail where a payer can pay hundreds of recipients and each recipient claims their funds with a cryptographic proof, while observers see nothing but a Merkle root.
We built on Midnight Network because it's the only L1 that treats privacy as a first-class primitive rather than a bolt-on. Zero-knowledge proofs aren't an afterthought — they're baked into every transaction.
## What It Does
Tesseract is a private business payments rail with six ZK circuits:
Submit Batch Root — Payer commits a Merkle tree of (recipient, amount) pairs on-chain. Only the root is revealed. Recipients, amounts, and count stay private.
Deposit Recipient Coin — Payer deposits a shielded coin per recipient. Each coin is locked to a compound key
hash(batchId, leafHash)— no on-chain link between sender and recipient.Claim Payment — Recipient proves Merkle membership (leaf index + path), proves ownership of the claim secret, and receives the shielded coin. A nullifier is burned to prevent double-spend.
Reclaim — Payer can reclaim unclaimed coins after the batch deadline passes, using a ZK proof of payer identity.
Create Payment Request — Requester commits a ZK proof of identity and creates an on-chain payment request with deadline, amount, and commitment hash.
Mark Request Paid — Payer marks request as paid, anchoring payment proof to the request ID.
All state changes are ZK-proved. No raw keys ever touch the ledger.
## How We Built It
- Compact (Midnight's ZK smart contract language): 6 circuits, 9 flat ledger maps, Pedersen-hashed Merkle tree with 16-depth paths, persistent commitment hashing, nullifier-based double-spend protection.
- TypeScript SDK (
midnight-js): CustomTesseractClientwrappingfindDeployedContract, flow modules for each circuit, Merkle tree construction with SHA-256 leaf hashing. - React + Vite frontend: Wallet connect via 1AM browser extension, live contract state subscriptions via indexer GraphQL, batch builder UI.
- Local devnet: Full Midnight undeployed network (node, indexer, proof server) via Docker Compose.
## Challenges We Faced
ZK proof routing across browser security boundaries: The 1AM wallet extension
runs its proving provider in a service worker (chrome-extension:// origin). The local
proof server's CORS policy only allows http://localhost:5173. Getting proving to work
required routing circuit proofs through httpClientProofProvider in the page context
while separately registering the zkConfigProvider with the wallet for DUST fee proving.
Merkle tree construction: Midnight's on-chain Merkle tree uses persistentHash
with MerkleTreePath<16, Bytes<32>> — building compatible off-chain proofs required
matching the exact leaf encoding (compound hash(batchId, leaf)) and producing
16-deep paths with correct padding.
ZSwap coin lifecycle: Each circuit that touches a shielded coin must pass
QualifiedShieldedCoinInfo as a witness, not a raw coin. Coordinating the witness
pipeline (indexer query → coin lookup → witness injection → proof) across sequential
transactions with 25s ZSwap propagation delays was the core infrastructure challenge.
## What We Learned
- Midnight's separation of public ledger state from private ZK witnesses is elegant and forces good security design — you can't accidentally leak private data because the type system prevents it.
persistentHashvstransientHashin Compact is a critical distinction: only persistent hashes appear in the same location across blocks.- Building on a pre-mainnet L1 means debugging blind — no block explorers, no decoded transactions, no community answers. All signal comes from proof server logs and raw indexer GraphQL.
## What's Next
- Multi-batch streaming payroll (recurring payments with deadline rollover)
- Sharable claim links (base64-encoded claim packages for off-chain distribution)
- Web2 → Web3 bridge: upload a CSV of wallet addresses and amounts, get a private batch in one click
- Port to other privacy-aware chains (Aleo, Aztec) via the same Merkle + nullifier primitive pattern
Built With
- 1am
- compact
- css
- docker
- graphql
- hash
- midnight
- midnight-js
- network
- node.js
- pedersen
- proofs
- react
- sdk
- sha-256
- tailwind
- typescript
- vite
- wallet
- zero-knowledge
Log in or sign up for Devpost to join the conversation.