Truy vấn trạng thái EVM lịch sử trong cửa sổ được hỗ trợ

Phân biệt cửa sổ trạng thái có xác thực, lịch sử không cần key và khoảng log. Chọn khối cố định cho eth_call và chẩn đoán lỗi state_window.

eth_call lịch sử phụ thuộc vào cửa sổ trạng thái của chuỗi, không phải giới hạn khoảng khối eth_getLogs. Kiểm tra cửa sổ trạng thái, chế độ xác thực endpoint và khối mục tiêu trước khi đọc giá trị hợp đồng trước đó.

Ba giới hạn lịch sử khác nhau

Trường trong GET /v1/chainsKiểm soát gìCần kiểm tra gì
state_window_blocksTruy vấn đọc trạng thái có xác thực như eth_call, eth_getBalance, eth_getCode và eth_getStorageAt có thể truy vấn ngược bao xaVới đầu chuỗi H và cửa sổ công bố W, khối có số cũ hơn H − W nằm ngoài cửa sổ. Kiểm tra cả methods.allow và methods.deny.
public.history_blocksTham chiếu khối lịch sử qua public.url không cần keyChỉ dùng public.methods. Với đọc trạng thái, áp dụng giới hạn nhỏ hơn giữa lịch sử công khai và cửa sổ trạng thái công bố.
max_logs_block_rangeSố khối trong một yêu cầu eth_getLogs có xác thựcTính toBlock − fromBlock + 1. Khoảng được phép không chứng minh trạng thái hợp đồng hoặc log cũ khả dụng.

Các giới hạn này tính bằng khối, không phải ngày. Cửa sổ trạng thái null hoặc không công bố không chứng minh phạm vi archive. Khả năng phương thức không cần key tách biệt với khả năng phương thức có xác thực: chỉ khoảng log không cho phép eth_getLogs công khai.

So sánh cửa sổ trạng thái theo chuỗi

Bảng hiển thị cửa sổ trạng thái công bố, lịch sử không cần key, khoảng log và bộ dữ liệu Data API khai báo từ snapshot công khai. Để gửi yêu cầu ngay lúc này, đọc lại GET /v1/chains và GET /v1/status.

Nhà phát triển và AI Agent nên kiểm tra riêng biệt cửa sổ trạng thái và phạm vi truy vấn log. Cửa sổ trạng thái null không có nghĩa là có lưu trữ toàn bộ lịch sử (archive). Lịch sử công khai chỉ áp dụng cho các phương thức công khai đã khai báo.

ChuỗiSlug chuỗiCửa sổ trạng thái xác thực: state_window_blocks (khối)Lịch sử không cần key: public.history_blocks (khối)Phạm vi truy vấn log xác thực: max_logs_block_range (khối)Bộ dữ liệu Data API đã khai báo
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_sepoliaChưa khai báo1,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
HyperEVMhyperevm_mainnetChưa khai báo1,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 không khả dụng

GET /v1/chains · Thời điểm lấy mẫu (UTC):

GET /v1/status · Thời điểm lấy mẫu (UTC):

Chọn block tag

Dùng latest cho giá trị hiện tại. Để so sánh lịch sử, đọc eth_blockNumber một lần và chuyển số khối đã chọn thành đại lượng thập lục phân như 0x18efa2f. Giữ số đó cố định cho mọi lệnh gọi trong phép so sánh; các lần gọi latest lặp lại có thể dùng khối khác nhau.

Với đọc trạng thái, earliest, safe và finalized trả -32011 theo chính sách cửa sổ trạng thái. Thay vào đó, chọn số khối tường minh trong cửa sổ công bố. Dạng hash khối không phải cách lấy thêm lịch sử: đọc trạng thái không cần key từ chối dạng này, và yêu cầu có xác thực vẫn phụ thuộc trạng thái khả dụng.

Số khối có thể trỏ tới khối khác sau tái tổ chức. Ghi hash khối bằng eth_getBlockByNumber nếu cần xác định khối của kết quả. Số khối trong cửa sổ cũng cần chuỗi đã đồng bộ và hợp đồng tồn tại tại độ cao đó.

Đọc hợp đồng tại khối cố định

Trên Ethereum, WETH tại 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 cung cấp decimals() với selector 0x313ce567. Lệnh gọi không cần key được lấy mẫu ngày 2026-10-08 (UTC) tại khối 0x18efa2f trả HTTP 200 với kết quả này:

Yêu cầu tới public.url của chuỗi:

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

Phản hồi:

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

Số nguyên mã hóa ABI là 18. Kết quả là giá trị số chữ số thập phân, không phải số dư, và không chứng minh khả năng truy cập tại độ cao khác. Khối cố định đó sẽ cũ dần và ra ngoài cửa sổ có giới hạn; dùng khối gần đây khi chạy ví dụ sau vào thời điểm muộn hơn.

Lưu ví dụ thành historical-state.mjs và chạy node historical-state.mjs với Node.js 24 trở lên và biến môi trường BLOCKVECTRA_API_KEY đã đặt. Ví dụ dùng endpoint có xác thực, giữ cùng hợp đồng và calldata, và so sánh latest, một khối cố định gần đây và một khối ngoài cửa sổ có xác thực công bố. Mỗi đầu ra có trạng thái HTTP thực tế và body JSON-RPC; HTTP 200 vẫn có thể chứa lỗi. Ví dụ dừng khi phản hồi không như dự kiến thay vì coi đó là truy vấn đọc thành công.

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

Phản hồi ghi nhận phía trên dùng public.url; script dùng API key. Để đọc không cần key, lấy URL trực tiếp từ public.url, bỏ key và chọn khối nằm trong cả public.history_blocks lẫn cửa sổ trạng thái. Đổi cách xác thực có thể đổi lịch sử được phép, ngay cả với cùng hợp đồng và calldata.

Chẩn đoán lỗi ngoài cửa sổ

Trên cùng endpoint không cần key, lệnh gọi được lấy mẫu ngày 2026-10-08 (UTC) chỉ đổi khối mục tiêu thành 0x18ef650 (và ID yêu cầu) trả HTTP 200 với error.code: -32011, error.data.reason: state_window và error.data.retryable: false. Thông báo là block reference is outside the public history window. Đây là lỗi lịch sử công khai; endpoint có xác thực có cửa sổ trạng thái riêng.

Dùng các trường này từ mục lỗi state_window để nhận diện lỗi thay vì dựa vào con số cửa sổ cụ thể trong thông báo:

TrườngGiá trị hoặc ý nghĩa được mô tả
Trạng thái HTTP200; kiểm tra error JSON-RPC ngay cả khi HTTP thành công
error.code-32011
error.messageLỗi cửa sổ trạng thái có xác thực mô tả số khối gần nhất được hỗ trợ; lỗi lịch sử công khai có thể dùng thông báo khác
error.data.reasonstate_window
error.data.docs_urlLiên kết tới giải thích state_window trong danh mục lỗi
error.data.retryablefalse: gửi lại cùng yêu cầu sau đó không khôi phục trạng thái cũ hơn

Chọn khối có số mới hơn hoặc dùng latest nếu tác vụ cần giá trị hiện tại. Giảm khoảng eth_getLogs không khôi phục trạng thái eth_call lịch sử. Các lý do -32011 khác cần thao tác khác: range_not_indexed cần khoảng được bao phủ; history_not_ready cho phép thử lại sau khi lập chỉ mục bắt kịp. Kiểm tra error.data.reason, không chỉ mã số.

Trạng thái nền cũng có thể không khả dụng với -32000, hoặc lịch sử khối bị cắt tỉa với 4444; xem danh mục lỗi. Đừng thử lại nguyên yêu cầu khối cũ hoặc giả định cửa sổ công bố lớn hơn bảo đảm mọi phản hồi.

Chọn truy vấn tiếp theo

Để có danh sách kiểm tra tác vụ đầy đủ và tự kiểm tra, bắt đầu với Cách chọn nhà cung cấp RPC.

Khi chọn nhà cung cấp cho đọc hợp đồng lặp lại, so sánh ngân sách hằng ngày và chu kỳ cho truy vấn đọc EVM. Kiểm tra các khối lịch sử cần thiết trước, rồi lập kế hoạch phân bố hằng ngày và thông lượng của tác vụ; phù hợp ngân sách credit không chứng minh phạm vi trạng thái.

Khi so sánh nhà cung cấp cho đọc lịch sử, trước hết xác nhận cả hai phục vụ được khối mục tiêu. So sánh phí vượt hạn mức cho yêu cầu full so sánh giá RU bổ sung với chi phí theo phương thức, tách hạn mức đi kèm khỏi mức sử dụng bổ sung và giải thích loại tính phí full với archive.

Với khối, giao dịch, chuyển tiền hoặc bộ dữ liệu khác trước đó đã lập chỉ mục, kiểm tra bộ dữ liệu Data API khai báo trong bảng và tham chiếu Data API. Bản ghi được lập chỉ mục không cung cấp thực thi hợp đồng lịch sử tùy ý hoặc hàm ý mọi chuỗi có số dư lịch sử.

Cập nhật lần cuối:

Trên trang này