Inspiration
DeFi lending protocols like Aave and Compound expose every user's balance, debt, and transaction history on-chain. That works for permissionless transparency, but it's terrible for financial privacy — competitors, friends, and strangers can see your exact positions. Midnight Network offers a different primitive: smart contracts where public pool state and private user state coexist. We built Nocturne Finance to answer a simple question — what if a money market could prove it's solvent without spilling your personal balances?
What it does
Nocturne Finance is a confidential lending protocol on Midnight Network. Users supply and borrow assets through a shielded dashboard powered by Lace wallet. The public ledger shows pool-level solvency (total supplied, total borrowed, utilization, interest indices), while each user's supplied balance, borrowed balance, and health factor remain zero-knowledge witness state visible only to their own wallet.
The full lending flow works end-to-end on Midnight Preview:
- Supply / Withdraw — deposit or redeem reserve units against tNIGHT, tUSDC, or tETH
- Borrow / Repay — draw against your collateral and repay debt
- Variable interest rates — supply and borrow APYs update with utilization using a kinked rate model
- Health factor dashboard — real-time confidential position tracking with Safe / Moderate / Risky categories
- Local transaction history — client-side log of all actions for verification
Behind the scenes, every action follows a PROVE → LACE → COMMIT flow: a proof server generates a ZK proof for the circuit call, the user signs with Lace, and private state is only persisted after the wallet accepts the submission.
How we built it
- Smart contracts (Compact):
nocturne_lending_multi.compactdefines public ledgers for three reserves (tNightReserve,tUsdcReserve,tEthReserve) and private witnesses for each user's position. Circuits implement deposit, withdraw, borrow, repay, and per-asset variants with overflow checks and interest accrual. - ZK interest math: A utilization-based two-slope rate model (
baseRate + slope1up to a kink, thenslope2beyond it). Interest index updates are proven inside circuits using a witness-and-verify fixed-point arithmetic pattern — no floating point, no trusted caller. - Proof server (
interact-server.ts): Node.js HTTP server that orchestrates Midnight's proof provider, indexer, and wallet SDK. It reads public reserve state, computes accrual increments, builds unproven call transactions, and returns signed proofs to the frontend. - Frontend (Next.js 16 + React 19): Dashboard connects to Lace wallet, renders confidential positions via
/api/contract/position, and submits proven transactions through API routes. Markets page shows aggregate pool metrics refreshed from the public indexer. - Privacy design: Private state lives in LevelDB keyed by the user's account ID. The backend is deliberately thin — it never sees or stores user balances, only proves state transitions.
Challenges we ran into
- Verifier key consistency: During development, switching between contract commits caused on-chain verifier mismatches. We solved this by pinning the deployed contract address in
.midnight-multi-state.jsonand ensuring the proof server always loads matching compiled artifacts fromcontract/managed/. - Dust balance management: Every contract call requires tDUST for fees. Automated faucet claims and retry logic with backoff were needed to handle Preview's limited dust generation.
- State synchronization: The Lace wallet, proof server, indexer, and frontend each maintain separate views of state. We added explicit refetch patterns, local transaction history, and confirmation polling to keep them consistent.
- Compact serialization: Reading public
ReserveStatefrom the indexer required careful deserialization throughCompactStateand type coercion forbigintfields, since the runtime returns mixed numeric types.
Accomplishments that we're proud of
- A working privacy-preserving lending MVP deployed on Midnight Preview with real ZK proofs and Lace wallet integration
- Multi-asset architecture supporting tNIGHT, tUSDC, and tETH reserves in a single contract
- Clean separation of public solvency data and private user positions — no custodial backend
- Interest rate math proven inside ZK circuits, not trusted from off-chain oracles
- Production-grade error handling with retries for dust shortages, proof server timeouts, and wallet disconnections
What we learned
- Midnight's witness / ledger / disclose model maps naturally to DeFi primitives when you think in terms of "what must be public" vs. "what must be private"
- The
PROVE → LACE → COMMITpattern is essential for privacy-state consistency — persisting private state only after wallet confirmation prevents corrupted positions - Fixed-point arithmetic in ZK circuits requires designing divisions as pre-proven increments rather than runtime operations
- TypeScript tooling for Midnight is still maturing; we invested in strong runtime guards and explicit type coercion for indexer responses
What's next for Nocturne Finance
- Liquidation engine with ZK health-factor enforcement
- Price oracles for collateral valuation (e.g., tETH/tUSDC in tNIGHT)
- Shielded deposit/withdraw of tUSDC and tETH via
receiveUnshielded/sendUnshielded - Multi-collateral health factor and cross-asset borrowing
- Gas optimization and circuit proof-time reduction
- Audit, formal verification of interest math, and testnet incentives
Built With
- compact
- defi
- docker
- lace-wallet
- leveldb
- midnight-network
- next.js
- node.js
- privacy
- proof-server
- react
- tailwind-css
- typescript
- zero-knowledge-proofs
- zk-circuits
Log in or sign up for Devpost to join the conversation.