Skip to main content
This tutorial uses the Uniswap V2 USDC–ETH pool at 0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc on Ethereum mainnet. Its contract holds USDC and WETH.
chain.event.eth → Filter the pool’s Sync events → Read end-of-block reserves → Reserve state table
Sign in to the Chaintable console and open your Space. This tutorial uses demo; replace it with your Space ID.

1. Create the reserve state table

In Tables, create a table named pool_reserves, select Block State, and choose Ethereum. Its table ID is demo.pool_reserves.eth.
Create the pool_reserves Block State Table on Ethereum
In the table’s Schema, change id to ADDRESS to store the pool address, then add the fields below. Keep the built-in block fields and other default settings:
Reserve fields added as UINT256 alongside the block fields

2. Run the Pipeline

Configure the Pipeline

In Notebooks, create backfill_pool_reserves and enter the code below. Set SPACE to your Space ID:
The code follows this flow:
  • Trigger filters the pool’s Sync events.
  • SOURCE_ROW passes each matching event to the function.
  • read_reserves calls getReserves() at the event’s block_id to read USDC and WETH reserves.
  • The Pipeline writes the returned record to the reserve table specified by target_table.
The function is defined directly in the Notebook. You can also set func to an existing Function ID. getReserves() reads reserves after the block has finished executing. Multiple Sync events in one block therefore return the same end-of-block state.

Run the backfill

Save and click Run, then check Output for progress and results. backfill() processes the specified 10 blocks, including both boundary heights, and exits when finished.
Notebook backfill processes 10 blocks and two reserve updates

3. Inspect current and historical state

Return to pool_reserves.eth and check processed blocks in Overview. This backfill covers a limited range; earlier history is still incomplete.
Reserve table progress with default settings and links to latest and archive
Open latest under Subtables to view current reserves. Records use the pool address as their ID, so the two state updates leave one current record.
The latest subtable retains one current reserve record per pool address
Open archive to inspect historical state by block. original_id is the pool address; id identifies the historical record.
The archive contains reserve updates at blocks 25939928 and 25939930
The result contains one state record at each of blocks 25939928 and 25939930. Reserve fields retain raw integers:
  • Divide the USDC value by 10^6 to get USDC units.
  • Divide the WETH value by 10^18 to get WETH units.

Optional: keep the table updated

Create a Notebook named update_pool_reserves, copy the code above, and replace the final pipeline.backfill(...) line with the call below. Save and run it:
By default, update() fills history from the earliest unfinished block, then continues processing new blocks after catching up. For large historical ranges, you can run backfill() in another Notebook at the same time to speed up the backfill.
Continuous update call and historical processing logs in update_pool_reserves
Choose Switch to Background to keep the instance running in the background. Cancel it under the account’s Instances page to stop it. Data already written remains in the table.