คิวรีสถานะ EVM ย้อนหลังภายในหน้าต่างที่รองรับ

แยกแยะหน้าต่างสถานะที่มีการยืนยันตัวตน, ประวัติแบบไม่ต้องใช้คีย์ และช่วงของ log เลือกบล็อกคงที่สำหรับ eth_call และวินิจฉัยข้อผิดพลาด state_window

eth_call ย้อนหลังขึ้นอยู่กับหน้าต่างสถานะของเชน ไม่ใช่ขีดจำกัดช่วงบล็อกของ eth_getLogs ตรวจสอบหน้าต่างสถานะ, โหมดการยืนยันตัวตนของ endpoint และบล็อกเป้าหมายก่อนอ่านค่าสัญญาย้อนหลัง

ขีดจำกัดย้อนหลัง 3 ประการที่แตกต่างกัน

ฟิลด์ใน GET /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 ช่วงที่ได้รับอนุญาตไม่ได้เป็นเครื่องยืนยันว่าสถานะสัญญาเก่าหรือ log จะพร้อมใช้งาน

ขีดจำกัดเหล่านี้มีหน่วยเป็นบล็อก ไม่ใช่จำนวนวัน หน้าต่างสถานะที่เป็นค่า null หรือไม่ได้ประกาศไว้ไม่ได้เป็นเครื่องยืนยันความครอบคลุมระดับ archive ความพร้อมใช้งานของเมธอดแบบไม่ต้องใช้คีย์จะแยกต่างหากจากความพร้อมใช้งานของเมธอดที่มีการยืนยันตัวตน: ช่วงของ log เพียงอย่างเดียวไม่ได้ทำให้สามารถใช้งาน eth_getLogs แบบสาธารณะได้

เปรียบเทียบหน้าต่างสถานะของแต่ละเชน

ตารางแสดงหน้าต่างสถานะที่เผยแพร่, ประวัติแบบไม่ต้องใช้คีย์, ช่วงของ log และชุดข้อมูล Data API ที่ประกาศไว้จากสแนปช็อตสาธารณะ สำหรับการส่งคำขอในขณะนี้ โปรดอ่าน GET /v1/chains และ GET /v1/status ใหม่อีกครั้ง

นักพัฒนาและ AI Agent ควรตรวจสอบช่วงสถานะและช่วงการสืบค้น log แยกกัน ค่าช่วงสถานะที่เป็น null ไม่ได้หมายความว่าครอบคลุมข้อมูลประวัติทั้งหมด (archive) ประวัติสาธารณะจะใช้ได้เฉพาะกับเมธอดสาธารณะที่ประกาศไว้เท่านั้น

เชนSlug ของเชนช่วงสถานะที่ต้องยืนยันตัวตน: state_window_blocks (บล็อก)ประวัติแบบไม่ต้องใช้คีย์: public.history_blocks (บล็อก)ช่วงการสืบค้น log ที่ต้องยืนยันตัวตน: max_logs_block_range (บล็อก)ชุดข้อมูล Data API ที่ประกาศไว้
Arbitrum Onearb_mainnet5,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_sepoliaไม่ได้ประกาศ1,0001,000blocks, transactions, address_transactions, transfers, token_metadata, freshness
HyperEVMhyperevm_mainnetไม่ได้ประกาศ1,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 ไม่พร้อมใช้งาน

GET /v1/chains · เวลาที่สุ่มเก็บข้อมูล (UTC):

GET /v1/status · เวลาที่สุ่มเก็บข้อมูล (UTC):

เลือกแท็กบล็อก

ใช้ latest สำหรับค่าปัจจุบัน สำหรับการเปรียบเทียบย้อนหลัง ให้อ่าน eth_blockNumber หนึ่งครั้งและแปลงหมายเลขบล็อกที่เลือกเป็นจำนวนเลขฐานสิบหก เช่น 0x18efa2f กำหนดตัวเลขนั้นให้คงที่สำหรับทุกการเรียกในการเปรียบเทียบ; การเรียก latest ซ้ำๆ อาจอ้างอิงถึงบล็อกที่แตกต่างกัน

