Help us improve
Share bugs, ideas, or general feedback.
From compact-core
This skill should be used when the user asks about Compact language mechanics, syntax reference, types (Field, Bytes, Uint, Boolean, Opaque, Vector, Maybe, Either, enums, structs), type casting with "as", operators, arithmetic, control flow, for loops, modules, imports, include, pragma, stdlib functions (persistentHash, transientHash, disclose, assert, pad, default), map and fold operations, or Compact compiler errors and wrong syntax patterns.
npx claudepluginhub devrelaicom/midnight-expert --plugin compact-coreHow this skill is triggered — by the user, by Claude, or both
Slash command
/compact-core:compact-language-refThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill covers Compact language mechanics: the type system, operators, expressions, type casting, control flow, the module system, and the standard library. It does not cover contract architecture (ledger declarations, circuit design, witness integration, constructor layout) -- those belong in `compact-structure`. Use this skill when you need to know how the language works rather than how to...
This skill should be used when the user asks about the Compact standard library (CompactStandardLibrary), stdlib types (Maybe, Either, JubjubPoint, MerkleTreeDigest, MerkleTreePath, ContractAddress, ZswapCoinPublicKey, UserAddress), deprecated stdlib names (CurvePoint, NativePoint, CoinInfo), stdlib constructor functions (some, none, left, right), elliptic curve functions (ecAdd, ecMul, ecMulGenerator, hashToCurve, jubjubPointX, jubjubPointY, constructJubjubPoint), Merkle tree path verification (merkleTreePathRoot, merkleTreePathRootNoLeafHash), or when the user needs to verify which functions exist in the standard library, prevent hallucination of non-existent stdlib functions, or search the Midnight MCP for stdlib source code.
Use this skill when an agent needs real, compilable examples of Compact smart contracts, TypeScript witnesses, or tests. Covers beginner contracts (counter, bulletin board), reusable modules (access control, security, tokens, math, crypto, data structures, identity, utils), composed token contracts (fungible, NFT, multi-token, shielded), and full applications (CryptoKitties, ZK lending, real-world assets). All examples compile with pragma language_version >= 0.22 and full proof generation.
Compact-specific claim classification and method routing. Determines what kind of Compact claim is being verified and which verification method applies: execution (compile+run), source inspection, or both. Loaded by the /midnight-verify:verify command alongside the hub skill. Provides the claim type → method routing table and guidance on negative testing.
Share bugs, ideas, or general feedback.
This skill covers Compact language mechanics: the type system, operators, expressions, type casting, control flow, the module system, and the standard library. It does not cover contract architecture (ledger declarations, circuit design, witness integration, constructor layout) -- those belong in compact-structure. Use this skill when you need to know how the language works rather than how to structure a contract.
Compact is statically and strongly typed. Every expression has a type known at compile time.
| Category | Type | Description |
|---|---|---|
| Primitive | Field | Scalar field element; unbounded within the field prime |
| Primitive | Boolean | true or false |
| Primitive | Bytes<N> | Fixed-size byte array of N bytes |
| Primitive | Uint<N> | N-bit unsigned integer; values 0 to 2^N - 1 |
| Primitive | Uint<0..MAX> | Bounded unsigned integer; 0 to MAX inclusive; Uint<8> = Uint<0..255> |
| Opaque | Opaque<"string"> | Hashed in circuits, full string in witnesses and TypeScript |
| Opaque | Opaque<"Uint8Array"> | Hashed in circuits, Uint8Array in witnesses and TypeScript |
| Collection | Vector<N, T> | Fixed-size array of N elements; shorthand for [T, T, ..., T] |
| Collection | Maybe<T> | Optional; some<T>(v) / none<T>(); inspect .is_some, .value |
| Collection | Either<L, R> | Sum type; left<L,R>(v) / right<L,R>(v); inspect .is_left, .left, .right |
| Custom | enum | Named variants; dot notation (State.active); export for TypeScript |
| Custom | struct | Named fields; named, positional, and spread construction |
| Custom | Tuples [T, ...] | Heterogeneous fixed-size sequences; [] is the unit type |
Numeric literals have tight bounded types: 42 has type Uint<0..42>. Subtyping allows implicit widening -- Uint<0..42> fits wherever Uint<64> is expected. All Uint types are subtypes of Field.
For full type documentation, default values, subtyping rules, and TypeScript representations, see references/types-and-values.md.
Compact provides three arithmetic operators. Division and modulo do not exist in the language.
| Operator | Name | Uint Result Type | Notes |
|---|---|---|---|
+ | Add | Uint<0..m+n> | Result widens; cast back before assignment |
- | Subtract | Uint<0..m> | Runtime error if result would be negative |
* | Multiply | Uint<0..m*n> | Result widens; cast back before assignment |
When either operand is Field, the result is Field and wraps modulo the field prime. Cast Uint to Field explicitly only if you want to force field arithmetic semantics. Because arithmetic widens result types, cast before assigning: balance = (balance + amount) as Uint<64>;.
| Operator | Name | Operand Requirement |
|---|---|---|
== | Equal | Any two values with types in a subtype relation |
!= | Not equal | Any two values with types in a subtype relation |
< | Less than | Both operands must be Uint (not Field) |
<= | Less or equal | Both operands must be Uint (not Field) |
> | Greater than | Both operands must be Uint (not Field) |
>= | Greater or equal | Both operands must be Uint (not Field) |
Relational operators do not work on Field. Cast to Uint first: (f as Uint<64>) > 0.
| Operator | Name | Behavior |
|---|---|---|
&& | Logical AND | Short-circuit; evaluates right only if left is true |
|| | Logical OR | Short-circuit; evaluates right only if left is false |
! | Negation | Unary prefix; flips true to false and vice versa |
All operands must have type Boolean.
For full details on operator type rules, conditional expressions, literal typing, and anonymous circuits (lambdas), see references/operators-and-expressions.md.
Compact uses the as keyword for casts. TypeScript-style angle-bracket casts are not supported.
| From | To | Kind | Example |
|---|---|---|---|
Uint<0..m> | Field | Static (always safe) | myUint as Field |
Uint<0..m> | Uint<0..n> (m <= n) | Static (widening) | small as Uint<64> |
Uint<0..m> | Uint<0..n> (m > n) | Checked (runtime) | big as Uint<8> |
Field | Uint<0..n> | Checked (runtime) | f as Uint<64> |
Field | Boolean | Conversion | f as Boolean (0 -> false, else true) |
Field | Bytes<N> | Conversion (checked) | f as Bytes<32> |
Boolean | Uint<0..n> | Conversion | flag as Uint<0..1> (false -> 0, true -> 1). Checked when n=0 (fails at runtime if true) |
Boolean | Field | Conversion | flag as Field (false -> 0, true -> 1) |
Uint<0..m> | Boolean | Conversion | myUint as Boolean (0 -> false, non-zero -> true) |
Bytes<N> | Field | Conversion (checked) | b as Field |
enum | Field | Conversion | Choice.rock as Field (variant index) |
| From | To | Direct | Via Field |
|---|---|---|---|
Uint<N> | Bytes<M> | amount as Bytes<32> | (amount as Field) as Bytes<32> |
Both routes compile and produce the same result. The Field intermediate step is optional.
For the complete cast path table, see references/operators-and-expressions.md.
All local bindings use const. No let, var, or reassignment. Multiple bindings in one statement: const x = 1, y = x + 1;. Destructuring works for tuples (const [a, b] = pair;) and structs (const { x, y } = point;).
Standard if/else with Boolean condition. No else if keyword -- use nested if/else. Ternary c ? a : b is also available. An if without else is allowed only when the circuit returns [].
Two forms, both requiring compile-time-known bounds:
for (const i of 0..5) -- 0 inclusive to 5 exclusivefor (const v of values) -- each element in orderThe compiler unrolls every loop into flat circuit gates. return inside a loop body is rejected. Nested loops multiply gate count.
| Omitted | Reason | Alternative |
|---|---|---|
while / do-while | Iteration count unknown at compile time | for with fixed range |
| Recursion | Unbounded call depth | Loops or repeated calls with fixed unrolling |
let / var | Mutable state complicates circuit generation | const and shadowing |
switch / match | Not part of the language | Nested if/else or ternary c ? a : b |
try / catch | No exception model | assert(condition, "message") |
break / continue | Every loop iteration must execute | if inside the loop body |
For full documentation on variable declarations, destructuring, shadowing, blocks, return statements, and loop restrictions, see references/control-flow.md.
Every source file begins with a version pragma:
pragma language_version >= 0.22;
Tip: Run
compact compile --language-versionto check which language version your compiler supports.
import CompactStandardLibrary; brings all stdlib types (Counter, Map, Set, MerkleTree, Maybe, Either, CoinInfo) and utility functions into scope.
include "path/to/file"; inserts contents verbatim. The compiler searches for path/to/file.compact in the current directory and COMPACT_PATH directories. No namespace isolation.
Modules group definitions into namespaces. Identifiers are private unless exported. Modules can accept generic type parameters specialized at import time.
| Form | Example | Effect |
|---|---|---|
| Standard library | import CompactStandardLibrary; | Brings all stdlib names into scope |
| Local module | import Runner; | Brings exported names from a local module into scope |
| Prefixed | import Runner prefix Run_; | All imported names get the Run_ prefix |
| Selective | import { test as t } from Test; | Imports specific names with optional renaming |
| Generic | import Identity<Field>; | Specializes a generic module |
| Path-based | import "utils/Auth"; | Imports from a file; file must contain a single module |
Top-level export on circuits, types, and ledger fields defines the public API for TypeScript. Re-export stdlib types with export { Maybe, Either };.
For all import forms, generic modules, path resolution, and file organization patterns, see references/modules-and-imports.md.
All functions are available after import CompactStandardLibrary;.
| Function | Signature | Purpose |
|---|---|---|
persistentHash | persistentHash<T>(value: T): Bytes<32> | SHA-256 based hash; stable across compiler upgrades; safe to store in ledger |
transientHash | transientHash<T>(value: T): Field | Circuit-efficient hash; may change across upgrades; use only within one circuit |
persistentCommit | persistentCommit<T>(value: T, rand: Bytes<32>): Bytes<32> | SHA-256 based commitment with randomness; stable; safe to store in ledger |
transientCommit | transientCommit<T>(value: T, rand: Field): Field | Circuit-efficient commitment with randomness; use only within one circuit |
Use persistent variants when storing in ledger or comparing across transactions. Use transient variants for in-circuit intermediates where lower cost matters.
| Function | Signature | Purpose |
|---|---|---|
degradeToTransient | degradeToTransient(x: Bytes<32>): Field | Convert persistent-domain Bytes<32> to transient-domain Field |
upgradeFromTransient | upgradeFromTransient(x: Field): Bytes<32> | Convert transient-domain Field back to persistent-domain Bytes<32> |
Use these when mixing persistent and transient operations in a single circuit.
| Function | Signature | Purpose |
|---|---|---|
pad | pad(length, value): Bytes<N> | Create fixed-size Bytes<N> from a string literal; pads with zero bytes; both args must be literals |
disclose | disclose(value: T): T | Mark a witness-derived value as publicly visible; required for ledger writes, cross-contract calls, and returns |
assert | assert(condition: Boolean, message: string): [] | Abort the transaction if condition is false; the only error-handling mechanism |
default | default<T>: T | Return the default value for any type (false, 0, all-zero bytes, first enum variant, etc.); keyword expression, not a function call |
disclose is required whenever a witness-derived value is stored in the public ledger, returned from an exported circuit, or passed to another contract via a cross-contract call. Commitments protect their inputs and the commitment result from disclosure. No disclose() wrapper is needed, even when storing the commitment in the ledger.
For full documentation on each function including examples, disclosure rules, and persistent vs. transient guidance, see references/stdlib-functions.md.
Common wrong-to-correct patterns:
| Wrong | Correct |
|---|---|
ledger { field: Type; } | export ledger field: Type; |
circuit fn(): Void | circuit fn(): [] |
pragma >= 0.22.0 | pragma language_version >= 0.22; |
Choice::rock | Choice.rock |
public_key(sk) | persistentHash<Vector<2, Bytes<32>>>([pad(32, "myapp:pk:"), sk]) |
counter.value() | counter.read() |
if (witness_val == x) | if (disclose(witness_val == x)) |
witness fn(): T { body } | witness fn(): T; (declaration only) |
For the complete compiler error reference, debugging strategies, and detailed explanations of disclosure and cast errors, see references/troubleshooting.md.
| Topic | Reference File |
|---|---|
| Type system: primitives, opaque, collections, custom types, defaults, subtyping, TypeScript mappings | references/types-and-values.md |
| Arithmetic, comparison, boolean operators, cast path table, conditional expressions, literals, lambdas | references/operators-and-expressions.md |
| const declarations, if/else, for loops, return, blocks, destructuring, what does not exist | references/control-flow.md |
| Pragma, include, modules, import forms, exports, file organization patterns | references/modules-and-imports.md |
| persistentHash, transientHash, persistentCommit, transientCommit, pad, disclose, assert, default | references/stdlib-functions.md |
| Compiler error reference, wrong-to-correct syntax, non-existent functions, debugging strategies | references/troubleshooting.md |