agentsclimarketplace

Midnight compact

Skill mzf11125/midnight_agent_skills/midnight-compact

7 modular agent skills for building on Midnight Network's zero-knowledge blockchain

Install
npx -y skills add mzf11125/midnight_agent_skills --skill midnight-compact

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Comprehensive guide to the Compact programming language (v0.22+) for writing privacy-preserving smart contracts on Midnight Network. Use when users need to write Compact smart contracts with zero-knowledge proofs, understand Compact syntax and language features, implement ZK circuit patterns and optimizations, generate contract boilerplate and project scaffolding, learn best practices for secure contract development, access Compact standard library functions, and compile and test Compact contracts.

SKILL.md

19.5 KB, as published. Nobody here has run it

Midnight Compact Language (v0.22+)

Complete guide to writing privacy-preserving smart contracts with Compact.

What is Compact?

Compact is a purpose-built programming language for zero-knowledge smart contracts. Unlike adapting existing languages, Compact was designed from scratch to make privacy-preserving programming natural and secure.

Key Design Goals:

  • Compile directly to efficient ZK circuits
  • Type-safe cryptographic operations
  • Familiar syntax for developers (TypeScript-like)
  • Automatic proof generation
  • Bounded for finite proving circuits

Key Features:

  • Strong static typing (no bypass via unsafe casts)
  • Generic type and numeric parameters
  • Explicit disclosure via disclose() wrappers
  • Module-based namespace management

Quick Start

Minimal Working Contract (v0.22+)

pragma language_version >= 0.20;
import CompactStandardLibrary;

// Ledger state (individual declarations)
export ledger counter: Counter;
export ledger owner: Bytes<32>;

// Witness for private data
witness local_secret_key(): Bytes<32>;

// Circuit (returns [] not Void)
export circuit increment(): [] {
  counter.increment(1);
}

Full Counter with Read/Write

pragma language_version >= 0.20;
import CompactStandardLibrary;

export ledger count: Counter;

export circuit increment(): [] {
  count.increment(1);
}

export circuit decrement(): [] {
  count.decrement(1);
}

export circuit getCount(): Uint<64> {
  return count.read();
}

export circuit setCount(newValue: Uint<64>): [] {
  count.write(newValue);
}

Core Concepts

Three-Part Contract Structure

Each Compact contract has three components:

  1. Replicated component on a public ledger
  2. Zero-knowledge circuit that confidentially proves correct execution
  3. Local, off-chain component for arbitrary code

Program Elements

A Compact program can contain:

  • module / import for namespace management
  • struct / enum / type for program-defined types
  • export ledger for public state storage
  • witness for callback functions (private state)
  • circuit for the operational core
  • constructor for initialization

Comprehensive Syntax Reference

✅ CORRECT vs ❌ WRONG Examples

Pragma:

✅ pragma language_version >= 0.20;
❌ pragma language_version >= 0.16.0;  // Outdated version
❌ pragma version "0.20";  // Wrong format

Ledger Declarations:

✅ export ledger counter: Counter;
✅ export ledger owner: Bytes<32>;
✅ export ledger balances: Map<Address, Uint<64>>;
❌ ledger { counter: Counter; }  // Block syntax deprecated
❌ ledger counter = Counter;  // Missing colon

Circuit Return Types:

✅ export circuit increment(): [] { }
✅ export circuit getBalance(): Uint<64> { return 0; }
✅ export circuit getData(): [Uint<32>, Boolean] { return [0, true]; }
❌ export circuit increment(): Void { }  // Void doesn't exist
❌ export circuit noReturn() { }  // Missing return type

Enum Access:

✅ if (choice == Choice.rock) { }
❌ if (choice == Choice::rock) { }  // Rust-style doesn't work
❌ if (choice == "rock") { }  // String not enum variant

Witness Declarations:

✅ witness local_secret_key(): Bytes<32>;
✅ witness verify_signature(msg: Bytes<32>, sig: Bytes<64>): Boolean;
❌ witness get_key(): Bytes<32> { return ...; }  // No body allowed
❌ witness calculate(): Uint<64> = 42;  // No body or default

Counter Operations:

✅ const val = counter.read();
✅ counter.increment(1);
✅ counter.decrement(1);
✅ counter.write(100);
❌ const val = counter.value();  // Method doesn't exist
❌ counter.add(1);  // Wrong method name

Pure Functions:

✅ pure circuit helper(x: Field): Field { return x + 1; }
✅ circuit helper(x: Field): Field { return x + 1; }
❌ pure function helper(x: Field): Field { ... }  // 'function' keyword doesn't exist
❌ pure fn helper(x: Field): Field { ... }  // 'fn' keyword doesn't exist

Disclosure in Conditionals:

✅ if (disclose(witness_val == x)) { ... }
❌ if (witness_val == x) { ... }  // Implicit disclosure error
❌ disclose(witness_val == x);  // Statement form without if

Vector/Tuple Operations:

✅ const first = vec[0];
✅ const len = vec.len();
✅ const [a, b] = tuple;
✅ for (i: Uint<64>, item: vec) { ... }
❌ const last = vec[-1];  // No negative indexing
❌ vec.push(item);  // No push - vectors are immutable

Common Mistakes to Avoid

❌ Wrong✅ CorrectExplanation
ledger { field: Type; }export ledger field: Type;Individual declarations
circuit fn(): Voidcircuit fn(): []Empty return uses []
enum State { ... }export enum State { ... }Enums need export
counter.value()counter.read()Use read() method
Choice::rockChoice.rockDot, not ::
Cell<T>FieldCell type deprecated
Vector.push(x)Create new vectorVectors immutable
disclose(a == b) aloneif (disclose(a == b))Needs conditional
Uint without sizeUint<64>Size required
Fn or functioncircuitUse circuit keyword

Type System

Primitive Types

TypeDescriptionExample Values
BooleanBoolean valuestrue, false
FieldPrime field integers0 to field max
Uint<N>N-bit unsignedUint<8>, Uint<32>, Uint<64>
Uint<M..N>Bounded unsignedUint<0..100>
Bytes<N>N-byte vectorBytes<32> for hashes
Vector<N, T>N-element homogeneousVector<10, Uint<32>>
[T1, T2, ...]Heterogeneous tuple[Uint<32>, Boolean]
Opaque<"string">Opaque stringDebug strings
Opaque<"Uint8Array">Opaque bytesBinary data

User-Defined Types

Struct:

struct Point {
  x: Field,
  y: Field,
}

struct Token<T> {
  amount: Uint<64>,
  data: T,
}

Enum:

export enum Direction { up, down, left, right }
export enum Status { pending, active, completed }

Type Alias:

type Address = Bytes<32>;
new type UserId = Uint<64>;  // Nominal (distinct)
type Vec3<T> = Vector<3, T>;  // Generic

Subtyping Rules

  • Uint<0..N> subtype of Uint<0..M> if N ≤ M
  • Uint<0..N> subtype of Field if N ≤ field max
  • Tuple subtypes when elements are subtypes

Generic Parameters

module M<T, #N> {
  export circuit foo<A>(x: T, v: Vector<N, A>): Vector<N, [A, T]> {
    return map((y) => [y, x], v);
  }
}
import M<Boolean, 3>;

export circuit bar(): Vector<3, [Uint<8>, Boolean]> {
  return foo<Uint<8>>(true, [101, 103, 107]);
}

Ledger State Types

Seven ledger state types for public data storage:

1. Counter

export ledger myCounter: Counter;
counter.increment(n);
counter.decrement(n);
counter.read();
counter.write(value);

2. Cell (Deprecated - use Field)

// Old way (avoid)
export ledger data: Cell<Uint<32>>;
// New way
export ledger data: Field;

3. Set

export ledger members: Set<Address>;
members.insert(addr);
members.contains(addr);
members.remove(addr);

4. Map

export ledger balances: Map<Address, Uint<64>>;
balances.get(addr);
balances.set(addr, value);
balances.contains(addr);
balances.remove(addr);

5. List

export ledger items: List<Uint<32>>;
items.push(value);
items.get(index);
items.len();

6. MerkleTree

export ledger tree: MerkleTree<Depth>;
tree.insert(value);
tree.getRoot();
tree.getPath(index);
tree.verifyPath(path, root);

7. HistoricMerkleTree

export ledger history: HistoricMerkleTree<Depth>;
history.insert(value);
history.getRoot();
history.getHistoricRoot(blockHeight);
history.getPath(index);

Standard Library

Data Types

Maybe<T>           // Optional values (some/none)
Either<L, R>       // Result types (left/right)
NativePoint        // BLS12-381 points
MerkleTreeDigest  // Tree hash digest
MerkleTreePath    // Proof path
ContractAddress   // Contract addressing
ZswapCoinPublicKey  // Shielded keys
UserAddress       // User addressing

Coin Management (Zswap)

tokenType() -> TokenType
nativeToken() -> TokenType
ownPublicKey() -> CoinPublicKey

createZswapInput(coin: ShieldedCoinInfo) -> ZswapInput
createZswapOutput(recipient: Recipient, amount: Uint<64>) -> ZswapOutput

mintShieldedToken(amount: Uint<64>, recipient: Recipient): [] { }
sendShielded(amount: Uint<64>, recipient: Recipient): [] { }
receiveShielded(input: ZswapInput): [] { }

mintUnshieldedToken(amount: Uint<64>): [] { }
sendUnshielded(amount: Uint<64>, recipient: Address): [] { }
unshieldedBalance(): Uint<64>

Hashing Functions

transientHash(data: Field): Field
transientCommit(data: Field, randomness: Field): Field
persistentHash(key: Field, value: Field): Field
persistentCommit(key: Field, value: Field): Field
degradeToTransient(value: Field): Field

Elliptic Curve

ecAdd(p1: Point, p2: Point): Point
ecMul(point: Point, scalar: Field): Point
ecMulGenerator(scalar: Field): Point
hashToCurve(data: Field): Point
upgradeFromTransient(value: Field): Field

Merkle Trees

merkleTreePathRoot(path: MerkleTreePath): Field
merkleTreePathRootNoLeafHash(path: MerkleTreePath): Field

Block Time

blockTimeLt(time: Uint<64>): Boolean
blockTimeGte(time: Uint<64>): Boolean
blockTimeGt(time: Uint<64>): Boolean
blockTimeLte(time: Uint<64>): Boolean

Control Flow

If/Else

if (condition) {
  // then branch
} else {
  // else branch
}

For Loop

// For each element
for (i: Uint<64>, item: vector) {
  process(item);
}

// With index
for (i: Uint<64>, item: vector) {
  log(i);
  log(item);
}

Match (for enums)

match direction {
  Direction.up => 1,
  Direction.down => 2,
  Direction.left => 3,
  Direction.right => 4,
}

Pure Circuit Functions

pure circuit addOne(x: Uint<32>): Uint<32> {
  return x + 1;
}

Explicit Disclosure

Private data must be explicitly disclosed:

witness secret(): Uint<32>;

export circuit checkEqual(x: Uint<32>): Boolean {
  // Must disclose private comparison
  return disclose(secret() == x);
}

// Valid patterns:
if (disclose(secret() > 100)) { ... }
const revealed = disclose(secret() + publicValue);

Development Workflow

1. Initialize Project

# Using official CLI
npx create-midnight-app my-project
cd my-project

# Or manual setup
mkdir my-contract && cd my-contract
npm init -y
npm install @midnight-ntwrk/compact-tools

2. Write Contract

Create contract.compact:

pragma language_version >= 0.20;
import CompactStandardLibrary;

export ledger counter: Counter;

export circuit increment(): [] {
  counter.increment(1);
}

export circuit getCount(): Uint<64> {
  return counter.read();
}

3. Compile

npx compact build contract.compact

4. Generate TypeScript

# Output in src/ directory

5. Deploy

# Using deployment script
npx midnight-deploy --network testnet

Real-World Contract Examples

Private Token (Zswap)

pragma language_version >= 0.20;
import CompactStandardLibrary;

export ledger totalSupply: Uint<64>;
export ledger balances: Map<Address, Uint<64>>;

export circuit mint(to: Address, amount: Uint<64>): [] {
  const current = balances.getOrDefault(to, 0);
  balances.set(to, current + amount);
  totalSupply.set(totalSupply.read() + amount);
}

export circuit burn(from: Address, amount: Uint<64>): [] {
  const current = balances.getOrDefault(from, 0);
  assert(current >= amount, "Insufficient balance");
  balances.set(from, current - amount);
  totalSupply.set(totalSupply.read() - amount);
}

export circuit transfer(from: Address, to: Address, amount: Uint<64>): [] {
  const fromBal = balances.getOrDefault(from, 0);
  assert(fromBal >= amount, "Insufficient balance");
  const toBal = balances.getOrDefault(to, 0);
  balances.set(from, fromBal - amount);
  balances.set(to, toBal + amount);
}

export circuit balanceOf(owner: Address): Uint<64> {
  return balances.getOrDefault(owner, 0);
}

Private Voting

pragma language_version >= 0.20;
import CompactStandardLibrary;

export ledger votes: Map<Bytes<32>, Uint<64>>;
export ledger voted: Set<Address>;

