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
“What are the currentMirrors Moralisreserve0/reserve1of DEX pair 0x…?”
GET /{pair_address}/reserves → { reserve0, reserve1 }. The recipe lands the pool’s absolute token balances and keeps the latest observation per pair, so a single read returns each pair’s current reserves.
What you get
The recipe lands one observation per swap — the pool’s absolute token balances captured after each trade — and the current reserves of a pair are the highest-(block_number, log_index) observation for that pair:
| Column | Description |
|---|---|
pair_address | The DEX pair (pool) the reserves belong to |
token0_address, token1_address | The pool’s two token sides |
reserve0 | Pool’s absolute token0 balance after the swap (raw uint256, as text) |
reserve1 | Pool’s absolute token1 balance after the swap (raw uint256, as text) |
block_number, log_index | Recency key — the latest pair wins |
event_ts / block_timestamp | Block time of the observation |
Source
The faithful source for reserves istokenSwaps. Each swap carries token0PostBalance / token1PostBalance — the pool’s absolute balances after the trade — which are exactly the pair’s reserves. The transform projects one reserve observation per swap, keyed by pair.
pairLiquidityChanges is not used: it only fires on add/remove liquidity, so a running sum of its deltas misses all swap-driven reserve movement and is window-relative. Aggregated swaps are also not used — they carry an aggregator address, not a pair_address, so their post-balances can’t be attributed to a specific pool.
Destination
| Destination | Table | By-pair access |
|---|---|---|
| ClickHouse (first-class) | fact_pair_reserves | argMax(reserveN, (block_number, log_index)) per pair over FINAL |
| Postgres | pair_reserves (materialized view over pair_reserve_observations) | Unique index (pair_address) |
| MySQL | pair_reserves (trigger-maintained over pair_reserve_observations) | Primary key (pair_address) |
sign = -1) and re-emits the corrected ones. Read with FINAL to collapse the ±1 pair, then argMax to pick the latest observation — never a bare WHERE sign = 1.
Postgres and MySQL append append-only observations and derive a latest-wins current-reserves surface per pair: Postgres via a DISTINCT ON (pair_address) … ORDER BY block_number DESC, log_index DESC materialized view, MySQL via an AFTER INSERT upsert trigger.
Full schema
The ClickHouse fact table is the per-pair observation table; the current reserves are anargMax over it. Reserves are raw uint256 stored as text (matching the API’s string reserves) — argMax over text ordered by the numeric recency key is exact. Keep the columns you need (see Schema & flexibility).
ClickHouse — fact_pair_reserves
ClickHouse — fact_pair_reserves
sign column drives reorg collapsing — read with FINAL then argMax. A single-node setup can use CollapsingMergeTree(sign) without the replication path.Postgres — pair_reserves
Postgres — pair_reserves
pair_reserve_observations table plus a pair_reserves table kept current by an AFTER INSERT latest-wins upsert trigger, with reserve0 / reserve1 as DECIMAL(65,0). position is the block-level cursor used during backfill.Example reads
Current reserves of a pair — latest observation;FINAL collapses reorg ±1 pairs before argMax (ClickHouse):
reserve0 (ClickHouse):
Modes
Shipped defaults: ClickHousehybrid (backfill → realtime), Postgres / MySQL historical (one-shot backfill). For live/reorg-safe ingestion, use ClickHouse — see the overview.
The Postgres / MySQL realtime reorg path needs a single-column
UNIQUE on the position column, but position is block-level. Run realtime / hybrid on ClickHouse (the collapsing log table corrects reorgs per block); the Postgres / MySQL configs target historical backfill and re-derive their current-reserves surface on refresh.Multichain
The recipe is chain-parametrized via thechain setting — point it at any supported EVM chain or Solana. On Solana, the event identity already folds in (pair_address, token0PostBalance, token1PostBalance) on top of (transaction_hash, log_index), so it stays row-unique despite Solana’s repeated log indices within an instruction.
Fidelity gaps
- Only pairs with a swap in-window get current reserves. Reserves are read from swap post-balances, so a pair appears only if it had at least one swap with resolved post-balances in the ingested window. A pool that is quiet (no swaps) for the whole window won’t be present — but quiet pools have unchanged reserves by definition, so widening the window picks them up.
- Swaps without resolved post-balances are skipped.
token0PostBalance/token1PostBalanceare nullable; rows where either is null/empty, or where the swap has no pair address, are filtered out. - V4 pair addresses arrive as
poolId_0xaddress(~109 chars) and are stored raw for a key that’s consistent across destinations. MySQL capspair_addressatVARCHAR(160); PostgresTEXTand ClickHouseStringare unbounded. - MySQL precision ceiling. MySQL
DECIMAL(65,0)cannot represent a full 78-digituint256— though no real reserve approaches that magnitude. Postgres (NUMERIC(76,0)) and ClickHouse (String) carry the larger range.
Related
Swaps by Pair
Every DEX trade for a pair — the same swap stream these reserves derive from.
Pair OHLCV
Per-pair price candles for charting.

