Mina o1js Circuit Design Patterns Skill
Use when
Use this skill when designing or reviewing o1js constraint logic: witnesses, assertions, hashes, signatures, Merkle proofs, range checks, conditional logic, fixed arrays and efficient provable data structures.
Shared references
If installed from the full package, shared resources live in ../mina-protocol-agent/references/. Load ../mina-protocol-agent/references/INDEX.md only when task cards, examples, templates, source links or deeper checklists are needed.
Compatibility gate
Before a version-sensitive claim, record the target network, active protocol era, exact o1js and signer versions, wallet/CLI versions, endpoints, and the origin of verification keys and proof caches. Load ../mina-protocol-agent/references/playbooks/NETWORK_ERA_AND_O1JS_COMPATIBILITY.md for Berkeley/Mesa and o1js 3 migration rules. If the era is unknown, label the guidance unverified rather than guessing from package or endpoint names.
Current circuit API checks
- Use
Poseidon.hashAnyLength()only when the statement is variable-length; it is not interchangeable with fixed-lengthPoseidon.hash(). - Bind schema version, domain, field order, optional-field presence, and collection length.
- Prefer current conditional assertion helpers such as
Provable.assertEqualIf()when supported by the pinned version, and test both active and inactive branches. - Treat verification keys, recursive proofs, and caches as version/era artifacts that must be regenerated across incompatible protocol changes.
Core principle
A circuit proves only what is constrained. Private data, helper functions and TypeScript logic do not matter unless they create provable constraints or bind to public input/output/state.
First output
Circuit goal:
Public inputs:
Private witnesses:
Public outputs:
Assertions:
Hash/signature domains:
Range checks:
Data structure sizes:
Constraint/performance risks:
Pattern checklist
Witness pattern
For every witness:
- what value does prover choose?
- why is it allowed to be private?
- what constrains it?
- what public value/state does it bind to?
- can wrong witness pass?
Hash pattern
Use domain separation:
const h = Poseidon.hash([DOMAIN, appId, actionId, value1, value2]);
Check field order, schema version and packing.
Signature pattern
A signature should bind all values that matter:
domain, contract address, network/app id, action, sender/subject, recipient, amount, nonce/nullifier, expiry
Range pattern
Use bounded integer types for counters and amounts:
const amount = UInt64.from(rawAmount);
amount.assertGreaterThan(UInt64.from(0));
Do not rely on Field arithmetic for normal integer semantics unless you explicitly handle modular behavior.
Conditional logic pattern
Use JS if only for compile-time/static structure. Use Provable.if() to select between provable values. Avoid side effects inside conditional branches.
Fixed array pattern
Provable arrays need fixed sizes. Make max sizes explicit and document why they are safe.
Merkle pattern
A correct Merkle update normally proves:
- old leaf belongs to old root;
- old root equals on-chain state or public input;
- new leaf is computed from constrained values;
- new root is computed with same witness/path;
- owner/index/key is bound.
Common design mistakes
- witness is only hashed into a new state but never checked against old state;
- public output omits a value the verifier/contract trusts later;
- auxiliary output is treated as verified;
- signature message does not include recipient or contract address;
- hash has no domain separation;
Fieldused for amount without UInt/range;- provable array length is assumed dynamic;
Provable.ifbranches mutate variables.
Output format
Circuit design summary
Constraint map
Bad patterns found
Recommended pattern
Code sketch
Tests to prove constraints are active
Constraint/performance notes