# 在支援的視窗內查詢歷史 EVM 狀態

> Source: https://docs.blockvectra.com/zh-hant/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`                                      | 透過免 key `public.url` 可用的歷史區塊參照                                                   | 僅使用 `public.methods`。對於狀態讀取，取公開歷史與宣告狀態視窗中較小者。                                     |
| `max_logs_block_range`                                       | 單次認證 `eth_getLogs` 請求中的區塊數                                                       | 計算 `toBlock − fromBlock + 1`。允許的跨度不代表較舊的合約狀態或日誌可用。                                |

這些限制以區塊為單位，不是天數。`null` 或未宣告的狀態視窗不代表 archive 涵蓋範圍。免 key 方法可用性與認證方法可用性分開：單有日誌跨度並不會啟用公開 `eth_getLogs`。

## 比較各鏈狀態視窗

表格顯示公開快照中已發布的狀態視窗、免 key 歷史、日誌跨度與宣告的 Data API 資料集。若要立即請求，請重新讀取 [GET /v1/chains](https://api.blockvectra.com/v1/chains) 與 [GET /v1/status](https://api.blockvectra.com/v1/status)。

開發者與 AI Agent 應分別核對狀態視窗與日誌查詢跨度。狀態視窗為 null 不代表歸檔覆蓋；公開歷史範圍只適用於宣告的公開方法。

| 鏈 | 鏈識別碼 | 認證狀態視窗：state_window_blocks（區塊數） | 免 key 歷史：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`。請改為在宣告視窗內選擇明確的區塊號。區塊雜湊形式不是取得額外歷史的方法：免 key 狀態讀取會拒絕它，而認證請求仍取決於可用的狀態。

區塊號在重組後可能指向不同的區塊。如果你需要識別結果所屬的區塊，請用 `eth_getBlockByNumber` 記錄區塊雜湊。視窗內的號碼也需要已同步的鏈，以及該高度存在合約。

## 固定區塊合約讀取

在以太坊上，位於 `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` 的 WETH 以選擇器 `0x313ce567` 公開 `decimals()`。一次在 2026-10-08（UTC）區塊 `0x18efa2f` 取樣的免 key 呼叫回傳 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.js 24 或以上、已設定 `BLOCKVECTRA_API_KEY` 環境變數的情況下執行 `node historical-state.mjs`。它使用認證端點，保留相同的合約與 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。若要進行免 key 讀取，請直接從 `public.url` 取得 URL、省略 key，並在 `public.history_blocks` 與狀態視窗兩者範圍內選擇區塊。即使合約與 calldata 相同，變更認證方式也可能改變允許的歷史。

## 診斷超出視窗的錯誤

在同一個免 key 端點上，一次在 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`；即使 HTTP 成功，也要檢查 JSON-RPC `error` |
| `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 價格與以方法為基礎的成本、區分包含配額與額外用量，並說明 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/)：錢包連線與專用 key。
* [支援的鏈](https://docs.blockvectra.com/en/chains/)：網路可用性；[CU 定價](https://docs.blockvectra.com/en/guides/reading-cu-pricing/)：方法成本。
