HyperEVM RPC のレート制限とログバックフィル

HyperEVM RPC のレート制限と 429 レスポンスに対応。有界な範囲で認証付き eth_getLogs をクエリし、カーソルを保存して欠落したアクティビティを復旧します。

要点

デフォルトの公式 HyperEVM 公開 RPC は、eth_getLogs クエリあたり 50 ブロックを許可します(出典:Hyperliquid 公式 JSON-RPC ドキュメント)。BlockVectra では、認証付き eth_getLogs リクエストはクエリあたり最大 1,000 ブロック(GET /v1/chains の hyperevm_mainnet.max_logs_block_range、両端を含む)をカバーします。これを超える範囲は HTTP 200、JSON-RPC -32602、および logs_range_too_large(retryable: false)を返します(エラーカタログを参照)。[from, min(from + max − 1, end)] に分割し、カーソルを保存し、成功後に末尾プラス 1 に進めて実行を再開します。公式公開 RPC の IP ごとのレート制限と BlockVectra のキー制限は、公式公開 RPC のレート制限と 429 および以下のサービスパラメータで個別に説明されています。

このガイドで達成できること

3 ステップタスク:有界な HyperEVM ログウィンドウのバックフィル

キーなしで最新ブロックを読み取り、キーを作成して、有限のブロックウィンドウにわたるコントラクトのイベントログを取得します。

必要なコントラクトとブロックウィンドウを選択します。このタスクはその有界なウィンドウをカバーするものであり、完全なコントラクト履歴を保証するものではありません。

1. 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":[]}'

JSON-RPC の result は十六進数の最新ブロック番号です。これは GET /v1/chains によって公開されている HyperEVM の public.url です。公開エンドポイントの public.methods には eth_getLogs は含まれていません。ステップ 3 にはキーが必要です。

2. API key を作成する

このバックフィルのためのキーを作成します。キーを作成し、hyperevm_mainnet で使用するためにダイアログに表示されたシークレットを保存します。

ブラウザを使用しない HTTP 経由の AI エージェントの場合は、プログラムによる登録ガイド に従ってください。サンプルの docs-signup ではなく、ガイド URL の有効な ref を POST /auth/siwe/login の JSON ボディに渡します。利用できない場合は省略してください。ユーザーにチャットへキーを貼り付けるよう求めないでください。

3. キーを使用してログをバックフィルする

完全なスターターテンプレート:blockvectra/hyperevm-backfill

以下のスクリプトを hyperevm-task.ts として保存します。追加のパッケージなしで、Node.js 24 以降で動作します。BLOCKVECTRA_API_KEY を保存したキーに、LOG_ADDRESS を検査したいイベント発行コントラクトアドレスに設定します。キーはサーバーまたはローカルターミナルに保管してください。

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

デフォルトでは、スクリプトは最新の max_logs_block_range ブロック(ジェネシス付近ではそれ以下)を取得します。実行時に /v1/chains からその制限を読み取ります。別の有限なウィンドウを選択するには、実行前に FROM_BLOCK と TO_BLOCK の両方を十進数または 0x 十六進数のブロック番号に設定します。これより大きなウィンドウは、公開された上限以下の連続したチャンクに分割されます。

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

リクエストは順次実行されます。JSON-RPC エラーは、error.data.retryable が true の場合にのみ再試行され、リクエストごとに最大 4 回の試行、指数バックオフとジッター、および Retry-After の秒数または HTTP 日付のサポートが行われます。30 秒を超える待機が発生した場合、後で再実行できるようにスクリプトを停止します。ネットワーク障害、タイムアウト、不正な形式のレスポンス、および再試行不可能なエラーは直ちに停止します。スクリプトは、完全なバックフィルを報告することなく、異常終了します。

標準出力の各行には、1 つのチャンクの fromBlock、toBlock、および result 配列が含まれます。result: [] は、そのチャンクに一致するログがなかったことを意味します。各ログの以下のフィールドを確認してください:

フィールド意味
addressイベントを発行したコントラクト。
blockNumber, blockHashログを含むブロック。番号は十六進数です。
transactionHash, transactionIndex, logIndexトランザクションおよびログの位置。インデックスは十六進数です。
topics, dataインデックス付きイベント引数および ABI エンコードされた非インデックス引数。コントラクト ABI でデコードします。
removedチェーンの再編成(reorg)によってログが削除されたかどうか。

最新ブロックはファイナリティのマーカーではありません。安定した履歴ウィンドウが必要な場合は、アプリケーションで確認済みの TO_BLOCK を選択し、チェーンの再編成を処理してください。

B = TO_BLOCK − FROM_BLOCK + 1 ブロックのウィンドウと公開制限 L に対して、チャンク数は N = ceil(B / L) です。GET /v1/plans から eth_getLogs と eth_blockNumber の method_weights[].cu_weight を読み取ります。スクリプトは標準エラー出力に見積もりを出力します:キー付きのヘッドルックアップを含めて N × weight(eth_getLogs) + weight(eth_blockNumber)。これには追加の呼び出しや課金対象の再試行は含まれません。精算については請求ルールを参照してください。CU は返されるログの数ではなく、呼び出し回数に依存します。

イベント配信: 以下のチャンク分割 HTTP ポーリングを使用するか、webhook push を使用して監視対象アドレスのイベントを HTTPS レシーバーに送信します。GET /v1/push/chains で対応チェーンと確認設定を確認できます。x-api-key で認証してください。Webhook 署名、重複排除、およびリプレイはそのガイドで説明されています。Webhook push は WebSocket サブスクリプション(/v1/chains の ws と subscriptions)とは別個の機能です。

viem または ethers で接続する

パラメータ / エンドポイント値 / テンプレート認証方式
チェーン ID(EIP-155)999—
JSON-RPC(パスに API key を指定)POST https://api.blockvectra.com/v1/hyperevm_mainnet/{api_key}URL パスに API key を指定
JSON-RPC(ヘッダーに API key を指定)POST https://api.blockvectra.com/v1/hyperevm_mainnetヘッダー x-api-key: {api_key}
Data API ベース URLGET https://api.blockvectra.com/v1/data/hyperevm_mainnet/…ヘッダー x-api-key: {api_key}
公開ステータスGET https://api.blockvectra.com/v1/status認証不要(公開)

開発者と AI エージェントは同じサーバー側設定を使用できます。Node.js 24 以降、viem 2 または ethers 6 を使用し、公開読み取りから始めます。キーが必要なメソッドについては、環境変数に BLOCKVECTRA_API_KEY を安全に設定してください。キーおよびキーを含む RPC URL は、ブラウザコード、ログ、およびバージョン管理から除外してください。

これを network.mjs として保存します。GET /v1/chains から chain_id とメソッドポリシーを読み取ります。キー不要の読み取りには、カタログの public.url と public.methods にリストされているメソッドのみを使用します。公開 HTTP の利用可能性は 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');

viem-client.mjs として保存し、npm install viem@2 でインストールしてから、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());

ethers の場合は ethers-client.mjs として保存し、npm install ethers@6 でインストールしてから、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();

Foundry または Hardhat でデプロイする

現在の hyperevm_mainnet カタログは ws=false であり、methods.allow に eth_sendRawTransaction がリストされていません。読み取りには BlockVectra を使用します。デプロイにはブロードキャストをサポートする RPC が必要です。DEPLOY_RPC_URL をそのプロバイダーの認証付き HTTP URL に設定します。BlockVectra のメソッドやログ範囲の制限と同じであるとは仮定しないでください。署名する前に選択したチェーン ID を確認してください。

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

共通の Foundry または Hardhat デプロイチュートリアル に進んでください。デプロイヤーに EVM HYPE を入金し、大規模デプロイの前に以下のデュアルブロック要件を確認してください。

HYPE、小型ブロック、および大規模デプロイ

HyperEVM の公式ネットワークガイド は、HYPE をガスとして指定しており、18 桁の小数を持ちます(参照日:2026-10-07)。デプロイヤーが HyperEVM 上で HYPE を保持していることを確認してください。HyperCore の残高だけでは EVM ガスの残高にはなりません。資金を移動する際は、リンク先のネイティブ転送手順に従ってください。

デュアルブロックガイド では、高速な小型ブロック(small blocks)と、より大きなトランザクション向けの低速な大型ブロック(big blocks)について説明されています(参照日:2026-10-07)。まずデプロイガスを見積もってください。小型ブロックの予算を超えるデプロイの場合、デプロイヤーは既存の HyperCore ユーザーであり、Core アクション {"type":"evmUserModify","usingBigBlocks":true} に署名する必要があります。トランザクションのガス制限を大きく設定するだけでは、大型ブロックは選択されません。終了後は usingBigBlocks=false に戻して、小型ブロックに戻してください。

それらをサポートするプロバイダーでは、eth_usingBigBlocks を使用してアドレスモードを確認し、eth_bigBlockGasPrice で大型ブロックのベースフィーを確認します。公式 JSON-RPC リファレンス にこれらのメソッドが記載されています(参照日:2026-10-07)。選択したプロバイダーのメソッドを確認してください。BlockVectra の場合は /v1/chains を使用します。上記の最小限のデプロイは小さなコントラクトを対象としており、Core アカウントモードは変更しません。

HyperCore と HyperEVM データ

EVM RPC はコントラクト、receipt、およびログを提供します。HyperCore の取引データとアクションは Core API を使用します。コントラクトはプリコンパイルを通じて Core の状態を読み取り、CoreWriter を通じてアクションを送信できます。これらのパスを統合する際は、公式インタラクションガイド を使用してください(参照日:2026-10-07)。EVM ログは、Core のオーダーブックやポジションのクエリに代わるものではありません。

HyperEVM システムトランザクション(HyperCore から HyperEVM への転送など)は標準の eth_getBlockByNumber レスポンスには含まれず、公式 RPC を通じて eth_getSystemTxsByBlockNumber および eth_getSystemTxsByBlockHash 経由で個別に提供されます(公式 JSON-RPC ドキュメント を参照、参照日:2026-10-07)。BlockVectra の HyperEVM ブロック、トランザクション、および Data API データには現在、システムトランザクションは含まれていません。システムトランザクションデータが必要な場合は、これら 2 つの公式 RPC メソッドを直接使用してください。

公式エラー 10055 の処理

公式 HyperEVM ガイド では、10055 を Core/EVM 境界エラーと定義しており、nonce、資金不足、ハッシュ重複、および低手数料による置き換え失敗が含まれます(参照日:2026-10-07)。復旧方法を決定する前に、ブロードキャスト RPC からのメッセージを確認してください:

  • Nonce: eth_getTransactionCount を保留中のトランザクションと比較します。1 つのデプロイヤーからの送信を直列化し、次の nonce を調整します。
  • 資金: 送金額にガス代を加えた合計に対して、デプロイヤーの EVM HYPE 残高を確認します。
  • 重複ハッシュ: 別のトランザクションを送信する前に、既存のトランザクションと receipt を検索します。
  • 置き換え手数料: 既存の nonce と手数料を確認し、ブロードキャスターの置き換えポリシーを使用します。同じバイト列を繰り返しても手数料は引き上げられません。

10055 だけでは盲目的な再試行を正当化することはできません。エラーとその復旧ガイダンスについては、BlockVectra エラーリファレンス で個別に確認してください。

公式公開 RPC のレート制限と 429

Hyperliquid の公式レート制限ドキュメント では、rpc.hyperliquid.xyz/evm に対して IP ごとに 1 分あたり最大 100 件の EVM JSON-RPC リクエストが規定されています。その JSON-RPC ドキュメント でも、eth_getLogs はクエリあたり 50 ブロック、最大 4 トピックに制限されています(参照日:2026-10-07)。

HTTP 429 が発生した場合はリクエストを一時停止し、まず Retry-After(秒数または HTTP 日付)に従ってください。存在しない場合は、ジッターを伴う指数バックオフと有界な再試行回数を使用し、同じ未完了のチャンクを再試行します。並行性とポーリング頻度を減らし、エンドポイントの制限内のチャンクにログクエリを分割します。チャンク分割だけではレート制限は解除されません。IP を共有するクライアントは、リクエストレートを協調させる必要があります。

