# Запрос исторического состояния EVM в пределах поддерживаемых окон

> Source: https://docs.blockvectra.com/ru/guides/evm-historical-state/

Исторические вызовы `eth_call` зависят от окна состояния сети, а не от лимита диапазона блоков `eth_getLogs`. Проверьте окно состояния, режим аутентификации эндпоинта и целевой блок перед чтением более раннего значения контракта.

## Три различных исторических лимита

| Поле в [GET /v1/chains](https://api.blockvectra.com/v1/chains) | Что контролирует                                                                                                                                                      | Что проверять                                                                                                                                      |
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state_window_blocks`                                          | На какую глубину в прошлое могут обращаться аутентифицированные операции чтения состояния, такие как `eth_call`, `eth_getBalance`, `eth_getCode` и `eth_getStorageAt` | При вершине `H` и объявленном окне `W` номерной блок старше `H − W` находится за пределами окна. Также проверьте `methods.allow` и `methods.deny`. |
| `public.history_blocks`                                        | Обращения к историческим блокам через `public.url` без ключа                                                                                                          | Используйте только `public.methods`. Для чтения состояния применяется меньшее из значений публичной истории и объявленного окна состояния.         |
| `max_logs_block_range`                                         | Количество блоков в одном аутентифицированном запросе `eth_getLogs`                                                                                                   | Рассчитывается как `toBlock − fromBlock + 1`. Допустимый диапазон не гарантирует доступность старого состояния контракта или логов.                |

Эти лимиты измеряются в блоках, а не в днях. Значение `null` или необъявленное окно состояния не означает архивного покрытия. Доступность методов без ключа не зависит от доступности аутентифицированных методов: один лишь диапазон логов не открывает публичный доступ к `eth_getLogs`.

## Сравнение окон состояния по сетям

В таблице показаны опубликованные окна состояния, история без ключа, диапазоны логов и объявленные наборы данных Data API из публичного снимка. Для актуального запроса повторно обратитесь к [GET /v1/chains](https://api.blockvectra.com/v1/chains) и [GET /v1/status](https://api.blockvectra.com/v1/status).

Разработчикам и AI-агентам следует проверять окна состояния и диапазоны запросов логов раздельно. Значение null для окна состояния не означает полного покрытия архива. Публичная история применяется только к объявленным публичным методам.

| Сеть | Слаг сети | Окно аутентифицированного состояния: state_window_blocks (блоков) | История без ключа: public.history_blocks (блоков) | Диапазон запроса аутентифицированных логов: max_logs_block_range (блоков) | Объявленные наборы данных Data API |
| --- | --- | --- | --- | --- | --- |
| Arbitrum One | `arb_mainnet` | 6,000 | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Base | `base_mainnet` | 10,000 | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| BNB Smart Chain | `bsc_mainnet` | 100 | 100 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Ethereum | `eth_mainnet` | 250,000 | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Ethereum Sepolia | `eth_sepolia` | Не объявлено | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| HyperEVM | `hyperevm_mainnet` | Не объявлено | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, balances, holders, nfts, freshness |
| Polygon | `polygon_mainnet` | 126 | 126 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Robinhood Chain | `robinhood_mainnet` | 900 | 900 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, balances, holders, nfts, dex_swaps, dex_prices, stocks, traces, freshness |
| Robinhood Chain Testnet | `robinhood_testnet` | 1,023 | 1,000 | 1,000 | Data API недоступен |

[GET /v1/chains](https://api.blockvectra.com/v1/chains) · Время выборки (UTC): 2026-10-09

[GET /v1/status](https://api.blockvectra.com/v1/status) · Время выборки (UTC): 2026-10-09

## Выбор тега блока

Используйте `latest` для получения текущего значения. Для исторического сравнения однократно запросите `eth_blockNumber` и преобразуйте выбранный номер блока в шестнадцатеричное значение, например `0x18efa2f`. Сохраняйте этот номер фиксированным для каждого вызова в сравнении; повторяющиеся вызовы `latest` могут использовать разные блоки.

Для чтения состояния теги `earliest`, `safe` и `finalized` возвращают `-32011` в соответствии с политикой окна состояния. Вместо них указывайте явный номер блока в пределах объявленного окна. Формат хэша блока не позволяет получить дополнительную историю: запросы состояния без ключа отклоняют его, а аутентифицированный запрос по-прежнему зависит от доступного состояния.

Номер блока может указывать на другой блок после реорганизации цепи. Зафиксируйте хэш блока с помощью `eth_getBlockByNumber`, если вам необходимо точно идентифицировать блок результата. Для блока в пределах окна также требуется синхронизированная сеть и наличие контракта на этой высоте.

## Чтение контракта на фиксированном блоке

В сети Ethereum контракт WETH по адресу `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` предоставляет метод `decimals()` с селектором `0x313ce567`. Запрос без ключа, выполненный 2026-10-08 (UTC) на блоке `0x18efa2f`, вернул HTTP 200 со следующим результатом:

Запрос к `public.url` сети:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "eth_call",
  "params": [
    { "to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "data": "0x313ce567" },
    "0x18efa2f"
  ]
}
```

Ответ:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000012"
}
```

Закодированное по ABI целое число равно 18. Этот результат представляет собой значение decimals, а не баланс, и не подтверждает доступность на других высотах. Данный фиксированный блок со временем выйдет за пределы ограниченного окна; используйте недавний блок при последующем запуске следующего примера.

Сохраните пример как `historical-state.mjs` и запустите команду `node historical-state.mjs` с Node.js 24 или новее, предварительно установив переменную окружения `BLOCKVECTRA_API_KEY`. Он использует аутентифицированный эндпоинт, сохраняет тот же контракт и calldata и сравнивает `latest`, один фиксированный недавний блок и блок за пределами опубликованного аутентифицированного окна. Каждый вывод включает фактический HTTP-статус и тело JSON-RPC; ответ HTTP 200 все еще может содержать ошибку. Скрипт останавливается при неожиданном ответе вместо того, чтобы считать его успешным чтением.

```js
const key = process.env.BLOCKVECTRA_API_KEY;
if (!key) throw new Error('Set BLOCKVECTRA_API_KEY');
const chainsUrl = 'https://api.blockvectra.com/v1/chains';
const catalogResponse = await fetch(chainsUrl, { signal: AbortSignal.timeout(15_000) });
if (!catalogResponse.ok) throw new Error(`Chains HTTP ${catalogResponse.status}`);
const catalog = await catalogResponse.json();
const chain = catalog.chains.find(item => item.chain === 'eth_mainnet');
const matches = (method, pattern) => pattern.endsWith('*')
  ? method.startsWith(pattern.slice(0, -1)) : method === pattern;
