Skip to content

Repository files navigation

ShieldPass Banner

πŸ›‘οΈ ShieldPass

Ultimate Privacy: Trustless Crypto β†’ Fiat Off-Ramp + Private Payments on Stellar

ShieldPass is a private cross-border remittance corridor. It lets anyone swap crypto for Nigerian naira instantly, paid straight to their bank account β€” without ever exposing their identity or banking data on-chain β€” and send funds privately to other users, where the amount, sender, and receiver stay hidden.

Your crypto lives in a Shielded Pool, not a standard escrow. You prove ownership and compliance with zero-knowledge proofs generated locally on your device, and the smart contract verifies every proof on-chain using Stellar's native BN254 pairing functions. The chain only ever sees mathematics β€” never your BVN, name, or bank details.

Built for the Stellar Hacks: ZK hackathon. The ZK proof is load-bearing: no valid proof β‡’ no swap, no transfer, no payout. Verification happens on Soroban itself, not off-chain.

Shielded Pool β€’ On-chain Groth16 (BN254) β€’ Private P2P Transfers β€’ Multi-Asset (XLM + USDC) β€’ Zero-Storage Backend β€’ Passkey Smart Wallets β€’ Gasless

shieldpass.demo.mp4

🧭 The Ultimate Privacy Architecture (Old vs New Way)

Traditional off-ramps force you to upload your identity, trust a custodian with your funds, and publicly broadcast your transactions. ShieldPass eliminates all three β€” and adds fully private peer-to-peer payments on top.

1. The Shielded Pool (The "Dark Pool")

Shielded Pool Concept

  • Old Way: When you sent money, everyone could see it sitting in an escrow account.
  • New Way: All user funds of a given asset are mixed in one pool. Each balance is a secret note (Poseidon(amount, owner, randomness, …)) owned by your shielded key. Nobody scanning the chain can tell which note is yours. ShieldPass currently runs two live pools β€” XLM and USDC β€” each a separate contract instance (same audited wasm/circuits, bound to a different token at init).

2. On-Chain ZK Verification (No Trusted Verifier)

  • Old Way: Proofs were checked off-chain by a backend you had to trust.
  • New Way: The Soroban contract verifies every Groth16 proof itself, on-chain, via the native bn254 pairing-check host function (~37.5M instructions β€” well within budget). The math is the only authority.

3. Trustless Append-Only Tree (No Trusted Root)

  • Old Way: The pool would have to trust a relayer to publish the correct Merkle root.
  • New Way: A dedicated merkle_insert ZK circuit proves every old_root β†’ new_root transition. The contract verifies each append, so the commitment tree advances trustlessly β€” no party can forge a root.

4. Private Payments (Hidden Amounts)

  • Old Way: Sending money revealed who, to whom, and how much.
  • New Way: A shielded_transfer proof spends your note and mints one note for the recipient + a change note for you β€” entirely inside the pool. The amount, sender, and receiver are all hidden; only the recipient learns the amount (from an encrypted blob).

5. Zero-Storage Banking (Hack-Proof Database)

Zero Storage Concept

  • Old Way: We saved your bank account in our database. A breach leaked your banking info.
  • New Way: We don't save your bank account at all. It lives in your browser; at swap time your phone flashes the details to the backend, we pay the bank, and the details are dropped from memory.

6. Trustless Refunds (Math-Enforced Escape Hatch)

Trustless Payout Concept

  • Old Way: If the off-ramp took your crypto but never paid the fiat, your funds were gone.
  • New Way: A withdrawal pre-commits a refund note on-chain. If the naira never settles, after a 1-hour time-lock you reclaim your value trustlessly β€” the platform can never simply keep the crypto. (A future zkTLS fiat_payout proof will make the claim itself fully trustless; today the claim is admin-gated and the refund is your guarantee.)

πŸ”„ How the App Works (Step-by-Step)

Meet Tobi, who wants to use ShieldPass:

