Skip to content

fotescodev/zk-kyc-hook

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZK-KYC Transfer Hook

Zero-knowledge KYC credential verification enforced via Solana Token-2022 transfer hooks.

Status: Proof of concept — devnet deployment target.

What This Does

Token transfers are gated by KYC credentials verified with zero-knowledge proofs. A user proves they hold valid KYC data from an approved issuer without revealing any personal information. The transfer hook checks this credential on every token transfer — if the sender or recipient isn't verified, the transfer reverts.

User (offchain)              Solana (onchain)
─────────────               ─────────────────
Generate ZK proof    →      KYC Verifier Program
(snarkjs/circom)             ├─ groth16_verify(proof)
                             └─ Store KycCredential PDA
                                  ↓
Token Transfer       →      Transfer Hook Program
                             ├─ Read KycCredential PDA
                             ├─ Check: exists + not expired
                             └─ Allow or revert transfer

Architecture

Two Solana programs + one Circom circuit:

KYC Verifier (programs/kyc-verifier/) — Accepts Groth16 ZK proofs, verifies them onchain using groth16-solana (<200k compute units), and stores credential status in PDAs. Manages an issuer registry of approved KYC providers.

Transfer Hook (programs/transfer-hook/) — Token-2022 transfer hook that reads credential PDAs on every transfer. Both sender and recipient must have valid, non-expired credentials. Uses ExtraAccountMetaList for automatic PDA resolution.

Circom Circuit (circuits/kyc_proof.circom) — Proves knowledge of KYC credential data (secret + issuer + expiry) hashed with Poseidon, bound to a specific wallet, with an expiry check. ~100 constraints, proof generation <500ms.

Tech Stack

Component Technology
ZK Circuit Circom 2.1 + Poseidon hash
Proof Generation snarkjs (Groth16)
Onchain Verification groth16-solana (audited, BN254)
Programs Anchor 0.30.0+
Token Standard Token-2022 (Token Extensions)

Prerequisites

# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

# Solana CLI (v1.18+)
sh -c "$(curl -sSfL https://release.solana.com/v1.18.0/install)"

# Anchor (v0.30+)
cargo install --git https://github.com/coral-xyz/anchor anchor-cli --locked

# Circom
npm install -g circom

# Node.js 18+ (for snarkjs)

Setup

# 1. Install dependencies
npm install
cd circuits && npm install && cd ..

# 2. Compile circuit + trusted setup
bash scripts/setup-circuit.sh

# 3. Generate Rust verification key constants
npx ts-node scripts/convert-vk.ts
# Copy the output into programs/kyc-verifier/src/lib.rs

# 4. Configure Solana for devnet
solana config set --url devnet
solana-keygen new  # if needed
solana airdrop 2

# 5. Build and deploy
anchor build
# Update program IDs (see deploy script)
bash scripts/deploy.sh

Demo

# Generate ZK proofs and run the transfer demo
cd client && npx ts-node src/client.ts

Test Scenarios

Scenario Expected Result
Both parties have valid KYC credentials Transfer succeeds
Recipient's credential is expired Transfer blocked (CredentialExpired)
Recipient has no credential Transfer blocked (RecipientNotVerified)
Tampered ZK proof submitted Verification rejected (ProofVerificationFailed)

Project Structure

zk-kyc-hook/
├── programs/
│   ├── kyc-verifier/       # Groth16 proof verification + credential PDAs
│   └── transfer-hook/      # Token-2022 transfer hook enforcement
├── circuits/
│   └── kyc_proof.circom    # ZK circuit (Poseidon hash + expiry check)
├── client/
│   └── src/
│       ├── prover.ts       # snarkjs proof generation
│       ├── setup.ts        # Token + account setup
│       └── client.ts       # End-to-end demo
├── tests/
│   └── integration.test.ts # 4 test scenarios
└── scripts/
    ├── setup-circuit.sh    # Circuit compilation + trusted setup
    ├── convert-vk.ts       # Verification key → Rust constants
    └── deploy.sh           # Build + deploy to devnet

Key Technical Notes

  • groth16-solana requires negating proof.A's y-coordinate — handled in prover.ts
  • Verification key conversion from snarkjs JSON to Rust byte arrays — scripts/convert-vk.ts
  • ExtraAccountMetaList seeds include KYC credential PDA derivation for automatic resolution
  • Cannot combine ConfidentialTransfer + TransferHook (Solana limitation)
  • Circuit uses Poseidon hash (BN254-native, efficient in circom)

References

License

MIT

About

Real ZK proof verification inside a Solana transfer hook

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors