Documentation Index
Fetch the complete documentation index at: https://docs.moralis.com/llms.txt
Use this file to discover all available pages before exploring further.
Question it answers
“Give me every DEX trade on pool/pair 0x…, newest first — each row is one trade with both token legs, the side, the protocol, and a USD notional.”Same trade projection as Swaps by Token, but keyed by the pool/pair address with one row per trade (no per-token-side unpivot).
pool_address is the pair address for direct pool fills and the aggregator address for aggregator-routed trades.
What you get
One row per trade, keyed bypool_address:
| Column | Description |
|---|---|
pool_address | Pair address (pool fills) or aggregator address (aggregator-routed trades) |
token0_address, token1_address | The pair’s two tokens |
amount0, amount1 | Raw token-unit deltas for each leg (uint256 as text) |
side | buy · sell — derived from the base token’s pool-balance delta |
source_kind | pool_fill (direct pool) · aggregator (aggregator-routed) |
price_usd | Bought-leg (token1) USD price; NULL with no in-block price update |
notional_usd | token1Amount / 10^token1Decimals × price_usd; NULL if unpriced |
protocol | DEX/protocol identifier |
fee_amount, fee_token, fee_usd | Trade fee — pool fills only (NULL on aggregator rows) |
trader_address | The trading wallet |
block_number, event_ts, tx_hash, vendor_event_id | On-chain ordering and identity |
Source
The transform reads two per-block trade arrays and projects them into one feed:tokenSwaps (direct pool fills, source_kind='pool_fill', pool_address = pairAddress) · aggregateTokenSwaps (aggregator-routed trades, source_kind='aggregator', pool_address = aggregatorAddress)
The two branches are disjoint by construction — together they give every fill exactly once. USD values are computed inline from the same block’s tokenPriceUpdates (the chronologically-last update wins on duplicate keys) — no separate price join at read time.
Destination
| Destination | Table | By-pair access |
|---|---|---|
| ClickHouse (first-class) | fact_swaps_by_pair | Prefix scan on (chain_id, pool_address, event_ts, …); read with FINAL or sum(sign) |
| Postgres | swaps | Index lead (pool_address, block_number DESC) |
| MySQL | swaps | Index lead (pool_address, block_number) |
+1/−1 reorg pair for a trade shares an identical key and collapses cleanly. The fact table’s sort key is pool-first, so a pair’s full trade feed is a contiguous range read.
Full schema
The complete read table this recipe produces — one row per trade. Keep the columns you need and drop the rest (see Schema & flexibility). Rawuint256 amounts are stored as text in ClickHouse (they exceed numeric precision); USD columns are nullable so an unpriced token leaves them empty.
ClickHouse — fact_swaps_by_pair
ClickHouse — fact_swaps_by_pair
sign column drives reorg collapsing — read with FINAL or sum(sign), never a bare WHERE sign=1. A single-node setup can use CollapsingMergeTree(sign) without the replication path.Postgres — swaps
Postgres — swaps
DECIMAL(38,18) for the USD columns. Amounts use NUMERIC(76,0) so large raw uint256 balances never overflow. position is the block-level cursor used during backfill.Example reads
All trades on a pool/pair, newest first (ClickHouse):FINAL):
Modes
Shipped defaults: ClickHousehybrid (backfill → realtime), Postgres / MySQL historical (one-shot backfill). For live/reorg-safe ingestion use ClickHouse — see the overview.
The realtime reorg path needs a single-column
UNIQUE on the position column, but position is block-level (many trades per block), so array-expanded trade rows can only carry a composite unique. Run realtime/hybrid on ClickHouse (the log table corrects reorgs per-block via the collapsing companion table); the Postgres / MySQL configs are intended for historical backfill.Multichain
The recipe is chain-parametrized — point it at any supported EVM chain or Solana. On Solana, the samelogIndex can be assigned to multiple events in one instruction, so for production the pool-fill event identity is widened with (token0_address, token1_address, token0_amount) to keep rows distinct; the trade feed it produces is identical in shape.
Fidelity gaps
price_usd/notional_usd/fee_usdareNULLwhen the relevant token had no in-blocktokenPriceUpdate(illiquid or brand-new tokens). The recipe enriches only from same-block price updates — there is no cross-block carry-forward.fee_*is populated for pool fills only. Aggregator-routed trades carry no per-fill fee, sofee_amount/fee_token/fee_usdareNULLonsource_kind='aggregator'rows.- One trade can appear under two
pool_addressvalues when it is observed both as a direct pool fill (the pair address) and as part of an aggregator route (the aggregator address). These are distinct events with distinctvendor_event_ids by design — the two source arrays are disjoint per event.
Related
Swaps by Token
The same trade projection, keyed by token with one row per token side.
Pair OHLCV
Roll a pair’s trades into candlesticks for charting.
Trading & Charting
The use case this per-pair trade feed powers.
Token Analytics
Pool-level volume and trade flow for token analytics.

