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.compact defines 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 + slope1 up to a kink, then slope2 beyond 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.json and ensuring the proof server always loads matching compiled artifacts from contract/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 ReserveState from the indexer required careful deserialization through CompactState and type coercion for bigint fields, 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 → COMMIT pattern 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
Share this project:

Updates

Submission history