Skip to content

zekele-win/zkvault-basic

Repository files navigation

❄️ 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

.
├── 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/
    circom --version
  3. Install Anvil (local testnet tool)
    Installation: https://github.com/foundry-rs/foundry

📦 Install Dependencies

npm install

🔧 Build Circuits

Generate r1cs and wasm files:

npm run build

🔐 Setup Circuits (Trusted Setup)

Precondition: Download powersOfTau28_hez_final_12.ptau and place it in the project root.

Run setup to generate proving key, verifying key, and Solidity verifier:

npm run setup

📄 Compile Smart Contracts

Compile Vault and Verifier contracts to EVM-compatible bytecode:

npm run compile

🧪 Run Tests

Includes Circom circuit tests and Solidity contract tests:

npm run test

🚀 Start Local Testnet (Anvil)

Default endpoint: http://127.0.0.1:8545:

npm run srv

🧾 Environment Variables (.env)

Create a .env file in the root directory, e.g.:

NETWORK = "test"
NODE_URL = "http://127.0.0.1:8545"
MNEMONIC = "<your test 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:

npm run deploy -- --denomination 1

🧭 CLI Commands

Make a Deposit

npm run cli -- deposit

The command will print a secret — store it securely.

Make a Withdrawal

Withdraw using the previously printed secret:

npm run cli -- withdraw --secret <your-secret>

📚 Further Reading & References


🚧 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 for details.

About

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.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors