skill
midnight-skill
Claude Code skill for writing and testing Compact smart contracts on Midnight
Set up
Skills are installed by placing their folder where your agent loads skills.
The download comes from github.com/ADAvault/midnight-skill itself, not from our server, so what you get is exactly what its author published.
Install instructions come from the README
README
synced Oct 17, 2026Midnight Compact Smart Contract Skill
An Agent Skill for writing, testing, and deploying Compact smart contracts on the Midnight blockchain. Built from Discord community knowledge, compiler validation, and real-world gotchas. This project extends the Midnight Network with additional developer tooling.
Supports Claude Code, Cursor, Gemini CLI, VS Code Copilot, and 30+ other AI coding assistants.
Important: These examples are educational references. Any smart contract generated using this skill should be professionally audited before deployment to mainnet. AI-assisted code generation does not replace security review.
Compiler-Validated
All examples compiled and tested against Compact 0.31.1 (compact-runtime 0.16.0, ledger v8). Also compatible with Compact 0.29.0 (ledger v7). See gotcha #74 for migration guide.
✅ Re-validated 2026-08-17 — all 29 examples compile on Compact 0.31.1
Every published example was recompiled from source against the current compiler.
set result 10 standalone validation contracts 10/10 PASS 19 remaining + new contracts 19/19 PASS vendored OpenZeppelin compact-contracts9/10 — see below Toolchain:
compactCLI 0.5.2, compiler 0.31.1,compact-runtime0.16.0, Midnight.js 4.1.1, Node 1.0.2 (mainnet + preprod; preview 1.0.1), Ledger 8.1.0. (support matrix)⚠
compact --versionreports the CLI wrapper (0.5.1), not the compiler (0.31.1). Easy to confuse when checking which you are on.🔴 Compact 0.31.0 has a soundness bug that can silently drop a range constraint. Use 0.31.1+. If you deployed anything built with 0.31.0, diff the verifier key — source review is explicitly not reliable for finding it. Details in
SKILL.md.Breaking changes found, and what they mean for older code:
CoinInfois now an unbound identifier — hit by OZ'sarchive/ShieldedToken.compact, which upstream has already retired. Expected, not a regression, but relevant to anyone carrying older Zswap coin code.- Zswap operators were renamed:
receive→receiveShielded,sendImmediate→sendImmediateShielded. The compiler names the replacement in the error, so this migrates cleanly. All current examples already use the new names.The per-example test counts in the table below were measured in March against Compact 0.29.0/0.30.0; compilation only was re-verified on 2026-08-17. The suites themselves were re-run on 2026-08-30 — see below.
✅ Re-validated 2026-08-30 — all 11 suites re-run, 676 tests passing
Not a recompile this time: every test suite was executed.
set result 10 standalone validation contracts 10/10 suites, 69 tests PASS vendored OpenZeppelin compact-contracts45/45 files, 1482 tests PASS 🔴 Do not
npm install @midnight-ntwrk/compact-runtime@latest, and do not copy a version out of a doc. The compiler decides the runtime version, not npm — derive it withcompact compile +<version> --runtime-versionand pin exactly that. Compiler 0.31.1 emits code for 0.16.0; compiler 0.34.0 emits code for 0.19.0. Any mismatch fails at load withCompactError: Version mismatch. Full matrix inSKILL.md.Changing the runtime means recompiling — the expected version is baked into the generated
contract/index.js. Bumping the npm package alone always fails.Also confirmed:
midnight-js3.x → 4.x is clean — all 10 suites pass onmidnight-js-network-id4.1.1 after being pinned at^3.2.0since March.- No Compact language regressions in six months — March-era
.compactsources compiled unchanged on 0.31.1.- Compiler 0.34.0 is available (
compact list) but the support matrix still names 0.31.1 as the tested version. Not adopted here; 0.31.1 remains the validated compiler.compact-contractsmoved tocompact-runtime0.16.0 +ledger-v88.1.0 by pulling upstream (which had already made the change), resolving the Ledger-v7 inconsistency.⚠ OpenZeppelin changed its identity model — party identity moved from
Either<ZswapCoinPublicKey, ContractAddress>toEither<Bytes<32>, ContractAddress>, where the bytes are an account id (persistentHash(secretKey)) proved via a witness.ZswapCoinPublicKeyno longer appears inFungibleTokenat all. Pre-mid-2026 contracts will not compile against the current library. Migration notes and a worked example inSKILL.mdandexamples/composition/.
✅ Re-validated 2026-09-23 — preprod deploy on the maintained stack
check result 29 examples compile, compiler 0.31.1 and 0.34.0 29/29 on both 10 standalone suites, 0.31.1 + runtime 0.16.0 10/10 PASS same suites, 0.34.0 + runtime 0.19.0 0/10: createCircuitContexttakes a circuit id first in 0.19example-bboardon preprod, fresh walletdeployed + called (sync 79.7 min, deploy 21 s, call 24 s) archived example-counterwallet stack, same synccannot finish: heap OOM (gotcha #80) Fixed in this round: the proof-server command silently dropped flags (gotcha #21). The version check in
security.mdrejected every real package set. The wallet setup code used removed APIs andledger-v7. The.npmrcpointed at a registry that does not resolve. Unshielded balances are public by address, not key-gated (gotcha #60). 0.34.0 targets ledger 9, which mainnet and preprod do not run yet, so 0.31.1 remains the compiler to deploy with.
| Example | Circuits | Tests | Status |
|---|---|---|---|
| Counter | 3 | 5/5 | Validated |
| Bulletin Board | 3 | 8/8 | Validated |
| Fungible Token | 7 | 6/6 | Validated (OZ modules) |
| NFT | 7 | 6/6 | Validated |
| Rock-Paper-Scissors | 3 | 6/6 | Validated |
| Shielded Voting | 6 | 9/9 | Validated |
| Sealed-Bid Auction | 6 | 8/8 | Validated |
| Identity Proof | 4 | 6/6 | Validated |
| Credential Registry | 5 | 6/6 | Validated |
| Prescription | 5 | 6/6 | Validated |
| Escrow | 5 | 8/8 | Validated |
| Time Lock | 3 | 7/7 | Validated |
| Multi-Sig | 6 | 6/6 | Validated |
| Staking | 5 | 6/6 | Validated |
| Crowdfunding | 5 | 6/6 | Validated |
| Lending | 6 | 6/6 | Validated |
| Prediction Market | 5 | 7/7 | Validated |
| Oracle Feed | 5 | 6/6 | Validated |
| Access Control | 8 | 6/6 | Validated |
| DID Registry | 5 | 6/6 | Validated |
| Micro-DAO | 7 | 7/7 | Validated |
| Contract Upgradability | V1: 3, V2: 7 | 8/8 | Validated |
| Token Swap | 6 | — | Preprod deployed |
| Token Minting | 3 | — | Preprod deployed |
| Privacy Mixer | 3 | 7/7 | Validated |
| Lottery | 4 | 8/8 | Validated |
| Vesting | 4 | 8/8 | Validated |
| Revenue Sharing | 3 | 7/7 | Validated |
| Supply Chain | 4 | 7/7 | Validated |
| Native Shielded Token | 2 | — | Compiled 0.31.1 |
30 examples. 29/29 compile on Compact 0.31.1 and on 0.34.0 (re-verified 2026-09-23). The 10 standalone suites pass on 0.31.1 + compact-runtime 0.16.0 (2026-09-23, Node 22). On 0.34.0 + 0.19.0 all 10 fail at createCircuitContext, whose signature changed (see SKILL.md). 11/11 test suites re-run 2026-08-30 — 1551 tests passing (69 standalone on compact-runtime 0.16.0 + 1482 in compact-contracts). Native Shielded Token added 2026-08-17, compiles on 0.31.1 (no test suite yet). 6 contracts deployed on v8 preprod; example-bboard deployed and called on preprod on the ledger-8.1.0 stack, 2026-09-23 (below).
Token Swap and Token Minting use Zswap coin operations (receiveShielded, sendImmediateShielded, mintToken) that require the full network stack for circuit calls. Both compile and deploy successfully.
Preprod Deployment
2026-09-23 re-validation (ledger 8.1.0 stack)
The maintained example-bboard was deployed and called from a fresh wallet on preprod, using the
stack in reference/offchain.md §7: wallet-sdk 1.2.0 (facade 4.1.0), Midnight.js 4.1.1,
ledger-v8 8.1.0, compiler 0.31.1 with compact-runtime 0.16.0, proof server 8.1.0, Node 24.
| Step | Result |
|---|---|
| Fresh-wallet sync (~1.55M ledger events) | 79.7 min, heap flat at ~150 MB |
| DUST registration | block 2677448; 268.7 DUST available at once (backdated generation, gotcha #75) |
| Deploy (2 circuits) | 21 s, block 2677456, SucceedEntirely |
post call | 24 s, block 2677460, SucceedEntirely |
| Read back via the public indexer | state=occupied, sequence=1, message as posted |
Contract 02a2550d0260571f3b7aeea331feb531da32e688af7f4512a7b539bca02f646c, checkable with the
indexer's contractAction(address) query. DUST fees were not measured: the indexer's fee field
reads "1" for every transaction, and the wallet reports 0 available DUST while change is pending.
The archived example-counter's wallet stack could not complete the same sync (gotcha #80).
v8 (Ledger 8.0.3, March 2026)
6 contracts deployed to Midnight preprod on Ledger v8 (protocol v22000). Representative sample validating all major patterns.
| Contract | Pattern | Circuits | DUST Fee | Deploy Time | Block |
|---|---|---|---|---|---|
| Counter | simplest | 3 | 252B | 17.8s | 114208 |
| Bulletin Board | auth | 2 | 267B | 19.1s | 114211 |
| Sealed-Bid Auction | commit-reveal + state machine | 5 | 554B | 16.9s | 114214 |
| Oracle Feed | time-based | 5 | 504B | 19.0s | 114217 |
| Multi-Sig | multi-party | 6 | 574B | 17.5s | 114220 |
| LOK | token ops (receiveUnshielded) | 4 | 423B | 17.4s | 114223 |
receiveUnshielded (wallet→contract token transfer) confirmed working on v8 — Issue #151 resolved.
v7 (historical, chain wiped)
30 contracts were previously deployed on Ledger v7 (protocol v21000). Chain state was reset with the v8 upgrade on March 25, 2026.
DUST Fee Economics
DUST is Midnight's fee token — generated continuously from tNight. Must register NIGHT UTxOs for dust generation before any deployment (gotcha #75).
- Generation rate: 5 DUST per NIGHT, ~1 week to cap
- Deploy time: 16-22s per contract on preprod
- Practical cost: 1000 tNight from faucet generates ample DUST for dozens of deployments
What's Covered
- Compact language — types, syntax, circuits, witnesses, disclosure, module system
- Privacy model — shielded vs unshielded state, ZK proof flow, witness security
- Security — 10 ZK-specific attack categories with mitigations
- Testing — simulator, standalone network, testnet (3-level testing approach)
- Design patterns — authentication, OZ composition, off-chain computation, circuit optimization
- Off-chain integration — TypeScript SDK, wallet connectivity, provider pattern, deployment
- Gotchas — compiler bugs, SDK pitfalls, proof server issues, design traps (sourced from Discord + real compilation)
- 30 worked examples — core patterns through DeFi, governance, identity, contract upgradability, supply chain, and native shielded tokens
Installation
Project-level (recommended)
mkdir -p .claude/skills
git clone https://github.com/adavault/midnight-skill.git .claude/skills/midnight-compact
Personal (all projects)
git clone https://github.com/adavault/midnight-skill.git ~/.claude/skills/midnight-compact
Usage
The skill auto-activates on keywords: Midnight, Compact, circuit, witness, ledger, disclose, proof server, DUST, NIGHT, Zswap, shielded.
Or invoke explicitly: /midnight-compact
Structure
midnight-skill/
├── SKILL.md # Core skill — workflow, syntax, patterns
├── README.md # This file
├── LICENSE # MIT
├── reference/ # Deep-dive reference documents
│ ├── language.md # Compact types, syntax, modules, operators
│ ├── privacy-model.md # Shielded/unshielded state, ZK proof flow
│ ├── security.md # 10 ZK attack categories and mitigations
│ ├── testing.md # Simulator, standalone, testnet testing
│ ├── patterns.md # Design patterns and circuit optimization
│ ├── stdlib.md # CompactStandardLibrary reference
│ ├── gotchas.md # 79 real-world issues from Discord + compilation
│ ├── offchain.md # TypeScript SDK, wallet, deployment
│ └── auditing.md # ZK contract audit methodology
└── examples/ # Working examples with tests
├── counter.md # Simplest contract — state + increment
├── bulletin-board.md # Witness auth, CRUD, multi-user
├── fungible-token.md # OZ module composition (FT + Ownable + Pausable)
├── nft.md # Commitment-based NFT ownership
├── rock-paper-scissors.md # Commit-reveal 2-player game
├── shielded-voting.md # Commit-reveal private ballot
├── sealed-bid-auction.md # Commit-reveal with state machine
├── identity-proof.md # Selective disclosure, parameterized witnesses
├── credential-registry.md # Nullifier-based double-use prevention
├── prescription.md # Batch registration, nullifier for healthcare
├── escrow.md # Two-party exchange with deadline
├── time-lock.md # Time-based LOK/RELEASE pattern
├── multi-sig.md # M-of-N authorization, composite keys
├── staking.md # Lock period + ZK reward calculation
├── crowdfunding.md # Anonymous backing, ZK refund proofs
├── lending.md # Collateral, health factor, liquidation
├── prediction-market.md # Commitment-based bets, ZK payout
├── oracle-feed.md # External data, freshness checks
├── token-swap.md # Atomic swap (coin operations)
├── access-control.md # Role hierarchy, internal guards
├── did-registry.md # DID document lifecycle management
├── micro-dao.md # Token-gated voting, treasury
├── contract-upgradability.md # V1/V2 migration pattern
├── token-minting.md # Zswap coin creation (mintShieldedToken)
├── privacy-mixer.md # Nullifier-based deposit/withdraw mixer
├── lottery.md # Commit-reveal multi-party randomness
├── vesting.md # Time-based tranche release schedule
├── revenue-sharing.md # Private share allocations, ZK withdrawal
├── supply-chain.md # Selective disclosure provenance tracking
└── native-shielded-token.md # Contract-minted shielded token + coin-info hazard
Key Findings from Validation
Compiling all examples against the real compiler uncovered several patterns not documented elsewhere:
- No sequence counter for registered key sets — contracts with multi-party key registries (multi-sig, ACL, oracle, identity) must use deterministic keys. A sequence increment invalidates all registered keys.
Uint<N> + literaltype widening —Uint<32> + 1producesUint<0..4294967297>. Fix:(expr + 1) as Uint<32>.- Parameterized witnesses work —
witness fn(param: T): Upasses circuit arguments to TypeScript witnesses as additional function parameters. - Internal circuits can access ledger — non-exported
circuit(notpure circuit) can read/writeexport ledgerstate, enabling reusable guard patterns. - Boolean/literal assignments skip
disclose()— compile-time constants written toexport ledgerdon't need disclosure wrapping.
All findings are documented in gotchas.md (#49-#52) and in each example's Key Concepts section.
v8 Migration Findings
The Ledger v8 release (March 2026) introduced significant SDK changes discovered during validation:
- Witness tuple return — witnesses now return
[nextPrivateState, value]instead of justvalue(gotcha #79) - Simulator API —
contract.initialState(ctx, ...args)replacescontract.constructor(ctx)(gotcha #79) - Dust registration required — must call
registerNightUtxosForDustGeneration()before any deployment (gotcha #75) signRecipebug fixed — wallet-sdk-facade 3.0.0 handles proof markers correctly (gotcha #76)nativeToken()trap — useunshieldedToken().rawfrom ledger-v7 for balance lookups (gotcha #77)- Private state encryption —
levelPrivateStateProvidernow requires encryption config (gotcha #78)
0.31.1 re-validation findings (August 2026)
Re-compiling every example against Compact 0.31.1 surfaced these:
CoinInfois now an unbound identifier — older Zswap coin code referencing it no longer compiles. OpenZeppelin has moved the affected contract toarchive/.- Zswap operators renamed —
receive→receiveShielded,sendImmediate→sendImmediateShielded. The compiler names the replacement in the error, so migration is mechanical. Opaque<"string">cannot be constructed in Compact — a string literal isBytes<N>and there is no cast between them. Names, symbols and descriptions must arrive as parameters from TypeScript. OpenZeppelin's own mocks follow this pattern.- Module prefixes concatenate, they do not dot —
import "./X" prefix Token_givesToken__mint(...), notToken_._mint(...). compact --versionreports the CLI, not the compiler — CLI 0.5.1 ships compiler 0.31.1. Easy to misread when checking which version you are on.- Simulator API stable across
compact-runtime0.14 → 0.16, changed in 0.19 — test suites pinned at^0.14.0pass unchanged against 0.16.0. The ceiling is per-compiler, not absolute: 0.16.0 is right for compiler 0.31.1 (verified 2026-08-30) and 0.19.0 is right for compiler 0.34.0 (verified 2026-08-31). In 0.19createCircuitContexttakes a circuit id first, and 0.34 targets ledger 9, which is not yet on mainnet (SKILL.md). Derive the runtime withcompact compile +<version> --runtime-versionrather than pinning a constant.
Sources
Built from:
- Midnight Discord dev-chat (Feb 2024 — Mar 2026)
- OpenZeppelin/compact-contracts — canonical Compact library
- Official Midnight examples — counter, bulletin board, counter-cli migration guide
- Brick Towers projects — seabattle, local-network, proof-server, RWA
- Existing community skills — UvRoxx, FractionEstate, OverGuild, mzf11125
- Compiler validation — every example compiled against Compact 0.29.0 and 0.30.0
- Simulator validation — 29/29 examples pass on compact-runtime 0.15.0
- Preprod deployment — 6 contracts deployed on v8 preprod (Ledger 8.0.3, protocol v22000)
- midnight-mcp — MCP server for searching across 102+ Midnight repos. Complementary tool: the MCP provides live search, this skill provides validated patterns. Several gotchas (#64, #65) were discovered via MCP search.
- docs.midnight.network/relnotes/overview — official v8 compatibility matrix
Contributing
PRs welcome — especially from Midnight developers, ZK researchers, and privacy protocol builders. Areas of interest:
- New contract patterns or examples
- Security findings or additional ZK attack vectors
- Corrections to existing reference material
- Off-chain integration patterns for new SDK versions
Companion Skills
- cardano-skill — Aiken smart contracts on Cardano (eUTxO, multi-validator, CIP-113)
- cardano-offchain-skill — CIP-30 wallets, MeshJS transactions, Ogmios/Kupo, Playwright testing
License
MIT — see LICENSE.
Built by ADAvault