Batas laju RPC HyperEVM dan backfill log

Tangani batas laju RPC HyperEVM dan respons 429, kueri eth_getLogs terautentikasi dalam rentang terbatas, simpan kursor, dan pulihkan aktivitas yang terlewat.

Jawaban langsung

RPC publik resmi default HyperEVM mengizinkan 50 blok per kueri eth_getLogs (sumber: dokumentasi JSON-RPC resmi Hyperliquid). Di BlockVectra, permintaan eth_getLogs terautentikasi mencakup hingga 1,000 blok per kueri (hyperevm_mainnet.max_logs_block_range dari GET /v1/chains), termasuk kedua titik akhir. Rentang yang lebih lebar mengembalikan HTTP 200, JSON-RPC -32602 dan logs_range_too_large, dengan retryable: false (lihat katalog error); bagi menjadi [from, min(from + max − 1, end)], simpan kursor Anda, dan lanjutkan ke akhir ditambah satu setelah berhasil untuk melanjutkan proses. Batas laju per IP RPC publik resmi dan batas kunci BlockVectra dijelaskan secara terpisah dalam Batas laju RPC publik resmi dan 429 serta parameter layanan di bawah ini.

Tugas yang dibantu panduan ini

  • Uji RPC HyperEVM dengan pembacaan publik menggunakan viem atau ethers sebelum memilih metode terautentikasi.
  • Lakukan backfill jendela log terbatas dalam batas eth_getLogs HyperEVM, dengan keputusan percobaan ulang berdasarkan error yang dikembalikan.
  • Baca aktivitas alamat melalui transaksi dan transfer terindeks dengan kunci, memeriksa cakupan dan metadata keterbaruan yang dikembalikan.

Tugas tiga langkah: backfill jendela log HyperEVM terbatas

Baca blok terbaru tanpa kunci, buat kunci, lalu ambil log peristiwa untuk suatu kontrak dalam jendela blok terbatas.

Pilih kontrak dan jendela blok yang Anda butuhkan. Tugas ini mencakup jendela terbatas tersebut; tugas ini tidak menjamin riwayat kontrak yang lengkap.

1. Baca blok terbaru tanpa 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 JSON-RPC adalah nomor blok terbaru dalam heksadesimal. Ini adalah public.url HyperEVM yang dipublikasikan oleh GET /v1/chains. public.methods endpoint publik tidak mencakup eth_getLogs; langkah 3 memerlukan kunci.

2. Buat API key

Buat kunci untuk backfill ini. Buat kunci dan simpan rahasia yang ditampilkan di dialog untuk digunakan dengan hyperevm_mainnet.

Untuk Agen AI yang menggunakan HTTP tanpa browser, ikuti Panduan pendaftaran terprogram. Teruskan ref valid URL panduan dalam isi JSON POST /auth/siwe/login alih-alih docs-signup dari contoh; hilangkan jika tidak tersedia. Jangan meminta pengguna menempelkan kunci ke dalam obrolan.

3. Lakukan backfill log dengan API key Anda

Templat awal lengkap: blockvectra/hyperevm-backfill

Simpan skrip berikut sebagai hyperevm-task.ts. Skrip ini berjalan dengan Node.js 24 atau lebih baru, tanpa paket tambahan. Tetapkan BLOCKVECTRA_API_KEY ke kunci yang Anda simpan dan LOG_ADDRESS ke alamat kontrak pemancar yang ingin Anda periksa; simpan kunci di server Anda atau di terminal lokal.

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

Secara default, skrip mengambil blok max_logs_block_range terbaru, atau lebih sedikit di dekat genesis. Skrip membaca batas tersebut dari /v1/chains pada waktu proses. Untuk memilih jendela terbatas lainnya, tetapkan FROM_BLOCK dan TO_BLOCK ke nomor blok desimal atau heksadesimal 0x sebelum menjalankan. Jendela yang lebih besar dibagi menjadi chunk berurutan, masing-masing paling banyak batas yang dipublikasikan.

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

Permintaan berjalan secara berurutan. Error JSON-RPC dicoba kembali hanya jika error.data.retryable bernilai true, dengan paling banyak empat percobaan per permintaan, backoff eksponensial dan jitter, serta dukungan untuk detik Retry-After atau tanggal HTTP. Menunggu lebih dari 30 detik akan menghentikan skrip sehingga Anda dapat menjalankannya kembali nanti. Kegagalan jaringan, batas waktu habis (timeout), respons tidak valid, dan error yang tidak dapat dicoba ulang langsung berhenti; skrip keluar dengan status tidak berhasil daripada melaporkan backfill yang lengkap.

