> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 追踪流动性池储备变化

> 在 Uniswap V2 USDC–ETH 池发生 Sync 事件时读取区块末储备，写入状态表并查看历史变化。

本教程使用以太坊主网的 Uniswap V2 USDC–ETH 池 `0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc`。合约内的两种代币是 USDC 和 WETH。

<div className="doc-flow">
  `chain.event.eth` → 筛选池子的 Sync 事件 → 读取区块末储备 → 储备状态表
</div>

登录 [Chaintable 控制台](https://chaintable.com/spaces)，进入自己的 Space。本文使用 `demo`，操作时替换为你的 Space ID。

## 1. 创建储备状态表

在 **Tables** 中创建表，名称填 `pool_reserves`，类型选择 **Block State**，链选择 **Ethereum**。创建后的表 ID 为 `demo.pool_reserves.eth`。

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/create-table.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=db0e6b765f548445a3cb50170978b35b" alt="创建 Ethereum 链的 pool_reserves 区块状态表" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/create-table.png" />
</Frame>

在表的 **Schema** 中，将 `id` 的类型改为 `ADDRESS`，用于存放池地址，并添加以下字段。保留系统预置的区块字段和其他默认配置：

| 字段名            | 类型        | 内容         |
| -------------- | --------- | ---------- |
| `reserve_usdc` | `UINT256` | USDC 原始储备量 |
| `reserve_weth` | `UINT256` | WETH 原始储备量 |

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/schema.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=74bf2e7288bc5a4ee55df70832cad515" alt="储备状态表保留区块字段，增加两种代币的 UINT256 储备字段" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/schema.png" />
</Frame>

## 2. 运行 Pipeline

### 配置 Pipeline

在 **Notebooks** 中创建 `backfill_pool_reserves`，输入以下代码，将 `SPACE` 改为你的 Space ID：

```python theme={null}
from blockdb import Column, filter
from blockx import Pipeline, Trigger, SOURCE_ROW

SPACE = 'demo'


def read_reserves(event):
    from leafage import ChainState

    pool = '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc'
    chain = ChainState('eth', event['block_id'])
    reserve_usdc, reserve_weth, _ = chain.call(
        pool, 'getReserves()(uint112,uint112,uint32)', []
    )
    return {
        'id': pool,
        'reserve_usdc': reserve_usdc,
        'reserve_weth': reserve_weth,
    }


pipeline = Pipeline(
    triggers=[
        Trigger(
            table='chain.event.eth',
            func=read_reserves,
            params=[SOURCE_ROW],
            operator=filter(
                (Column('contract_id') == '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc')
                & (Column('name') == 'Sync')
            ),
        ),
    ],
    target_table=f'{SPACE}.pool_reserves.eth',
)

pipeline.backfill(block_start=25939925, block_end=25939934)
```

这段代码的处理流程是：

* `Trigger` 筛选该池的 Sync 事件。
* `SOURCE_ROW` 将筛选出的事件传给函数。
* `read_reserves` 使用事件的 `block_id` 调用 `getReserves()`，读取 USDC 和 WETH 储备。
* Pipeline 将函数返回结果写入 `target_table` 指定的储备表。

这里直接在 Notebook 中定义函数，也可以将 `func` 设为已有 Function 的 ID。

`getReserves()` 读取区块执行完成后的储备。同一区块中的多条 Sync 事件会得到相同的区块末状态。

### 运行回填

保存并点击 **Run**，在 **Output** 中查看回填进度和处理结果。

`backfill()` 处理指定的 10 个区块，包含起止高度，完成后自动结束。

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/backfill.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=09bf3f82a1fe81012c0af654cad88991" alt="Notebook 回填指定 10 个区块，处理两个储备状态结果" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/backfill.png" />
</Frame>

## 3. 查看当前状态和历史变化

回到 `pool_reserves.eth`，在 **Overview** 查看已处理区块。本次只回填了一个局部区间，更早的历史尚未补齐。

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/overview.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=6f4d0de84be5debb0e2a690e7a1a9b72" alt="默认配置下储备表的处理进度和 latest、archive 子表入口" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/overview.png" />
</Frame>

打开 **Subtables** 下的 **latest**，查看当前储备。本例按池地址写入，两次状态更新最终保留一条当前记录。

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/latest.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=fdc543458ccb27ab1a57f673dc67d7a6" alt="latest 子表按池地址保留最新一条储备状态" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/latest.png" />
</Frame>

打开 **archive**，查看不同区块的历史状态。`original_id` 是池地址，`id` 是该历史记录的 ID。

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/archive.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=a84dcde0cfce67667b1f7b9564d0f8a1" alt="archive 子表保存区块 25939928 和 25939930 的两次储备变化" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/archive.png" />
</Frame>

图中在高度 `25939928` 和 `25939930` 各有一条状态记录。储备字段保留原始整数值：

* USDC 原始值除以 `10^6`，得到 USDC 数量。
* WETH 原始值除以 `10^18`，得到 WETH 数量。

## 可选：持续更新

新建 Notebook `update_pool_reserves`，复制前面的代码，将最后一行 `pipeline.backfill(...)` 替换为以下调用，保存并运行：

```python theme={null}
pipeline.update()
```

`update()` 默认从最小未完成区块开始补齐历史，追上最新区块后持续更新。

历史区块较多时，也可以在另一个 Notebook 中同时运行 `backfill()`，加快回填。

<Frame>
  <img src="https://mintcdn.com/opcodelabspteltd/TOA0bb8_WqdBuVVN/images/quickstart/track-pool-reserves/update.png?fit=max&auto=format&n=TOA0bb8_WqdBuVVN&q=85&s=977c8591363a7fa87b2dd488a258853d" alt="update_pool_reserves Notebook 的持续更新调用和历史区块处理日志" width="1920" height="1080" data-path="images/quickstart/track-pool-reserves/update.png" />
</Frame>

选择 **Switch to Background** 可转为后台运行。在账户的 **Instances** 中取消实例即可停止，已写入的数据会保留。