BlockVectra のキー付きエンドポイントについては、公式公開 RPC のブロック範囲や毎分リクエスト数の制限を適用するのではなく、GET /v1/chains から hyperevm_mainnet の max_logs_block_range、methods.allow、および methods.deny を読み取ります。リクエストレートは、キーの cu_per_sec、burst_cu、および無料プランの呼び出し制限の対象となります(次のセクションを参照)。429 が発生した場合は、error.data.reason と retryable を確認してください。request_exceeds_burst の場合は、バックオフを伴う変更なしの再試行ではなく、より小さなリクエストに分割する必要があります。

BlockVectra パラメータとサービスルール

BlockVectra は、JSON-RPC および REST Data API エンドポイントを介して HyperEVM メインネットを提供します:

  1. チェーンパラメータとログの制限: hyperevm_mainnet の GET /v1/chains より:
    • チェーン識別子(Slug):hyperevm_mainnet、チェーン ID 999。
    • max_logs_block_range:GET /v1/chains の max_logs_block_range フィールドに準拠。単一の eth_getLogs リクエストはこのブロック数(toBlock − fromBlock + 1)以下をカバーする必要があります。この範囲を超えると、HTTP 200 と JSON-RPC エラーコード -32602(eth_getLogs block range too large: max <N> blocks)が返されます(課金対象外)。
    • state_window_blocks:GET /v1/chains の state_window_blocks フィールドに準拠。状態読み取り呼び出し(eth_call や eth_getBalance など)はこのフィールドで宣言された保持ウィンドウの対象となります(null の場合、ローリングウィンドウ制限なしで完全な状態が保持されます)。
    • メソッドポリシー:methods.allow および methods.deny に準拠。標準的な EVM メソッド(eth_blockNumber、eth_getLogs、eth_call、eth_getBalance、eth_getBlockByNumber、eth_getTransactionReceipt など)は許可されます。フィルタおよび購読メソッド(eth_subscribe、eth_unsubscribe、eth_newFilter、eth_newBlockFilter)は拒否され、-32601 を返します(課金対象外)。
  2. 無料枠のレート制限とアップグレード: GET /v1/plans より:
    • free.max_calls_per_sec:最大毎秒 25 回の呼び出し。アカウント内のすべてのキー、すべてのチェーン、および Data API で共有されます。
    • デフォルトのキー制限:各 API key には CU バケット(cu_per_sec 補充速度、burst_cu 容量 — デフォルトは 400 CU/s、バースト容量は 1,600 CU)があります。メソッドは Compute Unit(CU)重み付けによって計測されます。
    • 制限のアップグレード:チャージ後、アカウント全体の毎秒呼び出し制限は解除されます。各キーには引き続き Compute Unit(CU)レートとバースト制限が適用されます。現在の料金と課金単位については、料金ページを参照してください。

過去のログのバックフィル:チャンク分割 eth_getLogs と再試行ロジック

過去のログをクエリする場合、広い区間は対象チェーンの max_logs_block_range で区切られた連続したチャンクに分割する必要があります。クライアントの再試行戦略では、エラーレスポンス内の retryable フィールドを検査する必要があります。

エラーレスポンスでの retryable の評価

BlockVectra では、JSON-RPC エラーオブジェクトに reason、docs_url、および retryable(ブール値)を含む error.data ペイロードが含まれます:

  • retryable: true:サービス過負荷(overloaded)、無料プランの毎秒呼び出し制限(free_plan_call_limit)、ノード同期中(node_syncing)、アップストリーム利用不可(upstream_unavailable)などの一時的な状態。クライアントは、存在する場合は Retry-After ヘッダーを尊重するか、ジッターを伴う指数バックオフを適用する必要があります。
  • retryable: false:ブロック範囲の上限超過(-32602 / logs_range_too_large)、無効なパラメータ(invalid_params)、API key の欠落(missing_api_key)、リクエストがバースト容量を超過(-32022 / request_exceeds_burst)などの非一時的なエラー。パラメータを調整せずに再試行しても成功しません。

以下は、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
    }
  }
}

大規模な getLogs スキャンの代わりに Data API エンドポイントを使用する

アプリケーションが特定のブロックまたはアドレスのトランザクション履歴やトークンの移動を追跡する場合、eth_getLogs を介したスキャンでは、max_logs_block_range で区切られた連続したチャンククエリを発行し、生の Transfer イベントログを解析する必要があります。

