Documentation

Prove income.
Reveal nothing.

Privacy-preserving income attestation on Solana. Prove your real-world income with client-side ZK proofs and unlock higher LTV borrowing on DeFi, without revealing any financial data.

How it works

  1. 01User connects to Plaid via OAuth. Income data stays in the browser.
  2. 02A Circom Groth16 ZK circuit runs locally and answers: "Is my 6-month average income above the threshold?"
  3. 03The proof (no names, no exact figures, no employer) is sent to the backend for verification.
  4. 04User pays a small USDC attestation fee via x402.
  5. 05A Soulbound SPL Token-2022 is minted on Solana, valid for 30 days, non-transferable.
  6. 06Lending protocols (Kamino, Save) read the token and unlock a higher LTV.

Raw income data never leaves the client.

Architecture

architecture
text
Browser  ├── OAuth (Plaid)                income data stays client-side  └── Circom Groth16 (snarkjs)     ZK proof generated locally Backend (Node.js + TypeScript, port 3001)  ├── /auth      OAuth callback, session token  ├── /income    Plaid income data (never forwarded to logs)  ├── /proof     snarkjs Groth16 proof verification  ├── /payment   x402 USDC attestation fee  └── /solana    Anchor program interaction, token minting Solana (devnet)  ├── Anchor Program   verify Groth16 proof (BN254), mint/expire/revoke attestation  └── SPL Token-2022   Soulbound (NonTransferable) attestation token

Income tiers

TierMonthly average (6-month floor)LTV
1>= $2,00075%
2>= $5,00080%

The circuit checks all 6 months have positive income and computes the floor average. The threshold is a private input. The verifier only sees the resulting tier.

Tech stack

LayerTechnology
ZK CircuitCircom 2.0 + snarkJS (Groth16, BN254)
Solana ProgramRust, Anchor 0.30, SPL Token-2022
BackendNode.js 18+, TypeScript, Express
FrontendNext.js 14, Tailwind CSS
WalletSolana Wallet Adapter (Phantom, Solflare)
Income APIsPlaid
Paymentx402 (USDC micropayment)
DeFiKamino SDK

Project structure

project structure
text
seel/├── circuits/│   └── income_proof_circom/   Circom circuit + trusted setup artifacts├── programs/seel/             Anchor program (Rust)├── backend/                   Node.js API├── frontend/                  Next.js app└── sdk/                       Integration SDK for lending protocols

Getting started

Prerequisites

  • Node.js 18+
  • Rust + Anchor CLI 0.30
  • Solana CLI (devnet)
  • circom + snarkJS (for circuit setup)

Backend

backend
bash
cd backendcp .env.example .env   # fill in your credentialsnpm installnpm run dev

Frontend

frontend
bash
cd frontendnpm installnpm run dev

Anchor program

anchor
bash
anchor build                              # production buildanchor build --features verify-skip       # local testing only, never deployanchor deploy --provider.cluster devnet

Never build for production with --features verify-skip.

Circom circuit

circom
bash
cd circuits/income_proof_circomnpm run setup   # compile, trusted setup, export verification key# artifacts land in frontend/public/circuits/ automatically

Environment variables

Copy backend/.env.example to backend/.env and fill in:

VariableDescription
PLAID_CLIENT_IDPlaid API client ID
PLAID_SECRETPlaid API secret
PLAID_ENVsandbox or production
SESSION_SECRETExpress session secret (random string)
SOLANA_RPC_URLSolana RPC endpoint
SEEL_PROGRAM_IDDeployed Anchor program address
USDC_MINTUSDC mint address (devnet or mainnet)
BACKEND_KEYPAIR_PATHPath to Solana keypair file
BACKEND_WALLET_ADDRESSSolana address to receive attestation fees

Deployed addresses (devnet)

ResourceValue
Program IDDwiHe1VWW9KXeWXJFaRFoMNzPt3mVs2Ac84gPbaeBkoJ
USDC Mint4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU

Privacy guarantees

  • Income figures are private circuit inputs. They never leave the browser.
  • The server receives a proof and public outputs only (tier: 1 or 2).
  • API responses containing income data are never logged on the server.
  • The on-chain token stores only tier, timestamps, and a proof hash.