A terminal UI that searches your filesystem for forgotten Solana keypairs and shows their balances across mainnet, devnet, and testnet, along with deployed programs and reclaimable rent.
Useful for finding old wallets you forgot about, auditing which keys you have lying around, and reclaiming SOL from buffer accounts and closeable program deployments.
- Recursive filesystem scan for Solana CLI keypair files (
[u8; 64]JSON arrays). - Streaming RPC fan-out: balances on mainnet, devnet, and testnet in parallel.
- Lists deployed programs and orphaned buffer accounts authored by each wallet, with total reclaimable lamports per row.
- Sortable, multi-selectable, identity-anchored cursor that survives sort changes.
- Skips accounts owned by BPF loaders so program keypairs do not clutter the wallet view.
- Validated keypair bytes are zeroed in memory immediately after the public key is derived.
- Bun 1.3 or later.
git clone https://github.com/arihantbansal/solana-keypair-search.git
cd solana-keypair-search
bun installbun run dev [ROOT...] [OPTIONS]Scans ~/.config/solana and the current directory by default.
# Default scan: ~/.config/solana + cwd, balances only
bun run dev
# Scan specific directories
bun run dev ~/projects ~/backup
# Enable program and buffer queries via Helius
HELIUS_API_KEY=xxx bun run dev
# Scan only mainnet
bun run dev --networks mainnet
# Use a custom mainnet RPC
bun run dev --mainnet-url https://my-rpc.example.comPublic mainnet rejects getProgramAccounts against the BPF loader, so the program
and buffer columns are disabled unless you supply a permissive endpoint via
HELIUS_API_KEY or --mainnet-url.
| Flag | Description |
|---|---|
ROOT... |
Directories to scan. Defaults to ~/.config/solana plus the current directory; supplying any ROOT replaces the default entirely. |
--helius-key <KEY> |
Helius API key. Also accepted via HELIUS_API_KEY. Enables program and buffer queries. |
--networks <LIST> |
Comma-separated subset of mainnet,devnet,testnet. Defaults to all three. Program and buffer queries run against mainnet only and are skipped when mainnet is excluded. |
--mainnet-url <URL> |
Override the mainnet RPC endpoint. |
--devnet-url <URL> |
Override the devnet RPC endpoint. |
--testnet-url <URL> |
Override the testnet RPC endpoint. |
-h, --help |
Print usage and exit. |
| Key | Action |
|---|---|
Up / Down |
Move cursor |
Space |
Toggle row selection |
Enter |
Copy selected addresses to the clipboard (OSC 52) |
Esc |
Clear selection |
Tab |
Switch focus between list and detail panes |
1-6 |
Sort by address, mainnet, devnet, testnet, programs, buffers |
q |
Quit |
Ctrl-C |
Quit |
Pressing the same sort key again toggles ascending and descending order. The clipboard copy uses OSC 52, which works over SSH and in most modern terminal emulators (iTerm2, Alacritty, Kitty, WezTerm, recent xterm).
- A bounded async walker traverses the roots, pruning ignored directories
(
node_modules,.git, build outputs, etc.) and skipping files whose extension or size cannot fit a 64-byte keypair JSON. - Surviving candidates are parsed in a 32-wide pool. Each parse validates the shape, derives the public key, and zeroes the secret buffer.
- Discovered addresses stream into a backpressured pipeline: balances are
fetched in batches of 50 via
getMultipleAccounts, and program and buffer queries are issued per address with bounded concurrency to avoid RPC bursts. - The Zustand store models every cell as a pending, loaded, skipped, or error union. The UI subscribes to a memoized selector that recomputes only when the underlying row map, sort key, or sort direction changes.
- Secret bytes are read once, validated, and used to derive the public key via
@solana/kit; the byte buffer and the parsed JSON array are then explicitly zeroed. - Complete erasure is not achievable in JavaScript: the file's UTF-8 string and SDK-internal key copies remain in memory until garbage collection. Treat zeroization as best-effort hygiene, not a hard guarantee.
- The tool never writes to disk and never sends keys over the network — only derived public addresses are sent to RPC providers.
- File paths to discovered keypairs are kept in memory only for display.
bun run dev # Run the TUI
bun run check # Typecheck, lint, format check, tests, and build
bun run test # Tests only
bun run lint # oxlint
bun run format # oxfmt
bun run build # Bundle to dist/The codebase uses strict TypeScript (exactOptionalPropertyTypes,
noPropertyAccessFromIndexSignature, no any, no non-null assertions, no
type assertions). All checks must pass before a commit.