# 节点近况与已索引历史：什么时候用 eth_getLogs，什么时候用转账接口

> 原文地址: https://docs.blockvectra.com/zh/guides/logs-vs-transfers/

## 两种读取日志与转账的方式 [#两种读取日志与转账的方式]

`eth_getLogs` 是 JSON-RPC 方法，通过 JSON-RPC 端点返回区块日志。Data API 则通过两个按链划分的端点提供代币转账历史：

* `GET /{chain}/addresses/{address}/transfers`——与某个地址相关的转账。
* `GET /{chain}/tokens/{token}/transfers`——某个代币合约的转账。

两者使用同一个 API key，并按方法权重以 CU 计量（见下方权重表）。选哪一种，取决于数据有多新、是否需要指定区块窗口，以及如何翻页。

## eth\_getLogs 受到的限制 [#eth_getlogs-受到的限制]

`eth_getLogs` 受公开的 `GET /v1/chains` 响应中逐链公布的各项限制约束：

* **区块跨度**：`max_logs_block_range` 是单次 `eth_getLogs` 请求允许跨越的最大区块数。该值因链而异——请从 `GET /v1/chains` 读取（链列表见[支持的链](/zh/chains/)），不要写死在代码里。超过该链上限时返回 JSON-RPC 错误 `-32602 eth_getLogs block range too large`（不计费）。
* **节点同步**：当某条链的节点未同步时，`eth_getLogs`（与除 `eth_chainId` 外的所有方法一样）返回 `-32010`；该调用不会被转发，也不计费。
* **状态窗口**：`GET /v1/chains` 中的 `state_window_blocks` 所描述的状态窗口只作用于状态类方法（如 `eth_call`、`eth_getBalance`），不作用于 `eth_getLogs`。
* **节点历史裁剪**：区块与日志查询不受状态窗口限制，但受节点保留历史的限制；已被裁剪的数据返回 `4444 pruned history unavailable`（不计费）。

过滤字段 `fromBlock`、`toBlock` 缺省或为 `null` 时按 `latest` 处理。

本服务暂不支持 WebSocket 订阅：`eth_subscribe` 返回 `-32601 method not available`。如需跟进新事件，请轮询 `eth_getLogs` 查询最新区块。

## Data API 转账接口提供什么 [#data-api-转账接口提供什么]

两个端点的必填参数不同：

