CA
GHOST

GHOST

Your assets. Your identity. Your privacy.

GHOST is a zero-knowledge wallet identity protocol for Solana. A user links any number of private ghost identities to one wallet and proves statements like

“I hold at least 1,000 of token M”

to a third-party app without revealing their wallet address, their exact balance, or any of their other holdings.

Status: research-grade, working MVP. It has not been independently audited. Do not rely on it to protect high-value assets or people at risk until it has been reviewed.

What is real

ComponentImplementation
Proving systemGroth16 over BN254, Circom 2.2 circuit (6,175 constraints), snarkjs 0.7
Trusted setupPhase 1: PSE Perpetual Powers of Tau (80 contributions). Phase 2: local single-party contribution + beacon (must be replaced by an MPC ceremony for production)
Hash / commitmentsPoseidon (circomlib parameters), domain-separated
Authenticated stateEd25519-signed snapshot of finalized SPL balances, Poseidon Merkle tree (depth 20)
Wallet ownershipEd25519 signMessage verification (tweetnacl) at registration
Replay protectionVerifier-issued single-use challenges bound into the proof + expiry + app-scoped nullifiers
WalletsSolana-native wallets only (Phantom, Solflare incl. web wallet, Backpack, Glow) via Solana Wallet Adapter; Wallet-Standard EVM/multichain wallets such as MetaMask are hidden and auto-disconnected (app/src/components/wallet-button.tsx)

Nothing is mocked: every “valid” in the UI is a pairing check performed by snarkjs plus an Ed25519 signature check. The only test double in the whole repository is an in-memory stand-in for Solana RPC in unit tests, plus one opt-in test that hits live devnet.

What a proof means

A valid GHOST proof establishes:

  1. Some identity registered in the GHOST registry, whose wallet's finalized balance of mint M at the snapshot slot range was ≥ threshold,
  2. is controlled by whoever generated this proof (knowledge of the identity secret and wallet key),
  3. for this app (appId), this challenge, before this expiry.

It is a statement about historical state — the snapshot's slot range — not a live balance. The bundle carries createdAt, baseSlot, slotMin and slotMax; verifiers decide how old a snapshot they accept (maxSnapshotAgeSeconds).

What a proof does not mean

  • It does not prove the user still holds the tokens now.
  • It does not prove anything without trusting the snapshot attester (see Architecture).
  • It does not guarantee anonymity. See Threat model.

Where to go next