สำหรับการอ่านสถานะ แท็ก earliest, safe และ finalized จะส่งกลับ -32011 ภายใต้นโยบายหน้าต่างสถานะ ให้เลือกหมายเลขบล็อกที่ชัดเจนภายในหน้าต่างที่ประกาศไว้แทน การใช้รูปแบบแฮชของบล็อกไม่ใช่วิธีการรับประวัติเพิ่มเติม: การอ่านสถานะแบบไม่ต้องใช้คีย์จะปฏิเสธรูปแบบนี้ และคำขอที่มีการยืนยันตัวตนยังคงขึ้นอยู่กับสถานะที่พร้อมใช้งาน

หมายเลขบล็อกอาจอ้างอิงถึงบล็อกที่แตกต่างกันได้หากเกิดการจัดระเบียบสายโซ่ใหม่ (reorganization) บันทึกแฮชของบล็อกด้วย eth_getBlockByNumber หากคุณต้องการระบุบล็อกของผลลัพธ์ หมายเลขที่อยู่ภายในหน้าต่างยังต้องการเชนที่ซิงค์สมบูรณ์และสัญญาที่มีอยู่จริง ณ ความสูงของบล็อกนั้นด้วย

การอ่านสัญญาด้วยบล็อกคงที่

บน Ethereum นั้น WETH ที่ 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 เปิดเผยฟังก์ชัน decimals() ด้วย selector 0x313ce567 การเรียกแบบไม่ต้องใช้คีย์ที่สุ่มเก็บตัวอย่างเมื่อ 2026-10-08 (UTC) ที่บล็อก 0x18efa2f ส่งคืน HTTP 200 พร้อมผลลัพธ์นี้:

คำขอไปยัง public.url ของเชน:

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

การตอบกลับ:

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

จำนวนเต็มที่เข้ารหัส ABI คือ 18 ผลลัพธ์นี้คือค่า decimals ไม่ใช่ยอดคงเหลือ และไม่ได้เป็นเครื่องยืนยันความพร้อมใช้งานที่ระดับความสูงบล็อกอื่นๆ บล็อกคงที่นั้นจะหลุดออกจากหน้าต่างที่จำกัดขอบเขตตามกาลเวลา; โปรดใช้บล็อกล่าสุดเมื่อรันตัวอย่างต่อไปนี้ในภายหลัง

บันทึกตัวอย่างนี้เป็น historical-state.mjs และรันด้วยคำสั่ง node historical-state.mjs โดยใช้ Node.js 24 หรือใหม่กว่า พร้อมทั้งตั้งค่าตัวแปรสภาพแวดล้อม BLOCKVECTRA_API_KEY สคริปต์นี้จะใช้ endpoint ที่มีการยืนยันตัวตน, ใช้สัญญาและ calldata เดียวกัน และเปรียบเทียบ latest, บล็อกล่าสุดคงที่หนึ่งบล็อก และบล็อกที่อยู่นอกหน้าต่างที่มีการยืนยันตัวตนที่เผยแพร่ไว้ แต่ละผลลัพธ์จะรวมสถานะ HTTP จริงและเนื้อหา JSON-RPC; การตอบกลับ HTTP 200 ยังคงสามารถมีข้อผิดพลาดอยู่ภายในได้ สคริปต์จะหยุดทำงานเมื่อได้รับการตอบกลับที่ไม่คาดคิดแทนที่จะถือว่าเป็นการอ่านที่สำเร็จ

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 ในการอ่านแบบไม่ต้องใช้คีย์ ให้นำ URL โดยตรงมาจาก public.url, ละเว้นคีย์ และเลือกบล็อกที่อยู่ภายใน public.history_blocks เช่นเดียวกับหน้าต่างสถานะ การเปลี่ยนโหมดการยืนยันตัวตนอาจเปลี่ยนประวัติที่ได้รับอนุญาตได้ แม้จะเป็นสัญญาและ calldata เดียวกันก็ตาม

วินิจฉัยข้อผิดพลาดอยู่นอกหน้าต่าง

บน endpoint แบบไม่ต้องใช้คีย์เดียวกัน การเรียกที่สุ่มเก็บตัวอย่างเมื่อ 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 นี่คือความล้มเหลวของประวัติสาธารณะ; โดย endpoint ที่มีการยืนยันตัวตนจะมีหน้าต่างสถานะของตนเอง

ใช้ฟิลด์เหล่านี้จาก รายการข้อผิดพลาด state_window เพื่อระบุความล้มเหลวแทนที่จะพึ่งพาตัวเลขหน้าต่างที่เฉพาะเจาะจงในข้อความ:

ฟิลด์ค่าหรือความหมายตามเอกสาร
สถานะ HTTP200; ตรวจสอบ JSON-RPC error แม้ว่า HTTP จะสำเร็จก็ตาม
error.code-32011
error.messageข้อผิดพลาดหน้าต่างสถานะที่มีการยืนยันตัวตนจะอธิบายจำนวนบล็อกล่าสุดที่รองรับ; ข้อผิดพลาดประวัติสาธารณะอาจใช้ข้อความที่แตกต่างออกไป
error.data.reasonstate_window
error.data.docs_urlลิงก์ไปยังคำอธิบาย state_window ในสารบบข้อผิดพลาด
error.data.retryablefalse: การส่งคำขอเดิมอีกครั้งในภายหลังจะไม่กู้คืนสถานะที่เก่ากว่าได้

เลือกหมายเลขบล็อกที่ใหม่กว่าหรือใช้ latest หากงานต้องการค่าปัจจุบัน การลดช่วงของ eth_getLogs จะไม่ช่วยกู้คืนสถานะ eth_call ย้อนหลังได้ เหตุผลอื่นๆ ของ -32011 มีการดำเนินการที่แตกต่างกัน: range_not_indexed จำเป็นต้องใช้ช่วงที่ครอบคลุม; history_not_ready อนุญาตให้ลองใหม่ได้หลังจากการทำดัชนีตามทัน ตรวจสอบ error.data.reason ไม่ใช่ดูเพียงรหัสตัวเลขเท่านั้น

สถานะเบื้องหลังอาจไม่พร้อมใช้งานด้วยรหัส -32000 หรือประวัติบล็อกถูกตัดทอน (pruned) ด้วยรหัส 4444; โปรดดู สารบบข้อผิดพลาด อย่าลองส่งคำขอสำหรับบล็อกเก่าซ้ำโดยไม่มีการเปลี่ยนแปลง หรือสันนิษฐานว่าหน้าต่างที่ประกาศไว้กว้างกว่าจะรับประกันทุกการตอบกลับ

เลือกคิวรีถัดไป

สำหรับรายการตรวจสอบภาระงานและการทดสอบด้วยตนเองฉบับสมบูรณ์ โปรดเริ่มต้นที่ วิธีเลือกผู้ให้บริการ RPC

เมื่อเลือกผู้ให้บริการสำหรับการอ่านสัญญาซ้ำๆ โปรด เปรียบเทียบงบประมาณรายวันและตามรอบบิลสำหรับการอ่าน EVM ตรวจสอบบล็อกย้อนหลังที่ต้องการก่อน จากนั้นจึงวางแผนการกระจายตัวของทราฟฟิกรายวันและทรูพุตของงาน; การที่พอดีกับงบประมาณเครดิตไม่ได้เป็นเครื่องยืนยันความครอบคลุมของสถานะ

เมื่อเปรียบเทียบผู้ให้บริการสำหรับการอ่านข้อมูลย้อนหลัง อันดับแรกให้ยืนยันว่าทั้งสองรายสามารถให้บริการบล็อกเป้าหมายได้ การ เปรียบเทียบค่าบริการส่วนเกินของคำขอแบบ full จะเปรียบเทียบราคา RU ส่วนเกินกับต้นทุนตามเมธอด, แยกโควตาที่รวมมาให้ออกจากปริมาณการใช้งานส่วนเกิน ตลอดจนอธิบายระดับการเรียกเก็บเงินแบบ full เทียบกับ archive

สำหรับบล็อก, ธุรกรรม, การโอน หรือชุดข้อมูลอื่นๆ ที่ทำดัชนีก่อนหน้านี้ โปรดตรวจสอบชุดข้อมูล Data API ที่ประกาศในตารางและ ข้อมูลอ้างอิง Data API บันทึกข้อมูลที่ทำดัชนีไม่ได้ให้บริการการประมวลผลสัญญาย้อนหลังตามใจชอบ หรือหมายความว่าทุกเชนจะมียอดคงเหลือย้อนหลัง

อัปเดตล่าสุด:

ในหน้านี้