Skip to main content
For the complete documentation index, see llms.txt

Build a privacy-preserving leaderboard DApp

This tutorial walks through a full-stack DApp on the Midnight Network: a Compact smart contract, a TypeScript API over it, a browser frontend that uses the Lace wallet, and a production deployment on Vercel.

The finished DApp is an arcade-style leaderboard. A player clicks as fast as they can for ten seconds, then submits the score on-chain. They choose how the entry is named: with a custom name, with part of their wallet address, or anonymously, as a generated name such as "Crimson Tiger". Later they can prove that an entry is theirs with a zero-knowledge proof.

What you learn​

Smart contract (Compact)​

  • Store structured on-chain data with Map, Counter, and a custom struct.
  • Choose what goes on-chain with disclose().
  • Use witnesses to feed private data into a circuit only when a branch needs it.
  • Prove ownership with assert over a hash commitment, without writing to the ledger.

TypeScript integration​

  • Work with the compiler's generated TypeScript bindings.
  • Build one API layer with midnight-js-contracts that runs in the browser and in Node.
  • Decode the contract's public state from the indexer.

Browser DApp​

  • Connect to the Lace wallet through the DApp Connector API.
  • Build Midnight.js providers from the wallet's configuration: proof server, indexer, wallet balancing, and transaction submission.
  • Serve the circuit keys from the DApp's own origin.
  • Read on-chain state from the indexer without a wallet.

Production deployment​

  • Deploy the DApp to Vercel as a static site from a monorepo.
  • Ship the compiled circuit keys with the site, since Vercel has no Compact compiler.
  • Configure the network and indexer through environment variables.

Prerequisites​

Wallet setup​

  1. Install the Lace extension and create a new wallet.
  2. Set the network to Preprod.
  3. Set the proof server to http://localhost:6300. Start one from examples/leaderboard with npm run proof:up. Lace generates the DApp's proofs, and proof inputs include private data, so prove locally rather than on a public proof server.
  4. Fund your wallet with tNIGHT from the Preprod faucet.
  5. Go to Tokens, click Generate tDUST, and confirm the transaction.

Note: tDUST pays transaction fees on Preprod. It is generated from your tNIGHT balance.

How it fits together​

┌──────────────── browser ────────────────┐
│ React DApp (ui/) │
│ └── LeaderboardAPI (api/) │
│ └── compiled contract (contract/)│
└───────┬─────────────┬──────────────┬─────┘
Lace wallet│ indexer │ static │ keys, ZKIR
(balance, sign, │ (contract │ files │ (from Vercel)
submit, proofs)│ state) │ │
▼ ▼ ▼
Midnight Preprod Blockfrost the DApp's own origin
  • Writes go through Lace. The DApp builds the transaction and has it proven with the circuit's keys, and Lace balances, signs, and submits it.
  • Reads go straight to the indexer. Anyone can view the leaderboard without a wallet.
  • Hosting is static. Vercel serves the built React app and the compiled circuit keys. There is no server code.

Project layout​

examples/leaderboard/
├── contract/
│ ├── leaderboard.compact # the Compact contract
│ ├── witnesses.ts # witnesses and private state
│ ├── index.ts # compiled contract exports
│ └── managed/leaderboard/ # compiler output (committed, for Vercel)
├── api/
│ ├── index.ts # LeaderboardAPI
│ ├── common-types.ts
│ └── utils/index.ts # display name decoder
├── src/ # Node harness: wallets, providers, tests
│ └── test/
│ ├── leaderboard.sim.test.ts # in-memory contract tests
│ └── leaderboard.test.ts # network tests
├── ui/ # React + Vite browser DApp
│ ├── vite.config.ts
│ └── src/
│ ├── App.tsx # game, leaderboard, wallet connection
│ ├── contexts/ # Lace provider bridge
│ └── hooks/ # indexer read hook
└── vercel.json # Vercel build settings

Tutorial sections​

Each part builds on the previous one. Work through them in order.

SectionWhat you build
Part 1: Smart contractThe Compact contract and its in-memory tests.
Part 2: TypeScript integrationWitnesses, contract exports, and the shared API layer, tested on a live network.
Part 3: Browser DAppThe React frontend with Lace and indexer reads.
Part 4: Production deploymentThe DApp on Vercel.