# 지원되는 윈도우 내에서 과거 EVM 상태 조회

> Source: https://docs.blockvectra.com/ko/guides/evm-historical-state/

과거 시점의 `eth_call`은 체인의 `eth_getLogs` 블록 범위 제한이 아니라 체인의 상태 윈도우(state window)에 따라 결정됩니다. 이전 컨트랙트 값을 읽기 전에 상태 윈도우, 엔드포인트의 인증 모드, 대상 블록을 확인하세요.

## 세 가지 서로 다른 과거 데이터 제한

| [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 Agent는 상태 윈도우와 로그 쿼리 범위를 각각 확인해야 합니다. null인 상태 윈도우가 아카이브 범위를 의미하지는 않습니다. 공개 이력은 선언된 공개 메서드에만 적용됩니다.

| 체인 | 체인 슬러그 | 인증된 상태 윈도우: state_window_blocks (블록 수) | 키 없는 공개 이력: 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`와 같은 16진수 수량으로 변환합니다. 비교 대상이 되는 모든 호출에서 해당 번호를 고정하여 사용하세요. `latest`를 반복 호출하면 서로 다른 블록이 사용될 수 있습니다.

상태 조회의 경우 상태 윈도우 정책에 따라 `earliest`, `safe`, `finalized`는 `-32011`을 반환합니다. 대신 선언된 윈도우 내의 명시적인 블록 번호를 지정하세요. 블록 해시 형식으로 요청한다고 해서 추가 과거 데이터를 얻을 수 있는 것은 아닙니다. 키 없는 상태 읽기에서는 거부되며, 인증된 요청 역시 제공 가능한 상태 범위에 종속됩니다.

블록 번호는 체인 재구성(reorg) 후 다른 블록을 가리킬 수 있습니다. 결과 블록을 정확히 식별해야 한다면 `eth_getBlockByNumber`로 블록 해시를 함께 기록하세요. 또한 윈도우 내의 블록 번호라 하더라도 동기화된 체인 상태와 해당 높이에 컨트랙트가 실제로 존재해야 합니다.

## 고정 블록 컨트랙트 조회

Ethereum에서 WETH(`0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`)는 선택자 `0x313ce567`을 갖는 `decimals()`를 제공합니다. 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.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를 사용합니다. 키 없는 조회를 수행하려면 `public.url`에서 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 status            | `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 가격과 메서드 기반 비용을 비교하고, 기본 포함된 쿼터와 초과 사용량을 구분하며, 전체 노드와 아카이브 노드 청구 등급을 설명합니다.

더 이전의 인덱싱된 블록, 트랜잭션, 전송 내역 또는 기타 데이터셋의 경우 표에 선언된 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/).
