Question it answers
“Give me every raw event log emitted by contract 0x…, in on-chain order.” Mirrors Moralis GET /{address}/logs.
This is the raw event log, topic0…topic3 plus the ABI-encoded data payload, keyed by the emitting contract. Unlike the decoded recipes (Token Transfers, Token Approvals, Swaps), it does not decode event names or arguments; you decode data in your application against the contract’s ABI. Use it to index any contract’s events, including custom or non-standard ones the decoded recipes don’t cover.
What you get
One row per log, keyed by the emitting contract:On-chain ordering
A log’s canonical position is the tuple(block_number, transaction_index, log_index), block height, then the transaction’s position in the block, then the log’s index within the transaction. Every destination is keyed contract_address first, then that tuple, so “all logs for a contract, in order” is a sort-free range scan:
- ClickHouse:
ORDER BY (chain_id, contract_address, block_number, transaction_index, log_index, …); a prefix scan returns rows already ordered. - Postgres / MySQL: a composite index on
(contract_address, block_number, transaction_index, log_index)answers the read with no filesort step.
Optional: group by event signature (topic0)
The primary key already leads withcontract_address, so “all Transfer logs for contract X” prefix-scans to the contract and filters topic0 within it. On a high-volume contract that emits many event types, an opt-in (contract_address, topic0, …) structure makes that read a tight (contract, topic0) range that stays sort-free and skips the contract’s other event types.
These are shipped commented-out in each schema (extra write cost + storage), uncomment to enable:
Source
Raw logs aren’t a decoded entity array, they live in the flattenedblock raw-passthrough array, where each block is expanded into items discriminated by itemType (log / transaction / …). The transform ARRAY JOINs block, keeps itemType = 'log', and lands one row per log. A log row carries no transaction hash, so tx_hash is recovered by joining each log to its sibling itemType = 'transaction' row on (block_number, transaction_index).
Destination
Logs are immutable append-only events, so the ClickHouse fact table is a plain collapsing table, each log is one row, and a chain reorganization cancels it via the companion log table. There’s no latest-wins logic; every log is a distinct row.
Full schema
The complete read table. Keep the columns you need;topic1…topic3 and data are nullable raw fields you decode in your app (see Schema & flexibility).
ClickHouse, fact_logs (contract-keyed)
ClickHouse, fact_logs (contract-keyed)
contract_logs table with a composite index (contract_address, block_number, transaction_index, log_index) and the optional (contract_address, topic0, …) index commented out.Example reads
All logs emitted by a contract, in on-chain order (ClickHouse):0xddf2… = keccak("Transfer(address,address,uint256)")):
Modes
Shipped defaults: ClickHousehybrid, Postgres / MySQL historical. For live/reorg-safe ingestion use ClickHouse, see the overview.
This recipe reads the wide raw
block column (every log and transaction per block), so per-block bytes are higher than the decoded recipes. The transaction rows are read only to recover tx_hash, drop that join if you don’t need it.EVM only
Contract Logs extracts EVM event logs (topic0…3 + data). Solana program logs are a different model and aren’t emitted into this array.
Fidelity gaps
Raw on-chain log primitives, emitter, all topics, raw data, block, tx hash, on-chain ordering, are fully covered. Fields with no onchain source are omitted:decoded_event (needs the contract ABI, decode in your app) and transaction receipt fields beyond tx_hash.
Migrating from the REST API
This recipe is the landing point for the two Blockchain API REST endpoints. Neither maps 1:1 to a single recipe: both responses are assembled from the rawblock array, the flattened header / transaction / log rows this recipe already reads (see The block array). This recipe lands the log rows in a queryable table; the transaction and header fields come from the sibling rows in the same array. The subsections below cover each endpoint: what replaces it, the field mapping (exact = straight from the chain, decoded = you decode from the ABI or a typed recipe covers it, gap = not delivered today, off-chain = add yourself), and the gotchas specific to that endpoint.
GET /transaction/:transaction_hash/verbose
The decoded-transaction-by-hash endpoint. Replaced by reading the transaction’s logs from this recipe’s table and its raw fields from the transaction row of the block array, the “verbose” ABI decoding becomes a step you own.
A transaction’s logs by hash (Postgres, same shape on ClickHouse with
FINAL):
- By-hash is a point lookup, but the shipped keys are contract-first. Add an index on
tx_hash(or keep your own hash-to-block map), the shipped(contract_address, block_number, …)ordering doesn’t serve it. - Index the history you’ll look up. Arbitrary historical hashes need a
historical/hybridbackfill across the range; recent-only lookups need only a recent window, see History & backfill. internal_transactionsis a gap today, it isn’t carried in theblockarray.- The
*_entity/*_label/*_entity_logofields are off-chain address labels; bring your own directory or drop them.
GET /block/:block_number_or_hash
The whole-block endpoint. Replaced by reading the block directly from the feed, a feed row’s native unit is a block, so the header, transactions, and logs arrive together in the block array and a by-number read is a direct positional read (a block’s position is its block number).
Resolve a by-hash read to a position, then all of that block’s logs (ClickHouse):
- By-hash resolves to a number first: the feed is positioned by block number; this recipe’s
block_hashcolumn (or your own index) does the resolve. - Raw log rows carry no tx hash: they join to their transaction on
(block_number, transaction_index); this recipe already attachestx_hashfor you. - Busy blocks are large (a megabyte or two of JSON), read by position and process the array.
- The REST responses return raw logs, matching this recipe exactly; if you also want decoded events, add the decoded recipes or decode from the ABI.
internal_transactionsand address labels: same gaps as above.
Related
Logs by Event Signature
The sibling, index one event type across all contracts.
Onchain Event Indexing
The use case these raw-log recipes power.

