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

區分認證狀態視窗、免 key 歷史與日誌跨度。為 eth_call 選擇固定區塊,並診斷 state_window 錯誤。

歷史 eth_call 取決於鏈的狀態視窗,而非其 eth_getLogs 區塊範圍限制。在讀取較早的合約值之前,請檢查狀態視窗、端點的認證模式與目標區塊。

三種不同的歷史限制

GET /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 與 GET /v1/status。

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

鏈鏈識別碼認證狀態視窗:state_window_blocks(區塊數)免 key 歷史:public.history_blocks(區塊數)認證日誌查詢跨度:max_logs_block_range(區塊數)明確宣告的 Data API 資料集
Arbitrum Onearb_mainnet6,0001,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
Basebase_mainnet10,0001,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
BNB Smart Chainbsc_mainnet1001001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
Ethereumeth_mainnet250,0001,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
Ethereum Sepoliaeth_sepolia未宣告1,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
HyperEVMhyperevm_mainnet未宣告1,0001,000blocks, transactions, address_transactions, transfers, token_metadata, balances, holders, nfts, freshness
Polygonpolygon_mainnet1261261,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
Robinhood Chainrobinhood_mainnet9009001,000blocks, transactions, address_transactions, transfers, token_metadata, balances, holders, nfts, dex_swaps, dex_prices, stocks, traces, freshness
Robinhood Chain Testnetrobinhood_testnet1,0231,0001,000Data API 不可用

GET /v1/chains · 取樣日期(UTC):

GET /v1/status · 取樣日期(UTC):

選擇區塊標籤

使用 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 發出請求:

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

回應:

{
  "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 仍可能包含錯誤。遇到非預期回應時它會停止,而不是將其視為成功的讀取。

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 錯誤條目中的這些欄位來辨識失敗,而不要依賴訊息中的特定視窗數字:

欄位文件值或意義
HTTP 狀態200;即使 HTTP 成功,也要檢查 JSON-RPC error
error.code-32011
error.message認證狀態視窗錯誤會描述最近支援的區塊數;公開歷史錯誤可能使用不同的訊息
error.data.reasonstate_window
error.data.docs_url連向錯誤目錄的 state_window 說明
error.data.retryablefalse:稍後送出相同請求不會恢復較舊的狀態

如果任務需要目前值,請選擇較新的編號區塊或使用 latest。縮小 eth_getLogs 跨度不會恢復歷史 eth_call 狀態。其他 -32011 原因有不同的處理方式:range_not_indexed 需要涵蓋的範圍;history_not_ready 允許在索引追上後重試。請檢查 error.data.reason,而不只是數字錯誤碼。

底層狀態也可能以 -32000 不可用,或區塊歷史被修剪而以 4444 表示;請參閱錯誤目錄。不要原封不動地重試舊區塊,也不要假設更大的宣告視窗能保證每個回應。

選擇下一個查詢

如需完整的工作負載檢查清單與自我測試,請從如何選擇 RPC 供應商開始。

為重複的合約讀取選擇供應商時,請比較 EVM 讀取的每日與週期預算。先檢查所需的歷史區塊,再規劃任務的每日分布與吞吐量;符合額度預算不代表具備狀態涵蓋範圍。

比較歷史讀取的供應商時,請先確認雙方都能提供目標區塊。完整請求超額比較會比較額外 RU 價格與以方法為基礎的成本、區分包含配額與額外用量,並說明 full 與 archive 計費類別。

若需要更早的已索引區塊、交易、轉帳或其他資料集,請檢查表中宣告的 Data API 資料集與 Data API 參考。已索引記錄不提供任意的歷史合約執行,也不代表每條鏈都有歷史餘額。

最後更新:

本頁目錄