Skip to content

0xE1337/conclave

Repository files navigation

English · 简体中文

Conclave

A confidential private-credit pool for tokenized RWA, built on Zama fhEVM. Borrower KYC tier, credit score, and pool position stay encrypted on a public L1; tier-based collateral resolution runs entirely on ciphertext; underwriting decisions are sealed in homomorphic conclave.

Live Sepolia Tests Lint License

Conclave home — phone shell with 4 apps and persona switcher

Four contracts surface as four apps; the persona switcher mirrors fhEVM's FHE.allow ACL — the same encrypted state, viewed through different keys.

The contrast

Same borrower, same block height — public chain vs Conclave's encrypted view

The same borrower at the same block height. Today, BlackRock's $2.75B BUIDL fund leaks every holder, balance, and flow. JPMorgan Kinexys keeps it private — but on a closed network. Conclave runs on a public L1, encrypted, with a per-borrower regulator viewing key.

How it works

Conclave is the on-chain stack for a private credit fund — built so every position, score, and KYC tier stays encrypted on a public L1.

A private credit deal has a natural life-cycle. Conclave maps each stage to one contract:

Stage Who Contract What happens
1. Admission Underwriting committee ListingConclave Members cast sealed votes on whether an asset can be listed. Tally is homomorphic — voters cannot prove their vote to a briber afterwards.
2. Onboarding Governor BorrowerRegistry Approved borrowers are registered with encrypted KYC tier (euint8) and accredited bond (euint64). Downstream contracts gate on meetsKycTier → ebool without ever seeing the raw tier.
3. Credit history Pool (atomic) CreditScoreEngine Score lives as mutable euint32 state. Every repayment and default updates the score in the same transaction as the loan event. No off-chain issuer needed.
4. Lending LPs + borrowers PrivateCreditPool LPs deposit. Borrowers draw with their collateral ratio decided by cascading FHE.select directly on the encrypted score — only the tier band (50 / 75 / 100 / 150%) ever leaves the ciphertext domain.

The killer featuregrantRegulatorAccess(id): a borrower opts in to give exactly one regulator address a viewing key over their score. That regulator can now decrypt; everyone else — LPs, MEV bots, other regulators — still sees ciphertext. Compliance without surveillance. This is the FHE differentiator versus ZK credentials, which would require off-chain re-issuance for each disclosure event.

Features

  • Encrypted credit score as mutable stateeuint32 score lives on-chain. Repayments and defaults atomically update the score in the same tx as the loan event. No off-chain issuer to re-sign credentials.
  • Cascading tier resolution on ciphertextFHE.select reveals only the resolved collateral band (50 / 75 / 100 / 150%) — never the score itself.
  • ERC-3643-inspired KYC hookmeetsKycTier(id, minTier) returns ebool so a downstream protocol composes the gate homomorphically without seeing the raw tier.
  • Anti-bribery underwriting conclave — homomorphic vote tally; voters cannot prove their vote to a briber afterwards. Stronger than commit-reveal, no trusted coordinator (unlike MACI).
  • Selective regulator disclosureFHE.allow(score, regulator) opt-in per borrower; the same encrypted state that supports public threshold proofs can be selectively decrypted by an authorized auditor.

Architecture

