How to Detect New Uniswap v4 Pools on Robinhood Chain
Detect every new Uniswap v4 pool on Robinhood Chain as it is created: the pools:new stream, raw PoolManager Initialize logs, and hook checks before you trade.
Crius 9 min read
Short answer: subscribe to the pools:new channel on the Crius WebSocket stream and you receive every new Uniswap v2, v3 and v4 pool on Robinhood Chain within about a block, with the v4 pool id, both currencies, the fee, the tick spacing and the hook’s decoded permissions. Without Crius, read Initialize logs from the v4 PoolManager (0x8366a39cc670b4001a1121b8f6a443a643e40951) over JSON-RPC, then check each hook before you trust the pool.
Uniswap v4 is where most new markets on Robinhood Chain are born, and many of them come with a hook that can change how swaps behave. This guide shows both ways to detect new v4 pools, what a new pool actually tells you, and how to screen hooks automatically. It is the practical companion to Uniswap v4 hooks on Robinhood Chain, explained, which covers how hook permissions work. Every output below comes from Robinhood Chain mainnet in October 2026.
How many new pools to expect
From the chain stats endpoint, the 24 hours before writing saw 8,890 new pools and curves:
| Venue | New in 24 h | 24 h volume |
|---|---|---|
| Uniswap v4 | 4,537 | $286.5 million |
| Uniswap v2 | 1,595 | $6.4 million |
| Pons bonding curves | 1,340 | $6.0 million |
| Other launch curves | 1,275 | $11,000 |
| Uniswap v3 | 143 | $237.6 million |
That is about three new v4 pools a minute. Most are launchpad pools or one-off memecoin pairs, but v4 also carries more than half of the chain’s DEX volume, so the few that matter matter a lot. Any detector therefore needs a cheap first pass (what was created, with which hook) and a second, more expensive look only at the pools worth it.
What defines a v4 pool
Uniswap v4 keeps every pool inside one contract, the PoolManager. A pool is identified by its PoolKey:
| Field | Meaning |
|---|---|
currency0, currency1 |
The two assets, sorted by address. Native ETH is 0x0000…0000, not WETH. |
fee |
The LP fee in hundredths of a basis point (3000 = 0.30%). 0x800000 (8,388,608) is the dynamic-fee flag: the hook sets the fee per swap. |
tickSpacing |
Price granularity of positions. |
hooks |
The hook contract, or 0x0 for none. |
The pool id is keccak256(abi.encode(PoolKey)), a 32-byte value rather than a contract address. You cannot reverse it into the key, which is why the creation event matters: it is the only place the key is published.
Option 1: the pools:new stream
The pools:new channel pushes one message per new pool on every DEX Crius indexes. A real v4 message, as received:
{ "ch": "pools:new", "seq": 168, "block": 96729348, "ts": 1792841269000, "data": { "dex": "uniswap_v4", "pool": "0xe821c1b762c480e8f4a93dd47b9c9e0c6b339b1b6c3890111de12a4f9e0c058c", "token0": "0x0000000000000000000000000000000000000000", "token1": "0xcb20c41d6ae4bdba0a87400c8e64634386e306d7", "fee": 0, "tick_spacing": 200, "hook": { "address": "0x745d717620052a97a22deee2e5eba59583f3e0cc", "permissions": ["beforeInitialize", "beforeSwap", "afterSwap", "beforeSwapReturnDelta", "afterSwapReturnDelta"] } }}Note what is already decoded for you: the hook’s permissions, read from the low bits of the hook address, are in the message. A pool with beforeSwap plus the ReturnDelta permissions can reprice or tax swaps; a pool without a hook has "hook": null.
This detector listens for v4 pools and, for hooked ones, looks the hook up with /hook/{address} to get its name, verification status, risk flags and how many pools use it (Node.js 22+):
// detect-v4.mjs: node detect-v4.mjsconst API = "https://api.crius.sh/v1/robinhood";const headers = { "x-api-key": process.env.CRIUS_API_KEY };const ws = new WebSocket(`wss://api.crius.sh/v1/robinhood/stream?api_key=${process.env.CRIUS_API_KEY}`);
ws.onopen = () => ws.send(JSON.stringify({ op: "subscribe", channels: ["pools:new"] }));ws.onmessage = async (e) => { const m = JSON.parse(e.data); if (m.ch !== "pools:new" || m.data.dex !== "uniswap_v4") return; const { pool, fee, tick_spacing, hook } = m.data; if (!hook) { console.log(`block ${m.block} v4 pool ${pool.slice(0, 10)}… fee ${fee} spacing ${tick_spacing} (no hook)`); return; } // Hooked pool: name, verification, risk flags and adoption of the hook. const r = await fetch(`${API}/hook/${hook.address}`, { headers }); const h = (await r.json()).data ?? {}; console.log(`block ${m.block} v4 pool ${pool.slice(0, 10)}… hook ${h.name ?? hook.address}`, `verified=${h.verified} risk=${(h.risk ?? []).join(",") || "none"} pools_using_hook=${h.pools}`);};Two minutes of real output:
block 96717627 v4 pool 0xc6fef029… hook 0x2c668601193e4a081dd82604785e436aff6a2acc verified=false risk=can_block_or_reprice_swaps,can_modify_swap_amounts,can_gate_liquidity_removal,can_gate_liquidity_add,unverified pools_using_hook=2219block 96717800 v4 pool 0x61e82d84… hook DopplerHookInitializer verified=true risk=can_modify_swap_amounts pools_using_hook=219074block 96718360 v4 pool 0xd03b7207… fee 10000 spacing 200 (no hook)The first line is exactly the kind of pool worth flagging: an unverified hook that can block swaps, change swap amounts and gate liquidity in both directions. The second is a widely used, verified hook behind more than 219,000 pools.
Option 2: raw Initialize logs from the PoolManager
If you run your own node or want to verify Crius against the chain, read the PoolManager’s Initialize event directly. Its signature in v4-core is:
event Initialize( PoolId indexed id, Currency indexed currency0, Currency indexed currency1, uint24 fee, int24 tickSpacing, IHooks hooks, uint160 sqrtPriceX96, int24 tick);The topic is keccak256("Initialize(bytes32,address,address,uint24,int24,address,uint160,int24)") = 0xdd466e67…6438. The pool id and both currencies are indexed topics; fee, tick spacing, hooks, starting price and tick are in the data. This Python script reads the last 3,000 blocks from the public RPC:
"""Find new Uniswap v4 pools straight from the chain: PoolManager `Initialize` logs over the last N blocks."""import requests
RPC = "https://rpc.mainnet.chain.robinhood.com"POOL_MANAGER = "0x8366a39cc670b4001a1121b8f6a443a643e40951"INITIALIZE = "0xdd466e674ea557f56295e2d0218a125ea4b4f0f6f3307b95f85e6110838d6438"
def rpc(method, params): r = requests.post(RPC, json={"jsonrpc": "2.0", "id": 1, "method": method, "params": params}, timeout=20).json() if "error" in r: raise RuntimeError(r["error"]) return r["result"]
def word(data: str, i: int) -> int: return int(data[2 + 64 * i: 2 + 64 * (i + 1)], 16)
head = int(rpc("eth_blockNumber", []), 16)logs = rpc("eth_getLogs", [{"address": POOL_MANAGER, "topics": [INITIALIZE], "fromBlock": hex(head - 3000), "toBlock": hex(head)}])for log in logs: t, d = log["topics"], log["data"] fee, tick_spacing, hooks = word(d, 0), word(d, 1), "0x" + d[2 + 64 * 2 + 24: 2 + 64 * 3] tick_spacing = tick_spacing - (1 << 256) if tick_spacing >= 1 << 255 else tick_spacing print(int(log["blockNumber"], 16), "pool", t[1][:12] + "…", "currency0", "0x" + t[2][-40:], "currency1", "0x" + t[3][-40:], "fee", fee, "spacing", tick_spacing, "hooks", hooks if int(hooks, 16) else "none")print(f"{len(logs)} new v4 pools in the last 3,000 blocks (~5 minutes)")Real output (trimmed):
96720270 pool 0x8bcebf89f8… currency0 0x5fc5360d0400a0fd4f2af552add042d716f1d168 currency1 0xc0d6457c16cc70d6790dd43521c899c87ce02f35 fee 8388608 spacing 10 hooks 0xfd72eea606df1050b110503b4953c10fd4c870c096720372 pool 0xf78f24593b… currency0 0x51418def50e62a103989b0760a2e504765679aac currency1 0xd917b029c761d264c6a312bbbcda868658ef86a6 fee 8388608 spacing 200 hooks 0x4e3468951d49f2eea976ed0d6e75ffcb44a9a54496720935 pool 0x8ac8300c4e… currency0 0x1fbc318342701e9797a229fa3f1c8d621a411e18 currency1 0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec fee 8388608 spacing 8 hooks 0x4e3468951d49f2eea976ed0d6e75ffcb44a9a54416 new v4 pools in the last 3,000 blocks (~5 minutes)Every pool here uses the dynamic-fee flag (8388608), so the fee you pay is whatever the hook decides at swap time. The first line pairs USDG with the Meta stock token through a hook (0xfd72…70c0) that, when we looked it up, was unverified, used by 8 pools and held beforeSwap: worth knowing before routing a stock-token trade through it.
The raw route is what an indexer does under the hood. To run it continuously you also need to handle reorgs, decode hook permissions from the address bits, look up token metadata, and backfill after restarts. The RPC vs indexed API comparison covers where that work pays off and where it doesn’t.
Look at the pool before you trade it
A new pool is a promise, not a market. Two things to check:
1. Is there liquidity? In v4, initializing a pool and adding liquidity are separate steps; Initialize can be followed by nothing at all. Call /pool/{pool} for live reserves, liquidity and lock status, or subscribe to lp:<token> to hear when liquidity is added. A graduated Pons pool, for example, comes back with:
{ "dex": "uniswap_v4", "hook": { "address": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044", "name": "PonsV2MemeHook", "permissions": ["beforeInitialize", "afterSwap", "afterSwapReturnDelta"], "risk": ["can_modify_swap_amounts"] }, "lp": { "locked_pct": 100, "locker": "pons_v2_launch_locker", "permanent": true, "status": "locked" }, "state": { "reserves": { "currency0": 4.70785, "currency1": 182066584, "kind": "real" }, "tick": 174715 }}2. What can the hook do? The hooks list ranks all 29,474 hooks Crius has seen on Robinhood Chain. The most used at time of writing:
| Hook | Pools | Verified | Risk flags |
|---|---|---|---|
| DopplerHookInitializer | 219,070 | yes | can_modify_swap_amounts |
| ClankerHookStaticFeeV2 | 15,354 | yes | can_block_or_reprice_swaps, can_modify_swap_amounts, can_gate_liquidity_add |
| PonsV2MemeHook | 10,428 | yes | can_modify_swap_amounts |
| CashCatHookV2 | 9,184 | yes | can_block_or_reprice_swaps, can_modify_swap_amounts, can_gate_liquidity_removal, … |
| LunchTaxHookPair (proxy) | 7,323 | yes | can_block_or_reprice_swaps, can_modify_swap_amounts |
| UniversalKlikHook | 6,929 | yes | can_block_or_reprice_swaps, can_modify_swap_amounts |
Risk flags describe capability, not intent: launchpad hooks need afterSwapReturnDelta to take their fee. What should raise your guard is an unverified hook with blocking or delta permissions and few pools. Filter for exactly that (at time of writing this returned 20,322 hooks, the busiest used by 5,343 pools):
curl -s "https://api.crius.sh/v1/robinhood/hooks?verified=false&risk=can_block_or_reprice_swaps&sort=pools&limit=20" \ -H "x-api-key: $CRIUS_API_KEY"For a token you are about to trade, /security goes further and measures the tax actually paid on real buys and sells through the hook, which catches behaviour a permission check cannot see. See the token security API.
A practical screening pipeline
Putting it together, a production detector usually looks like this:
- Stream
pools:newand keepdex == "uniswap_v4"(or more venues if you route anywhere; the channel also carries v2, v3 and launchpad curves). - Classify the hook from the message: no hook, a known launchpad hook (Pons, Doppler and so on), or something else.
- Enrich the rest with
/hook/{address}and cache the result per hook; with 29,000 hooks and 219,000 pools on the largest one, most lookups hit the cache. - Wait for liquidity via
lp:<token>or/pool/{pool}before treating the pool as a market. - Score the token with security, holders and bundle detection.
Step 1 costs no credits. Steps 3–5 cost a few credits per pool, and you only take them for pools that pass the earlier steps.
FAQ
How fast does pools:new deliver a new v4 pool?
As soon as the block containing the Initialize is indexed, typically within a second or two on Robinhood Chain’s ~100 ms blocks.
Does pools:new include Uniswap v2 and v3 pools too?
Yes. It carries every new market Crius indexes, with dex naming the venue: uniswap_v2, uniswap_v3, uniswap_v4, and launchpad bonding curves such as pons_curve and launch_curve. In a three-minute sample we saw 4 Pons curves, 3 v4 pools, 2 v2 pools and 2 other launch curves, so filter on dex if you only want v4.
What does fee 8388608 mean on a v4 pool?
It is 0x800000, Uniswap v4’s dynamic-fee flag. The pool’s LP fee is set by its hook on every swap instead of being fixed at creation.
Why does a new pool have no price?
Because nobody has added liquidity or traded yet. Initialize sets a starting price, but until liquidity arrives the pool cannot fill a trade.
Can I get every v4 pool ever created, not just new ones? Yes. Crius indexed Robinhood Chain from block one; use the Uniswap v4 API and historical data for backfills.
Related
- Uniswap v4 API and WebSocket API
- Uniswap v4 hooks on Robinhood Chain, explained
- Robinhood Chain WebSocket API: Live Trades, Prices and Reconnects
- Honeypot and tax detection
- Reference: pool detail, hooks, stream channels
Sources
- Uniswap docs: Pool manager and pool initialization
- Uniswap docs: IPoolManager reference
- Pool counts, volumes, hook statistics and outputs: Crius API and the public Robinhood Chain RPC (chain id 4663), October 24, 2026.
Build with Crius
Robinhood Chain data in one API
Free plan with 1M credits a month. Your key in under a minute.
More from the blog
- PonsLaunchpads
Track Pons Bonding Curve Progress With an API
Read Pons bonding curve progress on Robinhood Chain, ETH raised versus the 4.2 ETH threshold, in real time; with code for speed, ETA and graduation alerts.
- ScreenerNode.js
Build a Robinhood Chain Token Screener with Node.js
Build a Robinhood Chain token screener in Node.js: rank by volume or momentum, filter on liquidity, holders and flow, then check tax, honeypots and LP.
- Uniswap v4Security
Uniswap v4 hooks on Robinhood Chain, explained
What Uniswap v4 hooks are, how their permissions are encoded in the hook address, which permissions can block swaps or trap liquidity, and how to check any hook on Robinhood Chain.