Quick Start
Build your first zero-knowledge proof in 2 minutes.
Prerequisites
- Node.js 16+ and npm 7+
- Basic TypeScript/JavaScript knowledge
- 5 minutes of your time
Step 1: Install
Choose your package based on your needs:
For most use cases (membership proofs)
npm i @zk-kit/imt
For memory-constrained environments
npm i @zk-kit/lean-imt
For key-value storage with proofs
npm i @zk-kit/smt
Peer Dependencies
Some packages require additional dependencies (like hash functions). Check the package's npm page for requirements. For this example, you may need:
npm i poseidon-lite # for IMT/LeanIMT examples
Step 2: Create Your First Proof
// proof-demo.ts
import { IMT } from "@zk-kit/imt"
import { poseidon2 } from "poseidon-lite"
// 1. Create a Merkle tree
const tree = new IMT(poseidon2, 16, 0, 2)
// 2. Add members (e.g., user identities)
const members = [
BigInt("123456789"),
BigInt("987654321"),
BigInt("555555555")
]
members.forEach(member => tree.insert(member))
// 3. Generate a proof (proves membership without revealing which member)
const memberIndex = 0 // Only the prover knows this
const proof = tree.createProof(memberIndex)
// 4. Verify the proof (anyone can do this)
const isValid = tree.verifyProof(proof)
console.log("Proof valid?", isValid) // true
// 5. The proof can be sent to a smart contract or backend
console.log("Proof data:", {
root: tree.root,
leaf: proof.leaf,
siblings: proof.siblings,
pathIndices: proof.pathIndices
})
Step 3: Run It
# If using TypeScript
npx ts-node proof-demo.ts
# If using JavaScript, rename to .js and run
node proof-demo.js
What Just Happened?
- Tree Creation: You created a Merkle tree that can hold membership data
- Member Addition: Added identities without storing personal information
- Proof Generation: Created a cryptographic proof of membership
- Anonymous Verification: Verified membership without revealing identity
Interactive Example
Try it in your browser:
<!DOCTYPE html>
<html>
<head>
<title>ZK-Kit Demo</title>
</head>
<body>
<h1>Zero-Knowledge Proof Demo</h1>
<button id="run">Run Demo</button>
<pre id="output"></pre>
<script type="module">
import { IMT } from 'https://esm.sh/@zk-kit/imt'
import { poseidon2 } from 'https://esm.sh/poseidon-lite'
document.getElementById('run').addEventListener('click', () => {
const tree = new IMT(poseidon2, 16, 0, 2)
// Add members
tree.insert(BigInt(1))
tree.insert(BigInt(2))
tree.insert(BigInt(3))
// Create and verify proof
const proof = tree.createProof(0)
const isValid = tree.verifyProof(proof)
document.getElementById('output').textContent =
`Proof valid: ${isValid}\nRoot: ${tree.root}\nLeaves: ${tree.leaves.length}`
})
</script>
</body>
</html>
Real-World Example: Anonymous Voting
import { IMT } from "@zk-kit/imt"
import { poseidon2 } from "poseidon-lite"
class AnonymousVoting {
private tree: IMT
private votes = new Map<string, string>()
constructor() {
this.tree = new IMT(poseidon2, 20, 0, 2)
}
// Register a voter
registerVoter(voterCommitment: bigint): number {
return this.tree.insert(voterCommitment)
}
// Cast anonymous vote
castVote(
voterIndex: number,
nullifier: string,
candidate: string
): boolean {
// Create proof of voter eligibility
const proof = this.tree.createProof(voterIndex)
// Verify proof
if (!this.tree.verifyProof(proof)) {
throw new Error("Invalid voter")
}
// Check if already voted (prevent double voting)
if (this.votes.has(nullifier)) {
throw new Error("Already voted")
}
// Record vote
this.votes.set(nullifier, candidate)
return true
}
// Get results
getResults(): Map<string, number> {
const results = new Map<string, number>()
for (const candidate of this.votes.values()) {
results.set(candidate, (results.get(candidate) || 0) + 1)
}
return results
}
}
// Usage
const voting = new AnonymousVoting()
// Register voters
const voter1 = voting.registerVoter(BigInt("0x123..."))
const voter2 = voting.registerVoter(BigInt("0x456..."))
const voter3 = voting.registerVoter(BigInt("0x789..."))
// Vote anonymously
voting.castVote(voter1, "nullifier-1", "Alice")
voting.castVote(voter2, "nullifier-2", "Bob")
voting.castVote(voter3, "nullifier-3", "Alice")
// Get results
console.log(voting.getResults())
// Map(2) { 'Alice' => 2, 'Bob' => 1 }
Next Steps
Learn the Concepts
Continue Building
- Your First ZK Proof - Complete step-by-step guide
- Development Setup - Set up your environment
- TypeScript Setup - Configure TypeScript
Explore Packages
- Packages Overview - See all available packages
- Choosing a Package - Decision guide
- Ecosystem Projects - See real-world usage
Common Patterns
Pattern 1: Membership Proof
// Create a set of authorized users
const authorizedUsers = new IMT(poseidon2, 16, 0, 2)
// Add users
const myIndex = authorizedUsers.insert(myCommitment)
// Prove membership without revealing identity
const proof = authorizedUsers.createProof(myIndex)
// Verify
if (authorizedUsers.verifyProof(proof)) {
console.log("Access granted!")
}
Pattern 2: Double-Spend Prevention
// Use nullifiers to prevent reuse
const nullifier = poseidon2([secret, appId])
// Check if nullifier was used
if (usedNullifiers.has(nullifier.toString())) {
throw new Error("Already used")
}
// Mark as used
usedNullifiers.add(nullifier.toString())
Pattern 3: On-Chain Verification
// Generate proof off-chain
const proof = tree.createProof(index)
// Send to smart contract
await contract.verify(
proof.root,
proof.leaf,
proof.siblings,
proof.pathIndices
)
Common Questions
Q: Which package should I use? A: See our package selection guide
Q: How do I integrate with Ethereum? A: Check the Semaphore protocol for smart contract examples
Q: Is this production-ready? A: Yes! See who's using ZK-Kit
Q: How do I generate nullifiers? A: Use a hash of your secret and a unique identifier:
const nullifier = poseidon2([mySecret, appIdentifier])
Q: Can I use this in the browser? A: Yes! See the Installation guide for browser setup
Performance Tips
Tree Size Selection
// For 65,536 members (2^16)
const tree = new IMT(poseidon2, 16, 0, 2)
// For 1 million members (2^20)
const tree = new IMT(poseidon2, 20, 0, 2)
// For memory-constrained (same capacity, less memory)
const tree = new LeanIMT((a, b) => poseidon2([a, b]))
Batch Operations
// Instead of individual inserts
tree.insert(1n)
tree.insert(2n)
tree.insert(3n)
// Batch insert (if your use case allows)
const members = [1n, 2n, 3n]
const tree = new IMT(poseidon2, 16, 0, 2, members)
Get Help
Explore More
Ready to build something amazing? Let's go! 🚀