Skip to content

edycutjong/crisp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Crisp Icon

Crisp πŸ”¬

Real-Time Zero-Knowledge Proof-of-Reserves & Solvency Oracle for Stellar Issuers

Crisp Banner

βœ… Real Groth16 (BN254) proof verified on Stellar testnet.
Reproduce with npm run prove:demo β€” oracle CDXROOACFGK7FIOMNRO22O25O5YIMSHA3DKEIQXUUWHR74QGVGKXXSOY; a fresh snarkjs proof makes attest_reserves return true on-chain, and tampered inputs are rejected.
Honest status: the hosted web app is a demo sandbox (local crypto simulations for UX); the load-bearing ZK is the prove:demo pipeline plus the deployed contract.


Live Demo Pitch Deck Demo Video Built for Stellar Hacks


Next.js TypeScript Stellar Contracts Circom License CI


πŸ’‘ The Problem & Solution

Stablecoin issuers (USDC, EURC) hold billions in custodian reserves. In the wake of historical collapses, trust in stablecoins is at an all-time low. To restore trust, issuers publish Proof-of-Reserves (PoR). However:

  1. Privacy Leakage: Standard PoR unmasks individual customer balances and total company assets to competitors and the public.
  2. Centralization: Standard audits are retrospective and require trusting third-party accounting firms.

Crisp solves this by leveraging Zero-Knowledge Merkle-Sum Tree circuits. It allows Stellar stablecoin issuers to continuously prove that their off-chain bank reserves exceed their total liabilities ($R \ge L$) without exposing individual customer balances or revealing the exact total reserve amount.

Key Features:

  • ⚑ Real-Time Audits: Instant balance scraping and proof generation.
  • πŸ”’ Zero Leakage: All customer balances are blinded with private salts.
  • πŸ† Customer Inclusion Checks: Customers verify their balance was included in the solvency pool in under 1 second client-side.

πŸ“Έ See it in Action

Crisp Visualizer

Solvency Attestation Workflow:

  1. Connect Freighter Wallet inside the Issuer Dashboard.
  2. Submit reserve balances $\rightarrow$ off-chain engine scrapes balances & generates ZK proof.
  3. Verify Groth16 on-chain via Soroban $\rightarrow$ update public solvency state dynamically.
  4. Customer verifies balance inclusion locally using their private salt.

πŸ—οΈ Architecture & Tech Stack

sequenceDiagram
    autonumber
    actor Issuer as Stablecoin Issuer
    participant Horizon as Stellar Horizon API
    participant Client as Crisp Client Panel
    participant Supabase as Supabase Database
    participant Soroban as Stellar Soroban (Verifier)

    Issuer->>Client: Trigger Solvency Attestation
    Client->>Horizon: Pull all token holder balances
    Horizon-->>Client: Return balances list
    Client->>Client: Construct Merkle-Sum Tree & ZK Proof
    Client->>Soroban: Submit tx: attest_reserves(proof, root, total_L, reserve_R)
    Soroban->>Soroban: Run Groth16 verification via BN254 pairing host fn (Protocol 25/26)
    Soroban-->>Client: Attestation Event logged on-chain
    Client->>Supabase: Write attestation metadata and historical logs
Loading

ZK Compilation & Proving Toolchain Flow

graph TD
    Circuits[circom circuits/*.circom] -->|circom compiler| R1CS[circuit.r1cs]
    Circuits -->|circom compiler| WASM[circuit.wasm]
    R1CS -->|snarkjs setup| VKey[verification_key.json]
    R1CS -->|snarkjs setup| ProvingKey[circuit_final.zkey]
    WASM -->|snarkjs generate witness| Witness[witness.wtns]
    Witness -->|snarkjs prove| Proof[proof.json]
    Witness -->|snarkjs prove| Public[public.json]
    ProvingKey -->|snarkjs prove| Proof
    ProvingKey -->|snarkjs prove| Public
    Proof -->| Freighter / Client Submit | Soroban[Stellar Soroban Contract]
    Public -->| Freighter / Client Submit | Soroban
    VKey -->| Rust / cargo build | Soroban
Loading
Layer Technology Rationale
Frontend Next.js 16 (App Router), React 19 Standard high-performance UI.
ZK Circuits Circom (Groth16) Standard low-level constraint ZK engine.
Smart Contract Rust / Soroban SDK Deployed on Stellar Testnet, calls native cryptographic host functions.
Database Supabase (PostgreSQL) Caches historical reports and inclusion path proofs.

πŸ† Sponsor Tracks Targeted

Stellar Hacks: Real-World ZK

  1. Native BN254 Pairing Check (env.crypto().bn254().pairing_check()) β€” Protocol 25/26: The load-bearing primitive β€” attest_reserves runs the full Groth16 pairing equation on-chain with Stellar's native BN254 host functions, so solvency is verified by the ledger itself instead of trusted off-chain. Performing this in raw WASM would exhaust Soroban's CPU budget.
  2. Native BN254 G1 Operations (bn254().g1_mul / g1_add): The verifier folds the public inputs (liabilities root, total liabilities, reserve threshold) into the VK commitment vk_x using native scalar-multiply and point-add (the MSM that Protocol 26 accelerates).
  3. In-Circuit Poseidon Merkle-Sum Tree (Circom, bn128): Poseidon hashing runs inside the off-chain circuit (compiled over bn128, matching the on-chain BN254 field), so the entire liabilities tree collapses into one constant-size proof. On-chain we verify a single pairing β€” independent of account count.
  4. Horizon Accounts Indexing API: Natively tracks and indexes all token balances and trustlines, allowing our balance scraper to pull the liability database in seconds without running custom indexer nodes.
  5. Stellar Contract Events (env.events().publish()): Publishes historical solvency roots and telemetry to the ledger, which our Next.js dashboard indexes in real-time, preventing high storage fee overheads.

⛓️ Smart Contract Specifications

Compiler Requirements

Smart contracts target the wasm32v1-none compilation target (using cargo build --target wasm32v1-none or equivalent Soroban build parameters) under Rust 1.82+ to ensure compatibility with Stellar's Protocol 25/26 BN254 EC pairing host functions.

Deployed Contract Details

  • Oracle Contract: CDXROOACFGK7FIOMNRO22O25O5YIMSHA3DKEIQXUUWHR74QGVGKXXSOY

Contract Endpoints & Parameters

CrispOracle

Maintains trusted solvency verifications and allowlisted providers:

  • initialize(env: Env, admin: Bytes): Initialize contract with admin identity.
  • set_verification_key(env: Env, alpha: Bytes, beta: Bytes, gamma: Bytes, delta: Bytes, ic: Vec<Bytes>): Set Groth16 verification key points for pairing check.
  • add_provider(env: Env, provider: Address): Authorizes a trusted balance provider address.
  • attest_reserves(env: Env, proof: Bytes, kyc_root: BytesN<32>, total_liabilities: u128, reserves_threshold: u128, issuer_ax: BytesN<32>, issuer_ay: BytesN<32>) -> bool: Verify solvency using Groth16 verification against public inputs [kyc_root, total_liabilities, reserves_threshold, issuer_ax, issuer_ay]. Prevents duplicate attestation of the same root and updates the solvency attestation report.
  • get_attestation(env: Env) -> AttestationReport: Retrieve the latest verified attestation report details.

πŸ”­ v3 extension β€” deployed as a dedicated contract, not wired into the demo web app

Honest status: the v3 multi-issuer batch aggregation ships as a separate, dedicated contract with its own batch verification key. circuits/aggregator.circom is compiled and proven, attest_batch_v3 runs a real batch pairing check (no longer a structural-only stub), and it is verified on Stellar testnet β€” reproducible via npm run prove:demo:batch. It is not wired into the hosted demo web app, which exercises the v1 oracle only.

  • attest_batch_v3(...) [v3, shipped] β€” Multi-issuer aggregated solvency: a single batch Groth16 proof over N issuers (min 2), the system-wide invariant $R_{total} \ge L_{total}$, per-issuer root registration, and batch replay protection, against a dedicated batch VK on testnet contract CANW4N5YTB4UYDM4MO5WK5SUHGPLJBXG3FQATLZQ5QKAQ2A57TXQ2DL2. Reproduce: npm run prove:demo:batch.

πŸš€ Getting Started

Prerequisites

  • Node.js β‰₯ 20
  • Cargo / Rust (to run smart contract test suites)

Installation

  1. Clone the repository:
    git clone https://github.com/edycutjong/crisp.git
  2. Install dependencies:
    cd crisp
    npm install
  3. Configure environment variables:
    cp .env.example .env.local
    Note: In development and test environments, if Supabase keys are not populated, the application will automatically fall back to an in-memory database pre-seeded with mock profiles.
  4. Run Next.js dashboard locally:
    npm run dev

πŸ§ͺ Testing & CI

6-Stage CI/CD Pipeline

Quality $\rightarrow$ Security $\rightarrow$ Build $\rightarrow$ E2E Tests $\rightarrow$ Performance $\rightarrow$ Deploy Gate

# ── Code Quality ────────────────────────────
npm run lint          # ESLint source checks
npm run lint:fix      # ESLint source checks with auto-fixes
npm run format:check  # Prettier code format checks
npm run typecheck     # TypeScript static compiler check
npm run test          # Run cryptographic protocol tests
npm run test:coverage # Run cryptographic protocol tests with coverage

# ── E2E & Performance ───────────────────────
npm run e2e           # Playwright E2E tests (demo-mode)
npm run e2e:ui        # Playwright interactive E2E UI
npm run lighthouse    # Lighthouse CI metrics audit

# ── Security & Auditing ─────────────────────
make security-scan    # Vulnerability audit + License compliance audit
Layer Tool Status
Code Quality ESLint + TypeScript + Prettier βœ…
Unit Testing (JS) Custom runner (100+ assertions) βœ…
Unit Testing (Rust) Cargo test (Soroban smart contract) βœ…
E2E Testing Playwright (3 suites, responsive, solvency) βœ…
Security (SAST) CodeQL βœ…
Security (SCA) Dependabot + npm audit βœ…
Secret Scanning TruffleHog βœ…
Performance Lighthouse CI βœ…

πŸ“ Project Structure

dorahacks-stellarzh-crisp/
β”œβ”€β”€ .github/           # GitHub Actions (CI, Dependabot, CodeQL)
β”œβ”€β”€ circuits/          # ZK solvency constraint circuits (Circom)
β”œβ”€β”€ contracts/         # Soroban CrispOracle contract (Rust)
β”œβ”€β”€ db/                # Supabase schema definitions
β”œβ”€β”€ docs/              # DX logs, security audits, and visuals
β”œβ”€β”€ e2e/               # Playwright E2E tests
β”œβ”€β”€ public/            # Static files, icons, and OG cards
β”œβ”€β”€ scripts/           # Seeding, testing, and benchmark scripts
β”œβ”€β”€ src/               # React components, pages, and API routes
β”œβ”€β”€ Makefile           # Testing and scanning CLI automation
└── README.md          # You are here

πŸ“Š Performance & Gas Benchmarks

On-chain CPU instruction costs and memory consumption measured using soroban-sdk testutils:

Operation CPU Instructions Memory Bytes % of Limit
BN254 G1 Add / Mul (per op) ~14,488 0 <0.02%
Groth16 Verify β€” full BN254 pairing check (attest_reserves) ~22,450,000 ~120,400 ~22.4%

Reproduce with the contract's Cargo test suite (cargo test -- --nocapture), which prints the Soroban budget() CPU/memory for the real on-chain BN254 pairing check. Poseidon runs in-circuit (off-chain) and has no on-chain instruction cost.


πŸ—ΊοΈ Roadmap

  • Phase 1: Core Groth16 solvency circuit implementation (Circom)
  • Phase 2: Soroban attest_reserves contract with native BN254 pairing check
  • Phase 3: Merkle sum tree client-side library and browser proving
  • Phase 4: Freighter wallet integration and Next.js dashboard
  • Phase 5: Registered-oracle reserve attestation (v2) with Ed25519 signature verification β€” shipped & verified on-chain. set_oracle_key registers an authorized reserve-oracle Ed25519 key; verify_oracle_sig / attest_reserves_v2 verify the oracle's signature over reserves_threshold β€– kyc_root against that registered key (closing the prior "caller supplies its own key" gap) on testnet contract CBBO72ROVZVAC2KWYZOEN6PH2GAGFFIFFDO35FV5PGM3QWDEN4EO45PU. Reproduce: npm run prove:demo:oracle (registered-key sig β†’ true, tampered β†’ rejected). Covered by test_verify_oracle_sig_* + test_attest_reserves_v2_with_registered_oracle. Production transport: the oracle key would be operated by a TLSNotary notary that witnesses the custodian balance β€” that TLS-transcript layer is out of scope and not implemented.
  • Phase 6: Batch solvency attestation for multi-issuer aggregation (v3) β€” shipped & verified on-chain. Real aggregator.circom Groth16 circuit (per-issuer solvency + system-wide conservation + Poseidon batch-root commitment over N=4 issuers) β†’ BN254 proof β†’ on-chain verify_batch_proof / attest_batch_v3 against a dedicated batch VK on testnet contract CANW4N5YTB4UYDM4MO5WK5SUHGPLJBXG3FQATLZQ5QKAQ2A57TXQ2DL2. Reproduce: npm run prove:demo:batch (real proof β†’ true, tampered inputs β†’ false). Covered by contract unit tests test_batch_attestation_v3_*.
  • Phase 7: Hosted/decentralized prover network (e.g. Sindri) for production-grade proving β€” blocked on external infra: requires a third-party proving account + API key, not available in this environment. The prove:demo:batch / prove:demo pipelines are the integration point; plugging a remote prover in is a credentialed config change, not new protocol work. Not deployed β€” left honest rather than stubbed.

πŸ“½οΈ Demo Materials


πŸ“„ License

MIT Β© 2026 Edy Cu


πŸ™ Acknowledgments

Built for the Stellar Hacks: Real-World ZK Hackathon. Thank you to the Stellar Development Foundation for Protocol 25/26 cryptographic host primitives.

About

πŸ”¬ Real-Time Zero-Knowledge Proof-of-Reserves and Solvency Oracle for Stellar Issuers

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors