Skip to content

Index a bounded Mainnet range

Index 1,000 Ethereum Mainnet blocks and query every Uniswap V2 pool-reserve observation in them, each stamped with its hour. This uses public Xatu data, needs no API key, and stops after a finite range.

First install from source so leani and LEANI_SOURCE are available in your terminal. The commands below copy a complete configuration into a fresh temporary directory:

Bounded Mainnet run
LEANI_HISTORY_DIR="$(mktemp -d)"
cp "$LEANI_SOURCE/config/modes/windowed.toml" "$LEANI_HISTORY_DIR/leani.toml"
cd "$LEANI_HISTORY_DIR"
leani doctor --json
leani backfill \
--processor uniswap-v2-sync-30d --from-block 17000000 --to-block 17000999
leani serve
Use the checked-in configuration to index a finite Mainnet range.Verified source

doctor should report valid: true. The backfill report should show continuous coverage from 17000000 through 17000999. Leave serve running and query it from another terminal:

Query indexed Uniswap events
curl -s \
'http://127.0.0.1:8080/v1/processors/uniswap-v2-sync-30d/collections/uniswap_v2.sync/entities?limit=3'
curl -s \
'http://127.0.0.1:8080/v1/processors/uniswap-v2-sync-30d/status'
Read decoded Mainnet events and their exact processor coverage from the local API.Verified source

Expect one entity per Sync event in the range; limit=3 returns only the first page. Follow nextCursor to read the rest. Entities are ordered by their keys, a transaction hash and log index, so sort by blockNumber and logIndex when drawing a time series.

Each entity contains values.reserve0 (USDC, six decimals) and values.reserve1 (WETH, eighteen decimals). These are exact integer strings. bucketStartTimestamp names the hour of the event’s block, for grouping observations by hour; the processor computes no hourly aggregate or average price. The profile keeps a window of about 30 days of blocks and prunes older observations.

Check coverage

The status response’s available intervals should cover the entire requested range. A high processedThrough alone does not prove that earlier blocks were indexed. Xatu’s finalized label is a dataset assertion; this profile has independent finality verification disabled.

Try an explicit range query, then one outside your indexed interval:

Terminal window
curl -s 'http://127.0.0.1:8080/v1/processors/uniswap-v2-sync-30d/collections/uniswap_v2.sync/entities?fromBlock=17000000&toBlock=17000999'
curl -s 'http://127.0.0.1:8080/v1/processors/uniswap-v2-sync-30d/collections/uniswap_v2.sync/entities?fromBlock=17001000&toBlock=17001999'

The first should return retained entities with complete requested coverage. The second should return range_incomplete, rather than an empty success. An indexed range whose output was subsequently pruned returns output_not_retained.

Decode a different contract event

The configuration’s processors.settings selects the contract address and Solidity event ABI. For a small WETH transfer example, use the complete config at examples/weth-transfers/node.toml:

Terminal window
LEANI_EVENTS_DIR="$(mktemp -d)"
cp "$LEANI_SOURCE/examples/weth-transfers/node.toml" "$LEANI_EVENTS_DIR/leani.toml"
cd "$LEANI_EVENTS_DIR"
leani doctor --json
leani backfill --processor weth-transfers --from-block 17000000 --to-block 17000009
leani serve

This example uses API port 8081 so it can run beside the Uniswap node. Query:

Terminal window
curl -s 'http://127.0.0.1:8081/v1/processors/weth-transfers/collections/weth.transfers/entities?limit=100'

Expect 205 transfers across three pages. Each entity’s values contains from, to, and value; WETH uses 18 decimals. Change the address, ABI, collection, instance name, and block range for your contract. Keep history_mode = "on_demand" for explicit backfills, and use a fresh data directory when changing processor identity or schema.

For a transformation beyond event decoding, build your own processor with the Rust library.