graph TD
    Reg["<b>BorrowerRegistry</b><br/>encrypted KYC tier (euint8)<br/>+ accredited bond (euint64)"]
    Score["<b>CreditScoreEngine</b><br/>mutable encrypted euint32 score<br/>cross-contract ACL bridge → pool<br/>opt-in regulator disclosure"]
    Pool["<b>PrivateCreditPool</b><br/>LP-funded · tier-gated lending<br/>cascading FHE.select on ciphertext<br/>atomic score-as-state on repay/default"]
    Conclave["<b>ListingConclave</b><br/>anti-bribery sealed voting<br/>homomorphic tally · receipt-free"]

    Reg -- "meetsKycTier → ebool" --> Pool
    Reg -- "isActive guard" --> Score
    Score -- "FHE.allow(score, pool)" --> Pool
    Pool -. "recordRepayment / recordDefault" .-> Score
    Reg -- "borrower verification" --> Conclave

    classDef mint fill:#a3dec7,stroke:#19c8b9,stroke-width:2px,color:#3a2f1e
    classDef yellow fill:#f7cd67,stroke:#daa90e,stroke-width:2px,color:#3a2f1e
    classDef sky fill:#a8b5f5,stroke:#5264c7,stroke-width:2px,color:#3a2f1e
    classDef sage fill:#a3d4a3,stroke:#5ea05e,stroke-width:2px,color:#3a2f1e

    class Reg mint
    class Score yellow
    class Pool sky
    class Conclave sage
Loading
Contract Purpose LOC
BorrowerRegistry Encrypted KYC tier + ERC-3643-style transfer-compliance hook 100
CreditScoreEngine Mutable encrypted credit score, regulator selective disclosure, cross-contract ACL bridge 170
PrivateCreditPool LP-funded pool, cascading tier resolution, atomic score-as-state on repay/default 165
ListingConclave Anti-bribery encrypted underwriting committee 100

Install

git clone https://github.com/0xE1337/conclave.git
cd conclave
npm install

Quick start

# Run the test suite (mock fhEVM, ~1s)
npx hardhat test

# Compile + generate types
npx hardhat compile

# Deploy to Sepolia (auto-wires CRITICAL ACL bridge post-deploy)
npx hardhat vars set MNEMONIC
npx hardhat deploy --network sepolia --tags Conclave

# Frontend
cd frontend && npm install && npm run dev

Deployments

Sepolia testnet:

Contract Address
BorrowerRegistry 0x23D2566b41964AD73c649f607d35f745e6EB065A
ListingConclave 0x555B150429A8C7Ec8C5d19c894d956645C0878e1
CreditScoreEngine 0xd0AAFbd8C223f689E18Fba4F867500cE9A4DE8f1
PrivateCreditPool 0xe93C152887Cf45F01655641ff590F6c9CCf7e47C

ACL bridge (setPool) is wired automatically by the deploy script.

Usage

Register a borrower (governor)

// Encrypted inputs constructed client-side via @zama-fhe/relayer-sdk:
//   tier  : euint8  (1=retail, 2=accredited, 3=qualified-purchaser, 4=institutional)
//   bond  : euint64 (compliance bond, encrypted)
registry.register(walletAddr, encTier, tierProof, encBond, bondProof);

Score-as-state lifecycle

// Atomic update inside the loan tx — no off-chain issuer.
function repay(uint256 loanId) external payable {
    // ...
    credit.recordRepayment(borrowerId);   // bumps euint32 score in same tx
    // ...
}

Cascading tier resolution (only the band leaves ciphertext)

ebool isTier1 = FHE.ge(score, FHE.asEuint32(80));
ebool isTier2 = FHE.ge(score, FHE.asEuint32(50));
ebool isTier3 = FHE.ge(score, FHE.asEuint32(20));

euint32 pct = FHE.select(isTier1, FHE.asEuint32(50),
              FHE.select(isTier2, FHE.asEuint32(75),
              FHE.select(isTier3, FHE.asEuint32(100),
                                  FHE.asEuint32(150))));
FHE.makePubliclyDecryptable(pct);

Anti-bribery vote

// Vote is consumed by homomorphic addition — no per-voter handle survives.
ebool vote = FHE.fromExternal(encVote, proof);
p.approves = FHE.add(p.approves, FHE.select(vote, ONE, ZERO));
p.rejects  = FHE.add(p.rejects,  FHE.select(vote, ZERO, ONE));

Selective regulator disclosure

// Borrower opts in. Only the configured regulator address can decrypt
// the score; protocol, LPs, and other counterparties still see ciphertext.
function grantRegulatorAccess(uint256 id) external {
    require(msg.sender == registry.walletOf(id));
    FHE.allow(_credits[id].score, regulator);
}

