eth_getLogs 區塊跨度上限與分段查詢

處理 eth_getLogs 區塊範圍限制與 logs_range_too_large 報錯:讀取各鏈的 max_logs_block_range,將大跨度查詢切分為多個區段。

直接答案

單次 eth_getLogs 的上限是目標鏈在 GET /v1/chains 中的 max_logs_block_range(HyperEVM 為 1,000 個區塊),區塊數按 toBlock − fromBlock + 1 計算。超限回傳 HTTP 200、JSON-RPC -32602 與 error.data.reason: logs_range_too_large,且 retryable: false(見錯誤目錄)。將區間分成 [from, min(from + max − 1, end)],成功後從上一段末尾加一繼續。

將以下程式碼儲存為 logs-minimal.mjs,設定環境變數 BLOCKVECTRA_API_KEY、合約地址 LOG_ADDRESS 及已確認的區塊範圍 FROM_BLOCK、TO_BLOCK,然後執行 node logs-minimal.mjs(Node.js 24 或以上)。用 CHAIN 選擇目標鏈;預設使用 robinhood_mainnet。

const { BLOCKVECTRA_API_KEY: key, LOG_ADDRESS: address, FROM_BLOCK, TO_BLOCK } = process.env;
if (!key || !/^0x[0-9a-f]{40}$/i.test(address ?? '')) throw new Error('Set BLOCKVECTRA_API_KEY and LOG_ADDRESS');
if (![FROM_BLOCK, TO_BLOCK].every(value => /^(0x[0-9a-f]+|[0-9]+)$/i.test(value ?? ''))) {
  throw new Error('Set FROM_BLOCK and TO_BLOCK to nonnegative block numbers');
}
const start = BigInt(FROM_BLOCK), end = BigInt(TO_BLOCK);
if (start > end) throw new Error('FROM_BLOCK must not exceed TO_BLOCK');
const chainSlug = process.env.CHAIN ?? 'robinhood_mainnet';
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 === chainSlug);
if (!chain || !Number.isSafeInteger(chain.max_logs_block_range) || chain.max_logs_block_range <= 0) {
  throw new Error('Missing or invalid max_logs_block_range');
}
const matches = pattern => pattern.endsWith('*') ? 'eth_getLogs'.startsWith(pattern.slice(0, -1)) : pattern === 'eth_getLogs';
if (!chain.methods?.allow?.some(matches) || chain.methods?.deny?.some(matches)) {
  throw new Error('eth_getLogs is unavailable on this chain');
}
const max = BigInt(chain.max_logs_block_range);
const rpcUrl = new URL(`./${chainSlug}`, chainsUrl).href;
const hex = value => `0x${value.toString(16)}`;
for (let from = start; from <= end;) {
  const to = from + max - 1n < end ? from + max - 1n : end;
  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: 1, method: 'eth_getLogs',
      params: [{ address, fromBlock: hex(from), toBlock: hex(to) }] }),
  });
  const body = await response.json();
  if (!response.ok || body.error || !Array.isArray(body.result)) {
    throw new Error(`RPC HTTP ${response.status}: ${JSON.stringify(body.error ?? 'Invalid result')}`);
  }
  console.log(JSON.stringify({ fromBlock: hex(from), toBlock: hex(to), result: body.result }));
  from = to + 1n;
}

每行輸出一個已成功區段;任何 HTTP 或 JSON-RPC 錯誤都會終止範例,不會跳過失敗區段。429 處理見下方批次與速率限制說明。

eth_getLogs 的區塊跨度上限

在呼叫 JSON-RPC 方法 eth_getLogs 時,單次請求的區塊跨度按照 toBlock − fromBlock + 1 計算,不能超過目標鏈透過 GET /v1/chains 公布的 max_logs_block_range 上限。

該限制因鏈而異——各鏈的參數透過公開端點 GET /v1/chains 發布(鏈清單見支援的鏈)。該端點免驗證、不計費。在編寫用戶端程式碼時,請在執行時透過該端點動態讀取目標鏈的 max_logs_block_range,切勿將上限數值寫死在程式碼中。

過濾參數 fromBlock 與 toBlock 省略或為 null 時,按 latest 處理。

各鏈 eth_getLogs 上限

以下為 GET /v1/chains 公布的各鏈 max_logs_block_range;「未公布」不表示無限制。呼叫前同時核對該鏈的 methods.allow 與 methods.deny,其中 deny 優先;區塊跨度上限不代表結果數量或查詢耗時上限。

