Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

ShieldLend

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification.

StatusChainLicenseCircuits


The Problem

Every action on a public DeFi lending protocol is fully transparent. When you deposit collateral, borrow against it, or repay — the exact amount, your wallet address, and your entire position are permanently visible on-chain. Anyone can track which wallets are overleveraged, target liquidations, or build a complete financial profile of any user.

DeFi doesn't have to work this way.


The Solution

ShieldLend adds a ZK privacy layer to DeFi lending using the commit → prove → reveal pattern — the same cryptographic model that powers Tornado Cash, applied to a lending context.

Users deposit into a shielded pool using a cryptographic commitment. To withdraw, they generate a zero-knowledge proof that they know the secret behind a commitment in the pool — without revealing which commitment is theirs. The deposit and withdrawal are cryptographically unlinkable.


How It Works

DEPOSIT WITHDRAW
──────── ────────
1. Generate secret + nullifier 1. Load saved note (secret, nullifier)
2. Compute commitment 2. Fetch current Merkle root
= Pedersen(amount, secret) 3. Generate Merkle path for commitment
3. Submit commitment on-chain 4. Run withdraw.circom in browser →
→ stored in Merkle tree Groth16 proof (proves membership
4. Save your note securely + nullifier knowledge, no amounts)
(this is your only key) 5. Submit proof to zkVerify
6. zkVerify attestation → contract
7. Nullifier marked spent → funds sent
(no link to original deposit address)

Key Features

  • Private deposits — collateral amount hidden behind a Pedersen commitment; only a hash goes on-chain
  • Unlinkable withdrawals — Merkle membership proof + nullifier reveal; deposit and withdrawal addresses are cryptographically unlinked
  • Shielded collateral proofs — ZK range proof proves collateral ≥ min_ratio × borrowed without revealing the exact collateral amount
  • Browser-side proof generation — circuits compile to WebAssembly; users generate proofs locally, no trusted server
  • 91% cheaper verification — proofs verified via zkVerify chain instead of on-chain Ethereum L1 verifier contracts

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ Next.js + wagmi + snarkjs (WASM) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Deposit UI │ │ Withdraw UI │ │ Collateral │ │
│ │ 1. Enter amount │ │ 1. Enter note │ │ Proof UI │ │
│ │ 2. Gen secret │ │ 2. Gen Merkle │ │ │ │
│ │ 3. Compute │ │ proof │ │ 1. Prove │ │
│ │ commitment │ │ 3. Gen nullif. │ │ ratio > │ │
│ │ 4. Submit tx │ │ 4. Submit proof │ │ threshold │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ wallet tx + proof
┌──────────────────────────────▼──────────────────────────────────┐
│ ZK CIRCUITS (Circom) │
│ │
│ deposit.circom withdraw.circom collateral.circom│
│ ───────────── ──────────────── ──────────────── │
│ private: amount, private: secret, private: │
│ secret, nullifier nullifier, exact_amount │
│ public: commitment, pathElements[] public: │
│ nullifierHash public: root, min_ratio, │
│ recipient borrowed │
│ constraints: constraints: │
│ Merkle member. amount*100 ≥ │
│ + nullifier ratio*borrowed│
└──────────────────────────────┬──────────────────────────────────┘
│ Groth16 proof
┌────────────────┴────────────────┐
│ │
┌─────────────▼──────────────┐ ┌──────────────▼──────────────┐
│ SMART CONTRACTS │ │ ZKVERIFY CHAIN │
│ (Solidity on Horizen L3) │ │ │
│ │ │ 1. Receive proof via │
│ ShieldedPool.sol │ │ zkVerifyJS SDK │
│ • Incremental Merkle tree │ │ 2. Verify Groth16 proof │
│ • insertCommitment() │ │ (91% cheaper than L1) │
│ • getRoot() │ │ 3. Emit attestation event │
│ │ │ 4. Relayer reads event │
│ NullifierRegistry.sol │ │ → calls ShieldedPool │
│ • mapping: null→bool │ └─────────────────────────────┘
│ • markSpent(nullifier) │
│ • isSpent(nullifier) │
│ │
│ LendingPool.sol │
│ • Forked from Aave V3 │
│ • deposit(commitment) │
│ • borrow(proof, amount) │
│ • repay() │
│ • withdraw(proof) │
└────────────────────────────┘
Deployment: Horizen L3 on Base (testnet) — fallback: Base Sepolia

Tech Stack

