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)]로 분할하고, 성공 후에는 이전 청크의 끝에 1을 더한 블록부터 진행하세요.

이를 logs-minimal.mjs로 저장하고 BLOCKVECTRA_API_KEY, 컨트랙트 주소 LOG_ADDRESS, 확정된 블록 윈도우 FROM_BLOCK과 TO_BLOCK을 설정한 후 Node.js 24 이상에서 node logs-minimal.mjs를 실행하세요. 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로 계산되며 대상 체인에 고시된 max_logs_block_range를 초과할 수 없습니다.

이 제한은 체인마다 다릅니다. 체인별 파라미터는 퍼블릭 엔드포인트인 GET /v1/chains(지원 체인에 체인 목록 제공)를 통해 제공됩니다. 이 엔드포인트는 인증이 필요하지 않으며 비용이 청구되지 않습니다. 클라이언트 애플리케이션을 개발할 때는 코드에 블록 범위 제한을 하드코딩하지 말고 런타임에 이 엔드포인트를 동적으로 조회하세요.

필터 필드인 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. Read max_logs_block_range from the public chains endpoint (unauthenticated, unbilled)
curl -s "https://api.blockvectra.com/v1/chains"

# 2. Make a single compliant request within the chain's max_logs_block_range (toBlock - fromBlock + 1)
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 가중치 합계가 키의 버스트 용량(burst_cu)을 초과하면 HTTP 429 -32022 request cost <N> CU exceeds burst capacity <M> CU로 거부됩니다(미과금). 더 작은 배치로 나누어 요청하세요.
  • 버킷 용량 부족: 전체 가중치의 합이 버스트 용량을 초과하지 않더라도 토큰 버킷에 사용 가능한 용량이 부족하면 서비스는 HTTP 429와 오류 코드 -32005 rate limit exceeded 및 Retry-After 헤더를 반환합니다. 재시도 및 과금 세부사항은 과금되지 않는 요청: 오류 코드 및 과금 규칙을 참조하세요.

따라서 대규모 로그 쿼리를 실행할 때는 순차적 청크 분할 쿼리를 사용하는 것이 좋습니다. 배치를 사용할 경우 전체 가중치의 합이 버스트 용량 내에 유지되도록 배치당 호출 수를 충분히 작게 유지하세요.

관련 가이드 및 과금 규칙

다음 단계

최종 수정일:

이 페이지의 내용