Skip to main content

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

“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-empty from/to (or owner/approver) on the event. Six event types share one wide, flat table; per-type columns are populated as relevant:
event_typeRows per eventPopulated columnsUSD value
token_transfer2 (from + to)token_address, amount✅ inline
native_transfer2 (from + to)amount
nft_transfer2 (from + to)token_address, token_id, amount
swap1 (the wallet)pair_address, token_in/out_address, amount_in/out✅ inline
approval1 (approver)spender_address, token_address, amount
liquidity_change1 (LP owner)change_type, pair_address, token0/1_address, token0/1_amount✅ inline
Each row also carries direction (sent · received · self · minted · burned), counterparty, tx_hash, block_number, block_timestamp, and log_index.

Source

The transform reads six per-block arrays and UNIONs 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

DestinationTableRead pattern
ClickHouse (first-class)fact_wallet_history_fullPrefix scan on (chain_id, wallet_address, block_number, log_index); read with FINAL or sum(sign)
Postgreswallet_history_fullIndex on (wallet_address, block_number DESC)
MySQLwallet_history_fullIndex on (wallet_address, block_number)
ClickHouse uses the collapsing log-table pattern (see the recipes overview) so chain reorganizations self-correct. The fact table’s sort key is wallet-first, so a wallet’s feed — optionally filtered by 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). Raw uint256 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.
CREATE TABLE recipe_wallet_history_full.fact_wallet_history_full
(
    chain_id            UInt32,
    wallet_address      String,
    block_number        UInt64,
    log_index           UInt32,
    event_type          LowCardinality(String),   -- token_transfer | native_transfer | nft_transfer | swap | approval | liquidity_change
    vendor_event_id     String,
    block_timestamp     DateTime64(3),
    tx_hash             String,
    transaction_index   Nullable(Int32),
    direction           LowCardinality(String),   -- sent | received | self | minted | burned | n/a
    counterparty        String,
    token_address       String,
    amount              String,                   -- raw uint256
    amount_usd          Nullable(String),
    token_id            String,
    pair_address        String,
    token_in_address    String,
    amount_in           String,
    amount_in_usd       Nullable(String),
    token_out_address   String,
    amount_out          String,
    amount_out_usd      Nullable(String),
    spender_address     String,
    change_type         LowCardinality(String),   -- mint | burn | sync | ''
    token0_address      String,
    token0_amount       String,
    token0_amount_usd   Nullable(String),
    token1_address      String,
    token1_amount       String,
    token1_amount_usd   Nullable(String),
    sign                Int8
)
ENGINE = ReplicatedCollapsingMergeTree(
    '/clickhouse/tables/{database}/fact_wallet_history_full', '{replica}', sign)
PARTITION BY (chain_id, toYYYYMM(block_timestamp))
ORDER BY (chain_id, wallet_address, block_number, log_index, event_type, vendor_event_id);
The sign column drives reorg collapsing — read with FINAL or sum(sign). A single-node setup can use CollapsingMergeTree(sign) without the replication path.
CREATE TABLE public.wallet_history_full (
  position             BIGINT          NOT NULL,
  chain_id             BIGINT          NOT NULL,
  block_number         BIGINT          NOT NULL,
  block_timestamp      BIGINT          NOT NULL,      -- unix seconds
  tx_hash              TEXT            NOT NULL,
  log_index            BIGINT          NOT NULL,
  wallet_address       TEXT            NOT NULL,
  event_type           TEXT            NOT NULL,
  vendor_event_id      TEXT            NOT NULL,
  direction            TEXT            NOT NULL,
  counterparty         TEXT            NOT NULL,
  token_address        TEXT            NOT NULL,
  amount               TEXT            NOT NULL,       -- raw uint256
  amount_usd           NUMERIC(65, 18) NULL,
  token_id             TEXT            NOT NULL,
  pair_address         TEXT            NOT NULL,
  token_in_address     TEXT            NOT NULL,
  amount_in            TEXT            NOT NULL,
  amount_in_usd        NUMERIC(65, 18) NULL,
  token_out_address    TEXT            NOT NULL,
  amount_out           TEXT            NOT NULL,
  amount_out_usd       NUMERIC(65, 18) NULL,
  spender_address      TEXT            NOT NULL,
  change_type          TEXT            NOT NULL,
  token0_address       TEXT            NOT NULL,
  token0_amount        TEXT            NOT NULL,
  token0_amount_usd    NUMERIC(65, 18) NULL,
  token1_address       TEXT            NOT NULL,
  token1_amount        TEXT            NOT NULL,
  token1_amount_usd    NUMERIC(65, 18) NULL
);

-- Primary access pattern: every event for a wallet, newest first.
CREATE INDEX ON public.wallet_history_full (chain_id, wallet_address, block_number DESC);
-- Event-type filter within a wallet.
CREATE INDEX ON public.wallet_history_full (chain_id, wallet_address, event_type, block_number DESC);
MySQL is the same shape with 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):
SELECT block_number, log_index, event_type, direction, counterparty,
       token_address, amount, amount_usd
FROM recipe_wallet_history_full.fact_wallet_history_full FINAL
WHERE chain_id = 1
  AND wallet_address = lower('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
ORDER BY block_number DESC, log_index DESC
LIMIT 50;
Only swaps and token transfers (the event_type filter compresses the scan further):
SELECT block_number, event_type, token_in_address, amount_in,
       token_out_address, amount_out, amount_in_usd, amount_out_usd
FROM recipe_wallet_history_full.fact_wallet_history_full FINAL
WHERE chain_id = 1
  AND wallet_address = lower('0xd8dA…96045')
  AND event_type IN ('swap', 'token_transfer')
ORDER BY block_number DESC, log_index DESC;
Per-event-type breakdown (sign-aware, cheaper than FINAL):
SELECT event_type, sum(sign) AS events
FROM recipe_wallet_history_full.fact_wallet_history_full
WHERE chain_id = 1 AND wallet_address = lower('0xd8dA…96045')
GROUP BY event_type
ORDER BY events DESC;

USD valuation and fidelity gaps

  • Swaps and LP changes carry per-leg decimals, so their *_usd columns are true dollar values (raw / 10^decimals × price).
  • Token transfers have no decimals field on the transfer event, so amount_usd is raw_amount × price (unscaled). Divide by 10^token_decimals to 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 NULL by 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: ClickHouse hybrid (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 the chain 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.