LayerTechnologyRole
ChainHorizen L3 on BaseEVM-native L3, privacy-first execution environment
Proof verificationzkVerifyModular proof verification — 91% cheaper than L1
CircuitsCircom + circomlibZK-SNARK circuit language; Pedersen, Poseidon, Merkle templates
Proof systemGroth16 via snarkjs3-pairing verification; 192-byte proof; WASM-compilable
ContractsSolidity + FoundryShieldedPool, NullifierRegistry, LendingPool (Aave V3 fork)
FrontendNext.js + wagmiSSR + wallet connection + browser WASM proof generation

See docs/tech-stack.md for detailed rationale on every choice.


ZK Circuits

Three circuits handle the privacy layer. All compile to WebAssembly for browser-side proving.

circuits/deposit.circom

Proves that a commitment was correctly computed from a secret and amount.

  • Private inputs: amount, secret, nullifier
  • Public outputs: commitment = Pedersen(amount || secret), nullifierHash = Poseidon(nullifier)

circuits/withdraw.circom

Proves Merkle membership (the commitment is in the tree) and nullifier knowledge (the prover knows the secret), without revealing which leaf or how much.

  • Private inputs: secret, nullifier, pathElements[], pathIndices[]
  • Public inputs: root (current Merkle root), recipient (withdrawal address)
  • Public outputs: nullifierHash

circuits/collateral.circom

Proves that collateral meets the minimum ratio requirement without revealing the exact collateral amount.

  • Private inputs: exact_collateral_amount
  • Public inputs: min_ratio, borrowed_amount
  • Constraint: exact_collateral * 100 >= min_ratio * borrowed_amount

See docs/circuits.md for full signal definitions and constraint derivations.


Smart Contracts

ContractPurpose
ShieldedPool.solMaintains the incremental Merkle tree of commitments. Handles deposit() and withdraw(). Verifies zkVerify attestation before releasing funds.
NullifierRegistry.solTracks spent nullifier hashes. Prevents double-withdrawal. Called by ShieldedPool on every withdrawal.
LendingPool.solMinimal Aave V3 fork. Adds borrow() and repay() on top of the shielded pool, using collateral range proofs to gate borrowing without revealing positions.

See docs/architecture.md for full interface definitions and data flow diagrams.


Project Roadmap

StepTaskKey Output
0Design complete — architecture, circuits, contracts scopedThis repo
1Scaffold: forge init + Circom project structure + Next.js frontendProject skeleton
2deposit.circom — Pedersen commit(amount, secret, nullifier)Working deposit circuit
3withdraw.circom — Merkle membership + nullifier revealWorking withdraw circuit
4Trusted setup: Powers of Tau → per-circuit .zkeyProving keys
5Deploy ShieldedPool.sol + NullifierRegistry.sol on Horizen L3Live contracts
6Integrate zkVerifyJS SDK — submit proof, receive attestationWorking proof pipeline
7Frontend: MetaMask connect, browser WASM proof generationWorking UI
8Fork minimal Aave V3 pool — collateral/borrow mechanicsLendingPool.sol
9collateral.circom — range proof (collateral ratio ≥ threshold)Collateral circuit
10End-to-end tests + testnet deploy + demoDeployed MVP

See ROADMAP.md for detailed phase descriptions and deliverables.


Competitive Landscape

ProjectApproachOutcomeLesson
Sacred FinanceTornado Cash + Aave yield, on EthereumLaunched, low adoptionProves the pattern works. We improve with zkVerify (cheaper) + Horizen L3
Aztec ConnectFull privacy L2 rollup over EthereumShut down March 2023Full-stack privacy L2 is too complex. Feature-level privacy (our approach) is the right scope.
zkFiAcademic multi-asset privacy pool, EthereumResearch stageCircuit architecture reference. Their paper is a design input for withdraw.circom.
ZkreditMPC-based private lending, SolanaBuildingChose MPC because ZK was "too slow on Solana". zkVerify removes this constraint on EVM.

ShieldLend's angle: proven pattern (Sacred Finance / Tornado Cash) + proven lending mechanic (Aave V3) + novel stack (Horizen L3 + zkVerify — not tried before on this chain combination).


Team

NameRole
Opinder SinghCircuit design, smart contracts, zkVerify integration
ZuhaibSmart contracts, Foundry testing
PrathamFrontend, wagmi integration, UX

Cohort: Rump Labs ZK Crypto Blockchain Cohort 1 Instructor: Hridam Basu


Getting Started

Setup instructions will be added as we complete the project scaffold (Step 1). Follow the repo to be notified when the development environment setup is ready.


Resources


License

MIT — see LICENSE

About

ZK-based private DeFi lending protocol on Horizen L3 (Base) with zkVerify proof verification

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages