example-leaderboard-dapp
Skill18 starsChanged 17 days agoMITView on GitHub
Build a privacy-preserving arcade leaderboard DApp on Midnight Network — players submit click-challenge scores with anonymous, public, or custom display names, and prove entry ownership via zero-knowledge proofs without revealing secret keys. Covers leaderboard.compact (Map, Counter, witnesses, persistentHash owner commitments), Next.js frontend, 1AM wallet integration, low-level deploy/call, indexer reads without wallet, and ZK asset hosting. Use for gamified on-chain rankings, pseudonymous competitions, score attestations, or teaching disclose()/witness patterns. Triggers: leaderboard dApp, privacy leaderboard, submit score, verify ownership, anonymous player name, ZK proof of ownership, persistentHash identity, arcade game on Midnight. Also use when extending locker-dapp or payment-dapp wallet/provider patterns to social or gaming flows.
- Installs packages
In this repository36 other files it gives its agents.
- AGENTS.mdAGENTS.md
- 1am-walletSkill
- android-example-votingSkill
- ankr-midnightSkill
- compactSkill
- dynamic-midnight-walletSkill
- example-counterSkill
- example-hello-worldSkill
- example-locker-dappSkill
- example-passport-dappSkill
- example-payment-dappSkill
- example-private-party-dappSkill
- example-private-reserve-auction-dappSkill
- example-zk-loan-applicationSkill
- gsap-coreSkill
- gsap-utilsSkill
- indexerSkill
- midnight-consensusSkill
- midnight-cryptographySkill
- midnight-environment-setupSkill
- midnight-jsSkill
- midnight-onchain-logicSkill
- midnight-p2p-networkingSkill
- midnight-passportSkill
- midnight-rpcSkill
- midnightskillSkill
- midnight-storageSkill
- midnight-transactionsSkill
- multinetworkSkill
- nftSkill
- react-wallet-connectorSkill
- midnight-securitySkill
- testingSkill
- token-transfersSkill
- why-midnightSkill
- zk-vs-fhe-vs-mpcSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 50
Use createUnprovenDeployTx + submitTxAsync — not deployContract() (hangs on preprod)
- Distinctive: line 58
When helping the user, follow this sequence:
- Very specific: line 153
Call setCustomName(name) in TypeScript before submitScore when using public or custom display modes.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Midnight Network Leaderboard DApp
A privacy-preserving leaderboard where players submit scores on-chain with three display modes and prove they own an entry using verifyOwnership — a ZK circuit that checks ownerCommitment(secretKey) matches the stored ownerHash without revealing the secret.
Runnable template: Copy templates/leaderboard-dapp/ for a complete Next.js project (contract + UI). Run npm install && npm run compact && npm run sync:assets && npm run dev after installing the 1AM wallet.
What this skill produces:
contract/— Compact leaderboard + TypeScript witnesses + compile scriptsapp/leaderboard/— Next.js client UI (click game, privacy mode picker, leaderboard table)lib/midnight.ts— wallet session + patched indexer provider (copy fromreferences/midnight-session.mdortemplates/leaderboard-dapp/lib/midnight.ts)lib/leaderboard.ts— deploy,submitScore,verifyOwnership, ledger decodelib/display-name.ts— decode anonymous hash bytes to generated names ("Crimson Tiger")public/zk/leaderboard/— ZK proving assets synced from contract build
Shared references (canonical provider + troubleshooting — do not duplicate in prompts):
references/midnight-session.md—createConnectedSession, indexer patch, deploy/call helpersreferences/gotchas.md— preprod deploy hangs, GraphQLoffset: null, ZK asset pathsreferences/versions.json— pinned@midnight-ntwrk/*versions
Primary references:
example-locker-dapp//templates/locker-dapp/— Next.js + 1AM pattern, low-level deploy/callexample-payment-dapp/— provider wiring, indexer pollingcompact/—disclose(), witnesses,persistentHash,Map,Counter,assertsecurity/— what is public on-chain vs private in witness dataindexer/— read contract state without wallet connection
Key architecture notes:
scoresis a publicMap<Uint<64>, ScoreEntry>— readable from the indexer by anyonelocalSecretKeywitness returns a 32-byte secret from private state; never disclosed on-chainownerCommitment(sk)usespersistentHashwith domain separator"leaderboard:owner:"useCustomNameboolean selects witness-fed name vs hash-based anonymous display nameverifyOwnershipis a proof-only circuit — no ledger writes; use for "prove this is my score"- Persist secret key in
localStorageso ownership proofs work after page refresh - Use
createUnprovenDeployTx+submitTxAsync— notdeployContract()(hangs on preprod) - Wrap
indexerPublicDataProviderwith patchedqueryContractState(GraphQLoffset: nullbug) - Leaderboard table can load from indexer without wallet — only submit/verify need connection
Workflow
When helping the user, follow this sequence:
- Contract —
leaderboard.compactcompile + witnesses (localSecretKey,getCustomName) - Providers —
createConnectedSession(fromreferences/midnight-session.md) - Deploy — low-level deploy, persist private state + contract address
- Read state — indexer GraphQL →
ContractState.deserialize→ledger()→ decode entries - Submit score —
submitScore(score, useCustomName)with optionalsetCustomNamebeforehand - Verify ownership —
verifyOwnership(entryId)for prize claims or "yours" badges - UI — click game, privacy mode selector, leaderboard table with prove button
1) Project Structure
leaderboard-dapp/
├── package.json
├── next.config.mjs
├── lib/
│ ├── isomorphic-ws-fix.mjs
│ ├── midnight.ts # session, patched provider, hex helpers
│ ├── leaderboard.ts # deploy, submitScore, verifyOwnership, decode
│ └── display-name.ts # anonymous name generator
├── app/
│ ├── layout.tsx
│ └── leaderboard/
│ └── LeaderboardClient.tsx # game + wallet + leaderboard UI
├── contract/
│ ├── package.json
│ └── src/
│ ├── leaderboard.compact
│ ├── witnesses.ts
│ ├── index.ts
│ └── managed/leaderboard/ # compiler output (gitignored)
├── scripts/
│ └── sync-zk-assets.mjs # → public/zk/leaderboard/
└── public/zk/leaderboard/ # keys + zkir (gitignored until sync)2) Compact Contract
contract/src/leaderboard.compact:
struct ScoreEntry {
score: Uint<64>,
displayName: Bytes<32>,
ownerHash: Bytes<32>
}
export ledger scores: Map<Uint<64>, ScoreEntry>;
export ledger nextId: Counter;
witness localSecretKey(): Bytes<32>;
witness getCustomName(): Bytes<32>;
export circuit ownerCommitment(sk: Bytes<32>): Bytes<32> {
return persistentHash<Vector<2, Bytes<32>>>([pad(32, "leaderboard:owner:"), sk]);
}
export circuit submitScore(score: Uint<64>, useCustomName: Boolean): [] { /* ... */ }
export circuit verifyOwnership(targetEntryId: Uint<64>): [] { /* ... */ }Privacy design:
- Anonymous mode (
useCustomName = false):displayName = persistentHash(sk)— UI decodes to generated name - Public / Custom mode (
useCustomName = true):displayName = getCustomName()witness — app feeds address or user name - Ownership:
ownerHash = ownerCommitment(sk)stored on every entry;verifyOwnershipre-derives and compares
Compile:
cd contract && npm run compact && cd ..
npm run sync:assets3) Witnesses
contract/src/witnesses.ts:
export type LeaderboardPrivateState = { secretKey: Uint8Array };
export const witnesses = {
localSecretKey: (ctx) => [ctx.privateState, ctx.privateState.secretKey] as const,
getCustomName: (ctx) => [ctx.privateState, customName] as const,
};
export const setCustomName = (name: string) => { /* encode to Bytes<32> */ };Call setCustomName(name) in TypeScript before submitScore when using public or custom display modes.
4) TypeScript Integration
lib/leaderboard.ts mirrors the locker-dapp pattern:
| Function | Purpose |
getOrCreateSecretKey() | Persist 32-byte secret in localStorage |
deployLeaderboard(session) | createUnprovenDeployTx + submitTxAsync |
submitScore(session, addr, score, customName?) | submitCallTxAsync → submitScore circuit |
verifyOwnership(session, addr, entryId) | submitCallTxAsync → verifyOwnership circuit |
fetchLeaderboardState(queryUrl, addr) | Indexer poll + decodeLeaderboardState |
ZK asset path: /zk/leaderboard (synced to public/zk/leaderboard/).
5) Browser UI
LeaderboardClient.tsx flow:
- Connect —
detectWallet()→wallet.connect('preprod')→createConnectedSession(api, ZK_PATH) - Deploy / Join — deploy new contract or paste existing 64-char hex address
- Play — 10-second click challenge
- Submit — pick Anonymous / Public / Custom, then
submitScore - Leaderboard — auto-refresh from indexer every 15s (no wallet required to read)
- Prove —
verifyOwnership(entryId)marks entry as yours in UI
Indexer-only reads mean spectators can watch the leaderboard without connecting a wallet.
6) Prerequisites
| Component | Version |
| Compact compiler | 0.31.0 |
| Compact runtime | 0.16.0 |
| Ledger | 8.0.3 |
| midnight-js | 4.0.4 |
| proof server | 8.0.3 |
docker run -d -p 6300:6300 midnightntwrk/proof-server:8.0.3 -- \
midnight-proof-server --network preprodWallet: 1AM extension on Preprod, proof server http://localhost:6300, tNIGHT + tDUST from faucet.
7) Compatibility Matrix
Pin versions from references/versions.json. Cross-check docs.midnight.network support matrix before upgrading.
8) Production Notes
- Deploy frontend to Vercel/Netlify; set
public/zk/leaderboardassets in build step (npm run sync:assets) - Store default contract address in env or
localStoragefor returning players verifyOwnershipdoes not mutate ledger — safe for repeated "prove mine" UX- For mainnet, update network ID, indexer URLs, and proof server config via wallet
getConfiguration()
Quick Commands
cd templates/leaderboard-dapp
npm install
npm run compact
npm run sync:assets
npm run devOpen http://localhost:3000 → Connect 1AM → Deploy New → play → submit → refresh leaderboard.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