1. Onboard (invisible funding). Tobi signs up with a Passkey (Face ID). His shielded identity (spending + encryption keys) is derived from the passkey itself. ShieldPass mints him a secret note worth 500 XLM β€” no public transaction happens. To the outside world, his wallet holds $0.

2. Shield / Unshield. Tobi can move his own crypto β€” XLM or USDC β€” into the private pool (Shield) or pull it back out to his wallet (Unshield) at any time.

3. Withdraw to Naira. Tobi cashes out 100 XLM. On his phone a ZK proof spends his note, mints a 400 XLM change note, and authorizes the contract. The backend pays his bank via Paystack/Lenco; the change stays private.

4. Send privately. Tobi sends 50 XLM to a friend by email or shp_ address. A shielded_transfer proof moves the value inside the pool β€” fully private. His friend's app scans, decrypts the note, and their shielded balance just goes up. A πŸ”” notification fires.

5. Stay informed. Every action β€” faucet, shield, unshield, withdraw, send, and received payments β€” lands in an in-app Activity feed with an unread badge.


πŸ” Cryptographic Pipeline

Three Circom/Groth16 circuits over BN254, all verified on-chain:

  1. confidential_swap (browser): proves you own a valid note, derives the spend nullifier, and computes the change. Used by Withdraw and Unshield. swap_amount is public here (real value leaves the pool).
  2. shielded_transfer (browser): spends one note β†’ two output notes (recipient + change). Amounts are private; the recipient's owner tag is bound into the proof. Powers private P2P payments.
  3. merkle_insert (backend): proves each old_root β†’ new_root tree append so the pool's commitment tree advances trustlessly β€” no on-chain hashing needed.
flowchart LR
    subgraph Device["πŸ“± Your Device"]
        SK["Shielded key (from passkey PRF)"]
        PROOF["confidential_swap / shielded_transfer proof"]
        ENC["x25519 note encryption"]
    end
    subgraph Chain["⛓️ Soroban Shielded Pool"]
        VERIFY["bn254 pairing_check (on-chain Groth16)"]
        TREE["Poseidon commitment tree"]
        NULL["Spent nullifiers"]
    end
    subgraph Backend["βš™οΈ ShieldPass Relayer"]
        IDX["Tree indexer + merkle_insert prover"]
        PAY["Paystack / Lenco"]
        BLOB["Encrypted note blobs"]
    end

    SK -->|"prove locally"| PROOF
    PROOF -->|"verified on-chain"| VERIFY
    VERIFY --> TREE
    VERIFY --> NULL
    IDX -->|"advance root trustlessly"| TREE
    ENC -->|"deliver to recipient"| BLOB
    Backend -->|"naira"| PAY
Loading

🀝 Private Payments (How It Stays Private)

Sending to another ShieldPass user keeps everything hidden, like a locked mailbox with no names:

  1. You encrypt the note details to the recipient's published encryption key (x25519 ECDH + XChaCha20-Poly1305) and submit the shielded_transfer.
  2. The funds never leave the pool β€” your note is nullified; the recipient gets a new note; nothing public is revealed.
  3. The recipient's app scans new encrypted blobs, trial-decrypts with their key, finds the ones meant for them, and updates their shielded balance β€” automatically, with a notification.

Sending to an external G…/C… wallet instead unshields (the funds become public on arrival). Sending to a ShieldPass user stays fully shielded.


πŸͺœ Progressive KYC β€” Programmable Privacy

A tiered compliance model so small swaps stay frictionless while large ones stay regulated:

Tier Gate Unlocks
Tier 1 Passkey hardware attestation (hardware_attested) Everyday swaps
Tier 2 BVN verification (bvn_verified) High-value swaps (> ₦1,000,000)

The same circuit enforces both. A public input require_bvn is set by the backend from the swap's naira value; the contract rejects a high-value withdrawal that didn't use a Tier-2 proof. Compliance attributes are bound into the note.


