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.
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.
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.
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/.
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.
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).
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.
30 contracts were previously deployed on Ledger v7 (protocol v21000). Chain state was reset with the v8 upgrade on March 25, 2026.
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
- 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
mkdir -p .claude/skills
git clone https://github.com/adavault/midnight-skill.git .claude/skills/midnight-compactgit clone https://github.com/adavault/midnight-skill.git ~/.claude/skills/midnight-compactThe skill auto-activates on keywords: Midnight, Compact, circuit, witness, ledger, disclose, proof server, DUST, NIGHT, Zswap, shielded.
Or invoke explicitly: /midnight-compact
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
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.
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)
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.
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
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
- cardano-skill — Aiken smart contracts on Cardano (eUTxO, multi-validator, CIP-113)
- cardano-offchain-skill — CIP-30 wallets, MeshJS transactions, Ogmios/Kupo, Playwright testing
MIT — see LICENSE.
Built by ADAvault