Privacy-First Donations on Solana
Stealth Link enables creators, developers, and privacy-conscious users to receive SOL donations publicly on Twitter/X without revealing their wallet balance or transaction history. It bridges the viral distribution of Solana Blinks with the ZK-privacy of Light Protocol.
In the Web3 creator economy, accepting public donations means doxxing your wallet.
- Public Donations: Put your SOL address in your bio and everyone sees your balance, trade history, and net worth
- The "Anon" Dilemma: Privacy-focused users can't accept tips because it links their persona to their financial identity
- Existing Tools: Current solutions (Blink generators, tip jars) offer zero on-chain privacy
A Stealth Link (e.g., stealth.link/donate/ariel.stealth) unfurls as a native Blink on Twitter/X. When a supporter donates:
- Frontend: The Blink constructs a transaction using Light Protocol
- On-Chain: SOL is shielded (compressed) into a private UTXO owned by the creator
- Result: Explorer shows
Donor → Light Protocol Pool. The creator's personal wallet never receives a public transfer
The creator manages funds via a private Dashboard where they can view their shielded balance and unshield (withdraw) to any fresh wallet when needed - breaking the on-chain link entirely.
┌─────────────────────────────────────────────────────────────────────┐
│ TWITTER/X │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ BLINK PREVIEW │ │
│ │ ┌──────────┐ │ │
│ │ │ 🥷 Icon │ Donate to @creator │ │
│ │ └──────────┘ Support privately via Stealth Link │ │
│ │ │ │
│ │ [0.1 SOL] [0.5 SOL] [1 SOL] [Custom] │ │
│ └─────────────────────────────────────────────────────────────┘ │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ STEALTH LINK API │
│ │
│ GET /api/actions/donate/[username] → Blink metadata │
│ POST /api/actions/donate/[username] → Create shield transaction │
│ │
│ Resolves: .sol domains, .stealth handles, raw pubkeys │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ LIGHT PROTOCOL (ZK COMPRESSION) │
│ │
│ LightSystemProgram.compress() │
│ ┌──────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Donor │───▶│ Light Pool │───▶│ Shielded UTXO │ │
│ │ Wallet │ │ (Public) │ │ (Private Owner) │ │
│ └──────────┘ └──────────────┘ └─────────────────┘ │
│ │
│ PUBLIC EXPLORER: "Donor → Light Protocol" │
│ ACTUAL STATE: Creator owns compressed SOL │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CREATOR DASHBOARD │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Shielded Balance: 2.5 SOL │ │
│ │ │ │
│ │ [Unshield to Connected Wallet] │ │
│ │ [Unshield to Fresh Address: _____________ ] │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ LightSystemProgram.decompress() → Any destination wallet │
└─────────────────────────────────────────────────────────────────────┘
When a donor sends SOL through a Stealth Link:
// Blink API constructs this transaction
const ix = await LightSystemProgram.compress({
payer: donorPubkey,
toAddress: creatorPubkey,
lamports: amountLamports,
outputStateTree: stateTree, // V1 tree for compatibility
});The SOL enters Light Protocol's compression pool. The creator's ownership is recorded as a compressed account - invisible on standard explorers.
Creators withdraw from the Dashboard:
// Fetch creator's compressed accounts via Helius ZK-RPC
const accounts = await rpc.getCompressedAccountsByOwner(creatorPubkey);
// Decompress to any destination (fresh wallet for max privacy)
const ix = await LightSystemProgram.decompress({
payer: creatorPubkey,
toAddress: destinationPubkey, // Can be any wallet
lamports: totalBalance,
inputCompressedAccounts: accounts,
});On-chain Anchor program maps human-readable handles to wallets:
#[account]
pub struct RegistryEntry {
pub handle: String, // "ariel"
pub authority: Pubkey, // Wallet owner
pub bump: u8,
}
// PDA: seeds = ["stealth", handle.as_bytes()]| Component | Technology | Purpose |
|---|---|---|
| Privacy Engine | Light Protocol | ZK Compression for shielded SOL transfers |
| Distribution | Solana Blinks | Native Twitter/X integration |
| Indexing | Helius | ZK-enabled RPC for compressed state |
| Framework | Next.js 14 | App Router for API + Dashboard |
| Registry | Anchor Program | On-chain handle → wallet mapping |
- Node.js 18+
- Helius API Key (Get one here)
git clone https://github.com/mindsend-datatech/solana-stealth.git
cd solana-stealth
npm installCreate .env.local:
NEXT_PUBLIC_HELIUS_RPC_URL=https://devnet.helius-rpc.com/?api-key=YOUR_HELIUS_KEYnpm run dev- Open
http://localhost:3000/api/actions/donate/YOUR_WALLETto see the Blink JSON - Use dial.to to preview how the Blink renders
- Register a
.stealthhandle athttp://localhost:3000/register
Light Protocol has V1 and V2 state trees. We explicitly select V1 trees because:
- V2 trees require additional account permissions during decompression
- V1 provides stable, well-tested behavior for our use case
// Only use V1 trees (treeType: 1)
const outputStateTree = stateTrees.find(t => t.treeType === 1);Maximum privacy requires breaking the on-chain link. The Dashboard allows unshielding to any destination:
- Shield funds arrive at Light Pool → Creator's compressed account
- Creator unshields to a fresh wallet address (not their main wallet)
- No on-chain connection between donor, creator identity, and final funds
See ROADMAP.md for detailed status and plans.
Completed:
- Registration page for
.stealthhandles - Blink API with .sol/.stealth/pubkey resolution
- Dashboard with shielded balance display
- Unshield to connected wallet
- Unshield to fresh wallet (privacy mode)
- RPC error handling with retry
Planned:
- Gas relayer for donor privacy
- Meta-address support (one-time stealth addresses)
- SPL token support (USDC, BONK)
MIT
Built with support from:
- Light Protocol - ZK Compression infrastructure
- Helius - ZK-enabled RPC indexing
- Dialect - Blink specification and tooling