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

> Source: https://docs.blockvectra.com/uk/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 не означає повноти архіву (archive coverage). Загальнодоступна історія застосовується лише до оголошених публічних методів.

| Мережа | Slug мережі | Автентифіковане вікно стану: state_window_blocks (блоків) | Історія без ключа: public.history_blocks (блоків) | Автентифікований діапазон запитів логів: max_logs_block_range (блоків) | Оголошені набори даних Data API |
| --- | --- | --- | --- | --- | --- |
| Arbitrum One | `arb_mainnet` | 5,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-10

[GET /v1/status](https://api.blockvectra.com/v1/status) · Вибірку зроблено (UTC): 2026-10-10

## Вибір тегу блоку

Використовуйте `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 key. Щоб виконати читання без ключа, візьміть URL безпосередньо з `public.url`, опустіть ключ і оберіть блок у межах як `public.history_blocks`, так і вікна стану. Зміна способу автентифікації може змінювати дозволену глибину історії навіть для одного й того самого контракту та calldata.

## Діагностика помилки виходу за межі вікна

На тому самому ендпоінті без ключа виклик, зафіксований 2026-10-08 (UTC), у якому було змінено лише цільовий блок на `0x18ef650` (та ID запиту), повернув 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`; перевіряйте JSON-RPC `error`, навіть коли запит 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/). Спочатку перевірте необхідні історичні блоки, а потім плануйте щоденний розподіл і пропускну здатність завдання; вкладання в кредитний бюджет не гарантує покриття потрібного стану.

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

Для більш ранніх індексованих блоків, транзакцій, переказів або інших наборів даних перевірте заявлені в таблиці набори даних 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/) щодо вартості методів.
