A privacy-preserving credit passport for Stellar.
Prove your real-world financial standing clears a threshold — without revealing the number — and mint a reusable, non-transferable CreditPass that any Stellar lender, anchor, or RWA app can read to offer better-than-overcollateralized terms.
Prove your financial worth without exposing it, and carry that proof across Stellar.
Stellar's core users — remittance receivers, the underbanked — often have real, verifiable financial standing (steady income, consistent inflows) but no on-chain collateral. DeFi lending is overwhelmingly overcollateralized (post $150 to borrow $100), which is useless to the capital-poor. Avowa closes that gap without breaking privacy.
- Authenticity — a zkTLS attestation (Reclaim zkFetch) proves a financial value came from a real HTTPS source, with the user's address and value commitment bound into the attestation. (v1: a controlled sandbox source behind the same interface, so real providers swap in later without changing the pipeline.)
- Privacy — a small Circom circuit proves
value ≥ thresholdand commits the value via Poseidon. The raw value never leaves the client. - On-chain verification — the Groth16 proof is verified on Stellar by a Soroban contract using native pairing host functions (Protocol 25 X-Ray / Protocol 26 Yardstick).
- Credential — a soulbound CreditPass is minted; a nullifier registry blocks replay; the pass is bound to the caller's address.
- Consumption — any lender (starting with a demo
MockLendingPool) readsCreditPass.tier_of(address)and offers a tiered collateral ratio (Gold 110% / Silver 120% / Bronze 135% / none 150%). Passes expire and can be revoked.
[ Browser ] wallet connect → attest value → Poseidon commitment → Groth16 proof (value never leaves device)
│
▼
[ Soroban ] Verifier → NullifierRegistry → CreditPass (SBT) → read by MockLendingPool / any lender
Early build — Phase 4 (scoring, expiry, revocation) core complete. No mainnet, no real money is at risk anywhere in this repo yet. See docs/phase0-verify-log.md through docs/phase4-verify-log.md for what's been proved so far (curve/template decisions, deployed contracts, resolved [VERIFY] items, attestation binding, tiering).
| Phase | Goal | Status |
|---|---|---|
| 0 | Deploy a Groth16 verifier to Stellar testnet; verify a known-good proof | ✅ done — log |
| 1 | eligibility.circom + Verifier + NullifierRegistry + CreditPass mint |
✅ done — log |
| 2 | MockLendingPool + polished frontend E2E | 🔶 code + contract layer fully verified — log; one manual real-wallet click-through still needed to fully close the DoD |
| 3 | Real zkTLS data layer (Reclaim zkFetch) + address/commitment binding | 🔶 core done — log; real attested-value mint on testnet + independent auditor pass; same wallet click-through pending |
| 4 | Multi-tier scoring (Bronze/Silver/Gold) + expiry + revocation | 🔶 core done — log; tiered mint + on-chain bonus + revocation demonstrated on testnet; same wallet click-through pending |
| 5 | Real lender integration, audit, SCF submission | ⬜ not started |
- Not a lending protocol. It's an attestation/credential layer. Avowa v1 takes no default risk; a demo pool only shows the credential is consumable.
- Not solving enforcement or Sybil resistance yet. Both are real, open problems for an underbanked/no-bureau user base — tracked, not hand-waved. See the trust model below.
- Not multi-chain.
- The raw financial value is proven with a client-side Groth16 proof; the proving never leaves the browser and the value is never persisted.
- Authenticity is now a witness-signed zkTLS attestation (Reclaim zkFetch) with the user's Stellar address and value commitment bound into the tamper-evident attestation context — and anyone can independently audit that an on-chain CreditPass rests on a specific attestation (
web/scripts/verify-attestation.mjs). What it still does not do: verify the attestor's signature inside the circuit (ECDSA-in-circuit — expensive, tracked for v2), so the client relay is trusted to feed the attested value into the prover. The sandbox income source is our own domain; the identical interface is the point — a real income provider swaps in without pipeline changes. Full detail indocs/phase3-verify-log.md. - A CreditPass proves a point-in-time snapshot: it carries an on-chain expiry (30-day window) and must be re-proved, and an issuer/admin can revoke it — an expired or revoked pass is rejected by the pool exactly like having none.
- The tier (Bronze/Silver/Gold) fuses multiple inputs — an attested composite credit score (the circuit stays one comparison; the provider fuses the signals, like a real bureau) plus an on-chain balance signal — and is derived on-chain from the proven threshold, so it can't be self-claimed.
- Sybil resistance (one human ↔ one credit identity) is not solved in v1.
- Trusted setup is self-run and demo-grade, not a real multi-party ceremony — no audited, reusable phase-1 ceremony exists yet for BLS12-381 in a form our toolchain can consume. Finalized with a public randomness beacon and fully documented in
docs/phase1-verify-log.md, same as Stellar's own reference example does. mintis a permissionless relay: anyone can submit a valid proof's transaction, but the proof's address binding means the CreditPass can only ever go to the one address it was generated for.
avowa/
├─ circuits/ # eligibility.circom, vendored libs, ceremony artifacts
├─ contracts/ # verifier | credit_pass (SBT + nullifier registry) | mock_lending_pool
├─ web/ # Next.js frontend — proof generation + encoding live in web/lib/
│ # (folded into the frontend rather than a separate prover/ service,
│ # since proving is entirely client-side/in-browser)
├─ scripts/ # deploy, Friendbot funding, token setup
└─ docs/ # phase verify logs, trust model detail
- Circuits: Circom, circomlib, snarkjs (proving happens client-side, in-browser, via the same wasm/zkey served from
web/public/circuit/) - Contracts: Rust,
soroban-sdk, Stellar CLI - zkTLS: Reclaim (zkFetch) and/or Root14 — Phase 3
- Frontend: Next.js 16 (App Router), TypeScript, Tailwind v4, shadcn/ui,
@stellar/stellar-sdkv16,@creit.tech/stellar-wallets-kitv2.5.0 (note the dot in the npm scope),poseidon-bls12381+@noble/curvesfor client-side proof-input hashing/encoding - Network: Stellar testnet only until Phase 5
cd web
npm install
npm run devOpen http://localhost:3000. Connect a Freighter (or other SEP-43) wallet funded on Stellar testnet via Friendbot. Everything talks to the deployed testnet contracts listed in docs/phase2-verify-log.md — no local chain.
For the Phase 3 attested-income flow you also need a web/.env.local with a zkFetch-enabled Reclaim app (RECLAIM_APP_ID, RECLAIM_APP_SECRET from dev.reclaimprotocol.org — enable zkFetch in the app's Integration tab) and NEXT_PUBLIC_SANDBOX_BANK_URL. See docs/phase3-verify-log.md. Without it the app still builds and the wallet/UI work; only the attest step needs the credentials.
docs/phase0-verify-log.md— Phase 0 findings: curve/template decisions, deployed contract, resolved[VERIFY]itemsdocs/phase1-verify-log.md— Phase 1 findings: Poseidon/address-encoding parity checkpoints, trusted setup, deployed contracts, testnet E2Edocs/phase2-verify-log.md— Phase 2 findings: lending pool, token setup, client-side proving/encoding parity checkpoints, what's fully verified vs. what still needs a manual wallet click-throughdocs/phase3-verify-log.md— Phase 3 findings: Reclaim zkFetch integration (incl. the zk-enable gotcha), address/commitment binding, the independent auditor, real attested-value mint, on-chain-verifier verdictdocs/phase4-verify-log.md— Phase 4 findings: composite-score tiering without touching the circuit, on-chain bonus, expiry/revocation, tiered pool ratios, real tier/revoke testnet E2E