Kueri State EVM Historis dalam Jendela yang Didukung

Bedakan jendela state terautentikasi, riwayat tanpa API key, dan rentang log. Pilih blok tetap untuk eth_call dan diagnosis error state_window.

eth_call historis bergantung pada jendela state chain, bukan batas rentang blok eth_getLogs. Periksa jendela state, mode autentikasi endpoint, dan blok tujuan sebelum membaca nilai kontrak sebelumnya.

Tiga batas historis yang berbeda

Bidang dalam GET /v1/chainsYang dikendalikannyaYang perlu diperiksa
state_window_blocksSeberapa jauh pembacaan state terautentikasi seperti eth_call, eth_getBalance, eth_getCode, dan eth_getStorageAt dapat menelusuri ke belakangDengan head H dan jendela yang dinyatakan W, blok bernomor yang lebih lama dari H − W berada di luar jendela. Periksa juga methods.allow dan methods.deny.
public.history_blocksReferensi blok historis melalui public.url tanpa API keyGunakan hanya public.methods. Untuk pembacaan state, batas yang lebih kecil antara riwayat publik dan jendela state yang dinyatakan berlaku.
max_logs_block_rangeJumlah blok dalam satu permintaan eth_getLogs terautentikasiHitung toBlock − fromBlock + 1. Rentang yang diizinkan tidak memastikan state kontrak atau log lama tersedia.

Batas ini menggunakan satuan blok, bukan hari. Jendela state null atau yang tidak dinyatakan tidak memastikan cakupan arsip. Ketersediaan metode tanpa API key terpisah dari ketersediaan metode terautentikasi: rentang log saja tidak mengaktifkan eth_getLogs publik.

Bandingkan jendela state per chain

Tabel menampilkan jendela state yang dipublikasikan, riwayat tanpa API key, rentang log, dan dataset Data API yang dinyatakan dari snapshot publik. Untuk permintaan saat ini, baca kembali GET /v1/chains dan GET /v1/status.

Pengembang dan agen AI harus memeriksa jendela status dan rentang kueri log secara terpisah. Jendela status null tidak menetapkan cakupan arsip. Riwayat publik hanya berlaku untuk metode publik yang dideklarasikan.

RantaiSlug rantaiJendela status terautentikasi: state_window_blocks (blok)Riwayat tanpa key: public.history_blocks (blok)Rentang kueri log terautentikasi: max_logs_block_range (blok)Kumpulan data Data API yang dideklarasikan
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_sepoliaTidak dideklarasikan1,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
HyperEVMhyperevm_mainnetTidak dideklarasikan1,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 dinonaktifkan

GET /v1/chains · Diambil sampel (UTC):

GET /v1/status · Diambil sampel (UTC):

Pilih tag blok

Gunakan latest untuk nilai saat ini. Untuk perbandingan historis, baca eth_blockNumber sekali dan konversikan nomor blok yang dipilih ke kuantitas heksadesimal seperti 0x18efa2f. Pertahankan nomor tersebut untuk setiap panggilan dalam perbandingan; panggilan latest berulang dapat menggunakan blok berbeda.

Untuk pembacaan state, earliest, safe, dan finalized mengembalikan -32011 berdasarkan kebijakan jendela state. Pilih nomor blok eksplisit dalam jendela yang dinyatakan. Bentuk hash blok bukan cara memperoleh riwayat tambahan: pembacaan state tanpa API key menolaknya, dan permintaan terautentikasi tetap bergantung pada state yang tersedia.

Nomor blok dapat merujuk ke blok berbeda setelah reorganisasi. Catat hash blok dengan eth_getBlockByNumber jika perlu mengidentifikasi blok hasilnya. Nomor dalam jendela juga memerlukan chain yang tersinkronisasi dan kontrak yang sudah ada pada tinggi tersebut.

Pembacaan kontrak pada blok tetap

Di Ethereum, WETH pada 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 menyediakan decimals() dengan selector 0x313ce567. Panggilan tanpa API key yang diambil sampelnya pada 2026-10-08 (UTC) di blok 0x18efa2f mengembalikan HTTP 200 dengan hasil berikut:

Permintaan ke public.url chain:

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

Respons:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000012"
}

Bilangan bulat yang dikodekan ABI adalah 18. Hasilnya merupakan nilai decimals, bukan saldo, dan tidak memastikan ketersediaan pada tinggi lain. Blok tetap tersebut akan keluar dari jendela berbatas seiring waktu; gunakan blok terbaru saat menjalankan contoh berikut di kemudian hari.