| 端点                                           | `standard`                                           | 区块窗口                                                                                                                                  |
| -------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /{chain}/addresses/{address}/transfers` | 必填：`erc20` 或 `erc721`。`erc1155` 返回 `422 no_coverage` | `from_block` 与 `to_block` 均必填。结果按 `(block_number, log_index)` 降序排列。`direction`（`in`、`out` 或 `any`，默认 `any`）按方向过滤，`token` 可选，用于限定单个合约。 |
| `GET /{chain}/tokens/{token}/transfers`      | 必填：`erc20`、`erc721` 或 `erc1155`                      | `from_block` 与 `to_block` 可选。缺省 `to_block` 时默认取 `finalized_block`；显式传入高于它的值会直接返回 `409`，没有 `clamp` 回退。                                 |

### 分页 [#分页]

两个端点都使用游标分页（keyset pagination）：

* `limit` 默认 50；超过 500 会被收敛为 500，`0` 或非整数返回 `400 bad_request`。
* 仅当还有下一页时才返回 `next_cursor`；最后一页该字段整个不存在，绝不会是 `null`。
* 把返回的 `next_cursor` 原样作为 `cursor` 传回即可取下一页。游标只对签发它的链、端点和查询参数有效。

### 覆盖范围与最终性 [#覆盖范围与最终性]

* 两个端点都属于 `transfers` 能力。未提供该能力的链返回 `422 no_coverage`。提供该数据集的链以[支持的链](/zh/chains/)页面为准。
* `GET /v1/data/chains` 返回每条链的 `coverage`（`history_mode`、`from_block`，窗口链还有 `retention_days`）。完全早于该链首个已索引区块的窗口返回 `422 no_coverage`；起点早于它、但终点在其后的窗口会尽量返回，并带有 `meta.coverage = "partial"`。窗口链的 `coverage.from_block` 会向前移动——请在运行时读取。
* 按区块查询的响应只提供不高于 `meta.finalized_block` 的数据。它是重组安全水位线（不是共识最终性信号），按链以固定余量落后于 `as_of_block`。
* 对按地址查询的端点，`to_block` 高于 `finalized_block` 时返回 `409 finality_exceeded`，除非 `clamp=true` 将其截断到 `finalized_block`；窗口过大时返回 `409 window_too_large`，除非 `clamp=true`。按代币查询的端点没有 `clamp` 回退。

每条转账数据包含 `token`、`standard`、`from`、`to`、`block_number`、`block_timestamp`、`tx_hash`、`tx_index`、`log_index`。ERC-20 额外有 `amount`；ERC-721 额外有 `token_id`；ERC-1155 额外有 `operator`、`token_id`、`value`、`batch_index`。

## 该选哪一个 [#该选哪一个]

| 典型任务       | 更适合                                          | 理由                                                                                                            |
| ---------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| 最近几百个区块的事件 | `eth_getLogs`                                | 只要区间不超过该链的 `max_logs_block_range`，一次请求即可覆盖最近一段区块。与转账接口不同，它不限于 `finalized_block` 及以下的区块。                       |
| 某地址的历史转账   | `GET /{chain}/addresses/{address}/transfers` | 按地址维度查询，支持 `from_block`/`to_block` 窗口、`direction` 与 `token` 过滤，并用游标翻页；结果截止到 `finalized_block`。                |
| 某代币的全部转账   | `GET /{chain}/tokens/{token}/transfers`      | 按代币合约维度查询，`standard` 覆盖 `erc20`、`erc721`、`erc1155`，窗口可选，用游标分页遍历全部结果。                                          |
| 实时监听新事件    | `eth_getLogs`（轮询）                            | 本服务暂不支持 WebSocket 订阅（`eth_subscribe` 返回 `-32601`），且转账接口只提供不高于 `finalized_block` 的数据。请轮询 `eth_getLogs` 查询最新区块。 |

## 用 eth\_getLogs 查询日志 [#用-eth_getlogs-查询日志]

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY=rgw_your_api_key

    # fromBlock / toBlock 缺省时为 latest。跟进新事件时可显式指定最近区间，
    # 并让跨度不超过该链的 max_logs_block_range。
    curl -s "https://dev-api.blockvectra.network/v1/robinhood_mainnet" \
      -H 'Content-Type: application/json' \
      -H "x-api-key: $BLOCKVECTRA_API_KEY" \
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "eth_getLogs",
        "params": [{
          "address": "0x1111111111111111111111111111111111111111",
          "fromBlock": "latest",
          "toBlock": "latest"
        }]
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const res = await fetch("https://dev-api.blockvectra.network/v1/robinhood_mainnet", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "eth_getLogs",
        params: [{
          address: "0x1111111111111111111111111111111111111111",
          fromBlock: "latest",
          toBlock: "latest",
        }],
      }),
    });

    const { result } = await res.json();
    console.log(result);

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    res = requests.post(
        "https://dev-api.blockvectra.network/v1/robinhood_mainnet",
        headers={
            "Content-Type": "application/json",
            "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "eth_getLogs",
            "params": [{
                "address": "0x1111111111111111111111111111111111111111",
                "fromBlock": "latest",
                "toBlock": "latest",
            }],
        },
    )
    res.raise_for_status()
    print(res.json())
    ```
  </Tab>
</Tabs>

## 用 Data API 查询转账 [#用-data-api-查询转账]

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY=rgw_your_api_key

    # 这里 from_block / to_block 可选；缺省 to_block 时默认取 finalized_block。
    curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers?standard=erc20" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    let cursor: string | undefined;

    do {
      const url = new URL(
        "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers",
      );
      url.searchParams.set("standard", "erc20");
      if (cursor) url.searchParams.set("cursor", cursor);

      const res = await fetch(url, {
        headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
      });
      const body = await res.json();
      console.log(body.data);
      cursor = body.next_cursor; // 最后一页不存在该字段
    } while (cursor);

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    url = "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers"
    cursor = None

    while True:
        params = {"standard": "erc20"}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            url,
            params=params,
            headers={"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]},
        )
        res.raise_for_status()
        body = res.json()
        print(body["data"])
        cursor = body.get("next_cursor")  # 最后一页不存在该字段
        if not cursor:
            break
    ```
  </Tab>
</Tabs>

若要按地址查询，`from_block` 与 `to_block` 均为必填：

```bash
# clamp=true 会把过宽的窗口、或高于 finalized_block 的 to_block 截断，
# 而不是返回 409。
curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/transfers?standard=erc20&from_block=0&to_block=73000000&direction=any&clamp=true" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```

## 单次调用的 CU [#单次调用的-cu]

每个方法都按其 CU 权重计费。下方权重在构建时从平台计划接口读取：

<LogsVsTransfersCu lang="zh" />

当前价格与充值方式见[定价页](https://blockvectra.com/zh/pricing/)。

## 下一步 [#下一步]

* [浏览数据集目录](https://blockvectra.com/zh/data/)，查看 BlockVectra 索引的全部数据集。
* [查看免费额度与定价](https://blockvectra.com/zh/pricing/#free)，确认账户可用的方案。
* [登录控制台](https://console.blockvectra.com/zh/login/?next=%2Fzh%2Fkeys%2F)创建 API key。
