Robinhood query examples
Working ClickHouse SQL against the Supanode Robinhood Chain Historical Data Indexer: cross-version swap feeds, active pools, readable pairs, launchpad funnels, bonding-curve flow, and execution cost.
// updated 2026-09-07
Runnable patterns against the robinhood database. Every column used here exists in the Table reference; for connection details see Indexer overview.
Always filter block_timestamp in PREWHERE on the trade tables. It feeds the partition key. Without it, a query against uniswap_v3_trades scans all 34 GB.
Keep big integers as strings. amount0, amount1, sqrt_price_x96 and the fee columns are 128- and 256-bit. Wrap them in toString() before they leave the database, or a JSON or float64 consumer will truncate them without telling you.
Recent swaps across both Uniswap versions
The two versions key their pools differently - v3 by contract address, v4 by pool_id - and v4 records no recipient. Normalizing both into one shape is the first thing most pipelines need.
SELECT
protocol,
block_timestamp,
transaction_hash,
pool,
sender,
recipient,
amount0_raw,
amount1_raw,
tick,
sqrt_price_x96
FROM
(
SELECT
'uniswap_v3' AS protocol,
block_timestamp,
transaction_hash,
pool_address AS pool,
sender,
toNullable(recipient) AS recipient,
toString(amount0) AS amount0_raw,
toString(amount1) AS amount1_raw,
tick,
toString(sqrt_price_x96) AS sqrt_price_x96
FROM uniswap_v3_trades
PREWHERE block_timestamp >= now() - INTERVAL 1 HOUR
ORDER BY block_number DESC, transaction_index DESC, log_index DESC
LIMIT 50
UNION ALL
SELECT
'uniswap_v4' AS protocol,
block_timestamp,
transaction_hash,
pool_id AS pool,
sender,
CAST(NULL, 'Nullable(String)') AS recipient,
toString(amount0) AS amount0_raw,
toString(amount1) AS amount1_raw,
tick,
toString(sqrt_price_x96) AS sqrt_price_x96
FROM uniswap_v4_trades
PREWHERE block_timestamp >= now() - INTERVAL 1 HOUR
ORDER BY block_number DESC, transaction_index DESC, log_index DESC
LIMIT 50
)
ORDER BY block_timestamp DESC
LIMIT 100
Order by the block triple, not by timestamp. Many swaps share a block_timestamp to the second. block_number, then transaction_index, then log_index is the only ordering that reproduces on-chain sequence exactly.
Most active pools in the last complete hour
Scanning the last complete hour instead of a moving window keeps consecutive runs comparable - a partial current hour always looks quieter than it is.
WITH pool_activity AS
(
SELECT
'uniswap_v3' AS protocol,
pool_address AS pool,
count() AS trades,
uniqExact(transaction_hash) AS transactions,
min(block_timestamp) AS first_trade,
max(block_timestamp) AS last_trade
FROM uniswap_v3_trades
PREWHERE block_timestamp >= toStartOfHour(now()) - INTERVAL 1 HOUR
AND block_timestamp < toStartOfHour(now())
GROUP BY pool_address
UNION ALL
SELECT
'uniswap_v4' AS protocol,
pool_id AS pool,
count() AS trades,
uniqExact(transaction_hash) AS transactions,
min(block_timestamp) AS first_trade,
max(block_timestamp) AS last_trade
FROM uniswap_v4_trades
PREWHERE block_timestamp >= toStartOfHour(now()) - INTERVAL 1 HOUR
AND block_timestamp < toStartOfHour(now())
GROUP BY pool_id
)
SELECT
protocol,
pool,
trades,
transactions,
first_trade,
last_trade
FROM pool_activity
ORDER BY trades DESC, protocol, pool
LIMIT 50
Turn a pool into a readable pair
A pool_address on its own tells you nothing. Join it through the pool table to tokens and it becomes a ticker pair with the decimals you need for every later calculation.
SELECT
p.pool_address,
t0.symbol AS symbol0,
t1.symbol AS symbol1,
t0.decimals AS decimals0,
t1.decimals AS decimals1,
p.fee / 10000 AS fee_percent,
p.tick_spacing,
p.block_timestamp AS created_at
FROM uniswap_v3_pools AS p
LEFT JOIN tokens AS t0 ON t0.token_address = p.token0
LEFT JOIN tokens AS t1 ON t1.token_address = p.token1
ORDER BY p.block_timestamp DESC
LIMIT 100
fee is in hundredths of a basis point. A 3000 in that column is 0.30 percent, which is why the query divides by 10,000.
A human price out of sqrt_price_x96
Uniswap stores price as a Q64.96 fixed-point square root. Squaring it and dividing by 2 to the 192nd gives the raw ratio; the decimal difference between the two tokens turns that into a price a person can read.
SELECT
s.block_timestamp,
t0.symbol AS symbol0,
t1.symbol AS symbol1,
pow(toFloat64(s.sqrt_price_x96) / pow(2, 96), 2)
* pow(10, toInt16(t0.decimals) - toInt16(t1.decimals)) AS price_1_per_0,
s.tick
FROM uniswap_v3_trades AS s
INNER JOIN uniswap_v3_pools AS p ON p.pool_address = s.pool_address
LEFT JOIN tokens AS t0 ON t0.token_address = p.token0
LEFT JOIN tokens AS t1 ON t1.token_address = p.token1
PREWHERE s.block_timestamp >= now() - INTERVAL 1 DAY
WHERE t0.symbol != '' AND t1.symbol != ''
ORDER BY s.block_timestamp DESC
LIMIT 100
Converting to Float64 loses precision by design. That is fine for charting and ranking, and wrong for accounting. If a number ends up in a ledger, keep the integers and do the arithmetic in a decimal type downstream.
Launchpad funnel: launched versus graduated
Pons runs a bonding curve and graduates tokens onto a pool, so it carries both a creations table and a migrations table. The gap between them is the funnel.
SELECT
toDate(c.block_timestamp) AS day,
count() AS launched,
countIf(m.token != '') AS graduated,
round(100 * countIf(m.token != '') / count(), 2) AS graduation_rate_pct
FROM pons_creations AS c
LEFT JOIN
(
SELECT DISTINCT token FROM pons_migrations
) AS m ON m.token = c.token
GROUP BY day
ORDER BY day DESC
LIMIT 60
Bonding-curve flow for one token
Before graduation, trading happens on the curve rather than in a pool. is_buy splits the direction, and quote_amount is the ETH-side leg.
SELECT
toStartOfHour(block_timestamp) AS hour,
countIf(is_buy) AS buys,
countIf(NOT is_buy) AS sells,
uniqExact(sender) AS traders,
toString(sumIf(quote_amount, is_buy)) AS quote_in_raw,
toString(sumIf(quote_amount, NOT is_buy)) AS quote_out_raw,
toString(sum(fee)) AS fees_raw
FROM pons_curve_trades
PREWHERE block_timestamp >= now() - INTERVAL 7 DAY
WHERE token = 'PASTE_TOKEN_ADDRESS_HERE'
GROUP BY hour
ORDER BY hour DESC
What a swap cost to execute
Gas and the EIP-1559 fields sit on every decoded event, so cost analysis needs no second data source.
SELECT
toDate(block_timestamp) AS day,
count() AS swaps,
round(avg(gas_used)) AS avg_gas_used,
round(avg(toFloat64(base_fee_per_gas)) / 1e9, 4) AS avg_base_fee_gwei,
round(avg(toFloat64(max_priority_fee_per_gas)) / 1e9, 4) AS avg_max_priority_gwei,
round(sum(toFloat64(gas_used) * toFloat64(base_fee_per_gas)) / 1e18, 6) AS base_fee_eth
FROM uniswap_v3_trades
PREWHERE block_timestamp >= now() - INTERVAL 30 DAY
GROUP BY day
ORDER BY day DESC
base_fee_eth is the burnt portion, not the full bill. The priority fee a transaction actually paid is bounded by max_priority_fee_per_gas but is not stored directly, so treat the max columns as a ceiling rather than the amount charged.
New tokens and who deployed them
SELECT
block_timestamp AS created_at,
token_address,
symbol,
name,
decimals,
creator,
transaction_hash
FROM tokens
PREWHERE block_timestamp >= now() - INTERVAL 7 DAY
ORDER BY block_timestamp DESC
LIMIT 200
Creator concentration is one join away. Group the same table by creator to see who is deploying at volume - the pattern that separates a single project from a token factory.
Next steps
All 25 tables, column by column.
Connection details and how the tables join.