Question it answers
“Show me the full chronological event feed for wallet 0x…, newest first. Each row is one event with its actual payload — counterparty, amounts, token IDs, pair address, USD value — and I can filter by event type.”A single read returns what the public API stitches together client-side from
/wallets/{address}/erc20-transfers, /native-transactions, /nfts/transfers, /swaps, /approvals, and /defi/positions. Storing it pre-stitched, in your own database, is the value.
What you get
The recipe lands one row per wallet-bearing event. A wallet is “involved” if it is a non-emptyfrom/to (or owner/approver) on the event. Six event types share one wide, flat table; per-type columns are populated as relevant:
event_type | Rows per event | Populated columns | USD value |
|---|---|---|---|
token_transfer | 2 (from + to) | token_address, amount | ✅ inline |
native_transfer | 2 (from + to) | amount | — |
nft_transfer | 2 (from + to) | token_address, token_id, amount | — |
swap | 1 (the wallet) | pair_address, token_in/out_address, amount_in/out | ✅ inline |
approval | 1 (approver) | spender_address, token_address, amount | — |
liquidity_change | 1 (LP owner) | change_type, pair_address, token0/1_address, token0/1_amount | ✅ inline |
direction (sent · received · self · minted · burned), counterparty, tx_hash, block_number, block_timestamp, and log_index.
Source
The transform reads six per-block arrays andUNIONs them into the event feed:
tokenTransfers · nativeTransfers · nftTokenTransfers · tokenSwaps · tokenApprovals · pairLiquidityChanges
USD values are computed inline from the same block’s tokenPriceUpdates — no separate price join at read time.
Destination
| Destination | Table | Read pattern |
|---|---|---|
| ClickHouse (first-class) | fact_wallet_history_full | Prefix scan on (chain_id, wallet_address, block_number, log_index); read with FINAL or sum(sign) |
| Postgres | wallet_history_full | Index on (wallet_address, block_number DESC) |
| MySQL | wallet_history_full | Index on (wallet_address, block_number) |
event_type — is a contiguous range read.
Full schema
Below is the complete read table this recipe produces. It’s the full shape — every event type’s columns in one wide row. This is a starting point: keep the columns and event types you need and drop the rest (see Schema & flexibility). Rawuint256 amounts and token_id are stored as text (they exceed numeric precision); USD columns are wide decimals so a low-decimals token × price never overflows.
ClickHouse — fact_wallet_history_full
ClickHouse — fact_wallet_history_full
sign column drives reorg collapsing — read with FINAL or sum(sign). A single-node setup can use CollapsingMergeTree(sign) without the replication path.Postgres — wallet_history_full
Postgres — wallet_history_full
DECIMAL(65,18) for the USD columns. position is the block-level cursor used during backfill.Example reads
A wallet’s full feed, newest first (ClickHouse):event_type filter compresses the scan further):
FINAL):
USD valuation and fidelity gaps
- Swaps and LP changes carry per-leg decimals, so their
*_usdcolumns are true dollar values (raw / 10^decimals × price). - Token transfers have no decimals field on the transfer event, so
amount_usdisraw_amount × price(unscaled). Divide by10^token_decimalsto read dollars — fold in a decimals lookup from a Token Metadata sync if you need it pre-scaled. - Native and NFT transfers leave USD
NULL— native pricing needs a separate native-price feed; NFT pricing is out of scope (use an NFT Trades recipe for trade-priced data). - Approvals leave USD
NULLby design — an unlimited allowance × price is meaningless.
Lightweight variant: transaction pointers
If you only need a wallet’s transaction list — one pointer per(wallet, tx), not the full payload — there’s a slimmer variant that lands just the deduplicated pointers from the transfer arrays. Use the full feed above when you need amounts, counterparties, and USD value; use the pointer variant when you only need “which transactions touched this wallet.”
Modes
Shipped defaults: ClickHousehybrid (backfill → realtime), Postgres / MySQL historical (one-shot backfill). For live/reorg-safe ingestion, use ClickHouse — see the overview.
Multichain
The recipe is chain-parametrized via thechain setting — point it at any supported EVM chain or Solana. On Solana, the event identity is widened to stay unique under Solana’s repeated log indices; the wallet feed it produces is identical in shape.
Powers these use cases
Accounting & Tax
The chronological, USD-valued event feed behind a ledger.
Compliance & AML
Full counterparty and transfer trail per address.