export circuit castVote(voter: Address, candidate: Bytes<32>): [] {
  assert(!voted.contains(voter), "Already voted");
  voted.insert(voter);
  const current = votes.getOrDefault(candidate, 0);
  votes.set(candidate, current + 1);
}

export circuit getVoteCount(candidate: Bytes<32>): Uint<64> {
  return votes.getOrDefault(candidate, 0);
}

Access Control

pragma language_version >= 0.20;
import CompactStandardLibrary;

export ledger owner: Address;
export ledger admins: Set<Address>;

export circuit onlyOwner(): [] {
  assert(msgSender() == owner, "Not owner");
}

export circuit onlyOwnerOrAdmin(): [] {
  const sender = msgSender();
  assert(sender == owner || admins.contains(sender), "Not authorized");
}

witness msgSender(): Address;
// Note: msgSender is typically provided by runtime

OpenZeppelin Contracts

Standard implementations from @OpenZeppelin/compact-contracts:

  • FungibleToken: ERC-20 equivalent
  • MultiToken: ERC-1155 equivalent (fungible + NFT)
  • NonFungibleToken: NFT implementation
// Using OpenZeppelin
import OpenZeppelin.FungibleToken;

export ledger token: FungibleToken;

export circuit mint(to: Address, amount: Uint<64>): [] {
  token.mint(to, amount);
}

export circuit transfer(to: Address, amount: Uint<64>): [] {
  token.transfer(to, amount);
}

Awesome Midnight DApps Reference

Real-world implementations for learning:

Getting Started (Official)

Finance & DeFi

Identity & Privacy

Gaming

Developer Tools

Midnight MCP: AI-Assisted Development

Midnight MCP is an MCP server (Model Context Protocol) purpose-built for Midnight development. It gives AI assistants real compiler validation and semantic search across 102 Midnight repositories.

Why Use MCP?

AI assistants hallucinate Compact syntax. Without MCP:

  • They invent Int instead of Uint<32>
  • They use Void instead of [] for empty returns
  • They use state instead of ledger

With MCP, AI generates verified working code.

Installation

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "midnight": {
      "command": "npx",
      "args": ["-y", "midnight-mcp@latest"]
    }
  }
}

Cursor (.cursor/mcp.json):

{
  "mcpServers": {
    "midnight": {
      "command": "npx",
      "args": ["-y", "midnight-mcp@latest"]
    }
  }
}

29 Available Tools

CategoryTools
Searchmidnight-search-compact, midnight-search-docs, midnight-search-typescript, midnight-fetch-docs
Validationmidnight-compile-contract, midnight-analyze-contract
Analysismidnight-review-contract, midnight-extract-contract-structure
Generationmidnight-generate-contract, midnight-document-contract
Repositorymidnight-get-file, midnight-get-latest-syntax, midnight-list-examples
Versionmidnight-check-breaking-changes, midnight-get-migration-guide

Version Management

Use exact versions (e.g., 0.30.0) for reproducibility. Latest versions available on GitHub releases.

CI/CD Integration

setup-compact-action

Official GitHub Action for installing and caching the Compact compiler: midnightntwrk/setup-compact-action

Features:

  • Intelligent caching for fast subsequent runs (~2-5 seconds cached vs ~30-60 seconds fresh)
  • Version pinning support (e.g., 0.30.0) or use latest
- name: Setup Compact Compiler
  uses: midnightntwrk/setup-compact-action@v1
  with:
    compact-version: '0.30.0'

- name: Compile Compact code
  run: compact compile src/*.compact
InputDescriptionRequiredDefault
compact-versionVersion of Compact compilerNolatest
cache-enabledEnable cachingNotrue

Outputs: compact-version, cache-hit

Additional Resources

Official Documentation

Learning Resources

Cross Reference Skills

This skill covers Compact language and contracts. For supporting areas see these related skills.

  • midnight-concepts — Zero knowledge architecture, Kachina, privacy mechanisms
  • midnight-api — SDK integration, deployment APIs, verification, code quality
  • midnight-network — Local devnet, compiler tooling, Docker setup
  • midnight-wallet — Wallet SDK, DUST, key management for contract testing
  • midnight-dapp-dev — Frontend scaffolding, wallet connect UI, React patterns
  • midnight-expert — Health checks, version matrix, contract verification

Troubleshooting

  • Compilation errors: Check language version pragma
  • Type errors: Verify type annotations
  • Disclosure errors: Use disclose() in conditionals
  • Ledger errors: Import CompactStandardLibrary

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.