Simpan contoh sebagai historical-state.mjs dan jalankan node historical-state.mjs dengan Node.js 24 atau lebih baru dan variabel lingkungan BLOCKVECTRA_API_KEY diatur. Contoh menggunakan endpoint terautentikasi, mempertahankan kontrak dan calldata yang sama, serta membandingkan latest, satu blok tetap terbaru, dan blok di luar jendela terautentikasi yang dipublikasikan. Setiap keluaran menyertakan status HTTP dan body JSON-RPC nyata; HTTP 200 tetap dapat berisi error. Contoh berhenti pada respons yang tidak diharapkan alih-alih menganggapnya sebagai pembacaan berhasil.

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');
  }
}

Respons yang dicatat di atas menggunakan public.url; skrip menggunakan API key. Untuk pembacaan tanpa API key, ambil URL langsung dari public.url, hilangkan API key, dan pilih blok dalam public.history_blocks serta jendela state. Mengubah autentikasi dapat mengubah riwayat yang diizinkan, bahkan untuk kontrak dan calldata yang sama.

Diagnosis error di luar jendela

Pada endpoint tanpa API key yang sama, panggilan yang diambil sampelnya pada 2026-10-08 (UTC) dengan hanya mengubah blok tujuan menjadi 0x18ef650 (dan ID permintaan) mengembalikan HTTP 200 dengan error.code: -32011, error.data.reason: state_window, dan error.data.retryable: false. Pesannya adalah block reference is outside the public history window. Ini kegagalan riwayat publik; endpoint terautentikasi memiliki jendela state sendiri.

Gunakan bidang berikut dari entri error state_window untuk mengenali kegagalan, alih-alih mengandalkan angka jendela tertentu dalam pesan:

BidangNilai atau makna yang didokumentasikan
Status HTTP200; periksa error JSON-RPC meskipun HTTP berhasil
error.code-32011
error.messageError jendela state terautentikasi menjelaskan jumlah blok terbaru yang didukung; error riwayat publik dapat menggunakan pesan berbeda
error.data.reasonstate_window
error.data.docs_urlTautan ke penjelasan state_window dalam katalog error
error.data.retryablefalse: mengirim permintaan yang sama kemudian tidak memulihkan state lama

Pilih blok bernomor yang lebih baru atau gunakan latest jika tugas memerlukan nilai saat ini. Mengurangi rentang eth_getLogs tidak memulihkan state eth_call historis. Alasan -32011 lain membutuhkan tindakan berbeda: range_not_indexed memerlukan rentang yang tercakup; history_not_ready mengizinkan percobaan ulang setelah pengindeksan menyusul. Periksa error.data.reason, bukan hanya kode numeriknya.

State dasar juga dapat tidak tersedia dengan -32000, atau riwayat blok yang dipangkas dengan 4444; lihat katalog error. Jangan mencoba ulang blok lama tanpa perubahan atau menganggap jendela yang dinyatakan lebih besar menjamin setiap respons.

Pilih kueri berikutnya

Untuk daftar pemeriksaan beban kerja lengkap dan pengujian mandiri, mulai dengan Cara memilih penyedia RPC.

Saat memilih penyedia untuk pembacaan kontrak berulang, bandingkan anggaran harian dan siklus untuk pembacaan EVM. Periksa blok historis yang diperlukan terlebih dahulu, lalu rencanakan distribusi harian dan throughput tugas; sesuai dengan anggaran kredit tidak memastikan cakupan state.

Saat membandingkan penyedia untuk pembacaan historis, pastikan terlebih dahulu keduanya dapat melayani blok tujuan. Perbandingan penggunaan berlebih untuk permintaan lengkap membandingkan harga RU tambahan dengan biaya berbasis metode, memisahkan kuota yang disertakan dari penggunaan tambahan, dan menjelaskan kelas penagihan full versus archive.

Untuk blok terindeks sebelumnya, transaksi, transfer, atau dataset lain, periksa dataset Data API yang dinyatakan dalam tabel dan referensi Data API. Catatan terindeks tidak menyediakan eksekusi kontrak historis sebarang atau menyiratkan bahwa setiap chain memiliki saldo historis.

Terakhir diperbarui:

Di halaman ini