鏈鏈識別碼max_logs_block_range(區塊數)
Arbitrum Onearb_mainnet1,000
Basebase_mainnet1,000
BNB Smart Chainbsc_mainnet1,000
Ethereumeth_mainnet1,000
Ethereum Sepoliaeth_sepolia1,000
HyperEVMhyperevm_mainnet1,000
Polygonpolygon_mainnet1,000
Robinhood Chainrobinhood_mainnet1,000
Robinhood Chain Testnetrobinhood_testnet1,000

常見報錯原文

先區分區塊跨度、結果數量與查詢耗時;同一個 JSON-RPC 錯誤碼可能代表不同問題。

報錯原文 / 識別代號出處處理方式
eth_getLogs block range too large: max <N> blocks;-32602;logs_range_too_largeBlockVectra 錯誤目錄<N> 是該鏈的 max_logs_block_range;縮小跨度後重發,原樣重試無效。
query block range exceeds server limit, narrow your filter: <N>Erigon eth_getLogs 原始碼<N> 是該節點的區塊範圍上限;縮小查詢區間後重發。
query returns too many logs, narrow your filter: <N>Erigon eth_getLogs 原始碼<N> 是該節點的結果數量上限;縮小區間,並用 address、topics 收窄過濾。即使只查一個區塊,也可能需要更精確的過濾條件。

以上報錯範本中的 <N> 在實際回應中替換為端點上限。第三方原文對應其自身的端點與限制,措辭可能隨用戶端版本變化;使用 BlockVectra 時,以 /v1/chains 和 error.data.reason 為準。

超出跨度上限的表現

當單次請求的區塊跨度 toBlock − fromBlock + 1 超過該鏈的 max_logs_block_range 時,請求回傳 HTTP 200 與 JSON-RPC 錯誤:

  • 錯誤碼:-32602
  • 錯誤訊息:eth_getLogs block range too large: max <N> blocks
  • 計費狀態:不計費。

範例請求

以下請求僅在區塊跨度大於目標鏈目前的 max_logs_block_range 時超限:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_getLogs",
  "params": [
    {
      "fromBlock": "0x45a2409",
      "toBlock": "0x45a27f1"
    }
  ]
}

範例回應

超出上限時回傳的錯誤回應範例:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "eth_getLogs block range too large: max <N> blocks"
  }
}

其中 <N> 為該鏈的 max_logs_block_range(透過 GET /v1/chains 發布)。

在包含多個呼叫的批次請求中,如果其中某個 eth_getLogs 跨度過大,該呼叫同樣會在對應位置回傳上述 -32602 錯誤,且該呼叫不計費。

分段查詢實作

當需要檢索跨越較長區間的日誌時,應先讀取目標鏈的 max_logs_block_range,再將目標區間按照 [from, from + max - 1] 劃分為若干段閉區間,按順序逐段請求並合併結果。

以下範例以 robinhood_mainnet 為例示範分段查詢流程:

export BLOCKVECTRA_API_KEY="rgw_your_api_key"

# 1. 從公開端點讀取目標鏈的 max_logs_block_range(免驗證、不計費)
curl -s "https://api.blockvectra.com/v1/chains"

# 2. 發起單次合規請求:區塊跨度(toBlock - fromBlock + 1)不超過該鏈的 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": "0x45a2409",
      "toBlock": "0x45a246c"
    }]
  }'

批次請求注意事項

若考慮將切分後的多個分段呼叫打包進單個 JSON-RPC 批次請求,需要遵循批次與突發上限規則:

  • 批次大小限制:批次請求支援 1~100 個呼叫;超過 100 個呼叫會被直接拒絕,回傳 HTTP 200 與錯誤碼 -32600 batch too large: max 100 calls(不計費)。
  • 單請求突發上限:若單次請求內所有呼叫的 CU 權重之和超過該 key 的突發容量(burst_cu),請求會被拒絕並回傳 HTTP 429 -32022 request cost <N> CU exceeds burst capacity <M> CU(不計費);請拆分成更小的批次。
  • 權杖桶容量不足:若單請求滿權重之和未超過突發上限,但目前權杖桶內可用容量不足,服務回傳 HTTP 429 與錯誤碼 -32005 rate limit exceeded(帶 Retry-After),詳見哪些情況不扣費:錯誤碼與計費規則。

因此,在大跨度日誌查詢時,建議採用循序逐段請求;若使用批次請求,應嚴格控制批內呼叫數量,避免滿權重之和超出突發上限。

相關指南與計費規則

下一步

最後更新:

本頁目錄