# eth_getLogs против Token Transfers API: история переводов ERC-20

> Source: https://docs.blockvectra.com/ru/guides/logs-vs-transfers/

Для истории кошелька или сверки переводов ERC-20 начните с [Token Transfers API](https://blockvectra.com/ru/data/transfers/). Используйте `eth_getLogs`, когда вам требуются журналы событий контрактов. Разработчики и ИИ-агенты могут запрашивать проиндексированные переводы по адресам через тот же API блокчейн-данных. [Руководство по активам кошелька](https://docs.blockvectra.com/en/guides/wallet-assets/) объединяет балансы токенов, историю переводов и метаданные; [справочник по Data API](https://docs.blockvectra.com/en/api/data/) определяет параметры запросов и схемы ответов.

## Задачи, которые помогает решить это руководство

* [Запрос журналов событий контрактов](#querying-logs-with-eth_getlogs) через аутентифицированный RPC в ограниченных диапазонах блоков для мониторинга или выгрузки логов.
* [Запрос индексированной истории переводов ERC-20](#querying-transfers-with-the-data-api) через API блокчейн-данных по адресу или контракту токена, с курсорной пагинацией и проверкой покрытия.

## Два способа чтения логов и переводов

`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` ограничен лимитами для конкретной сети, которые публикуются в ответе публичного `GET /v1/chains`:

* **Диапазон блоков**: `max_logs_block_range` — это максимальное количество блоков, которое может охватывать один запрос `eth_getLogs`. Оно различается по сетям — считывайте его из `GET /v1/chains` (список сетей приведен на странице [Поддерживаемые сети](https://docs.blockvectra.com/en/chains/)) вместо жесткого кодирования. Более широкий диапазон отклоняется с ошибкой JSON-RPC `-32602 eth_getLogs block range too large` (не тарифицируется).
* **Синхронизация узла**: пока узел сети не синхронизирован, `eth_getLogs` возвращает `-32010` (не тарифицируется).
* **Окно состояния**: окно состояния, которое `GET /v1/chains` возвращает в поле `state_window_blocks`, применяется к методам чтения состояния, таким как `eth_call` и `eth_getBalance`, а не к `eth_getLogs`.
* **Очистка данных узла (pruning)**: чтение блоков и логов не ограничено окном состояния, однако оно ограничено историей, сохраняемой узлом. Данные, которые были удалены при очистке, возвращают `4444 pruned history unavailable` (не тарифицируется).

Когда поля фильтра `fromBlock` и `toBlock` опущены или равны `null`, они по умолчанию принимают значение `latest`.

Вызов `eth_subscribe` по протоколу HTTP возвращает `-32601 method not available`. В сетях, где `ws` равен `true` в `/v1/chains`, `eth_subscribe` доступен через WebSocket (см. [Поддерживаемые сети](https://docs.blockvectra.com/en/chains/)); в противном случае выполняйте опрос `eth_getLogs` по новейшим блокам.

## Что предоставляют эндпоинты переводов 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` по умолчанию используется `as_of_block`; явное указание `to_block` или `from_block` выше него строго возвращает `409 not_indexed_yet` без возможности отсечения через `clamp`.                              |

### Пагинация

Оба эндпоинта используют курсорную пагинацию (keyset):

* `limit` по умолчанию равен 50; значения выше 500 ограничиваются до 500, а `0` или нецелое число возвращают `400 bad_request`.
* `next_cursor` появляется только при наличии следующей страницы. На последней странице ключ полностью отсутствует и никогда не равен `null`.
* Передавайте полученное значение обратно в качестве `cursor` без изменений для получения следующей страницы. Курсор действителен только для той сети, эндпоинта и параметров запроса, которые его выдали.

### Покрытие и финализация

Переводы Data API индексируют исторические переводы токенов от `coverage.from_block` каждой сети до `meta.as_of_block`. Сведения о сетях, поддерживающих эту функцию, см. в разделе [Поддерживаемые сети](https://docs.blockvectra.com/en/chains/).

Каждый элемент перевода содержит `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` этой сети.                                                                 |
| Исторические переводы адреса                  | `GET /{chain}/addresses/{address}/transfers`               | Запрос в области адреса с окном `from_block`/`to_block`, фильтрами `direction` и `token`, а также курсорной пагинацией; результаты выдаются вплоть до `as_of_block`. |
| Все переводы токена                           | `GET /{chain}/tokens/{token}/transfers`                    | Запрос в области контракта токена, охватывающий `erc20`, `erc721` и `erc1155`, с опциональным окном и курсорной пагинацией для полного набора результатов.           |
| Отслеживание новых событий в реальном времени | `eth_subscribe` (сети с WebSocket) / `eth_getLogs` (опрос) | Подписка на новые вершины или логи через WebSocket там, где это поддерживается, либо периодический опрос недавних диапазонов блоков.                                 |

## Запрос логов с помощью eth\_getLogs

**cURL**

```bash
export BLOCKVECTRA_API_KEY=rgw_your_api_key

# fromBlock / toBlock default to latest. Set an explicit recent range to follow
# new events, and keep its span within the chain's max_logs_block_range.
curl -s "https://api.blockvectra.com/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"
    }]
  }'
```


  **TypeScript**

```ts
const res = await fetch("https://api.blockvectra.com/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
```


  **Python**

```python
import os
import requests

res = requests.post(
    "https://api.blockvectra.com/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())
```


## Запрос переводов с помощью Data API

**cURL**

```bash
export BLOCKVECTRA_API_KEY=rgw_your_api_key

# from_block / to_block are optional here; omitting to_block defaults to as_of_block.
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers?standard=erc20" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```


  **TypeScript**

```ts
let cursor: string | undefined;

do {
  const url = new URL(
    "https://api.blockvectra.com/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; // absent on the last page
} while (cursor);

// npx tsx example.mts
```


  **Python**

```python
import os
import requests

url = "https://api.blockvectra.com/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")  # absent on the last page
    if not cursor:
        break
```


Чтобы выполнить запрос по адресу, параметры `from_block` и `to_block` обязательны:

```bash
# clamp=true truncates a too-wide window, or a to_block above as_of_block,
# instead of returning 409.
curl -s "https://api.blockvectra.com/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. Приведенные ниже веса считываются из API тарифных планов платформы:

**Вес CU на вызов**

| Метод | CU на вызов |
| --- | --- |
| `eth_getLogs` | 30 |
| `data.address_transfers` | 25 |
| `data.token_transfers` | 25 |

Для актуальных цен и вариантов пополнения см. [страницу цен](https://blockvectra.com/ru/pricing/).

## Следующие шаги

* [Ознакомьтесь с каталогом наборов данных](https://blockvectra.com/ru/data/), чтобы увидеть все наборы данных, которые индексирует BlockVectra.
* [Посмотрите бесплатный план и цены](https://blockvectra.com/ru/pricing/#free), чтобы узнать, что включено в ваш аккаунт.
* [Войдите в консоль](https://console.blockvectra.com/login/?next=%2Fkeys%2F), чтобы создать API key.
