getReserves polling loop.
🛰️ Production Example: LiquidityRadar
The full open-source version of what you’ll build here. A zero-dependency engine, a clone-and-run CLI, and a deployable Cloudflare Worker with a public status page, webhook alerts, and flood protection. Fork it and point it at your own pools. View source →
How detection works
Every block in which a subscribed pool’s reserves change, the stream emits apool_reserves event with USD values already computed, including total_delta_usd, the signed dollar change of the whole pool in that block.
That one field is the entire trick:
A real capture of a 46,015
, the other -$45,930, net total_delta_usd: 84.37`. The same pool’s biggest one-block liquidity moves run six figures with nothing on the other side. A drain cannot disguise itself as trading volume.
Step 1: Watch a pool’s reserves live
No signup, no key. This streams real reserve changes for the largest USDC/WETH pool on Ethereum, one event per block in which its reserves changed:Live capture from production
total_reserve_usd: the pool’s current total liquidity in USDtotal_delta_usd: the signed USD change this block (the swap above nets to84.37, which is noise)block: where it happened, for receipts
Step 2: The detector
Watch up to 25 pools on a single connection by POSTing a JSON array. Events route back viarequest_id, the index of the subscription in your array. Mixing chains in one connection works.
The detection rule, two thresholds that scale together:
|total_delta_usd| ≥ MIN_USD, which ignores dust|total_delta_usd| / reserve_before ≥ MIN_PCT, which ignores moves the pool barely feels
node drain-detector.mjs / python drain_detector.py). At the default thresholds, alerts are rare by design. A 20% single-block move on a real pool is an event. Output from a live run with thresholds lowered for demonstration:
Live run (thresholds lowered to show output)
Step 3: Tune the thresholds
Why 20%? Because liquidity bots churn below it
Why 20%? Because liquidity bots churn below it
Major pools host JIT (just-in-time) liquidity bots that add and remove six-figure positions around individual swaps. We’ve measured them moving ±4% of an $8M pool every block, which is exactly the oscillation in the sample output above. Liquidity genuinely moves, so a detector genuinely fires; it’s just market plumbing, not an exit. A 20% bar clears that churn with a 5x margin. If you watch pools with heavy JIT activity and want a lower bar, net the deltas over a few consecutive blocks: JIT cycles cancel out, drains don’t.
Watching fresh, small pools (where rugs actually happen)
Watching fresh, small pools (where rugs actually happen)
For day-old pools in the 500k range, the defaults already work: a rug pull on a 70k negative delta at ~100% of the pool. If you want earlier partial-exit signals, drop
MIN_USD to ~$5k and keep MIN_PCT at 20%. On small pools the percentage gate does the real work.Finding pools worth watching
Finding pools worth watching
The REST pool search endpoint finds rug-candidates programmatically: pools created in the last week with real activity:Feed the resulting pool IDs straight into
POOLS. One connection covers 25 of them.Step 4: Route alerts anywhere
Replaceconsole.log with a webhook call and the detector becomes a bot. Discord, for example:
pool_id + block, hold a per-pool cooldown, and cap sends per hour, because a multi-block drain fires once per block otherwise. LiquidityRadar implements that gate, plus reconnection with backoff, a persistent status page, and one-command Cloudflare deployment, if you’d rather fork than build.
Limits and errors worth knowing
- 25 subscriptions per POST connection, 10 concurrent streams per IP. Chunk bigger watchlists across connections.
- One invalid entry rejects the whole multiplexed connection. The stream sends an
errorevent naming the bad asset ("pool not found: …"), then closes. Validate addresses before subscribing. errorevents are not all equal: subscription problems (asset not found,unsupported chain) are permanent: fix the input, don’t retry. Capacity problems (ip stream limit exceeded) clear on their own, so retry those with backoff.- A
pingarrives every ~15s; if nothing arrives for 30s+, reconnect. See error handling for the full catalog.
FAQ
Can a rug pull hide from this detector?
Can a rug pull hide from this detector?
Not by swapping: swaps net to ~zero in
total_delta_usd regardless of size. An exit could in principle drip liquidity out in many small blocks below your thresholds, which is why percentage-of-pool matters more than absolute USD: 100 blocks of 1% exits still cross a 20% net threshold if you accumulate deltas over a window.Why SSE instead of polling getReserves or listening to Sync events?
Why SSE instead of polling getReserves or listening to Sync events?
Polling misses everything between polls. A pool can drain in one block, and a 30-second loop finds out after the money is gone. Decoding
Sync/Mint/Burn logs over a WebSocket node connection works but costs a node provider subscription and per-DEX decoding logic. The reserve stream pushes per-block deltas with USD values pre-computed, for every major DEX shape (Uniswap v2/v3/v4, Curve, Aerodrome, Raydium, Orca…), with no key.Is this free? What's the catch?
Is this free? What's the catch?
No API key and no credit card to start. The limits are the catch, and there are three: 25 subscriptions per connection, 10 concurrent streams per IP, and each update delivered counts as one request against your monthly quota. A quiet market costs almost nothing; a fast-moving pair draws down quota much like polling would. See rate limits.
Does it work on Solana / Base / my chain?
Does it work on Solana / Base / my chain?
The same code in this guide watches Ethereum, Base, and Solana on one connection. Any chain DexPaprika indexes works, see networks.
Next steps
Reserve Streaming Overview
The full feature guide: both subscription methods, payload schemas, limits.
Streaming Quick Start
Prices and reserves from zero in 10 minutes.
Multi-Pool API Reference
The POST /sse/reserves endpoint in full detail.
LiquidityRadar on GitHub
The production version: deployable worker, status page, alert gate.
Get support
Join Discord
Connect with our community and get real-time support.
Give Feedback
Share your experience and help us improve.