Real-time pump.fun token discovery. Lily watches the chain and surfaces coins side by side, filtered by transparent on-chain quality gates β so you spend time on interesting coins instead of scrolling past obvious rugs.
It is a discovery board only. No trading, no wallet connection, no order placement, no financial advice anywhere in this repo.
ββββββββββββββββββββββ¬βββββββββββββββββββββ¬βββββββββββββββββββββ
β Old pre-bond β New pairs β Bonded β
β β β β
β older, un-bonded β fresh launches, β freshly graduated β
β coins that just β gated live for β coins, gated for β
β took fresh bids β bundles / rug β bundles / rug β
β again β holders / dumps β holders / dumps β
ββββββββββββββββββββββ΄βββββββββββββββββββββ΄βββββββββββββββββββββ
New pairs and Bonded share one gate pipeline, and each coin flows through three mutually-exclusive states β Unchecked β Blocked β Tradable.
Most coins die young. Once in a while an older coin that never bonded starts taking bids again. Lily lists tokens that are old enough, still un-bonded, and have logged a burst of recent on-chain activity (measured with a key-less RPC signature probe).
Brand-new pump.fun launches, tracked live on their pre-bond trade tape. Launches are a firehose, so cost is bounded: Lily caps how many it watches at once and only spends RPC gating coins that show real traction. Survivors run the same gates as Bonded. A coin leaves this column when it bonds (it then appears in Bonded).
Coins that bond (migrate to an AMM) are tracked live for market cap, all-time high
and drawdown, and gated for bundles, rug holders (whale-float /
creator-retention), early dumps (deeply negative first-minute return and net
flow), and craters. Three tabs reflect each coin's state β Unchecked
(pending), Blocked (tripped a gate), Tradable (passed every gate). Gate
thresholds live in
discovery/lib/gates.mjs as documented, tunable
constants β verify them on your own data.
Each row shows accurate, on-chain-derived stats only: USD market cap, volume, TX count, age, dev (creator) %, top-10 holder concentration, and the gate verdict.
discovery/
lib/ config Β· PumpPortal WS client Β· Solana RPC (Token-2022 safe) Β·
gates Β· SQLite store Β· sol price Β· request metrics
old-prebond.mjs reawakened old coins -> SQLite (feed "old")
bonded.mjs migrations + gating -> SQLite (feed "bonded")
server/server.mjs public read API (CORS, cache, optional keys, rate limit)
src/ Vite + React UI (three-column board)
Daemons persist each flush to SQLite (durable current board + ~48h history); the API reads from it. Nothing shares memory, so you can run any subset, and the board survives restarts.
cp .env.example .env # then edit β at minimum set a real SOLANA_RPC_URL
npm installA dedicated RPC provider (Helius / Triton / QuickNode) is strongly recommended β
the gates make many getSignaturesForAddress / getTokenLargestAccounts calls.
The PumpPortal data WebSocket is free and key-less. Each column header shows that
section's live API request rate so you can watch your load.
Two terminals:
npm start # API server + Old/Bonded discovery daemons (auto-restart)
npm run dev # the UI at http://localhost:5174Or individually: npm run api, npm run discover:old, npm run discover:bonded.
The backend exposes precomputed JSON (CORS-open, ~2s cache, optional API keys via
LILY_API_KEYS, rate-limited via RATE_LIMIT_PER_MIN):
GET /api/old GET /api/bonded GET /api/health
Because discovery runs once on the backend, serving these to many consumers does not increase RPC cost β readers just read cached output.
See DEPLOY.md for Render (blueprint included), Railway, persistence
(SQLite on a volume), and serving the UI under a subpath like enrich.fun/lily.
MIT β see LICENSE. Provided as-is, for informational/educational use. Nothing here is financial advice.
