Reusable Circom ZK circuit templates: hashing (Poseidon, MiMC, SHA-256), comparators, Merkle trees, nullifiers, voting, and utilities. No dependency on circomlib. Internal security audit applied in 0.5.0.
Add the circuits folder to your include path and include templates:
include "opencircom/circuits/hashing/poseidon.circom";
include "opencircom/circuits/comparators.circom";
include "opencircom/circuits/merkle/merkle_inclusion.circom";
template MyCircuit(levels) {
signal input secret;
signal input pathElements[levels];
signal input pathIndices[levels];
signal output root;
component hasher = Poseidon(1);
hasher.inputs[0] <== secret;
component tree = MerkleInclusionProof(levels);
tree.leaf <== hasher.out;
for (var i = 0; i < levels; i++) {
tree.pathElements[i] <== pathElements[i];
tree.pathIndices[i] <== pathIndices[i];
}
root <== tree.root;
}If you clone or link this repo as opencircom in your project root, compile with:
circom your.circom --r1cs --wasm -o build -l opencircom/circuitsFrom npm:
npm install opencircomOr add to your package.json: "opencircom": "^0.7.0".
-
Add
opencircomas a dependency (see Install). -
In your Circom build step (e.g. Hardhat plugin or script), pass the package circuits as the include path:
circom circuits/YourCircuit.circom --r1cs --wasm -o build -l node_modules/opencircom/circuits
-
Use snarkjs (or your flow) to generate the verifier contract; deploy or import it in your Hardhat tests.
- Add
opencircomvia npm (npm install opencircom) or as a Git submodule:git submodule add https://github.com/jose-blockchain/opencircom.git lib/opencircom
- In a script, run
circomwith the opencircom circuits on the include path:- npm:
-l node_modules/opencircom/circuits - submodule:
-l lib/opencircom/circuits
- npm:
- Use snarkjs (or circom) to generate the Solidity verifier; put the generated
.solinsrc/. - Run
forge buildandforge test; your Solidity code calls the verifier contract as usual.
Starter repos that wire opencircom with a build pipeline and verifier contracts:
- opencircom-hardhat-boilerplate — Circom + snarkjs + Hardhat; compile circuits, generate verifier, test from JS.
- opencircom-foundry-boilerplate — Circom + snarkjs + Foundry; verifier in
src/,forge testfor Solidity tests.
Use them to jump-start a project without configuring the toolchain from scratch.
- Documentation site: docs/index.html (promo) and docs/documentation.html (full reference with searchable template catalog).
- API markdown: generated from circuit comments (not committed). To build:
npm run docsOutput markdown is written to docs/api/; the HTML documentation page at docs/documentation.html is regenerated at the same time.
| Category | Template | Description |
|---|---|---|
| Hashing | Poseidon(nInputs) |
Hades Poseidon (configurable width). |
| Hashing | PoseidonEncrypt() |
Symmetric encryption: ciphertext = plaintext + Poseidon(key); decryption off-chain. |
| Hashing | MiMC7(nrounds), MultiMiMC7(nInputs, nRounds) |
MiMC-7. |
| Hashing | Sha256(nBits) |
SHA-256 (FIPS 180-4). Input length in bits; padding is applied. |
| Comparators | LessThan(n), GreaterThan(n), IsEqual(), IsZero(), AssertNotEqual() |
Range, equality, aliasing-safe (force a ≠ b). |
| Comparators | StrictNum2Bits(n) |
Num2Bits with in ∈ [0, 2^n−1] enforced. |
| Comparators | RangeProof(n) |
Prove a ≤ x ≤ b (inputs x, a, b; n-bit range). |
| Bitify | Num2Bits(n), Bits2Num(n) |
Bit decomposition (see also compconstant.circom, aliascheck.circom). |
| Gates | AND, OR, NOT, XOR, MultiAND(n) |
Boolean gates. |
| Utils | Mux1, Mux2, MuxN / SelectByIndex(N, nBits), Switcher |
Multiplexer and conditional swap; N-way select by index. |
| Arithmetic | Sum(n), InnerProduct(n), DivRem(n), ExpByBits(n) |
Sum, dot product; safe div/rem; field exponentiation (exp as bits). |
| Utils | PadBits(n, target), OneOfN(n), IndexOf(N, nBits) |
Zero-pad bits; 1 if value in array; prove index where arr[i]==value. |
| Utils | Min2(n), Max2(n) |
Minimum / maximum of two n-bit values. |
| Utils | MinN(n, N), MaxN(n, N) |
Minimum / maximum of an array of N n-bit values. |
| Utils | AllEqual(n), CountMatches(N) |
1 if all array elements equal; count of arr[i]==value. |
| Utils | Tally(numChoices, numVotes) |
Vote counts per choice (votes in [0, numChoices−1]); for anonymous tally. |
| Utils | PadBits10Star(n, totalBits) |
Pad bits with 1 then zeros (hash-style padding). |
| Utils | PadPKCS7(blockBytes) |
PKCS#7-style padding: bytes from numUsed to end equal (blockBytes − numUsed); for hashing/symmetric crypto. |
| Utils | ConditionalSelect() |
Private if-then-else: out = condition ? a : b (condition 0/1). |
| Utils | BalanceProof(n) |
Prove balance ≥ amount and newBalance = balance − amount; for transfer proofs. |
| Utils | VoteInAllowlist(n) |
1 if vote in allowedChoices (allowlist voting). |
| Merkle | MerkleInclusionProof(levels) |
Binary Merkle inclusion. |
| Merkle | AllowlistMembership(levels) |
Prove identity in allowlist (Poseidon(1)(identity) + Merkle path); use with nullifier on-chain. |
| Merkle | SparseMerkleInclusion(levels), SparseMerkleExclusion(levels) |
Sparse Merkle: prove leaf at key equals value, or is empty. |
| Merkle | IncrementalMerkleInclusion(levels) |
Append-only tree: prove leaf at numeric index. |
| Merkle | MerkleUpdateProof(levels) |
Prove old root → new root by changing one leaf on the same path. |
| Set membership | AccumulatorMembership(n) |
Field-based accumulator: prove witness^member = accumulator (uses PoEVerify). Build A = g^(∏ elements); witness for e is g^(∏/e). |
| Identity | IdentityCommitment(), SemaphoreMembership(levels) |
Semaphore-style commitment = Poseidon(identity, secret); prove (identity, secret) in allowlist tree. Use with Nullifier. |
| Identity | Nullifier(domainSize) |
Nullifier hash for double-spend prevention. |
| Voting | VoteCommit(numChoices), VoteCommitAllowlist(n), VoteReveal() |
Commit-reveal; allowlist variant constrains choice to allowedChoices[n]; double-vote prevention (nullifier-based). |
| MACI | MACICommandPack(), MACICommandUnpack(), MACICommandHash(), MACISharedKeyHash() |
MACI v1 command packing and hashes (spec). |
| MACI | MACIMessageEncrypt(), MACIMessageDecrypt(), MACIMessageHash() |
Additive Poseidon keystream message encryption (composable; full DuplexSponge is coordinator-side in MACI). |
| MACI | MACIVoteCommit(), MACIVoteDecryptVerify(n) |
Encrypt command+signature plaintext; coordinator verifies decrypted vote in allowlist with nonce/poll checks. |
| String & data | Utf8Validation(n), FixedStringMatch(n), BytesAllInRange(n, lo, hi), ByteInRange(lo, hi) |
UTF-8 byte-sequence validation; fixed string equality; bytes in [lo, hi] (e.g. digits). |
- Arithmetic: Sum(n), InnerProduct(n), DivRem(n) (safe div/rem, all operands range-checked), ExpByBits(n), AssertNotEqual().
- Set membership: Merkle allowlist (
AllowlistMembership), sparse Merkle inclusion/exclusion, incremental and update proofs; accumulator-based (AccumulatorMembership(n)— witness^member = accumulator, uses PoEVerify). - Payments: Balance proof (
BalanceProof(n)— balance ≥ amount, newBalance = balance − amount). - Padding: PadBits, PadBits10Star (1||0*), PadPKCS7 (block bytes).
- Symmetric encryption: Poseidon-based (
PoseidonEncrypt()— ciphertext = plaintext + Poseidon(key); decryption off-chain). - Identity & credentials: Semaphore-style commitment (
IdentityCommitment(),SemaphoreMembership(levels)— prove (identity, secret) in allowlist; use with Nullifier). Age/threshold proofs: useRangeProof(n)(a = threshold, b = max). - Voting: Commit-reveal with nullifier, allowlist variant (
VoteCommitAllowlist), tally (Tally). - MACI: Command pack/unpack/hash, shared-key binding, message encrypt/decrypt,
MACIVoteCommit,MACIVoteDecryptVerify(MACI v1 layout; EdDSA/ECDH off-circuit). - String & data: UTF-8 validation (
Utf8Validation(n)), fixed string match (FixedStringMatch(n)), bytes-in-range (BytesAllInRange(n, lo, hi),ByteInRange(lo, hi)).
Planned or community-requested; not yet implemented:
- Hashing: Pedersen (Baby Jubjub) and Keccak-256 are deferred (high constraint cost / implementation effort); Poseidon, MiMC, and SHA-256 remain the supported primitives.
- Signatures: EdDSA verify (Baby JubJub) and ECDSA verify (secp256k1) are deferred (high constraint cost / implementation effort).
- Encryption: ElGamal and ECDH shared secret deferred (curve cost). Symmetric (Poseidon-based) implemented as
PoseidonEncrypt(). - Identity & credentials: Semaphore-style commitment implemented (
IdentityCommitment,SemaphoreMembership). Selective disclosure buildable from RangeProof + Merkle + commitments. Age/threshold: useRangeProof(n). - Set membership: Accumulator-based membership implemented (
AccumulatorMembership(n), uses PoEVerify). Merkle/sparse already done. - Payments: Confidential transfer and mixer (deposit/withdraw) deferred to application repos; build with Merkle, Nullifier, BalanceProof from this lib.
- String & data: UTF-8 validation and simple fixed/range patterns implemented; full regex and JSON field extraction deferred.
- Utilities: Other padding or encoding schemes (e.g. ISO padding, length-prefix, base64 in-circuit) deferred; PadBits, PadBits10Star, PadPKCS7 remain.
Contributions welcome; open an issue to propose or prioritize.
- Range checks: Use
StrictNum2Bits(n)orRangeProof(n)for untrusted inputs;LessThan(n)assumes inputs < 2^n. - Merkle:
pathIndices[i]are constrained binary in-circuit;Switcherconstrainsselto {0,1}. - Nullifier: Use a unique
externalNullifierper action to avoid cross-action replay. - Hashing: Poseidon uses standard Hades parameters (same as circomlib); constants in
circuits/hashing/poseidon_constants.circom. - Audit: An internal security audit (0.5.0) fixed binary constraints and range checks in Switcher, ForceEqualIfEnabled, IncrementalMerkleInclusion, DivRem, and PadPKCS7. See CHANGELOG for details.
See SECURITY.md for more.
Requires circom 2.x on PATH (peer dependency; not bundled).
npm install opencircom
# Include path for -l flag
npx opencircom path
# Compile your circuit (adds opencircom/circuits to -l automatically)
npx opencircom compile circuits/MyCircuit.circom -o build
# Compile all library test wrappers (used by npm test)
npx opencircom compile --all-test -o build
# List exported templates with descriptions
npx opencircom list
# Scaffold a minimal Poseidon commitment circuit + mocha test
npx opencircom init my_circuitFrom this repo, npm run compile:test compiles every file in test/circuits/ before tests run.
Tests use real ZK where applicable: circuits are compiled with Circom, then a small Powers of Tau and zkey are generated, and a Groth16 proof is created and verified with snarkjs (no mocks).
Coverage (153+ tests): Poseidon, PoseidonEncrypt, SHA-256, Comparators, IdentityCommitment, SemaphoreMembership, AccumulatorMembership, Gates, Bitify, Merkle (inclusion, AllowlistMembership, sparse, incremental, update), MiMC, Mux1/Mux2, MuxN, Arithmetic (incl. PoEVerify), Utils (PadBits, PadBits10Star, PadPKCS7, OneOfN, IndexOf, Min2, Max2, MinN, MaxN, AllEqual, CountMatches, Tally, ConditionalSelect, BalanceProof, VoteInAllowlist), String (Utf8Validation, FixedStringMatch, BytesAllInRange), Switcher, VoteCommitAllowlist, Nullifier, Voting, MACI, and one full Groth16 prove/verify.
npm install
npm testnpm test runs compile:test (all test circuits), then setup:zk (ptau + zkey). First run can take about a minute.
MIT