if (!chain?.jsonrpc || !['eth_call', 'eth_blockNumber'].every(method =>
  chain.methods?.allow?.some(pattern => matches(method, pattern)) &&
  !chain.methods?.deny?.some(pattern => matches(method, pattern)))) {
  throw new Error('Required methods are unavailable');
}
const window = chain.state_window_blocks;
if (!Number.isSafeInteger(window) || window < 10) {
  throw new Error('This example needs a declared state window of at least 10 blocks');
}
const rpcUrl = new URL('./eth_mainnet', chainsUrl).href;
let id = 0;
async function rpc(method, params) {
  const response = await fetch(rpcUrl, {
    method: 'POST', redirect: 'error', signal: AbortSignal.timeout(15_000),
    headers: { 'Content-Type': 'application/json', 'x-api-key': key },
    body: JSON.stringify({ jsonrpc: '2.0', id: ++id, method, params }),
  });
  return { http: response.status, body: await response.json() };
}
const headResponse = await rpc('eth_blockNumber', []);
if (headResponse.http !== 200 || headResponse.body.error ||
    !/^0x[0-9a-f]+$/i.test(headResponse.body.result ?? '')) {
  throw new Error(`Cannot read head: ${JSON.stringify(headResponse)}`);
}
const head = BigInt(headResponse.body.result);
if (head <= BigInt(window)) throw new Error('Head is too low for an out-of-window block');
const hex = value => `0x${value.toString(16)}`;
const fixedBlock = hex(head - 10n);
const outsideBlock = hex(head - BigInt(window) - 1n);
const call = { to: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', data: '0x313ce567' };
for (const block of ['latest', fixedBlock, outsideBlock]) {
  const reply = await rpc('eth_call', [call, block]);
  console.log(JSON.stringify({ head: hex(head), block, ...reply }));
  if (block === outsideBlock) {
    if (reply.http !== 200 || reply.body.error?.code !== -32011 ||
        reply.body.error?.data?.reason !== 'state_window') {
      throw new Error('Expected state_window; inspect the actual response above');
    }
  } else if (reply.http !== 200 || reply.body.error ||
      reply.body.result !== '0x0000000000000000000000000000000000000000000000000000000000000012') {
    throw new Error('Expected the WETH decimals result; inspect the actual response above');
  }
}
```

Приведенный выше записанный ответ использует `public.url`; скрипт использует API-ключ. Чтобы выполнить чтение без ключа, возьмите URL напрямую из `public.url`, опустите ключ и выберите блок в пределах `public.history_blocks`, а также окна состояния. Смена способа аутентификации может изменить допустимую глубину истории даже для одного и того же контракта и calldata.

## Диагностика ошибки выхода за пределы окна

На том же эндпоинте без ключа вызов, выполненный 2026-10-08 (UTC) с изменением только целевого блока на `0x18ef650` (и идентификатора запроса), вернул HTTP 200 с `error.code: -32011`, `error.data.reason: state_window` и `error.data.retryable: false`. Его сообщение было `block reference is outside the public history window`. Это сбой публичной истории; аутентифицированный эндпоинт имеет собственное окно состояния.

Используйте эти поля из [записи об ошибке state\_window](https://docs.blockvectra.com/en/errors/#state_window) для распознавания сбоя, а не полагайтесь на конкретное число блоков в сообщении:

| Поле                   | Документированное значение или значение по смыслу                                                                                                            |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| HTTP-статус            | `200`; проверяйте объект `error` в JSON-RPC, даже когда HTTP-запрос успешен                                                                                  |
| `error.code`           | `-32011`                                                                                                                                                     |
| `error.message`        | Ошибки аутентифицированного окна состояния содержат количество последних поддерживаемых блоков; ошибки публичной истории могут использовать другое сообщение |
| `error.data.reason`    | `state_window`                                                                                                                                               |
| `error.data.docs_url`  | Ссылка на описание `state_window` в каталоге ошибок                                                                                                          |
| `error.data.retryable` | `false`: повторная отправка того же запроса позже не восстановит старое состояние                                                                            |

Выберите более новый номерной блок или используйте `latest`, если задаче требуется текущее значение. Уменьшение диапазона `eth_getLogs` не восстанавливает историческое состояние `eth_call`. Другие причины ошибок `-32011` требуют иных действий: [range\_not\_indexed](https://docs.blockvectra.com/en/errors/#range_not_indexed) требует покрытия диапазона; [history\_not\_ready](https://docs.blockvectra.com/en/errors/#history_not_ready) допускает повторную попытку после завершения индексации. Проверяйте `error.data.reason`, а не только числовой код.

Базовое состояние также может быть недоступно с кодом `-32000`, либо история блоков может быть удалена с кодом `4444`; см. [каталог ошибок](https://docs.blockvectra.com/en/errors/). Не повторяйте запрос к старому блоку без изменений и не предполагайте, что большее объявленное окно гарантирует каждый ответ.

## Выбор следующего запроса

Полный чек-лист рабочей нагрузки и тесты для самопроверки см. в руководстве [Как выбрать RPC-провайдера](https://docs.blockvectra.com/en/guides/choose-rpc-provider/).

При выборе провайдера для регулярного чтения контрактов [сравните суточные и цикловые бюджеты для чтения EVM](https://docs.blockvectra.com/en/guides/infura-alternative/). Сначала определите необходимые исторические блоки, затем спланируйте суточное распределение и пропускную способность задачи; вхождение в бюджет кредитов не гарантирует покрытие состояния.

При сравнении провайдеров для исторического чтения сначала убедитесь, что оба могут предоставить целевой блок. [Сравнение стоимости перерасхода по запросам](https://docs.blockvectra.com/en/guides/chainstack-alternative/) сопоставляет цены на дополнительные RU с затратами по методам, разделяет включенную квоту и дополнительное использование, а также объясняет различия между полными и архивными классами тарификации.

Для ранее проиндексированных блоков, транзакций, переводов или других наборов данных обратитесь к объявленным наборам данных Data API в таблице и к [справочнику по Data API](https://docs.blockvectra.com/en/api/data/). Проиндексированные записи не обеспечивают произвольное выполнение контрактов в прошлом и не означают, что на каждой сети доступны исторические балансы.

* [Справочник по методу eth\_call](https://docs.blockvectra.com/en/api/json-rpc/methods/eth_call/) для параметров вызова и кодирования возвращаемых значений.
* [Диапазон блоков eth\_getLogs и составные запросы](https://docs.blockvectra.com/en/guides/getlogs-block-range/) для истории событий логов.
* [Настройка пользовательского RPC в кошельке](https://docs.blockvectra.com/en/guides/wallet-custom-rpc/) для подключения кошельков и выделенных ключей.
* [Поддерживаемые сети](https://docs.blockvectra.com/en/chains/) для проверки доступности сетей и [тарификация CU](https://docs.blockvectra.com/en/guides/reading-cu-pricing/) для стоимости методов.
