Help us improve
Share bugs, ideas, or general feedback.
From compact-core
This skill should be used when the user asks about Compact circuit costs, ZK proof generation costs, gate counts, loop unrolling behavior, hash function cost tradeoffs (transientHash vs persistentHash), commitment function costs (transientCommit vs persistentCommit), pure circuit optimization benefits, vector operation costs (map/fold/slice unrolling), compiler optimization passes, runtime gas model (readTime, computeTime, bytesWritten, bytesDeleted), ledger state storage costs, privacy-cost tradeoffs, or how to write cost-efficient Compact smart contracts on Midnight.
npx claudepluginhub devrelaicom/midnight-expert --plugin compact-coreHow this skill is triggered — by the user, by Claude, or both
Slash command
/compact-core:compact-circuit-costsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill covers the cost model for Compact smart contracts across three dimensions: circuit/proving costs, runtime gas costs, and state costs. For loop and vector syntax details, see `compact-language-ref`. For ADT operation semantics, see `compact-ledger`. For hash and commitment function signatures, see `compact-standard-library`. For pure circuit declarations, see `compact-structure`.
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.
This skill should be used when the user asks about zero-knowledge proofs, ZK SNARKs, witness data, prover/verifier roles, constraint systems, proof generation, proof verification, privacy boundaries, or how Midnight uses ZK cryptography for transaction privacy and data protection.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Share bugs, ideas, or general feedback.
This skill covers the cost model for Compact smart contracts across three dimensions: circuit/proving costs, runtime gas costs, and state costs. For loop and vector syntax details, see compact-language-ref. For ADT operation semantics, see compact-ledger. For hash and commitment function signatures, see compact-standard-library. For pure circuit declarations, see compact-structure.
Midnight contracts have three independent cost dimensions. Optimizing one may increase another.
| Dimension | What It Affects | Key Driver | Paid By |
|---|---|---|---|
| Circuit/Proving | Proof generation time (user-side latency) | Gate count in ZK circuit | Transaction submitter |
| Runtime/Gas | Transaction fees | readTime, computeTime, bytesWritten, bytesDeleted | Transaction submitter |
| State | Ongoing storage requirements | Ledger type choice and data volume | Network |
All for loops are fully unrolled at compile time. The range a..b produces b - a iterations (upper bound is exclusive). Each iteration produces a complete copy of the loop body in the circuit.
| Pattern | Gate Cost | Example |
|---|---|---|
for (const i of 0..N) { body } | N × body_cost | 0..100 with 1 add = 100 additions |
Nested: for i of 0..A { for j of 0..B { body } } | A × B × body_cost | 0..10 × 0..10 = 100× body |
| Triple nested | A × B × C × body_cost | 0..9 × 0..9 × 0..9 = 729× body |
Nested loops are the most common source of circuit size explosions. A 4-level nested loop with 9 iterations each produces 6,561 copies of the innermost body.
| Function | Circuit Cost | Return Type | Safe for Ledger State? | Protects from Disclosure? |
|---|---|---|---|---|
transientHash<T> | LOW (circuit-native) | Field | No | No |
persistentHash<T> | HIGH (SHA-256) | Bytes<32> | Yes | No |
transientCommit<T> | LOW (circuit-native) | Field | No | Yes |
persistentCommit<T> | HIGH (SHA-256) | Bytes<32> | Yes | Yes |
Rule of thumb: Use transient* for in-circuit consistency checks. Use persistent* for anything stored in ledger state. Use degradeToTransient()/upgradeFromTransient() to convert between domains.
The pure modifier signals that a circuit should have no side effects. The compiler's identify-pure-circuits pass checks for ledger access, witness calls, and calls to impure circuits. The enforcement is contextual — the pure modifier primarily affects whether the circuit generates ZK proving keys and appears in pureCircuits exports.
| Property | Impure Circuit | Pure Circuit |
|---|---|---|
| ZK proving keys generated | Yes | No |
| zkir generated | Yes | No |
| State transcript entries | Yes | No |
| Can be fully inlined | Sometimes | Always |
| Requires on-chain transaction | Yes | No |
Declare with pure circuit or export pure circuit to enforce purity and make the circuit available in TypeScript via pureCircuits.
map, fold, and slice are all unrolled at compile time, just like loops.
| Operation | Cost | Example |
|---|---|---|
map(f, vector) | length × f_cost | map((x) => x + x, vec10) = 10 additions |
fold(f, init, vector) | length × f_cost | fold((acc, v) => acc + v, 0, vec10) = 10 additions |
slice<N>(vector, offset) | Zero additional gates | Compile-time extraction |
Spread [...vector] | Zero additional gates | Compile-time operation |
Complex anonymous circuits inside map/fold multiply: every statement in the body is replicated per element.
The compiler performs these circuit-phase optimization passes automatically (in order). These are a subset of the full compilation pipeline (~41 passes total across 10 frontend, 15 analysis, 11 circuit, 2 ZKIR, 2 TypeScript, and 1 metadata phase):
3 + 4 → 7)x + 0 → xassert(true, ...)These cascade: copy propagation creates dead bindings, constant folding enables copy propagation, etc. Non-literal vector indexes resolve through this cascade (e.g., v[2 * i] where i = 4 becomes v[8]).
| Dimension | What Contributes | Optimization Strategy |
|---|---|---|
readTime | Ledger state reads (.read(), .lookup(), .member()) | Cache reads in local variables; avoid redundant state queries |
computeTime | Circuit computation complexity | Reduce gate count; use pure circuits where possible |
bytesWritten | Ledger state writes (.insert(), .increment(), field assignments) | Batch writes; minimize state mutations |
bytesDeleted | Ledger state deletions (.remove(), .resetToDefault()) | Delete only when necessary |
Circuits can have a gasLimit set; execution fails if the limit is exceeded.
| Ledger Type | State Size | Growth Pattern | Privacy | Relative Cost |
|---|---|---|---|---|
Direct field (Field, Bytes<N>, etc.) | Fixed | None | Public | Lowest |
Counter | Fixed (Uint<64>) | None | Public | Low |
Map<K, V> | Variable | Grows with entries | Public (keys + values visible) | Medium |
Set<T> | Variable | Grows with entries | Public (elements visible) | Medium |
List<T> | Variable | Grows with entries | Public | Medium |
MerkleTree<N, T> | Fixed (2^N capacity) | Sparse, lazy (grows on demand) | Insert hides leaf (via leaf_hash()); privacy via membership proofs | Higher |
HistoricMerkleTree<N, T> | Fixed + root history | Sparse, lazy + history | Insert hides leaf (via leaf_hash()); privacy via membership proofs | Highest |
sealed fields eliminate state-write circuit costs entirely since they are set once at construction.
Need to store result in ledger state?
├── Yes → persistentHash / persistentCommit (SHA-256, stable across upgrades)
└── No → Is this an in-circuit consistency check only?
├── Yes → transientHash / transientCommit (circuit-native, much cheaper)
└── No → Need to protect from disclosure?
├── Yes → transientCommit (cheap + disclosure protection)
└── No → transientHash (cheapest option)
Check in order:
persistentHash in loops? Switch to transientHash if result isn't stored in ledger.read()/.lookup() results in local variablespure circuitmap/fold bodies are minimalWhat kind of data?
├── Single numeric value → Counter (cheapest)
├── Key-value lookups → Map<K, V>
│ └── Need nested state? → Map<K, V> where V is any ledger type (Counter, Set<T>, List<T>, MerkleTree, or another Map)
├── Membership checks →
│ ├── Privacy required? → MerkleTree<N, T> (membership proofs hide which leaf)
│ └── No privacy needed? → Set<T> (cheaper, simpler)
├── Ordered sequence → List<T> (front-access only)
└── Single immutable value → sealed ledger field (zero ongoing cost)
| Expensive Pattern | Better Alternative | Why |
|---|---|---|
| Nested loops when a flat loop suffices | Restructure to single loop | Nested loops multiply gate count |
persistentHash inside a loop body | transientHash if result isn't stored | SHA-256 costs many more gates than circuit-native hash |
| Impure circuit that only computes from inputs | Refactor to pure circuit | Avoids zkir/key generation and state transcript |
Repeated .read() / .lookup() on same key | Cache in const and reuse | Each ledger operation has gas cost |
Complex computation in map/fold body | Extract to named pure circuit | Clearer and enables the compiler to optimize better |
| Large MerkleTree when Set would suffice | Use Set<T> if privacy not needed | MerkleTree has higher state cost and requires path proofs |
| Unsealed field used as immutable config | Use sealed ledger | Sealed eliminates state-write circuits entirely |
| Topic | Reference File |
|---|---|
| Gate counts, loop unrolling, hash costs, pure circuits, vector ops, compiler passes, proving benchmarks | references/circuit-proving-costs.md |
| Gas model dimensions, RunningCost, CostModel, gas limits, cost-efficient patterns | references/runtime-gas-costs.md |
| Ledger type cost comparison, privacy-cost tradeoffs, sealed fields, state design, nested ADTs | references/state-costs.md |