Anti-pattern audit

All four contracts pass fhevm-lint (12-rule static checker for fhEVM-specific privacy and ACL bugs):

contracts/BorrowerRegistry.sol    ✓ no fhEVM anti-patterns detected
contracts/CreditScoreEngine.sol   ✓ no fhEVM anti-patterns detected
contracts/PrivateCreditPool.sol   ✓ no fhEVM anti-patterns detected
contracts/ListingConclave.sol     ✓ no fhEVM anti-patterns detected

Two layer-3 (privacy boundary) issues were found and fixed pre-tag in PrivateCreditPool:

  • #15 trivial encryption — removed an FHE.eq(stored, FHE.asEuint32(claimedPct)) whose right operand was a plaintext calldata uint256. Tier integrity is enforced via off-chain coprocessor decryption of the makePubliclyDecryptable stored handle, not redundant on-chain encryption of an already-public value.
  • #16 event leakBorrowed now emits type(uint256).max placeholder for collateral and collateralPct per ERC-7984 convention. Plaintext values remain readable from the loans[loanId] storage struct, which is public by design (tier policy is public — only the credit score is private).

Tests

62 passing
1 pending (Sepolia-only smoke)
Suite Count
BorrowerRegistry 10
CreditScoreEngine 18
PrivateCreditPool 19
ListingConclave 13
FHECounter (template) 2

Run with npx hardhat test.

Selective Disclosure (the magnetic shot)

Score app — TierBand resolved + 3-pane DecryptionReveal with Regulator authorized

Cascading FHE.select resolves only the tier (50% Elite); the score never decrypts. FHE.allow(score, regulator) grants exactly one address selective decrypt access — Public still sees ciphertext, Regulator sees plaintext, Borrower retains full ownership.

Why FHE here

The project deliberately uses FHE rather than ZK selective-disclosure credentials because:

  1. Mutable state. A credit score is updated by every repayment and default. ZK credentials require an off-chain issuer to re-sign every update; FHE state is mutated directly by the lending contract atomically with the loan event.
  2. Composable handles. Downstream fhEVM contracts granted ACL access compose homomorphically against the live euint32 score. Access auto-revokes if the borrower's tier degrades — a one-shot ZK proof cannot express that.
  3. Selective decryption. The same encrypted state that supports public threshold proofs (FHE.ge(score, threshold) → ebool) can be selectively granted to a regulator address (FHE.allow(score, regulator)). ZK disclosure typically requires a separate off-chain re-issuance for each disclosure scope.

Frontend

The demo dApp is a Next.js 16 / Tailwind 4 / framer-motion / Turbopack app, designed in Animal-Crossing-inspired warm pastel with the cinematic constraint "warm in chrome, sharp in data" — soft shapes for chrome, monospace tabular numerics for state.

Four screens, one persona-switcher row:

Registry roster Score with tier band resolved
Registry — institutional borrower roster with encrypted KYC tier (revealed for the persona who owns the row, sealed otherwise) Score — encrypted credit score, formula breakdown, cascading FHE.select tier resolution
Pool KPI + tier distribution + active loans Listing Conclave — sealed proposals
Pool — TVL / active loans / repayments / default rate, tier-distribution bar, active-loan table Conclave — sealed RWA listing proposals; underwriter-only voting via homomorphic tally

Source: frontend/. Hero screenshot pipeline in scripts/capture-screenshots.mjs.

Acknowledgments

Built with @fhevm/solidity@0.11.1, patterns from OpenZeppelin Confidential Contracts (ERC-7984), the Zama fhEVM Hardhat template, and ERC-3643 transfer-compliance hook ideas from T-REX Network.

License

MIT — see LICENSE.

About

A confidential private-credit pool for tokenized RWA, built on Zama fhEVM. Encrypted positions, ERC-3643-inspired KYC tier gating, anti-bribery encrypted underwriting committee, opt-in regulator disclosure.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors