https://github.com/zekele-win/zkvault-basic
A minimal, functional zero-knowledge proof project based on zkSNARKs, designed to help developers understand the fundamental workflow of zk applications—including circuit writing, proof generation, and smart contract verification.
https://github.com/zekele-win/zkvault-basic
circom learning-project privacy security solidity web3 zero-knowledge zk-snark
Last synced: 2 months ago
JSON representation
A minimal, functional zero-knowledge proof project based on zkSNARKs, designed to help developers understand the fundamental workflow of zk applications—including circuit writing, proof generation, and smart contract verification.
- Host: GitHub
- URL: https://github.com/zekele-win/zkvault-basic
- Owner: zekele-win
- License: mit
- Created: 2025-04-06T05:22:39.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-04-14T03:31:01.000Z (about 1 year ago)
- Last Synced: 2025-08-23T03:43:57.432Z (10 months ago)
- Topics: circom, learning-project, privacy, security, solidity, web3, zero-knowledge, zk-snark
- Language: TypeScript
- Homepage:
- Size: 87.9 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ❄️ zkvault-basic
**zkvault-basic** is a minimal, functional zero-knowledge proof project based on zkSNARKs, designed to help developers understand the fundamental workflow of zk applications—including circuit writing, proof generation, and smart contract verification.
This project guides you through building a complete zk application, from ciruits build setup and contract compile to testing and deployment, CLI operations.
---
## 🎯 Project Goals
zkvault-basic is designed as a learning and sharing tool to replicate a typical anonymous deposit/withdrawal scenario:
- Use zkSNARK to enable anonymous deposits and withdrawals to arbitrary wallets
- Combine Circom and Solidity to construct a full zero-knowledge workflow
- Emphasize minimal implementation, focusing on core concepts for easier understanding
This project is a simplified version of Tornado Cash’s basic mechanism—an ideal reference for getting started with zk app development.
---
## ✨ Feature Overview
### Deposit
- The user generates a random secret
- A commitment is derived from this secret
- ETH of a fixed denomination is deposited into _CONTRACT_ using this commitment, enabling anonymous deposit
### Withdrawal
- Provide the original deposit secret
- Generate the corresponding zero-knowledge proof
- Use any wallet to execute the withdrawal and transfer funds to any desired address
---
## Design Philosophy
The purpose of `zkvault` is to achieve the following three core characteristics:
- **Consistency**
- **Security**
- **Privacy** (Not implemented yet)
We will explain these three characteristics by detailing the deposit and withdrawal processes.
### Deposit Process
1. _APP_ prepares to call _CONTRACT_ function `deposit(commitment)`
- _APP_ randomly generates a `secret` (the user ensures it is kept private)
- _APP_ calculates the parameter `commitment = hash(secret)` needed for _CONTRACT_ function `deposit`. `commitment` is public.
2. _CONTRACT_ function `deposit(commitment)` handles the process:
- Ensures that the user's deposit amount (`msg.value`) is correct.
- Saves the `commitment` and marks it as `DEPOSITED` status.
### Withdrawal Process
1. _APP_ prepares to call _CONTRACT_ function `withdraw(pA, pB, pC, pubSignals)`
- Parameter explanation:
- `(pA, pB, pC, pubSignals)` are standard zkSNARK proof parameters.
- `pubSignals[0] = commitment`, `pubSignals[1] = recipient`
- _APP_ uses the `secret` saved during deposit and the specified `recipient` to call _zk-circuit_ `Withdraw(public: commitment, public: recipient, private: secret)`, generating the zkProof (standard components: `pA`, `pB`, `pC`, `pubSignals`).
- The zkProof generated by _zk-circuit_ binds `commitment`, `recipient`, and the algorithm that calculates `commitment`, ensuring that any modification of values in the zkProof cannot be validated by _CONTRACT_ .
- If the `commitment` in the zkProof for the `withdraw` (i.e., `pubSignals[0]`) does not match the `commitment` from the deposit, _CONTRACT_ will reject the `withdraw` — either the verifier fails or the `commitment` status cannot be properly recognized. This ensures **Consistency**.
- Additionally, because the zkProof binds the `recipient`, even if an unexpected failure (e.g., insufficient gas) causes the execution of _CONTRACT_ function `withdraw` to fail, other users cannot steal funds by modifying the `recipient` parameter. Changing the `recipient` would require the _APP_ to regenerate the zkProof, and to do so, the `secret` is needed. This ensures **Security**.
2. _CONTRACT_ function `withdraw(pA, pB, pC, pubSignals)` handles the process:
- Checks the `commitment` status.
- Calls the `verifier` to validate the zkProof (`pA`, `pB`, `pC`, `pubSignals`).
- The `verifier` is the corresponding contract generated when _APP_ compiles _zk-circuit_.
### Risks
Throughout the deposit/withdraw process, to ensure **Consistency**, both `deposit` and `withdraw` expose the `commitment`, which means the account addresses for performing the deposit and withdrawal are linked. This results in a lack of **Privacy**.
---
## 🧱 Project Structure
```bash
.
├── circuits/ # Circom ZK circuits
│ └── ZkVaultBasic.circom # Main circuit implementing deposit/withdraw logic
│
├── contracts/ # Solidity smart contracts
│ ├── ZkVaultBasic.sol # ZK-enabled Vault contract
│ └── ZkVaultBasicVerifier.sol # Auto-generated Groth16 verifier contract
│
├── scripts/ # CLI and deployment scripts
│ ├── cli.ts # Command-line interface for deposit/withdraw testing
│ └── deploy.ts # Deploys contracts to local or testnet environments
│
├── test/ # Tests for circuits and contracts
│ ├── utils.hex.test.ts # Unit tests for hex encoding utilities
│ ├── utils.pedersen.test.ts # Unit tests for Pedersen hash implementation
│ ├── ZkVaultBasic.circom.test.ts # Tests for circuit correctness and witness verification
│ └── ZkVaultBasic.sol.test.ts # Tests for smart contract behavior and proof validation
│
├── types/ # Type declarations for external JS/TS libraries
│ ├── circom_tester.d.ts # Types for circom_tester (circuit tester wrapper)
│ └── ffjavascript.d.ts # Types for ffjavascript (bigint/buffer utils)
│
├── utils/ # Utility modules used across scripts and tests
│ ├── hex.ts # Hex encoding/decoding helpers
│ └── pedersen.ts # Pedersen hash implementation (compatible with circom)
│
├── .mocharc.json # Mocha testing framework configuration
├── hardhat.config.ts # Hardhat config for smart contract compilation/deployment
├── package.json # Project dependencies and scripts
└── tsconfig.json # TypeScript configuration
```
---
## ⚙️ Prerequisites
1. Install Node.js (recommended version: **v22**)
2. Install Circom 2
Installation guide: [https://docs.circom.io/getting-started/installation/](https://docs.circom.io/getting-started/installation/)
```bash
circom --version
```
3. Install Anvil (local testnet tool)
Installation: [https://github.com/foundry-rs/foundry](https://github.com/foundry-rs/foundry)
---
## 📦 Install Dependencies
```bash
npm install
```
---
## 🔧 Build Circuits
Generate `r1cs` and `wasm` files:
```bash
npm run build
```
---
## 🔐 Setup Circuits (Trusted Setup)
Precondition: Download `powersOfTau28_hez_final_12.ptau` and place it in the project root.
- Download: [https://storage.googleapis.com/zkevm/ptau/powersOfTau28_hez_final_12.ptau](https://storage.googleapis.com/zkevm/ptau/powersOfTau28_hez_final_12.ptau)
- If the link is broken, refer to [iden3/snarkjs](https://github.com/iden3/snarkjs?tab=readme-ov-file#7-prepare-phase-2)
Run setup to generate proving key, verifying key, and Solidity verifier:
```bash
npm run setup
```
---
## 📄 Compile Smart Contracts
Compile Vault and Verifier contracts to EVM-compatible bytecode:
```bash
npm run compile
```
---
## 🧪 Run Tests
Includes Circom circuit tests and Solidity contract tests:
```bash
npm run test
```
---
## 🚀 Start Local Testnet (Anvil)
Default endpoint: `http://127.0.0.1:8545`:
```bash
npm run srv
```
---
## 🧾 Environment Variables (.env)
Create a `.env` file in the root directory, e.g.:
```env
NETWORK = "test"
NODE_URL = "http://127.0.0.1:8545"
MNEMONIC = ""
```
⚠️ The mnemonic is for local testing only. **Do NOT use real wallet credentials!**
---
## 📤 Deploy Contracts to Local Testnet
Deploy the contract with 1 ETH denomination:
```bash
npm run deploy -- --denomination 1
```
---
## 🧭 CLI Commands
### Make a Deposit
```bash
npm run cli -- deposit
```
The command will print a `secret` — store it securely.
### Make a Withdrawal
Withdraw using the previously printed secret:
```bash
npm run cli -- withdraw --secret
```
---
## 📚 Further Reading & References
- [Circom 2 Documentation](https://docs.circom.io/)
- [Snarkjs Tutorial](https://github.com/iden3/snarkjs)
- [Zero Knowledge Proofs on Ethereum.org](https://ethereum.org/en/zero-knowledge-proofs/)
---
## 🚧 Next Steps
Upgrade to `zkvault-classic` by implementing a Merkle Tree to fully decouple deposit and withdrawal addresses.
---
## 📄 License
This project uses the MIT license. See [LICENSE](./LICENSE) for details.