Giới hạn tốc độ HyperEVM RPC và backfill log

Xử lý giới hạn tốc độ RPC và phản hồi 429 trên HyperEVM, truy vấn eth_getLogs có xác thực trong các khoảng có giới hạn, lưu cursor và khôi phục hoạt động bị thiếu.

Câu trả lời trực tiếp

RPC công khai HyperEVM chính thức mặc định cho phép 50 khối cho mỗi truy vấn eth_getLogs (nguồn: tài liệu JSON-RPC chính thức của Hyperliquid). Trên BlockVectra, các yêu cầu eth_getLogs đã xác thực bao phủ tối đa 1,000 khối cho mỗi truy vấn (hyperevm_mainnet.max_logs_block_range từ GET /v1/chains), bao gồm cả hai đầu mút. Khoảng rộng hơn trả về HTTP 200, mã JSON-RPC -32602 và logs_range_too_large, với retryable: false (xem danh mục lỗi); hãy chia nhỏ thành [from, min(from + max − 1, end)], lưu cursor của bạn, và tiến lên sau điểm cuối cộng một sau khi thành công để tiếp tục các lần chạy. Giới hạn tốc độ theo IP của RPC công khai chính thức và các giới hạn key của BlockVectra được mô tả riêng trong phần Giới hạn tốc độ RPC công khai chính thức và 429 cùng các thông số dịch vụ bên dưới.

Các nhiệm vụ hướng dẫn này giúp bạn hoàn thành

  • Thăm dò HyperEVM RPC bằng một lần đọc công khai sử dụng viem hoặc ethers trước khi chọn các phương thức có xác thực.
  • Backfill một cửa sổ log có giới hạn trong giới hạn eth_getLogs của HyperEVM, với quyết định thử lại dựa trên lỗi được trả về.
  • Đọc hoạt động địa chỉ thông qua các giao dịch và chuyển giao đã được lập chỉ mục với một key, kiểm tra siêu dữ liệu độ bao phủ và độ tươi mới trả về.

Nhiệm vụ ba bước: backfill một cửa sổ log HyperEVM có giới hạn

Đọc khối mới nhất không cần key, tạo một key, sau đó lấy event log cho một hợp đồng trong một cửa sổ khối hữu hạn.

Chọn hợp đồng và cửa sổ khối bạn cần. Nhiệm vụ này bao gồm cửa sổ có giới hạn đó; nó không cam kết toàn bộ lịch sử hợp đồng.

1. Đọc khối mới nhất không cần API key

curl -sS "https://api.blockvectra.com/v1/hyperevm_mainnet/public" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'

result của JSON-RPC là số hiệu khối mới nhất ở dạng thập lục phân. Đây là public.url của HyperEVM được công bố bởi GET /v1/chains. public.methods của endpoint công khai không bao gồm eth_getLogs; bước 3 yêu cầu một key.

2. Tạo API key

Tạo một key cho lần backfill này. Tạo một key và lưu chuỗi secret hiển thị trong hộp thoại để sử dụng với hyperevm_mainnet.

Đối với AI Agent sử dụng HTTP không có trình duyệt, hãy làm theo Hướng dẫn đăng ký theo chương trình. Truyền ref hợp lệ của URL hướng dẫn trong thân JSON của POST /auth/siwe/login thay vì docs-signup của ví dụ; bỏ qua nếu không có. Không yêu cầu người dùng dán key vào đoạn chat.

3. Backfill log bằng key của bạn

Starter template hoàn chỉnh: blockvectra/hyperevm-backfill

Lưu đoạn script sau thành hyperevm-task.ts. Script chạy với Node.js 24 trở lên, không cần cài thêm package nào. Đặt BLOCKVECTRA_API_KEY thành key đã lưu của bạn và LOG_ADDRESS thành địa chỉ hợp đồng phát log mà bạn muốn kiểm tra; giữ key trên máy chủ của bạn hoặc trong terminal cục bộ.

export BLOCKVECTRA_API_KEY='replace-with-your-key'
export LOG_ADDRESS='replace-with-contract-address'
node hyperevm-task.ts

Theo mặc định, script lấy max_logs_block_range khối gần đây nhất, hoặc ít hơn gần khối nguyên thủy (genesis). Nó đọc giới hạn đó từ /v1/chains khi chạy. Để chọn một cửa sổ hữu hạn khác, hãy đặt cả FROM_BLOCK và TO_BLOCK thành số hiệu khối dạng thập phân hoặc thập lục phân 0x trước khi chạy. Các cửa sổ lớn hơn được chia thành các phân đoạn liên tiếp, mỗi phân đoạn tối đa bằng giới hạn đã công bố.

const apiKey = process.env.BLOCKVECTRA_API_KEY;
const address = process.env.LOG_ADDRESS;
if (!apiKey || apiKey === "replace-with-your-key") throw new Error("Set BLOCKVECTRA_API_KEY");
if (!address || !/^0x[0-9a-f]{40}$/i.test(address)) throw new Error("Set LOG_ADDRESS to a contract address");

const fromBlock = process.env.FROM_BLOCK;
const toBlock = process.env.TO_BLOCK;
if ((fromBlock !== undefined || toBlock !== undefined) && (!fromBlock || !toBlock)) {
  throw new Error("Set both FROM_BLOCK and TO_BLOCK");
}

const chainsUrl = "https://api.blockvectra.com/v1/chains";
const rpcUrl = new URL("./hyperevm_mainnet", chainsUrl).href;
async function readJson(url: string) {
  const response = await fetch(url, { signal: AbortSignal.timeout(15_000) });
  if (!response.ok) throw new Error(`Metadata HTTP ${response.status}`);
  return response.json();
}
const catalog = await readJson(chainsUrl);
const chain = catalog.chains.find((item: { chain: string }) => item.chain === "hyperevm_mainnet");
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");
}
if (!chain.methods.allow.includes("eth_getLogs") || chain.methods.deny.includes("eth_getLogs")) {
  throw new Error("eth_getLogs is unavailable on this chain");
}
const maxRange = BigInt(chain.max_logs_block_range);
const plans = await readJson("https://console-api.blockvectra.com/v1/plans");
function weight(method: string): bigint {
  const row = plans.method_weights.find((item: { method: string }) => item.method === method);
  if (!row || !Number.isSafeInteger(row.cu_weight) || row.cu_weight <= 0) {
    throw new Error(`Missing or invalid CU weight for ${method}`);
  }
  return BigInt(row.cu_weight);
}
const headWeight = weight("eth_blockNumber");
const logsWeight = weight("eth_getLogs");

type RpcBody = {
  result?: unknown;
  error?: { code: number; data?: { retryable?: boolean } };
};
async function rpc(method: string, params: unknown[]): Promise<unknown> {
  for (let attempt = 0; attempt < 4; attempt++) {
    const response = await fetch(rpcUrl, {
      method: "POST",
      redirect: "error",
      headers: { "Content-Type": "application/json", "x-api-key": apiKey! },
      body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
      signal: AbortSignal.timeout(15_000),
    });
    const body = await response.json() as RpcBody;
    if (response.ok && !body.error && body.result !== undefined) return body.result;
    if (body.error?.data?.retryable !== true || attempt === 3) {
      throw new Error(`RPC failed: HTTP ${response.status}, code ${body.error?.code ?? "unknown"}`);
    }
    const retryAfter = response.headers.get("Retry-After");
    const delay = retryAfter === null ? 0 : /^\d+$/.test(retryAfter)
      ? Number(retryAfter) * 1000 : Date.parse(retryAfter) - Date.now();
    if (delay > 30_000) throw new Error("Retry-After exceeds 30 seconds; rerun later");
    const backoff = 1000 * 2 ** attempt + Math.random() * 250;
    await new Promise((resolve) => setTimeout(resolve, Math.max(backoff, Number.isFinite(delay) ? delay : 0)));
  }
  throw new Error("Retry limit reached");
}
function block(value: string): bigint {
  if (!/^(0x[0-9a-f]+|[0-9]+)$/i.test(value)) throw new Error("Invalid block number");
  return BigInt(value);
}
const head = await rpc("eth_blockNumber", []);
if (typeof head !== "string" || !/^0x[0-9a-f]+$/i.test(head)) throw new Error("Invalid chain head");
const latest = block(head);
const end = toBlock ? block(toBlock) : latest;
const start = fromBlock ? block(fromBlock)
  : end >= maxRange - 1n ? end - maxRange + 1n : 0n;
if (start > end || end > latest) throw new Error("Require 0 <= FROM_BLOCK <= TO_BLOCK <= latest");
const chunks = (end - start + maxRange) / maxRange;
console.error(`Window ${start}..${end}; chunks=${chunks}; estimated CU=${headWeight + chunks * logsWeight}`);

const hex = (value: bigint) => `0x${value.toString(16)}`;
for (let from = start; from <= end; from += maxRange) {
  const to = from + maxRange - 1n < end ? from + maxRange - 1n : end;
  const result = await rpc("eth_getLogs", [{ address, fromBlock: hex(from), toBlock: hex(to) }]);
  if (!Array.isArray(result)) throw new Error("Expected eth_getLogs result array");
  console.log(JSON.stringify({ fromBlock: hex(from), toBlock: hex(to), result }));
}

Các yêu cầu chạy tuần tự. Lỗi JSON-RPC chỉ được thử lại khi error.data.retryable là true, với tối đa bốn lần thử cho mỗi yêu cầu, backoff hàm mũ kèm jitter ngẫu nhiên, và hỗ trợ tiêu đề Retry-After tính theo giây hoặc ngày HTTP. Thời gian chờ dài hơn 30 giây sẽ dừng script để bạn có thể chạy lại sau. Lỗi mạng, quá thời gian chờ, phản hồi không đúng định dạng và các lỗi không thể thử lại sẽ dừng ngay lập tức; script thoát không thành công thay vì báo cáo một lần backfill hoàn tất.

Mỗi dòng output tiêu chuẩn chứa fromBlock, toBlock và mảng result của một phân đoạn. result: [] có nghĩa là không có log phù hợp trong phân đoạn đó. Đọc các trường này trong mỗi log:

TrườngÝ nghĩa
addressHợp đồng đã phát sự kiện.
blockNumber, blockHashKhối chứa log; số hiệu là dạng thập lục phân.
transactionHash, transactionIndex, logIndexVị trí giao dịch và log; các chỉ số ở dạng thập lục phân.
topics, dataCác đối số sự kiện đã được lập chỉ mục và các đối số không được lập chỉ mục đã mã hóa ABI; giải mã bằng ABI của hợp đồng.
removedLiệu log có bị xóa do tái tổ chức chuỗi (chain reorganization) hay không.

Khối mới nhất không phải là dấu mốc tính bất biến sau cùng (finality). Nếu bạn cần một cửa sổ lịch sử ổn định, hãy chọn TO_BLOCK đã được xác nhận của ứng dụng và xử lý các trường hợp tái tổ chức chuỗi.

Đối với một cửa sổ gồm B = TO_BLOCK − FROM_BLOCK + 1 khối và giới hạn công bố L, số lượng phân đoạn là N = ceil(B / L). Đọc method_weights[].cu_weight cho eth_getLogs và eth_blockNumber từ GET /v1/plans. Script in ra một ước tính tới standard error: N × weight(eth_getLogs) + weight(eth_blockNumber), bao gồm cả tra cứu head có key của nó. Điều này không bao gồm các lệnh gọi bổ sung và các lần thử lại bị tính phí; xem quy tắc tính phí để biết cách quyết toán. CU phụ thuộc vào các lệnh gọi, thay vì số lượng log được trả về.

Chuyển phát sự kiện: Sử dụng phương thức polling HTTP theo từng phân đoạn bên dưới, hoặc gửi các sự kiện của địa chỉ được theo dõi đến một đầu nhận HTTPS bằng webhook push. GET /v1/push/chains liệt kê các chuỗi được hỗ trợ và cài đặt số xác nhận; xác thực bằng x-api-key. Chữ ký webhook, loại bỏ trùng lặp và phát lại (replay) được đề cập trong hướng dẫn đó. Webhook push tách biệt với các đăng ký WebSocket (ws và subscriptions trong /v1/chains).

Kết nối với viem hoặc ethers

Tham số / EndpointGiá trị / MẫuXác thực
Chain ID (EIP-155)999—
JSON-RPC (key trên đường dẫn)POST https://api.blockvectra.com/v1/hyperevm_mainnet/{api_key}API key trong đường dẫn URL
JSON-RPC (key trong header)POST https://api.blockvectra.com/v1/hyperevm_mainnetHeader x-api-key: {api_key}
Gốc Data APIGET https://api.blockvectra.com/v1/data/hyperevm_mainnet/…Header x-api-key: {api_key}
Trạng thái công khaiGET https://api.blockvectra.com/v1/statusKhông xác thực (công khai)

Nhà phát triển và AI Agent có thể sử dụng cùng các cài đặt phía máy chủ. Sử dụng Node.js 24 trở lên, viem 2 hoặc ethers 6, và bắt đầu với các lệnh đọc công khai. Thiết lập BLOCKVECTRA_API_KEY một cách an toàn trong môi trường cho các phương thức có key. Giữ các key và URL RPC chứa key ngoài mã trình duyệt, log và hệ thống quản lý phiên bản (version control).

Lưu tệp này dưới dạng network.mjs. Nó đọc chain_id và chính sách phương thức từ GET /v1/chains. Đối với các lệnh đọc không cần key, hãy sử dụng public.url của danh mục và chỉ các phương thức được liệt kê trong public.methods; tính khả dụng của HTTP công khai không đồng nghĩa với quyền truy cập WebSocket.

const chainSlug = process.env.BLOCKVECTRA_CHAIN ?? 'hyperevm_mainnet';
const key = process.env.BLOCKVECTRA_API_KEY;
const catalogUrl = 'https://api.blockvectra.com/v1/chains';
const response = await fetch(catalogUrl, { signal: AbortSignal.timeout(15_000) });
if (!response.ok) throw new Error(`Chains HTTP ${response.status}`);
const catalog = await response.json();
export const chainInfo = catalog.chains.find(item => item.chain === chainSlug);
if (!chainInfo || !Number.isSafeInteger(chainInfo.chain_id) || chainInfo.chain_id <= 0) {
  throw new Error('Missing chain or chain_id');
}
export function allows(method) {
  const matches = pattern => pattern.endsWith('*')
    ? method.startsWith(pattern.slice(0, -1)) : pattern === method;
  if (!key) return (chainInfo.public?.methods ?? []).some(matches);
  return (chainInfo.methods?.allow ?? []).some(matches)
    && !(chainInfo.methods?.deny ?? []).some(matches);
}
if (!allows('eth_chainId')) throw new Error('eth_chainId is unavailable');
export const rpcUrl = key
  ? new URL(`./${chainSlug}/${encodeURIComponent(key)}`, catalogUrl).href
  : chainInfo.public?.url;
if (!rpcUrl) throw new Error('Public RPC is unavailable; set BLOCKVECTRA_API_KEY');

Lưu thành viem-client.mjs, cài đặt bằng npm install viem@2, sau đó chạy node viem-client.mjs.

import { createPublicClient, defineChain, http } from 'viem';
import { chainInfo, rpcUrl } from './network.mjs';

export const chain = defineChain({
  id: chainInfo.chain_id,
  name: chainInfo.name,
  nativeCurrency: { name: 'HYPE', symbol: 'HYPE', decimals: 18 },
  rpcUrls: { default: { http: [rpcUrl] } },
});
export const client = createPublicClient({ chain, transport: http(rpcUrl) });
if (await client.getChainId() !== chain.id) throw new Error('RPC chain ID mismatch');
console.error(await client.getBlockNumber());

Đối với ethers, lưu thành ethers-client.mjs, cài đặt bằng npm install ethers@6, sau đó chạy node ethers-client.mjs.

import { JsonRpcProvider } from 'ethers';
import { chainInfo, rpcUrl } from './network.mjs';

const provider = new JsonRpcProvider(rpcUrl, chainInfo.chain_id, { batchMaxCount: 1 });
const network = await provider.getNetwork();
if (network.chainId !== BigInt(chainInfo.chain_id)) throw new Error('RPC chain ID mismatch');
console.log(await provider.getBlockNumber());
provider.destroy();

Triển khai với Foundry hoặc Hardhat

Danh mục hyperevm_mainnet hiện tại có ws=false và không liệt kê eth_sendRawTransaction trong methods.allow. Sử dụng BlockVectra cho các lệnh đọc; việc triển khai yêu cầu một RPC hỗ trợ phát sóng giao dịch (broadcasting). Đặt DEPLOY_RPC_URL thành URL HTTP có xác thực của nhà cung cấp đó. Không mặc định rằng nó chia sẻ cùng các giới hạn về phương thức hoặc phạm vi log như BlockVectra. Kiểm tra Chain ID đã chọn trước khi ký.

: "${DEPLOY_RPC_URL:?Set a broadcasting RPC URL}"
export RPC_URL="$DEPLOY_RPC_URL"
export CHAIN_ID="$(node --input-type=module -e "import { chainInfo } from './network.mjs'; console.log(chainInfo.chain_id)")"

Tiếp tục với hướng dẫn triển khai Foundry hoặc Hardhat dùng chung. Nạp tiền cho người triển khai bằng HYPE trên EVM và xem lại các yêu cầu về khối kép bên dưới trước khi triển khai lớn.

HYPE, khối nhỏ và triển khai lớn

Hướng dẫn mạng chính thức của HyperEVM xác định HYPE là gas, với 18 chữ số thập phân (truy cập: 2026-10-07). Đảm bảo người triển khai nắm giữ HYPE trên HyperEVM; chỉ riêng số dư HyperCore không phải là số dư gas EVM. Làm theo hướng dẫn chuyển coin gốc được liên kết khi chuyển tiền.

Hướng dẫn cấu trúc khối kép mô tả các khối nhỏ nhanh và các khối lớn chậm hơn cho các giao dịch lớn hơn (truy cập: 2026-10-07). Hãy ước tính gas triển khai trước. Đối với các triển khai vượt quá ngân sách của khối nhỏ, người triển khai phải là người dùng HyperCore hiện tại và ký hành động Core {"type":"evmUserModify","usingBigBlocks":true}; chỉ đặt giới hạn gas giao dịch lớn hơn sẽ không tự chọn các khối lớn. Khôi phục lại usingBigBlocks=false sau đó để quay về các khối nhỏ.

Trên nhà cung cấp hỗ trợ chúng, sử dụng eth_usingBigBlocks để kiểm tra chế độ địa chỉ và eth_bigBlockGasPrice cho phí cơ bản của khối lớn. Tài liệu tham khảo JSON-RPC chính thức ghi lại các phương thức này (truy cập: 2026-10-07). Kiểm tra các phương thức của nhà cung cấp đã chọn; sử dụng /v1/chains đối với BlockVectra. Triển khai tối thiểu ở trên nhắm vào một hợp đồng nhỏ và không làm thay đổi chế độ tài khoản Core.

Dữ liệu HyperCore và HyperEVM

EVM RPC phục vụ các hợp đồng, biên lai và log. Dữ liệu giao dịch và các hành động của HyperCore sử dụng Core API. Các hợp đồng có thể đọc trạng thái Core thông qua các precompile và gửi các hành động thông qua CoreWriter; sử dụng hướng dẫn tương tác chính thức khi tích hợp các đường dẫn này (truy cập: 2026-10-07). Các log EVM không thay thế cho việc truy vấn sổ lệnh (order-book) hoặc vị thế của Core.

Các giao dịch hệ thống của HyperEVM (chẳng hạn như chuyển khoản từ HyperCore sang HyperEVM) không được đưa vào các phản hồi eth_getBlockByNumber tiêu chuẩn và được cung cấp riêng biệt bởi RPC chính thức qua eth_getSystemTxsByBlockNumber và eth_getSystemTxsByBlockHash (xem tài liệu JSON-RPC chính thức, truy cập: 2026-10-07). Dữ liệu khối, giao dịch và Data API của BlockVectra trên HyperEVM hiện không bao gồm các giao dịch hệ thống; hãy sử dụng trực tiếp hai phương thức RPC chính thức này khi cần dữ liệu giao dịch hệ thống.

Xử lý mã lỗi chính thức 10055

Hướng dẫn chính thức về HyperEVM xác định 10055 là lỗi ranh giới giữa Core/EVM, bao gồm các lỗi về nonce, không đủ số dư, trùng lặp hash và thay thế dưới giá (underpriced-replacement) (truy cập: 2026-10-07). Kiểm tra thông báo từ RPC phát sóng trước khi quyết định cách khôi phục:

  • Nonce: so sánh eth_getTransactionCount với các giao dịch đang chờ xử lý (pending) của bạn; tuần tự hóa việc gửi từ một người triển khai và đối chiếu nonce tiếp theo của nó.
  • Số dư: kiểm tra số dư HYPE trên EVM của người triển khai đối chiếu với giá trị cộng với chi phí gas.
  • Trùng lặp hash: tra cứu giao dịch và biên lai hiện có trước khi gửi một giao dịch khác.
  • Phí thay thế: xác minh nonce và phí hiện tại, sau đó sử dụng chính sách thay thế của bên phát sóng; việc lặp lại cùng các byte dữ liệu sẽ không làm tăng phí.

Chỉ riêng 10055 không thể biện minh cho việc thử lại một cách mù quáng. Đọc các lỗi và hướng dẫn khắc phục của chúng một cách riêng biệt trong tài liệu tham khảo lỗi của BlockVectra.

Giới hạn tốc độ RPC công khai chính thức và 429

Tài liệu giới hạn tốc độ chính thức của Hyperliquid quy định tối đa 100 yêu cầu EVM JSON-RPC mỗi phút cho mỗi IP đối với rpc.hyperliquid.xyz/evm. Tài liệu JSON-RPC của nó cũng giới hạn eth_getLogs ở mức 50 khối cho mỗi truy vấn và tối đa 4 topic. Truy cập: 2026-10-07.

Khi gặp HTTP 429, hãy tạm dừng các yêu cầu và ưu tiên tuân thủ Retry-After trước (tính theo giây hoặc ngày HTTP). Nếu không có, hãy sử dụng backoff hàm mũ kèm jitter và số lần thử lại có giới hạn, thử lại cùng phân đoạn chưa hoàn thành đó. Giảm số lượng yêu cầu đồng thời và tần suất polling, đồng thời chia nhỏ các truy vấn log thành các phân đoạn trong giới hạn của endpoint. Riêng việc phân đoạn không loại bỏ được giới hạn tốc độ; các client dùng chung IP cần điều phối tốc độ yêu cầu của họ.

Đối với endpoint có key của BlockVectra, hãy đọc max_logs_block_range, methods.allow, và methods.deny của hyperevm_mainnet từ GET /v1/chains thay vì áp dụng khoảng khối hoặc giới hạn số yêu cầu mỗi phút của RPC công khai chính thức. Tốc độ yêu cầu được tính riêng theo cu_per_sec, burst_cu của key và giới hạn lệnh gọi của gói miễn phí (xem phần tiếp theo). Khi gặp 429, hãy kiểm tra error.data.reason và retryable; request_exceeds_burst đòi hỏi các yêu cầu nhỏ hơn thay vì thử lại cùng yêu cầu cũ với backoff.

Thông số và quy tắc dịch vụ của BlockVectra

BlockVectra phục vụ mainnet HyperEVM thông qua các endpoint JSON-RPC và REST Data API:

  1. Thông số chuỗi và giới hạn log: Từ GET /v1/chains đối với hyperevm_mainnet:
    • Mã định danh chuỗi (Slug): hyperevm_mainnet, Chain ID 999.
    • max_logs_block_range: Được quản lý bởi trường max_logs_block_range từ GET /v1/chains. Một yêu cầu eth_getLogs đơn lẻ có thể kéo dài tối đa số lượng khối này (toBlock − fromBlock + 1). Vượt quá khoảng này trả về HTTP 200 kèm mã lỗi JSON-RPC -32602 (eth_getLogs block range too large: max <N> blocks), trường hợp này không bị tính phí.
    • state_window_blocks: Được quản lý bởi trường state_window_blocks từ GET /v1/chains. Các lệnh gọi đọc trạng thái (chẳng hạn như eth_call và eth_getBalance) phải tuân theo cửa sổ lưu giữ do trường này khai báo (khi là null, toàn bộ trạng thái được giữ lại mà không có giới hạn cửa sổ cuộn).
    • Chính sách phương thức: Được quản lý bởi methods.allow và methods.deny. Các phương thức EVM tiêu chuẩn (eth_blockNumber, eth_getLogs, eth_call, eth_getBalance, eth_getBlockByNumber, eth_getTransactionReceipt, v.v.) được cho phép; các phương thức bộ lọc và đăng ký (eth_subscribe, eth_unsubscribe, eth_newFilter, eth_newBlockFilter) bị từ chối, trả về -32601 (không tính phí).
  2. Giới hạn tốc độ gói miễn phí và nâng cấp: Từ GET /v1/plans:
    • free.max_calls_per_sec: tối đa 25 lệnh gọi mỗi giây, chia sẻ trên tất cả các key trong tài khoản, mọi chuỗi và Data API.
    • Giới hạn key mặc định: Mỗi API key có một bucket CU (nạp lại cu_per_sec, dung lượng burst_cu — mặc định là 400 CU/s và burst 1,600 CU). Các phương thức được đo lường theo trọng số Compute Unit (CU).
    • Nâng cấp giới hạn: Sau khi nạp tiền, giới hạn số lệnh gọi mỗi giây trên toàn tài khoản sẽ được gỡ bỏ; mỗi key vẫn phải tuân theo giới hạn tốc độ và burst của Compute Unit (CU). Để biết mức giá và đơn vị thanh toán hiện tại, hãy xem Trang bảng giá.

Backfill log lịch sử: phân đoạn eth_getLogs và logic thử lại

Khi truy vấn log lịch sử, các khoảng rộng phải được chia thành các phân đoạn liền kề bị giới hạn bởi max_logs_block_range của chuỗi mục tiêu. Chiến lược thử lại của client nên kiểm tra trường retryable bên trong các phản hồi lỗi.

Đánh giá trường retryable trong phản hồi lỗi

Trên BlockVectra, các đối tượng lỗi JSON-RPC bao gồm payload error.data chứa reason, docs_url, và retryable (boolean):

  • retryable: true: Các tình trạng tạm thời, bao gồm dịch vụ quá tải (overloaded), giới hạn lệnh gọi mỗi giây của gói miễn phí (free_plan_call_limit), đồng bộ hóa nút (node_syncing), hoặc thượng nguồn không khả dụng (upstream_unavailable). Client nên tôn trọng header Retry-After khi có mặt hoặc áp dụng backoff hàm mũ kèm jitter.
  • retryable: false: Các lỗi không mang tính tạm thời, chẳng hạn như khoảng khối vượt quá giới hạn (-32602 / logs_range_too_large), tham số không hợp lệ (invalid_params), thiếu API key (missing_api_key), hoặc yêu cầu vượt quá dung lượng burst (-32022 / request_exceeds_burst). Việc thử lại mà không điều chỉnh tham số sẽ không thành công.

Dưới đây là phản hồi được trả về khi bỏ qua API key:

{
  "jsonrpc": "2.0",
  "id": null,
  "error": {
    "code": -32024,
    "message": "missing API key: send it in the request path (/v1/{chain}/<api_key>) or in the x-api-key header",
    "data": {
      "reason": "missing_api_key",
      "docs_url": "https://docs.blockvectra.com/en/errors/#missing_api_key",
      "retryable": false
    }
  }
}

Sử dụng các endpoint Data API thay vì quét getLogs trên diện rộng

Khi một ứng dụng theo dõi lịch sử giao dịch hoặc các dịch chuyển token cho một địa chỉ cụ thể, việc quét qua eth_getLogs đòi hỏi phải đưa ra các truy vấn phân đoạn tuần tự bị giới hạn bởi max_logs_block_range và phân tích cú pháp các log sự kiện Transfer thô.

BlockVectra Data API cung cấp các endpoint REST được lập chỉ mục sẵn cho hyperevm_mainnet, hỗ trợ các cửa sổ lên tới 100,000 khối với phân trang dựa trên con trỏ (cursor-based pagination):

  1. Giao dịch của địa chỉ: GET /v1/data/hyperevm_mainnet/addresses/{address}/transactions
    • Tham số: from_block (bắt buộc), to_block (bắt buộc), direction (tùy chọn: from, to, any, mặc định any), clamp (chuỗi boolean tùy chọn, mặc định false; khi đặt thành true, các cửa sổ vượt quá 100,000 khối hoặc cao hơn as_of_block sẽ bị cắt ngắn thay vì trả về 409), limit (tùy chọn, tối đa 500), cursor (mã phân trang).
  2. Chuyển giao token của địa chỉ: GET /v1/data/hyperevm_mainnet/addresses/{address}/transfers
    • Tham số: standard (bắt buộc: erc20 hoặc erc721; erc1155 không thể truy vấn theo địa chỉ và trả về 422 no_coverage), token (bộ lọc hợp đồng token tùy chọn), from_block (bắt buộc), to_block (bắt buộc), direction (tùy chọn: in, out, any), clamp (tùy chọn), limit, cursor.

Cấu trúc phản hồi

Các phản hồi sử dụng khung phản hồi tiêu chuẩn:

  • data: Mảng các bản ghi. Giao dịch bao gồm hash, block_number, block_timestamp, from, to, value, tx_index, gas_limit, gas_used, và status. Chuyển giao bao gồm token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index, và log_index (amount cho ERC-20, token_id cho ERC-721).
  • next_cursor: Mã phân trang không trong suốt được trả về khi còn các bản ghi tiếp theo (không xuất hiện trên trang cuối cùng, không phải là null).
  • meta: Siêu dữ liệu chứa chain, chain_slug, chain_external_id, as_of_block, safe_block, finalized_block, coverage (full hoặc partial), và refreshed_at.

Ví dụ mã nguồn: Truy vấn Data API

export BLOCKVECTRA_API_KEY="rgw_your_api_key"

# 1. Query address transaction history (clamp=true prevents 409 errors)
curl -s "https://api.blockvectra.com/v1/data/hyperevm_mainnet/addresses/0x2222222222222222222222222222222222222222/transactions?from_block=0&to_block=50000&clamp=true" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

# 2. Query address ERC-20 token transfers
curl -s "https://api.blockvectra.com/v1/data/hyperevm_mainnet/addresses/0x2222222222222222222222222222222222222222/transfers?standard=erc20&from_block=0&to_block=50000&clamp=true" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

Theo dõi thời gian thực: polling các khối mới

Đối với giao thức truyền tải HTTP, hãy theo dõi các khối bằng cách polling và lấy event log theo các phân đoạn liên tiếp trong max_logs_block_range. Chỉ chọn WebSocket khi /v1/chains báo cáo ws=true và có mục subscriptions bắt buộc. Để chuyển phát đến một đầu nhận HTTPS, hãy sử dụng webhook push.

Để chạy thử hợp đồng Hello đã triển khai, hãy đặt LOG_ADDRESS thành địa chỉ của nó. Gửi ping() thông qua RPC phát sóng, sau đó backfill khối của biên lai bằng script backfill trên trang này. Tiếp tục từ phân đoạn hoàn thành gần nhất cho các sự kiện mới.

cast send "$CONTRACT_ADDRESS" "ping()" --rpc-url "$DEPLOY_RPC_URL" \
  --private-key "$DEPLOYER_PRIVATE_KEY"

Quy trình polling

  1. Phát các lệnh gọi nhẹ định kỳ tới eth_blockNumber để kiểm tra phần đầu chuỗi (head) mới nhất.
  2. So sánh số hiệu khối trả về với lastSeenBlock đã được xử lý trước đó.
  3. Nếu currentBlock > lastSeenBlock, hãy chia [lastSeenBlock + 1, currentBlock] thành các phân đoạn tối đa bằng max_logs_block_range. Chỉ lưu bền vững lastSeenBlock sau khi đã xử lý thành công từng phân đoạn; khi thất bại, hãy thử lại phân đoạn chưa hoàn thành. Loại bỏ trùng lặp theo (blockHash, transactionHash, logIndex) và phát lại phần gối đầu sau khi kết nối lại để đối chiếu tái tổ chức chuỗi (reorg).
  4. watchBlockNumber hoặc watchBlocks của viem triển khai nguyên bản cơ chế polling HTTP dưới giao thức HTTP, cho phép tùy chỉnh thông qua tham số pollingInterval (chẳng hạn như 1000 ms).

Polling event log theo từng phân đoạn có giới hạn

Lưu thành poll-logs.mjs bên cạnh network.mjs và viem-client.mjs. Đặt BLOCKVECTRA_API_KEY, LOG_ADDRESS và FROM_BLOCK, sau đó chạy node poll-logs.mjs. Ví dụ hữu hạn này lấy mẫu head 12 lần, cách nhau 5 giây, và truy vấn mỗi phạm vi mới theo các phân đoạn tuần tự. Một lỗi sẽ dừng script trước khi phân đoạn thất bại được nâng lên.

import { isAddress } from 'viem';
import { client } from './viem-client.mjs';
import { chainInfo, allows } from './network.mjs';

const address = process.env.LOG_ADDRESS;
const start = process.env.FROM_BLOCK;
if (!address || !isAddress(address)) throw new Error('Set LOG_ADDRESS');
if (!/^(0x[0-9a-f]+|[0-9]+)$/i.test(start ?? '')) throw new Error('Set FROM_BLOCK');
if (!allows('eth_getLogs')) throw new Error('eth_getLogs requires an available keyed endpoint');
if (!Number.isSafeInteger(chainInfo.max_logs_block_range) || chainInfo.max_logs_block_range <= 0) {
  throw new Error('Invalid max_logs_block_range');
}
const max = BigInt(chainInfo.max_logs_block_range);
let from = BigInt(start);
for (let poll = 0; poll < 12; poll++) {
  const head = await client.getBlockNumber();
  while (from <= head) {
    const to = from + max - 1n < head ? from + max - 1n : head;
    const logs = await client.getLogs({ address, fromBlock: from, toBlock: to });
    console.log(JSON.stringify({ from: from.toString(), to: to.toString(), logs },
      (_, value) => typeof value === 'bigint' ? value.toString() : value));
    from = to + 1n;
  }
  if (poll < 11) await new Promise(resolve => setTimeout(resolve, 5_000));
}

Mỗi output ghi lại một phân đoạn đã hoàn thành. Để tiếp tục, hãy đặt FROM_BLOCK thành to + 1 của nó; bên tiêu thụ bền vững phải lưu các sự kiện và con trỏ cùng nhau, loại bỏ trùng lặp và đối chiếu các đợt reorg như được mô tả ở trên. Đối với lỗi 429 hoặc các lỗi có thể thử lại khác, hãy áp dụng hướng dẫn backoff có giới hạn cho cùng phân đoạn chưa hoàn thành đó.

Các bước tiếp theo

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

Trên trang này