# Midnight Compact Guide

> Comprehensive guide to writing Compact smart contracts for Midnight Network. Use this skill when writing, reviewing, debugging, or learning Compact code. Triggers on "write a contract", "Compact syntax", "Midnight smart contract", "ledger state", "circuit function", or "ZK proof".

- **Type:** Skill
- **Install:** `agentstack add skill-uvroxx-midnight-agent-skills-midnight-compact-guide`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [UvRoxx](https://agentstack.voostack.com/s/uvroxx)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [UvRoxx](https://github.com/UvRoxx)
- **Source:** https://github.com/UvRoxx/midnight-agent-skills/tree/main/skills/midnight-compact-guide

## Install

```sh
agentstack add skill-uvroxx-midnight-agent-skills-midnight-compact-guide
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Midnight Compact Language Reference (v0.19+)

> **CRITICAL**: This reference is derived from **actual compiling contracts** in the Midnight ecosystem (MeshJS starter template). Always verify syntax against this reference before generating contracts.

## Quick Start Template

Use this as a starting point - it compiles successfully:

```compact
pragma language_version >= 0.19;

import CompactStandardLibrary;

// Ledger state (individual declarations, NOT a block)
export ledger counter: Counter;
export ledger owner: Bytes;

// Witness for private/off-chain data (declaration only)
witness local_secret_key(): Bytes;

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

---

## 1. Pragma (Version Declaration)

**CORRECT** - simple minimum version:
```compact
pragma language_version >= 0.19;
```

**WRONG** - these will cause issues:
```compact
pragma language_version >= 0.14.0;           // ❌ outdated version
pragma language_version >= 0.16 && = 0.19
```

---

## 2. Imports

Always import the standard library:
```compact
import CompactStandardLibrary;
```

For modular code:
```compact
import "path/to/module";
import { SomeType } from "other/module";
```

---

## 3. Ledger Declarations

**CORRECT** - individual declarations with `export ledger`:
```compact
export ledger counter: Counter;
export ledger owner: Bytes;
export ledger balances: Map, Uint>;

// Private state (off-chain only)
ledger secretValue: Field;  // no export = private
```

**WRONG** - block syntax is DEPRECATED:
```compact
// ❌ This causes parse error: found "{" looking for an identifier
ledger {
  counter: Counter;
  owner: Bytes;
}
```

### Ledger Modifiers

```compact
export ledger publicData: Field;           // Public, readable by anyone
export sealed ledger immutableData: Field; // Set once in constructor, cannot change
ledger privateData: Field;                 // Private, not exported
```

---

## 4. Data Types

### Primitive Types

| Type | Description | Example |
|------|-------------|---------|
| `Field` | Finite field element (basic numeric) | `amount: Field` |
| `Boolean` | True or false | `isActive: Boolean` |
| `Bytes` | Fixed-size byte array | `hash: Bytes` |
| `Uint` | Unsigned integer (N = 8, 16, 32, 64, 128, 256) | `balance: Uint` |
| `Uint` | Bounded unsigned integer | `score: Uint` |

**⚠️ Uint Type Equivalence:** `Uint` and `Uint` are the **SAME type family**.
- `Uint` = `Uint`
- `Uint` = `Uint`
- `Uint` = `Uint`

### Collection Types

| Type | Description | Example |
|------|-------------|---------|
| `Counter` | Incrementable/decrementable | `count: Counter` |
| `Map` | Key-value mapping | `Map, Uint>` |
| `Set` | Unique value collection | `Set>` |
| `Vector` | Fixed-size array | `Vector` |
| `List` | Dynamic list | `List>` |
| `Maybe` | Optional value | `Maybe>` |
| `Either` | Union type | `Either>` |
| `Opaque` | External type from TypeScript | `Opaque` |

### Custom Types

**Enums** - must use `export` to access from TypeScript:
```compact
export enum GameState { waiting, playing, finished }
export enum Choice { rock, paper, scissors }
```

**Enum Access** - use DOT notation (not Rust-style ::):
```compact
// ✅ CORRECT - dot notation
if (choice == Choice.rock) { ... }
game_state = GameState.waiting;

// ❌ WRONG - Rust-style double colon
if (choice == Choice::rock) { ... }  // Parse error!
```

**Structs**:
```compact
export struct PlayerConfig {
  name: Opaque,
  score: Uint,
  isActive: Boolean,
}
```

---

## 5. Circuits

Circuits are on-chain functions that generate ZK proofs.

**CRITICAL**: Return type is `[]` (empty tuple), NOT `Void`:

```compact
// ✅ CORRECT - returns []
export circuit increment(): [] {
  counter.increment(1);
}

// ✅ CORRECT - with parameters
export circuit transfer(to: Bytes, amount: Uint): [] {
  assert(amount > 0, "Amount must be positive");
  // ... logic
}

// ✅ CORRECT - with return value
export circuit getBalance(addr: Bytes): Uint {
  return balances.lookup(addr);
}

// ❌ WRONG - Void does not exist
export circuit broken(): Void {  // Parse error!
  counter.increment(1);
}
```

### Circuit Modifiers

```compact
export circuit publicFn(): []      // Callable externally
circuit internalFn(): []           // Internal only, not exported
export pure circuit hash(x: Field): Bytes  // No state access
```

---

## 6. Witnesses

Witnesses provide off-chain/private data to circuits. They run locally, not on-chain.

**CRITICAL**: Witnesses are declarations only - NO implementation body in Compact!
The implementation goes in your TypeScript prover.

```compact
// ✅ CORRECT - declaration only, semicolon at end
witness local_secret_key(): Bytes;
witness get_merkle_path(leaf: Bytes): MerkleTreePath>;
witness store_locally(data: Field): [];
witness find_user(id: Bytes): Maybe;

// ❌ WRONG - witnesses cannot have bodies
witness get_caller(): Bytes {
  return public_key(local_secret_key());  // ERROR!
}
```

---

## 7. Constructor

Optional - initializes sealed ledger fields at deploy time:

```compact
export sealed ledger owner: Bytes;
export sealed ledger nonce: Bytes;

constructor(initNonce: Bytes) {
  owner = disclose(public_key(local_secret_key()));
  nonce = disclose(initNonce);
}
```

---

## 8. Pure Circuits (Helper Functions)

Use `pure circuit` for helper functions that don't modify ledger state:

```compact
// ✅ CORRECT - use "pure circuit"
pure circuit determine_winner(p1: Choice, p2: Choice): Result {
  if (p1 == p2) {
    return Result.draw;
  }
  // ... logic
}

// ❌ WRONG - "function" keyword doesn't exist
pure function determine_winner(p1: Choice, p2: Choice): Result {
  // ERROR: unbound identifier "function"
}
```

---

## 9. Common Operations

### Counter Operations
```compact
counter.increment(1);           // Increase by amount (Uint)
counter.decrement(1);           // Decrease by amount (Uint)
const val = counter.read();     // Get current value (returns Uint)
const low = counter.lessThan(100); // Compare with threshold (Boolean)
counter.resetToDefault();       // Reset to zero

// ⚠️ WRONG: counter.value() does NOT exist - use counter.read()
```

### Map Operations
```compact
// Insert/update operations
balances.insert(address, 100);           // insert(key, value): []
balances.insertDefault(address);         // insertDefault(key): []

// Query operations (all work in circuits ✅)
const balance = balances.lookup(address);  // lookup(key): value_type
const exists = balances.member(address);   // member(key): Boolean
const empty = balances.isEmpty();          // isEmpty(): Boolean
const count = balances.size();             // size(): Uint

// Remove operations
balances.remove(address);                // remove(key): []
balances.resetToDefault();               // resetToDefault(): []
```

### Set Operations
```compact
// Insert/remove operations
members.insert(address);                    // insert(elem): []
members.remove(address);                    // remove(elem): []
members.resetToDefault();                   // resetToDefault(): []

// Query operations (all work in circuits ✅)
const isMember = members.member(address);   // member(elem): Boolean
const empty = members.isEmpty();            // isEmpty(): Boolean
const count = members.size();               // size(): Uint
```

### Maybe Operations
```compact
const opt: Maybe = some(42);
const empty: Maybe = none();

if (opt.is_some) {
  const val = opt.value;
}
```

### Type Casting
```compact
const bytes: Bytes = myField as Bytes;  // Field to Bytes
const num: Uint = myField as Uint;      // Field to Uint (bounds not checked!)
const field: Field = myUint as Field;           // Uint to Field (safe)
```

### Hashing
```compact
// Persistent hash (same input = same output across calls)
const hash = persistentHash>>([data1, data2]);

// Persistent commit (hiding commitment)
const commit = persistentCommit(value);
```

---

## 10. Assertions

```compact
assert(condition, "Error message");
assert(amount > 0, "Amount must be positive");
assert(disclose(caller == owner), "Not authorized");
```

---

## 11. Common Patterns

### Authentication Pattern
```compact
witness local_secret_key(): Bytes;

// IMPORTANT: public_key() is NOT a builtin - use this pattern
circuit get_public_key(sk: Bytes): Bytes {
  return persistentHash>>([pad(32, "myapp:pk:"), sk]);
}

export circuit authenticated_action(): [] {
  const sk = local_secret_key();
  const caller = get_public_key(sk);
  assert(disclose(caller == owner), "Not authorized");
  // ... action
}
```

### Commit-Reveal Pattern
```compact
pragma language_version >= 0.19;

import CompactStandardLibrary;

export ledger commitment: Bytes;
export ledger revealed_value: Field;
export ledger is_revealed: Boolean;

witness local_secret_key(): Bytes;
witness store_secret_value(v: Field): [];
witness get_secret_value(): Field;

// Helper: compute commitment hash
circuit compute_commitment(value: Field, salt: Bytes): Bytes {
  const value_bytes = value as Bytes;
  return persistentHash>>([value_bytes, salt]);
}

// Commit phase
export circuit commit(value: Field): [] {
  const salt = local_secret_key();
  store_secret_value(value);
  commitment = disclose(compute_commitment(value, salt));
  is_revealed = false;
}

// Reveal phase
export circuit reveal(): Field {
  const salt = local_secret_key();
  const value = get_secret_value();
  const expected = compute_commitment(value, salt);
  assert(disclose(expected == commitment), "Value doesn't match commitment");
  assert(disclose(!is_revealed), "Already revealed");

  revealed_value = disclose(value);
  is_revealed = true;
  return disclose(value);
}
```

### Disclosure in Conditionals
When branching on witness values, wrap comparisons in `disclose()`:

```compact
// ✅ CORRECT
export circuit check(guess: Field): Boolean {
  const secret = get_secret();  // witness
  if (disclose(guess == secret)) {
    return true;
  }
  return false;
}

// ❌ WRONG - will not compile
export circuit check_broken(guess: Field): Boolean {
  const secret = get_secret();
  if (guess == secret) {  // implicit disclosure error
    return true;
  }
  return false;
}
```

---

## 12. Common Mistakes to Avoid

| Mistake | Correct |
|---------|---------|
| `ledger { field: Type; }` | `export ledger field: Type;` |
| `circuit fn(): Void` | `circuit fn(): []` |
| `pragma >= 0.16.0` | `pragma language_version >= 0.19;` |
| `enum State { ... }` | `export enum State { ... }` |
| `if (witness_val == x)` | `if (disclose(witness_val == x))` |
| `Cell` | `Field` (Cell is deprecated) |
| `counter.value()` | `counter.read()` |
| `pure function helper()` | `pure circuit helper()` |
| `Choice::rock` | `Choice.rock` (use dot, not ::) |

---

## 13. Exports for TypeScript

To use types/values in TypeScript, they must be exported:

```compact
// These are accessible from TypeScript
export enum GameState { waiting, playing }
export struct Config { value: Field }
export ledger counter: Counter;
export circuit play(): []

// Standard library re-exports (if needed in TS)
export { Maybe, Either, CoinInfo };
```

---

## Reference Contracts

These contracts compile successfully and demonstrate correct patterns:

1. **Counter** (beginner): `midnightntwrk/example-counter`
2. **Bulletin Board** (intermediate): `midnightntwrk/example-bboard`
3. **Naval Battle Game** (advanced): `ErickRomeroDev/naval-battle-game_v2`
4. **Sea Battle** (advanced): `bricktowers/midnight-seabattle`

When in doubt, reference these repos for working syntax.

---

## Rules

See `/rules/` directory for detailed pattern documentation:
- `privacy-selective-disclosure.md` - ZK disclosure patterns
- `tokens-shielded-unshielded.md` - Token vault patterns

## References

- [Midnight Docs](https://docs.midnight.network)
- [Compact Language Guide](https://docs.midnight.network/develop/reference/compact/lang-ref)
- [OpenZeppelin Compact Contracts](https://github.com/OpenZeppelin/compact-contracts)
- [midnight-mcp](https://github.com/Olanetsoft/midnight-mcp)

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [UvRoxx](https://github.com/UvRoxx)
- **Source:** [UvRoxx/midnight-agent-skills](https://github.com/UvRoxx/midnight-agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-uvroxx-midnight-agent-skills-midnight-compact-guide
- Seller: https://agentstack.voostack.com/s/uvroxx
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
