Documentation
Prove income.
Reveal nothing.
Privacy-preserving income attestation on Solana. Prove your real-world income with client-side ZK proofs and get verified for rent, work, payroll, or web3, without revealing any financial data.
How it works
- 01User connects to Plaid via OAuth. Income data stays in the browser.
- 02A Circom Groth16 ZK circuit runs locally and answers: "Is my 6-month average income above the threshold?"
- 03The proof (no names, no exact figures, no employer) is sent to the backend for verification.
- 04User pays a small USDC attestation fee via x402.
- 05A Soulbound SPL Token-2022 is minted on Solana, valid for 30 days, non-transferable.
- 06Verifiers (a landlord, client, employer, or app) read the token and confirm you qualify.
Raw income data never leaves the client.
Architecture
architecture
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 tokenIncome tiers
| Tier | Monthly average (6-month floor) | Band |
|---|---|---|
| 1 | >= $2,000 | Verified |
| 2 | >= $5,000 | Verified plus |
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
| Layer | Technology |
|---|---|
| ZK Circuit | Circom 2.0 + snarkJS (Groth16, BN254) |
| Solana Program | Rust, Anchor 0.30, SPL Token-2022 |
| Backend | Node.js 18+, TypeScript, Express |
| Frontend | Next.js 14, Tailwind CSS |
| Wallet | Solana Wallet Adapter (Phantom, Solflare) |
| Income APIs | Plaid |
| Payment | x402 (USDC micropayment) |
| Verification | TypeScript SDK |
Project structure
project structure
seel/
├── circuits/
│ └── income_proof_circom/ Circom circuit + trusted setup artifacts
├── programs/seel/ Anchor program (Rust)
├── backend/ Node.js API
├── frontend/ Next.js app
└── sdk/ Verification SDK for integratorsGetting started
Prerequisites
- Node.js 18+
- Rust + Anchor CLI 0.30
- Solana CLI (devnet)
- circom + snarkJS (for circuit setup)
Backend
backend
cd backend
cp .env.example .env # fill in your credentials
npm install
npm run devFrontend
frontend
cd frontend
npm install
npm run devAnchor program
anchor
anchor build # production build
anchor build --features verify-skip # local testing only, never deploy
anchor deploy --provider.cluster devnetNever build for production with --features verify-skip.
Circom circuit
circom
cd circuits/income_proof_circom
npm run setup # compile, trusted setup, export verification key
# artifacts land in frontend/public/circuits/ automaticallyEnvironment variables
Copy backend/.env.example to backend/.env and fill in:
| Variable | Description |
|---|---|
| PLAID_CLIENT_ID | Plaid API client ID |
| PLAID_SECRET | Plaid API secret |
| PLAID_ENV | sandbox or production |
| SESSION_SECRET | Express session secret (random string) |
| SOLANA_RPC_URL | Solana RPC endpoint |
| SEEL_PROGRAM_ID | Deployed Anchor program address |
| USDC_MINT | USDC mint address (devnet or mainnet) |
| BACKEND_KEYPAIR_PATH | Path to Solana keypair file |
| BACKEND_WALLET_ADDRESS | Solana address to receive attestation fees |
Deployed addresses (devnet)
| Resource | Value |
|---|---|
| Program ID | DwiHe1VWW9KXeWXJFaRFoMNzPt3mVs2Ac84gPbaeBkoJ |
| USDC Mint | 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU |
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.
