Inspiration

DeFi lending is stuck at 150%+ collateralization because lenders have no way to evaluate a borrower's creditworthiness without seeing their entire wallet history — every balance, every trade, every liquidation, exposed forever on a public ledger. That's a privacy problem masquerading as a risk-management requirement. Midnight's zero-knowledge circuits let us separate the two: prove the fact ("this borrower clears the bar") without disclosing the data behind it.

What it does

Ghost Credit lets a borrower prove "my credit score is above 700, I've never been liquidated, and my wallet is at least 6 months old" as a zero-knowledge circuit. The lending pool contract verifies the proof on-chain and approves the loan — the score, liquidation history, and account age never touch the public ledger. A lender sees a checkmark and a loan amount. Nothing else.

How we built it

  • Contract: a Compact smart contract (ghost-credit.compact) with two witnesses — localSecretKey and creditData — that never leave the borrower's device. Borrower identity on-chain is a persistentHash of a witness-held secret, never a wallet address, and deliberately never ownPublicKey() (which is prover-claimed and unverifiable, so it can't be trusted for access control).
  • Deploy/test harness: a real local Midnight devnet (node + indexer + proof server via Docker) with a full deploy-and-exercise test suite — requestLoan, repayLoan, and getLoanStatus all verified against actual ZK proofs, not mocks.
  • Frontend: a two-screen React app (borrower / lender view) wired to a real deployed contract, showing the same request side-by-side with what a normal DeFi dashboard exposes today vs. what Ghost Credit's lender actually sees.

Challenges we ran into

  • The Compact compiler's disclosure analysis treats circuit parameters as potential witness leaks by default — even public loan amounts needed an explicit disclose() before touching the ledger.
  • A duplicate-module-instance bug: having the contract package and the deploy harness as separate npm packages meant two copies of compact-js in the dependency tree, which broke class-identity checks deep in the deploy path with an opaque Cannot read properties of undefined (ctor) error. Fixed by converting to an npm workspace so dependencies dedupe.
  • DUST (Midnight's fee-generation token) doesn't accrue automatically — NIGHT has to be explicitly registered for DUST generation via its own on-chain transaction, and the wallet's own sync-progress signal for DUST never resolves until after that registration succeeds. That's a chicken-and-egg trap if you naively wait on it.

What's next

  • Wire the frontend to a real wallet connection via the Midnight DApp Connector (Lace), so proof generation happens client-side instead of through our dev bridge server.
  • Replace the seeded wallet-history fixture with a real Solana/EVM indexer.
  • Push past local devnet to a live Preprod deployment.

Built With

Share this project:

Updates