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
assertover 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-contractsthat 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
- Node.js v22+ (use
nvm install 22if needed). - The Compact toolchain.
- Docker, for the local proof server.
- The Lace wallet browser extension.
- A Blockfrost project ID for Midnight Preprod. The DApp reads the leaderboard from Blockfrost's Preprod indexer.
Wallet setup
- Install the Lace extension and create a new wallet.
- Set the network to Preprod.
- Set the proof server to
http://localhost:6300. Start one fromexamples/leaderboardwithnpm 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. - Fund your wallet with tNIGHT from the Preprod faucet.
- 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.
| Section | What you build |
|---|---|
| Part 1: Smart contract | The Compact contract and its in-memory tests. |
| Part 2: TypeScript integration | Witnesses, contract exports, and the shared API layer, tested on a live network. |
| Part 3: Browser DApp | The React frontend with Lace and indexer reads. |
| Part 4: Production deployment | The DApp on Vercel. |