Composable zero-knowledge primitives for smart accounts — built with Noir
A suite of Noir circuit libraries and executable circuits for privacy-preserving identity, threshold signatures, and programmable access control. Designed to integrate natively with Safe and Nexus (ERC-7579) smart accounts via the ERC-8039 proof verification standard.
Traditional smart account authorization leaks sensitive data on-chain: signer addresses, approval thresholds, and access policies are all publicly visible. This kit replaces those on-chain disclosures with zero-knowledge proofs.
Only a cryptographic commitment (a Merkle root called stateRoot) is stored on-chain. The ZK proof proves that the authorization policy was satisfied — without revealing which signers approved, what the full policy is, or any other sensitive details.
zk-smart-account-kit/
├── Nargo.toml # Noir workspace manifest
│
├── lib/ # Reusable circuit libraries (type = "lib")
│ ├── zk_signer/ # ECDSA address recovery inside ZK
│ ├── zk_labels/ # Privacy-preserving identity / role naming
│ ├── zk_multisig/ # M-of-N threshold signature logic
│ └── zk_scope/ # Programmable transaction access control
│
└── circuits/ # Executable circuits (type = "bin")
├── zk_signers/ # Single-signer ECDSA authorization circuit
├── zk_multi_sig_ecdsa_private_state_validation/ # State commitment validation circuit
├── zk_multi_sig_ecdsa/ # Main threshold signature circuit
├── zk_label_binding/ # Label-to-signer binding circuit
├── zk_multi_sig_label_binding/ # Multi-sig + label binding combined
└── zk_scope_validation/ # Transaction scope policy circuit
The cryptographic foundation. Verifies secp256k1 ECDSA signatures inside a Noir circuit, recovers the Ethereum address, and encodes it as a BN254 field element. Used by every other library and circuit in this kit.
Key exports:
verify_ecdsa_get_address(pubkey, sig, hash)— ECDSA verification + address recoverysigner_leaf(address)— Poseidon1 leaf hash of a signer addressaddress_to_field(address)— 20-byte address → BN254 field elementcmp_gt(a, b)— constant-time comparison (used for anti-replay ordering)
Binds human-readable labels (e.g. "cfo", "auditor", "treasury") to Signer identifier(e.g. Ethereum addresse) in a Merkle tree. Proves label membership without revealing the full registry.
Dependencies: binary_merkle_root, poseidon
Key exports:
verify_label_membership(label, address, root, proof)— Merkle membership prooflabel_leaf(label, address)— Poseidon leaf hash for a label-address binding
Implements M-of-N threshold signature logic. Verifies that M distinct authorized signers (from a committed signer set) have signed a given message, without revealing their identities.
Key constants:
MAX_SIGNERS = 5,MAX_THRESHOLD = 5,SIGNERS_MAX_DEPTH = 4
Key exports:
compute_state_root(signers_root, threshold)— reconstructs the on-chainstateRootverify_multi_sig(signers, sigs, hash, threshold, signers_root, proof_lengths, proof_indices, proof_siblings, state_root)— full M-of-N check
Defines per-account transaction authorization rules (allowed targets, calldata patterns, value ranges). Proves that a transaction complies with a committed policy without disclosing the full rule set.
| Circuit | Libraries used | Purpose |
|---|---|---|
zk_signers |
zk_signer |
Prove a single authorized signer approved a transaction |
zk_multi_sig_ecdsa_private_state_validation |
zk_signer, zk_multisig |
Prove a signer set + threshold commitment is valid |
zk_multi_sig_ecdsa |
zk_signer, zk_multisig |
Prove M-of-N signers approved a transaction |
zk_label_binding |
zk_signer, zk_labels |
Prove a label is bound to a signer in the registry |
zk_multi_sig_label_binding |
zk_signer, zk_labels, zk_multisig |
Prove M-of-N signers with label membership |
zk_scope_validation |
zk_scope |
Prove a transaction satisfies the committed scope policy |
The circuits in this kit produce proofs that are verified on-chain via the ERC-8039 interface — a proof-system-agnostic standard analogous to EIP-1271 for ZK proofs.
Deploy a ZKMultiSigEcdsaFactory and use the resulting proxy as a Safe owner. The proxy stores stateRoot and verifier as immutables and delegates all calls to the singleton, which verifies the ZK proof via ERC-8039.
Install ZKMultiSigValidator as a validator module on any ERC-7579 account. Each account stores its own stateRoot. The module validates ERC-4337 UserOperations by verifying a ZK proof.
The Solidity adapter contracts live in the microchain-zk-signers repository.
| Tool | Version | Install |
|---|---|---|
| Noir / nargo | 1.0.0-beta.18 |
noirup -v 1.0.0-beta.18 |
| Barretenberg / bb | 3.0.0-nightly.20260102 |
bbup -v 3.0.0-nightly.20260102 |
# From the workspace root — compiles every lib and circuit
nargo build# Run all test functions across the workspace
nargo test
# Run tests for a single circuit with output
nargo test --package zk_multi_sig_ecdsa --show-outputThe Prover.toml file in each circuit directory provides the private inputs.
cd circuits/zk_multi_sig_ecdsa
nargo execute
# Produces: target/zk_multi_sig_ecdsa.gzbb prove \
-b ./target/zk_multi_sig_ecdsa.json \
-w ./target/zk_multi_sig_ecdsa.gz \
-o ./target/proof
# Produces: target/proof/proofbb write_vk \
-b ./target/zk_multi_sig_ecdsa.json \
-o ./target/vk
# Produces: target/vk/vkbb verify \
-k ./target/vk/vk \
-p ./target/proof/proof
# Exits 0 on successReplace <circuit> with any circuit name (e.g. zk_multi_sig_ecdsa, zk_scope_validation, ...).
CIRCUIT=zk_multi_sig_ecdsa
cd circuits/$CIRCUIT
nargo build
nargo execute
bb prove -b ./target/$CIRCUIT.json -w ./target/$CIRCUIT.gz -o ./target/proof
bb write_vk -b ./target/$CIRCUIT.json -o ./target/vk
bb verify -k ./target/vk/vk -p ./target/proof/proofAll circuits target the BN254 scalar field and use:
- Poseidon1 for signer leaves; Poseidon2 for state roots, label leaves, scope rule leaves
- Binary Merkle Tree (
binary_merkle_root) for membership proofs - secp256k1 for ECDSA signature verification
- UltraHonk / Barretenberg as the default backend
MIT — see LICENSE