πŸ”‘ Passkey Smart Wallets + Shielded Identity

No seed phrases, no extensions, no XLM required. Each user gets an OpenZeppelin Smart Account secured by a WebAuthn passkey; transactions are submitted gaslessly via the relayer. Your shielded key (which owns your private notes) is derived from the passkey's PRF β€” so resetting your login PIN never touches your private funds.


πŸ› οΈ Tech Stack

Layer Tech
ZK Circuits Circom + snarkjs Groth16, Poseidon (BN254)
On-chain verification Soroban native bn254 pairing-check (Rust contract)
Smart Contract Rust / Soroban β€” Shielded Pool (deposit Β· insert Β· confidential_swap Β· unshield Β· shielded_transfer Β· claim Β· refund), one instance per supported asset
Note encryption x25519 + XChaCha20-Poly1305 (@noble)
Wallets OpenZeppelin Smart Accounts (WebAuthn passkeys / secp256r1)
Gasless Relay OpenZeppelin Channels
Backend Node, Express, Prisma 7 + Neon Postgres
Frontend React, Vite, Tailwind, Framer Motion
Fiat Payouts Paystack + Lenco (stateless / zero-storage)

πŸ“‘ Deployment


πŸš€ Local Development

Prerequisites: Node 20+ (Prisma 7 requires β‰₯ 20.19), Rust + stellar CLI (for the contract), circom (only to regenerate circuits), a backend .env, and a frontend .env.

# Backend β€” prisma generate is required (the v7 client is gitignored)
cd backend && npm install && npx prisma generate && npm run dev   # http://localhost:3001

# Frontend
cd frontend && npm install && npm run dev                          # http://localhost:5173

Build & test the Soroban Shielded Pool:

cd SDK/contracts/shielded_pool && cargo test           # contract tests (real proofs)
stellar contract build                                  # wasm for deploy

Regenerate circuits + proving keys (only if you change a circuit):

cd SDK/circom && npm install
bash scripts/build.sh confidential_swap
bash scripts/build.sh merkle_insert
bash scripts/build.sh shielded_transfer

Key environment variables:

File Var Purpose
backend/.env NEON_CONNECTION_STRING Neon Postgres direct URL (?sslmode=require, no channel_binding)
backend/.env STELLAR_CONTRACT_ID / USDC_POOL_CONTRACT_ID XLM / USDC Shielded Pool contract ids
backend/.env XLM_SAC_ADDRESS / USDC_SAC_ADDRESS Stellar Asset Contract addresses backing each pool
backend/.env STELLAR_RELAYER_SECRET Admin/relayer keypair (pool init, faucet seeding)
backend/.env CHANNELS_URL / CHANNELS_API_KEY OpenZeppelin Channels β€” required for gasless smart-account submission
backend/.env CORS_ORIGIN Comma-separated allowed frontend origin(s); unset = allow all (dev only)
backend/.env PAYSTACK_SECRET_KEY / LENCO_* Naira payout providers
backend/.env FAUCET_NOTE_AMOUNT / FAUCET_NOTE_ASSET Onboarding seed note (defaults 500 / XLM)
backend/.env SEED_TOKENS Fallback public-funding for brand-new smart wallets (comma-separated contractId:amount)
frontend/.env VITE_API_URL Backend URL (powers swaps, transfers, scanning, notifications)
frontend/.env VITE_ACCOUNT_WASM_HASH / VITE_WEBAUTHN_VERIFIER_ADDRESS smart-account-kit account wasm + WebAuthn verifier contract
frontend/.env VITE_XLM_POOL_CONTRACT_ID / VITE_USDC_POOL_CONTRACT_ID Per-asset Shielded Pool contract ids
frontend/.env VITE_XLM_SAC / VITE_USDC_SAC Per-asset SAC addresses (drives which assets show up in the UI)

πŸ“œ License

See LICENSE.

About

Private Compliance for Cross-Border Payments

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages