Zero-knowledge wallet identity · Solana
GHOST
Your assets. Your identity. Your privacy.
“Prove what you own without revealing who you are.”
One wallet, many unlinkable identities. Show that you hold at least N of an SPL token without showing your address, your balance, or your other holdings — using real Groth16 proofs over an attested Solana snapshot.
Computed in your browser with the circuit's Poseidon parameters, shown in base58 like Solana keys. These are commitments, not wallet addresses — that's the point: none of them can be traced to a wallet. Demo secrets are random and discarded.
A verifier learns
- ✓Some GHOST identity held ≥ the threshold of this mint at the snapshot slot
- ✓Which signed snapshot was used, and roughly how many identities could have produced the proof
- ✓A nullifier unique to their app — used to stop double claims
- ✓That the proof answers their own fresh, unexpired challenge
A verifier never receives
- ✕Your wallet address or any signature from it
- ✕Your exact balance or your other token holdings
- ✕Your identity commitment or position in the tree
- ✕Anything that links you across different apps
Protocol
Four steps. One circuit.
01
Create a ghost
Generate a random identity secret in your browser. Only its Poseidon commitment is registered, with a wallet signature proving you own the wallet.
02
Snapshot
At a finalized Solana slot, the attester reads SPL balances over RPC and commits every (identity, mint, balance) into a Merkle tree, then signs the root.
03
Prove
Your browser generates a Groth16 proof: “some leaf in this signed tree is mine and its balance ≥ threshold”. The secret never leaves your device.
04
Verify
Any app checks the pairing equation, the attester signature, expiry, a single-use challenge and an app-scoped nullifier. No wallet address involved.
Snapshot, not live
Proofs establish your balance at the snapshot's finalized slot range — historical, not current. Every proof carries the slot and time; verifiers choose how old is acceptable.
Explicit trust
Solana offers no light-client state proofs a circuit can check, so balances are attested by a snapshot signer. A dishonest attester could include false balances; it cannot forge proofs or deanonymise nullifiers.
Privacy has limits
Small anonymity sets, timing, IP addresses and RPC logs can all correlate activity. GHOST reduces what is revealed; it does not guarantee anonymity. Threat model →
Developers
Gate anything on private holdings.
The TypeScript SDK issues requests with single-use challenges, opens GHOST for the user, and verifies the returned proof server-side with replay and sybil protection.
SDK reference →import { GhostVerifier } from "@ghost/sdk/verifier";
const verifier = new GhostVerifier({
appId: "https://dao.example/vote-7",
verificationKey,
trustedAttesters: [GHOST_ATTESTER],
});
// 1. server: issue a request
const request = await verifier.createRequest({
mint: JUP_MINT,
minBalance: 1_000_000_000n, // raw units
nullifierMode: "wallet", // one per wallet
});
// 2. browser: requestGhostProof(request, …)
// 3. server:
const { valid, statement } = await verifier.verify(bundle);