Setiap baris output standar berisi fromBlock, toBlock, dan array result untuk satu chunk. result: [] berarti tidak ada log yang cocok dalam chunk tersebut. Baca bidang-bidang berikut di setiap log:

BidangArti
addressKontrak yang memancarkan peristiwa.
blockNumber, blockHashBlok yang berisi log; nomornya adalah heksadesimal.
transactionHash, transactionIndex, logIndexPosisi transaksi dan log; indeks adalah heksadesimal.
topics, dataArgumen peristiwa terindeks dan argumen non-terindeks yang dikodekan dengan ABI; dekode dengan ABI kontrak.
removedApakah log dihapus oleh reorganisasi rantai (reorg).

Blok terbaru bukanlah penanda finalitas. Jika Anda memerlukan jendela historis yang stabil, pilih TO_BLOCK terkonfirmasi untuk aplikasi Anda dan tangani reorganisasi rantai.

Untuk jendela B = TO_BLOCK − FROM_BLOCK + 1 blok dan batas yang dipublikasikan L, jumlah chunk adalah N = ceil(B / L). Baca method_weights[].cu_weight untuk eth_getLogs dan eth_blockNumber dari GET /v1/plans. Skrip mencetak perkiraan ke standard error: N × weight(eth_getLogs) + weight(eth_blockNumber), termasuk pencarian head dengan kunci. Ini tidak termasuk panggilan ekstra dan percobaan ulang yang dapat ditagih; lihat aturan penagihan untuk penyelesaian. CU bergantung pada panggilan, bukan jumlah log yang dikembalikan.

Pengiriman peristiwa: Gunakan polling HTTP terbagi di bawah ini, atau kirim peristiwa alamat yang dipantau ke penerima HTTPS dengan webhook push. GET /v1/push/chains mencantumkan rantai yang didukung dan pengaturan konfirmasi; autentikasi dengan x-api-key. Tanda tangan webhook, deduplikasi, dan pemutaran ulang (replay) dibahas dalam panduan tersebut. Webhook push terpisah dari langganan WebSocket (ws dan subscriptions di /v1/chains).

Hubungkan dengan viem atau ethers

Parameter / EndpointNilai / TemplatAutentikasi
Chain ID (EIP-155)999—
JSON-RPC (kunci di path)POST https://api.blockvectra.com/v1/hyperevm_mainnet/{api_key}API key di path URL
JSON-RPC (kunci di header)POST https://api.blockvectra.com/v1/hyperevm_mainnetHeader x-api-key: {api_key}
Basis Data APIGET https://api.blockvectra.com/v1/data/hyperevm_mainnet/…Header x-api-key: {api_key}
Status publikGET https://api.blockvectra.com/v1/statusTanpa autentikasi (publik)

Pengembang dan Agen AI dapat menggunakan pengaturan sisi server yang sama. Gunakan Node.js 24 atau lebih baru, viem 2 atau ethers 6, dan mulailah dengan pembacaan publik. Tetapkan BLOCKVECTRA_API_KEY secara aman di lingkungan untuk metode yang menggunakan kunci. Jauhkan kunci dan URL RPC yang berisi kunci dari kode browser, log, dan kontrol versi.

Simpan ini sebagai network.mjs. Skrip ini membaca chain_id dan kebijakan metode dari GET /v1/chains. Untuk pembacaan tanpa kunci, gunakan public.url dari katalog dan hanya metode yang tercantum dalam public.methods; ketersediaan HTTP publik tidak menyiratkan akses 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');

Simpan sebagai viem-client.mjs, instal dengan npm install viem@2, lalu jalankan 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());

Untuk ethers, simpan sebagai ethers-client.mjs, instal dengan npm install ethers@6, lalu jalankan 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();

Deploy dengan Foundry atau Hardhat

Katalog hyperevm_mainnet saat ini memiliki ws=false dan tidak mencantumkan eth_sendRawTransaction dalam methods.allow. Gunakan BlockVectra untuk pembacaan; deployment memerlukan RPC yang mendukung penyiaran (broadcasting). Tetapkan DEPLOY_RPC_URL ke URL HTTP terautentikasi penyedia tersebut. Jangan berasumsi bahwa penyedia tersebut memiliki batas metode atau batas rentang log yang sama dengan BlockVectra. Periksa Chain ID yang dipilih sebelum menandatangani.

: "${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)")"

Lanjutkan dengan tutorial deployment Foundry atau Hardhat bersama. Danai deployer dengan EVM HYPE dan tinjau persyaratan blok ganda (dual-block) di bawah ini sebelum deployment besar.

HYPE, blok kecil, dan deployment besar

Panduan jaringan resmi HyperEVM mengidentifikasi HYPE sebagai gas, dengan 18 desimal (diakses: 2026-10-07). Pastikan deployer memiliki HYPE di HyperEVM; saldo HyperCore saja bukan saldo gas EVM. Ikuti instruksi transfer native yang ditautkan saat memindahkan dana.

Panduan blok ganda menjelaskan blok kecil yang cepat dan blok besar yang lebih lambat untuk transaksi yang lebih besar (diakses: 2026-10-07). Perkirakan gas deployment terlebih dahulu. Untuk deployment yang melebihi anggaran blok kecil, deployer harus menjadi pengguna HyperCore yang sudah ada dan menandatangani tindakan Core {"type":"evmUserModify","usingBigBlocks":true}; menetapkan batas gas transaksi yang lebih besar saja tidak memilih blok besar. Kembalikan usingBigBlocks=false setelahnya untuk kembali ke blok kecil.

Pada penyedia yang mendukungnya, gunakan eth_usingBigBlocks untuk memeriksa mode alamat dan eth_bigBlockGasPrice untuk biaya dasar blok besar. Referensi JSON-RPC resmi mendokumentasikan metode-metode ini (diakses: 2026-10-07). Periksa metode penyedia yang dipilih; gunakan /v1/chains untuk BlockVectra. Deployment minimal di atas menargetkan kontrak kecil dan tidak mengubah mode akun Core.

Data HyperCore dan HyperEVM

RPC EVM melayani kontrak, resi (receipt), dan log. Data perdagangan dan tindakan HyperCore menggunakan Core API. Kontrak dapat membaca status Core melalui precompile dan mengirim tindakan melalui CoreWriter; gunakan panduan interaksi resmi saat mengintegrasikan jalur ini (diakses: 2026-10-07). Log EVM tidak menggantikan kueri buku pesanan (order book) atau posisi Core.

Transaksi sistem HyperEVM (seperti transfer HyperCore ke HyperEVM) tidak disertakan dalam respons standar eth_getBlockByNumber dan disediakan secara terpisah oleh RPC resmi melalui eth_getSystemTxsByBlockNumber dan eth_getSystemTxsByBlockHash (lihat dokumentasi JSON-RPC resmi, diakses: 2026-10-07). Data blok, transaksi, dan Data API HyperEVM BlockVectra saat ini tidak menyertakan transaksi sistem; gunakan kedua metode RPC resmi ini secara langsung jika memerlukan data transaksi sistem.

Menangani error resmi 10055

Panduan resmi HyperEVM mendefinisikan 10055 sebagai error batas Core/EVM, termasuk kegagalan nonce, dana tidak mencukupi, hash duplikat, dan penggantian dengan harga terlalu rendah (underpriced replacement) (diakses: 2026-10-07). Periksa pesan dari RPC penyiaran sebelum memutuskan cara pemulihan:

  • Nonce: bandingkan eth_getTransactionCount dengan transaksi tertunda Anda; buat serialisasi pengiriman dari satu deployer dan rekonsiliasi nonce berikutnya.
  • Dana: periksa saldo EVM HYPE deployer terhadap nilai ditambah biaya gas.
  • Hash duplikat: cari transaksi dan resi yang ada sebelum mengirimkan transaksi lain.
  • Biaya penggantian: verifikasi nonce dan biaya yang ada, lalu gunakan kebijakan penggantian penyiar; mengulang byte yang sama tidak menaikkan biaya.

10055 saja tidak membenarkan percobaan ulang tanpa pertimbangan. Baca error dan panduan pemulihannya secara terpisah dalam referensi error BlockVectra.

Batas laju RPC publik resmi dan 429

Dokumentasi batas laju resmi Hyperliquid menetapkan paling banyak 100 permintaan EVM JSON-RPC per menit per IP untuk rpc.hyperliquid.xyz/evm. Dokumentasi JSON-RPC-nya juga membatasi eth_getLogs hingga 50 blok per kueri dan hingga 4 topik. Diakses: 2026-10-07.

Pada HTTP 429, jeda permintaan dan hormati Retry-After terlebih dahulu (detik atau tanggal HTTP). Jika tidak ada, gunakan backoff eksponensial dengan jitter dan jumlah percobaan ulang terbatas, mencoba kembali chunk yang belum selesai yang sama. Kurangi konkurensi dan frekuensi polling, dan bagi kueri log menjadi chunk dalam batas endpoint. Pembagian chunk saja tidak menghilangkan batas laju; klien yang berbagi IP perlu mengoordinasikan laju permintaan mereka.

Untuk endpoint dengan kunci BlockVectra, baca max_logs_block_range, methods.allow, dan methods.deny untuk hyperevm_mainnet dari GET /v1/chains alih-alih menerapkan rentang blok atau batas permintaan per menit RPC publik resmi. Laju permintaan secara terpisah tunduk pada cu_per_sec, burst_cu kunci, dan batas panggilan paket gratis (lihat bagian berikutnya). Pada 429, periksa error.data.reason dan retryable; request_exceeds_burst memerlukan permintaan yang lebih kecil daripada percobaan ulang tanpa perubahan dengan backoff.

Parameter dan aturan layanan BlockVectra

BlockVectra melayani mainnet HyperEVM melalui endpoint JSON-RPC dan REST Data API:

  1. Parameter rantai dan batas log: Dari GET /v1/chains untuk hyperevm_mainnet:
    • Pengidentifikasi rantai (Slug): hyperevm_mainnet, Chain ID 999.
    • max_logs_block_range: Diatur oleh bidang max_logs_block_range dari GET /v1/chains. Satu permintaan eth_getLogs dapat mencakup paling banyak jumlah blok ini (toBlock − fromBlock + 1). Melebihi rentang ini mengembalikan HTTP 200 dengan kode error JSON-RPC -32602 (eth_getLogs block range too large: max <N> blocks), yang tidak ditagih.
    • state_window_blocks: Diatur oleh bidang state_window_blocks dari GET /v1/chains. Panggilan pembacaan status (seperti eth_call dan eth_getBalance) tunduk pada jendela retensi yang dinyatakan oleh bidang ini (jika null, status penuh dipertahankan tanpa batas jendela bergulir).
    • Kebijakan metode: Diatur oleh methods.allow dan methods.deny. Metode standar EVM (eth_blockNumber, eth_getLogs, eth_call, eth_getBalance, eth_getBlockByNumber, eth_getTransactionReceipt, dll.) diizinkan; metode filter dan langganan (eth_subscribe, eth_unsubscribe, eth_newFilter, eth_newBlockFilter) ditolak, mengembalikan -32601 (tidak ditagih).
  2. Batas laju tingkat gratis dan peningkatan: Dari GET /v1/plans:
    • free.max_calls_per_sec: hingga 25 panggilan per detik, dibagi di antara semua kunci di akun, semua rantai, dan Data API.
    • Batas kunci default: Setiap API key memiliki bucket CU (pengisian ulang cu_per_sec, kapasitas burst_cu — default adalah 400 CU/dtk dan burst 1,600 CU). Metode diukur berdasarkan bobot Compute Unit (CU).
    • Meningkatkan batas: Setelah melakukan top up, batas panggilan per detik di seluruh akun dihapus; setiap kunci tetap tunduk pada batas laju Compute Unit (CU) dan burst. Untuk tarif dan unit penagihan saat ini, lihat halaman Harga.

Melakukan backfill log historis: eth_getLogs terbagi dan logika percobaan ulang

Saat mengueri log historis, interval lebar harus dibagi menjadi chunk berurutan yang dibatasi oleh max_logs_block_range rantai target. Strategi percobaan ulang klien harus memeriksa bidang retryable di dalam respons error.

Mengevaluasi retryable dalam respons error

Di BlockVectra, objek error JSON-RPC menyertakan payload error.data yang berisi reason, docs_url, dan retryable (boolean):

  • retryable: true: Kondisi sementara, termasuk kelebihan beban layanan (overloaded), batas panggilan per detik paket gratis (free_plan_call_limit), sinkronisasi node (node_syncing), atau upstream tidak tersedia (upstream_unavailable). Klien harus menghormati header Retry-After jika ada atau menerapkan backoff eksponensial dengan jitter.
  • retryable: false: Error non-sementara, seperti rentang blok melebihi batas (-32602 / logs_range_too_large), parameter tidak valid (invalid_params), API key tidak ada (missing_api_key), atau permintaan melebihi kapasitas burst (-32022 / request_exceeds_burst). Mencoba lagi tanpa menyesuaikan parameter tidak akan berhasil.

Berikut adalah respons yang dikembalikan saat API key dihilangkan:

{
  "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
    }
  }
}

Menggunakan endpoint Data API alih-alih pemindaian getLogs yang ekstensif

Ketika sebuah aplikasi melacak riwayat transaksi atau perpindahan token untuk alamat tertentu, memindai melalui eth_getLogs memerlukan pengiriman kueri terbagi berurutan yang dibatasi oleh max_logs_block_range dan mem-parsing log peristiwa Transfer mentah.

Data API BlockVectra menyediakan endpoint REST yang telah diindeks sebelumnya untuk hyperevm_mainnet, mendukung jendela hingga 100.000 blok dengan paginasi berbasis kursor:

  1. Transaksi alamat: GET /v1/data/hyperevm_mainnet/addresses/{address}/transactions
    • Parameter: from_block (wajib), to_block (wajib), direction (opsional: from, to, any, default any), clamp (string boolean opsional, default false; saat disetel ke true, jendela yang melebihi 100.000 blok atau lebih tinggi dari as_of_block dipotong alih-alih mengembalikan 409), limit (opsional, maks 500), cursor (token paginasi).
  2. Transfer token alamat: GET /v1/data/hyperevm_mainnet/addresses/{address}/transfers
    • Parameter: standard (wajib: erc20 atau erc721; erc1155 tidak dapat dikueri berdasarkan alamat dan mengembalikan 422 no_coverage), token (filter kontrak token opsional), from_block (wajib), to_block (wajib), direction (opsional: in, out, any), clamp (opsional), limit, cursor.

Struktur respons

Respons menggunakan struktur respons standar:

  • data: Array rekaman. Transaksi mencakup hash, block_number, block_timestamp, from, to, value, tx_index, gas_limit, gas_used, dan status. Transfer mencakup token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index, dan log_index (amount untuk ERC-20, token_id untuk ERC-721).
  • next_cursor: Token paginasi buram (opaque) yang dikembalikan jika rekaman berikutnya ada (tidak ada pada halaman terakhir, bukan null).
  • meta: Metadata yang berisi chain, chain_slug, chain_external_id, as_of_block, safe_block, finalized_block, coverage (full atau partial), dan refreshed_at.

Contoh kode: kueri Data API

export BLOCKVECTRA_API_KEY="rgw_your_api_key"

# 1. Kueri riwayat transaksi alamat (clamp=true mencegah error 409)
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. Kueri transfer token ERC-20 alamat
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"

Pelacakan real-time: polling blok baru

Untuk transportasi HTTP, lacak blok dengan polling dan ambil log peristiwa dalam chunk berurutan dalam batas max_logs_block_range. Pilih WebSocket hanya jika /v1/chains melaporkan ws=true dan entri subscriptions yang diperlukan. Untuk pengiriman ke penerima HTTPS, gunakan webhook push.

Untuk mencoba kontrak Hello yang telah di-deploy, tetapkan LOG_ADDRESS ke alamatnya. Kirim ping() melalui RPC penyiaran, lalu lakukan backfill blok resi dengan skrip backfill di halaman ini. Lanjutkan dari chunk terakhir yang selesai untuk peristiwa baru.

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

Alur polling

  1. Keluarkan panggilan ringan berkala ke eth_blockNumber untuk memeriksa head rantai terbaru.
  2. Bandingkan nomor blok yang dikembalikan dengan lastSeenBlock yang diproses sebelumnya.
  3. Jika currentBlock > lastSeenBlock, bagi [lastSeenBlock + 1, currentBlock] menjadi chunk paling banyak max_logs_block_range. Simpan lastSeenBlock hanya setelah berhasil memproses setiap chunk; saat gagal, coba lagi chunk yang belum selesai. Hapus duplikasi berdasarkan (blockHash, transactionHash, logIndex) dan putar ulang tumpang tindih setelah menyambung kembali untuk merekonsiliasi reorg.
  4. watchBlockNumber atau watchBlocks dari viem secara bawaan mengimplementasikan polling HTTP di bawah transportasi HTTP, memungkinkan kustomisasi melalui parameter pollingInterval (seperti 1000 ms).

Lakukan polling log peristiwa dalam chunk terbatas

Simpan sebagai poll-logs.mjs di samping network.mjs dan viem-client.mjs. Tetapkan BLOCKVECTRA_API_KEY, LOG_ADDRESS dan FROM_BLOCK, lalu jalankan node poll-logs.mjs. Contoh terbatas ini mengambil sampel head sebanyak 12 kali, berjarak lima detik, dan mengueri setiap rentang baru dalam chunk berurutan. Error akan menghentikan skrip sebelum memajukan chunk yang gagal.

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

Setiap output mencatat chunk yang telah selesai. Untuk melanjutkan, tetapkan FROM_BLOCK ke nilai to + 1-nya; konsumen persisten harus menyimpan peristiwa dan kursor bersama-sama, mendeduplikasi, dan merekonsiliasi reorg seperti yang dijelaskan di atas. Untuk 429 atau kegagalan lain yang dapat dicoba ulang, terapkan panduan backoff terbatas pada chunk yang belum selesai yang sama.

Panduan terkait

Langkah selanjutnya

Terakhir diperbarui:

Di halaman ini