BlockVectra Data API は、hyperevm_mainnet 向けに事前インデックス化された REST エンドポイントを提供し、カーソルベースのページネーションで最大 100,000 ブロックのウィンドウをサポートします:

  1. アドレスのトランザクション:GET /v1/data/hyperevm_mainnet/addresses/{address}/transactions
    • パラメータ:from_block(必須)、to_block(必須)、direction(オプション:from、to、any、デフォルトは any)、clamp(オプションのブール値文字列、デフォルトは false。true に設定すると、100,000 ブロックを超えるウィンドウや as_of_block より高いウィンドウは、409 を返す代わりに切り詰められます)、limit(オプション、最大 500)、cursor(ページネーショントークン)。
  2. アドレストークン送金:GET /v1/data/hyperevm_mainnet/addresses/{address}/transfers
    • パラメータ:standard(必須:erc20 または erc721。erc1155 はアドレスでクエリできず、422 no_coverage を返します)、token(オプションのトークンコントラクトフィルタ)、from_block(必須)、to_block(必須)、direction(オプション:in、out、any)、clamp(オプション)、limit、cursor。

レスポンス構造

レスポンスは標準のエンベロープスキーマを使用します:

  • data:レコードの配列。トランザクションには hash、block_number、block_timestamp、from、to、value、tx_index、gas_limit、gas_used、および status が含まれます。送金には token、standard、from、to、block_number、block_timestamp、tx_hash、tx_index、および log_index が含まれます(ERC-20 の場合は amount、ERC-721 の場合は token_id)。
  • next_cursor:後続のレコードが存在する場合に返される不透明なページネーショントークン(最後のページでは存在せず、null ではありません)。
  • meta:chain、chain_slug、chain_external_id、as_of_block、safe_block、finalized_block、coverage(full または partial)、および refreshed_at を含むメタデータ。

コード例: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"

リアルタイム追跡:新しいブロックのポーリング

HTTP トランスポートの場合、ポーリングによってブロックを追跡し、max_logs_block_range 内の連続したチャンクでイベントログを取得します。WebSocket は、/v1/chains が ws=true および必要な subscriptions エントリを報告している場合にのみ選択してください。HTTPS レシーバーへの配信には、webhook push を使用します。

デプロイされた Hello コントラクトをテストするには、LOG_ADDRESS をそのアドレスに設定します。ブロードキャスト RPC を介して ping() を送信し、このページのバックフィルスクリプトを使用して receipt のブロックをバックフィルします。新しいイベントについては、最後に完了したチャンクから継続します。

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

ポーリングフロー

  1. 定期的な軽量呼び出しを eth_blockNumber に発行して、最新のチェーン先頭を検査します。
  2. 返されたブロック番号を以前に処理された lastSeenBlock と比較します。
  3. currentBlock > lastSeenBlock の場合、[lastSeenBlock + 1, currentBlock] を最大 max_logs_block_range のチャンクに分割します。各チャンクを正常に処理した後にのみ lastSeenBlock を永続化し、失敗した場合は未完了のチャンクを再試行します。(blockHash, transactionHash, logIndex) で重複排除し、再接続後に重複部分をリプレイして reorg を調整します。
  4. viem の watchBlockNumber または watchBlocks は、HTTP トランスポート下でネイティブに HTTP ポーリングを実装しており、pollingInterval パラメータ(1000 ms など)によるカスタマイズが可能です。

有界なチャンクでのイベントログのポーリング

network.mjs と viem-client.mjs の横に poll-logs.mjs として保存します。BLOCKVECTRA_API_KEY、LOG_ADDRESS、および FROM_BLOCK を設定してから、node poll-logs.mjs を実行します。この有限のサンプルは、5 秒間隔で先頭を 12 回サンプリングし、すべての新しい範囲を順次チャンクでクエリします。エラーが発生した場合、失敗したチャンクを進める前にスクリプトを停止します。

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

各出力は完了したチャンクを記録します。再開するには、FROM_BLOCK をその to + 1 に設定します。永続的なコンシューマーは、イベントとカーソルを一緒に保存し、重複排除し、上記で説明したように reorg を調整する必要があります。429 またはその他の再試行可能な障害については、同一の未完了チャンクに対して有界バックオフガイダンスを適用してください。

関連ガイド

次のステップ

最終更新:

このページの目次