How to track Solana DEX trades with the new swaps dataset
Comparing execution across Solana's DEXes is the same problem equities markets solved with the consolidated tape system. Here's how to get every swap into one uniform shape in your own ClickHouse, then measure effective price, spread, venue share, and routed flow.

Software Engineer
Solana has become one of the busiest venues for trading digital assets, aiming to be the internet's capital market. The question is whether onchain markets can deliver execution that stands up next to traditional venues, and attract the capital watching from the sidelines.
That comes down to what any trading desk measures a venue on: reliable fills, tight spreads, and low fees. But measuring them means getting clean trade data, which, on Solana is surprisingly hard to get!
Why tracking Solana DEX swaps is hard
A DEX (decentralized exchange) is a venue where trades settle onchain rather than through a broker and a clearing house. On Solana, each DEX is deployed as its own program: Solana's term for a smart contract, living at its own address, written by its own team.
There's no unified swap standard on Solana. Each program is built on its own framework, with its own instruction layout and event encoding.
The mechanics of how each venue prices a trade differ too. And developers update their programs, introducing new instructions, events, and fields to track.
So before you can ask about price or volume, you're maintaining a decoder for each venue and keeping it current.
This is the same problem that US equities had in the 1970s. There were dozens of venues, no common format, and no way to compare a fill on one against a fill on another. They solved it with the consolidated tape, the normalized stream that made execution analysis routine – and has run continuously since.
Goldsky's Solana DEX Trades dataset does the equivalent job.
Programs in the Solana DEX swaps dataset
solana.dex_swaps currently decodes swaps from seven programs:
DEX (project) | Program ID |
|---|---|
raydium_clmm | CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK |
raydium_cpmm | CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C |
orca_whirlpool | whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc |
meteora_damm_v1 | Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB |
meteora_damm_v2 | cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG |
pump_amm | pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA |
pumpfun | 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P |
Every row is a single swap from the trader's perspective. These columns are probably what you're most interested in:
token_bought_amount/token_sold_amount— What the trader received and paid.- These are already scaled by token decimals into human units. Onchain, token balances are stored as integers. USDC for example carries six decimals, so 1,000,000 base units is $1. The dataset does the division for you.
- Raw base-unit integers (
_amount_raw) are there too if you need exact arithmetic.
token_bought_mint_address/token_sold_mint_address— The SPL mints on each side of the trade.project— The human-readable DEX label, so you canGROUP BYvenue without memorizing program IDs.trade_source— For top-level swaps, this isdirect. For inner instructions, it's the program ID of the router/aggregator (e.g. Jupiter) that invoked the swap.- Routers are smart order routers in the conventional sense: a trader submits an order to the router, and it splits and forwards the fill across whichever venues offer the best price. This column tells you which flow arrived that way, which makes routing analysis easy.
block_timestamp,block_slot,tx_id,instruction_index— Everything you need to trace a row back onchain.
The suffixes describe pricing mechanics. Automated market makers (AMMs) quote against a fixed formula. Concentrated liquidity market makers (CLMMs) let providers deploy capital into specific price bands instead. Pump.fun's launchpad uses dynamic bonding curves, where price moves as supply is minted. The dataset normalizes all of them into the same row shape.
The full schema is in the docs.
Stream Solana DEX trades into ClickHouse
With Turbo, getting Solana DEX trades into your own ClickHouse is a single pipeline definition. Turbo creates the tables automatically with a ReplacingMergeTree engine. It deduplicates on the primary key, so replays and chain reorganizations resolve to one row per swap rather than accumulating duplicates:
name: solana-dex-swaps-clickhouse
resource_size: m
sources:
dex_swaps:
type: dataset
dataset_name: solana.dex_swaps
version: 1.0.0
start_block: 426725306
options:
dex: raydium_clmm,orca_whirlpool,raydium_cpmm,pump_amm,pumpfun
Transform: {}
sinks:
clickhouse_swaps:
type: clickhouse
from: dex_swaps
table: solana_dex_swaps
primary_key: id
secret_name: MY_CLICKHOUSEWith all the trade data in ClickHouse, we can run price execution research on the following token pair:
- wSOL (
So11111111111111111111111111111111111111112) - While SOL is Solana's native asset, it doesn't natively conform to the SPL token standard that DEX programs expect. So it's wrapped into a token that does conform (Wrapped SOL).
- USDC (
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v)
to answer four sample questions:
- What was the effective price on each DEX?
- How wide was the spread across venues?
- Which DEX is winning market share?
- How much flow comes from aggregators vs. direct?
Example 1: Compare execution price for the same pair across DEXes
At any given minute, what was the effective SOL/USDC price on each venue? Every row carries both sides of the trade in human units, so price is just usdc / sol. The only work is normalizing trade direction: a row can be either "bought SOL with USDC" or "bought USDC with SOL".
-- Minute-by-minute volume-weighted SOL/USDC price, per DEX, last 24h
SELECT
toStartOfMinute(toDateTime(block_timestamp)) AS minute,
project,
sum(usdc_amount) / sum(sol_amount) AS vwap_usdc_per_sol,
count() AS trades,
sum(sol_amount) AS sol_volume
FROM
(
SELECT
block_timestamp,
project,
if(token_bought_mint_address = 'So11111111111111111111111111111111111111112',
token_bought_amount, token_sold_amount) AS sol_amount,
if(token_bought_mint_address = 'So11111111111111111111111111111111111111112',
token_sold_amount, token_bought_amount) AS usdc_amount
FROM solana_dex_swaps FINAL
WHERE (
(token_bought_mint_address = 'So11111111111111111111111111111111111111112'
AND token_sold_mint_address = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
OR (token_bought_mint_address = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
AND token_sold_mint_address = 'So11111111111111111111111111111111111111112')
)
AND block_timestamp >= toUnixTimestamp(now() - INTERVAL 24 HOUR)
AND token_sold_amount > 0
AND token_bought_amount > 0
)
GROUP BY minute, project
ORDER BY minute, project;
Example 2: Quantify the cross-DEX spread (and find who's cheapest)
You can build on the same subquery to collapse the per-DEX prices into a single cross-venue spread series. This gives a direct measure of arbitrage opportunity and market efficiency. The wider and more persistent the gap, the more a naive fill costs.
-- Per-minute spread between the cheapest and most expensive venue, in bps
WITH venue_prices AS
(
SELECT
toStartOfMinute(toDateTime(block_timestamp)) AS minute,
project,
sum(if(token_bought_mint_address = 'So11111111111111111111111111111111111111112',
token_sold_amount, token_bought_amount))
/ sum(if(token_bought_mint_address = 'So11111111111111111111111111111111111111112',
token_bought_amount, token_sold_amount)) AS vwap
FROM solana_dex_swaps FINAL
WHERE (
(token_bought_mint_address = 'So11111111111111111111111111111111111111112'
AND token_sold_mint_address = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
OR (token_bought_mint_address = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
AND token_sold_mint_address = 'So11111111111111111111111111111111111111112')
)
AND block_timestamp >= toUnixTimestamp(now() - INTERVAL 24 HOUR)
GROUP BY minute, project
HAVING count() >= 3 -- require a few trades so thin minutes don't skew the spread
)
SELECT
minute,
argMin(project, vwap) AS cheapest_venue,
argMax(project, vwap) AS priciest_venue,
min(vwap) AS min_price,
max(vwap) AS max_price,
round((max(vwap) - min(vwap)) / min(vwap) * 10000, 2) AS spread_bps
FROM venue_prices
GROUP BY minute
HAVING count() >= 2 -- only minutes where at least two venues traded
ORDER BY minute;
Example 3: Solana DEX market share over time
Which venue is actually winning the flow? SOL-denominated volume is a clean common denominator, since the vast majority of pairs route through SOL:
-- Daily SOL-denominated volume and swap count per DEX, last 30 days
SELECT
toDate(toDateTime(block_timestamp)) AS day,
project,
count() AS swaps,
round(sumIf(token_bought_amount,
token_bought_mint_address = 'So11111111111111111111111111111111111111112')
+ sumIf(token_sold_amount,
token_sold_mint_address = 'So11111111111111111111111111111111111111112'), 0) AS sol_volume,
uniqExact(trader_id) AS unique_traders
FROM solana_dex_swaps FINAL
WHERE block_timestamp >= toUnixTimestamp(now() - INTERVAL 30 DAY)
AND (token_bought_mint_address = 'So11111111111111111111111111111111111111112'
OR token_sold_mint_address = 'So11111111111111111111111111111111111111112')
GROUP BY day, project
ORDER BY day, sol_volume DESC;
Example 4: How much flow comes from aggregators vs. direct?
trade_source makes this a one-liner. Direct swaps carry the literal string direct. Anything else is the program ID of the router that invoked the DEX. Jupiter v6, for instance, is JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.
SELECT
project,
if(trade_source = 'direct', 'direct', 'router') AS flow,
count() AS swaps,
round(100.0 * count() / sum(count()) OVER (PARTITION BY project), 1) AS pct_of_dex
FROM solana_dex_swaps FINAL
WHERE block_timestamp >= toUnixTimestamp(now() - INTERVAL 7 DAY)
GROUP BY project, flow
ORDER BY project, flow;And to rank the routers themselves:
-- Top routers by swaps delivered, last 7 days
SELECT
trade_source AS router_program_id,
count() AS swaps,
uniqExact(project) AS venues_reached
FROM solana_dex_swaps FINAL
WHERE trade_source != 'direct'
AND block_timestamp >= toUnixTimestamp(now() - INTERVAL 7 DAY)
GROUP BY router_program_id
ORDER BY swaps DESC
LIMIT 10;
Benefits of Goldsky's DEX swaps dataset
Solana's DEX landscape is fragmented by design. Each venue is its own program with its own quirks.
- Goldsky's
solana.dex_swapsdataset flattens that mess into one uniform shape. - Every swap is one row. Both sides come denominated in human units, tagged with the venue and the router that delivered the flow.
- Goldsky handles the decoding, the decimal scaling, and the routing attribution for you.
- Get full unlimited historical access back to the genesis block.
- Datasets stay up-to-date for you as new programs and instruction layouts ship.
That leaves you free to focus on the questions you and your customers care about. Where's the best price? How wide is the spread? Who's winning the volume? How much flow is aggregated?
These become ordinary SQL against your own ClickHouse.
Reproduce this in your own ClickHouse
Want to reproduce any of this? The fastest way is to spin up a Solana DEX trade pipeline with Turbo pipeline above. Check out our documentation for the full schema and latest information on our Solana DEX trade datasets: https://docs.goldsky.com/turbo-pipelines/sources/solana-dex-trades
If the DEX trade dataset you're looking for isn't listed, please contact [email protected]