Limites de débit RPC et rétro-remplissage des logs HyperEVM

Sur BlockVectra, les requêtes eth_getLogs authentifiées HyperEVM couvrent jusqu'à 1,000 blocs, en comptant les deux bornes ; découpez les fenêtres plus longues et enregistrez le dernier bloc terminé pour reprendre.

Réponse directe

Le RPC public officiel HyperEVM par défaut autorise 50 blocs par requête eth_getLogs (source : documentation JSON-RPC officielle d'Hyperliquid). Sur BlockVectra, les requêtes eth_getLogs authentifiées couvrent jusqu'à 1,000 blocs par requête (hyperevm_mainnet.max_logs_block_range issu de GET /v1/chains), en incluant les deux bornes. Une plage plus large renvoie HTTP 200, le code JSON-RPC -32602 et logs_range_too_large, avec retryable: false (consultez le catalogue des erreurs) ; découpez en [from, min(from + max − 1, end)], enregistrez votre curseur et avancez jusqu'à la fin plus un après chaque succès pour reprendre vos exécutions. La limite de débit par IP du RPC public officiel et les limites par clé de BlockVectra sont décrites séparément dans Limites de débit du RPC public officiel et 429 et les paramètres de service ci-dessous.

  • Première étape : Lire le dernier bloc sans clé API à l'aide de la commande curl ci-dessous.
  • Terminé quand : Le script de rétro-remplissage affiche fromBlock, toBlock et un tableau result pour chaque segment dans la fenêtre choisie ; un tableau vide signifie qu'aucun log correspondant n'a été trouvé dans ce segment.

Paramètres de chaîne et options d'accès.

Tâches que ce guide vous aide à accomplir

Tâche en trois étapes : rétro-remplir une fenêtre de logs HyperEVM bornée

Lisez le dernier bloc sans clé, créez une clé, puis récupérez les logs d'événements pour un contrat sur une fenêtre de blocs finie.

Choisissez le contrat et la fenêtre de blocs dont vous avez besoin. Cette tâche couvre cette fenêtre bornée ; elle ne garantit pas l'historique complet du contrat.

1. Lire le dernier bloc sans clé API

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

Le result JSON-RPC est le dernier numéro de bloc en hexadécimal. Il s'agit du public.url HyperEVM publié par GET /v1/chains. La liste public.methods de l'endpoint public n'inclut pas eth_getLogs ; l'étape 3 nécessite une clé.

2. Créer une clé API

Créer une clé pour ce rétro-remplissage. Créez une clé et enregistrez le secret affiché dans la boîte de dialogue pour l'utiliser avec hyperevm_mainnet.

Pour un agent IA utilisant HTTP sans navigateur, suivez le Guide d'inscription programmatique. Transmettez la valeur ref valide de l'URL du guide dans le corps JSON de POST /auth/siwe/login au lieu du docs-signup de l'exemple ; omettez-la si elle n'est pas disponible. Ne demandez pas à l'utilisateur de coller la clé dans le chat.

3. Rétro-remplir les logs avec votre clé

Modèle de départ complet : blockvectra/hyperevm-backfill

Enregistrez le script suivant sous le nom hyperevm-task.ts. Il fonctionne avec Node.js 24 ou une version ultérieure, sans paquets supplémentaires. Définissez BLOCKVECTRA_API_KEY avec votre clé enregistrée et LOG_ADDRESS avec l'adresse du contrat émetteur que vous souhaitez inspecter ; conservez la clé sur votre serveur ou dans un terminal local.

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

Par défaut, le script récupère les max_logs_block_range blocs les plus récents, ou moins près de la genèse. Il lit cette limite depuis /v1/chains au moment de l'exécution. Pour sélectionner une autre fenêtre finie, définissez FROM_BLOCK et TO_BLOCK avec des numéros de bloc décimaux ou hexadécimaux 0x avant l'exécution. Les fenêtres plus grandes sont divisées en segments consécutifs, chacun au maximum de la limite publiée.

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

Les requêtes s'exécutent de manière séquentielle. Une erreur JSON-RPC n'est réessayée que lorsque error.data.retryable est true, avec un maximum de quatre tentatives par requête, un recul exponentiel avec gigue (jitter), et la prise en charge de Retry-After en secondes ou sous forme de date HTTP. Une attente supérieure à 30 secondes interrompt le script afin que vous puissiez le réexécuter plus tard. Les échecs réseau, les délais d'attente dépassés, les réponses malformées et les erreurs non réessayables provoquent un arrêt immédiat ; le script se termine en échec plutôt que de signaler un rétro-remplissage complet.

Chaque ligne de la sortie standard contient les champs fromBlock, toBlock et le tableau result d'un segment. result: [] indique qu'aucun log correspondant n'a été trouvé dans ce segment. Lisez ces champs dans chaque log :

ChampSignification
addressContrat ayant émis l'événement.
blockNumber, blockHashBloc contenant le log ; le numéro est en hexadécimal.
transactionHash, transactionIndex, logIndexPosition de la transaction et du log ; les index sont en hexadécimal.
topics, dataArguments d'événements indexés et arguments non indexés encodés en ABI ; à décoder avec l'ABI du contrat.
removedIndique si le log a été supprimé par une réorganisation de chaîne.

Le dernier bloc n'est pas un marqueur de finalité. Si vous avez besoin d'une fenêtre historique stable, choisissez un TO_BLOCK confirmé pour votre application et gérez les réorganisations de chaîne.

Pour une fenêtre de B = TO_BLOCK − FROM_BLOCK + 1 blocs et une limite publiée L, le nombre de segments est N = ceil(B / L). Lisez method_weights[].cu_weight pour eth_getLogs et eth_blockNumber depuis GET /v1/plans. Le script affiche une estimation sur l'erreur standard : N × weight(eth_getLogs) + weight(eth_blockNumber), y compris sa recherche de tête avec clé. Cela exclut les appels supplémentaires et les nouvelles tentatives facturables ; consultez les règles de facturation pour le règlement. Les CU dépendent des appels, et non du nombre de logs renvoyés.

Remise d'événements : Utilisez la scrutation HTTP par segments ci-dessous, ou envoyez les événements des adresses surveillées vers un récepteur HTTPS avec le push webhook. GET /v1/push/chains liste les chaînes prises en charge et les paramètres de confirmation ; authentifiez-vous avec x-api-key. Les signatures de webhook, la déduplication et le rejeu sont traités dans ce guide. Le push webhook est distinct des abonnements WebSocket (ws et subscriptions dans /v1/chains).

Se connecter avec viem ou ethers

Paramètre / EndpointValeur / ModèleAuthentification
Chain ID (EIP-155)999—
JSON-RPC (clé dans le chemin)POST https://api.blockvectra.com/v1/hyperevm_mainnet/{api_key}Clé API dans le chemin d'URL
JSON-RPC (clé dans l'en-tête)POST https://api.blockvectra.com/v1/hyperevm_mainnetEn-tête x-api-key: {api_key}
Base de la Data APIGET https://api.blockvectra.com/v1/data/hyperevm_mainnet/…En-tête x-api-key: {api_key}
Statut publicGET https://api.blockvectra.com/v1/statusNon authentifié (public)

Les développeurs et les agents IA peuvent utiliser les mêmes paramètres côté serveur. Utilisez Node.js 24 ou une version ultérieure, viem 2 ou ethers 6, et commencez par des lectures publiques. Définissez BLOCKVECTRA_API_KEY de manière sécurisée dans l'environnement pour les méthodes nécessitant une clé. Gardez les clés et les URL RPC contenant des clés hors du code de navigateur, des logs et du contrôle de version.

Enregistrez ceci sous le nom network.mjs. Il lit chain_id et la politique des méthodes depuis GET /v1/chains. Pour les lectures sans clé, utilisez le public.url du catalogue et uniquement les méthodes listées dans public.methods ; la disponibilité HTTP publique n'implique pas l'accès 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');

Enregistrez sous le nom viem-client.mjs, installez avec npm install viem@2, puis exécutez 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());

Pour ethers, enregistrez sous le nom ethers-client.mjs, installez avec npm install ethers@6, puis exécutez 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();

Déployer avec Foundry ou Hardhat

Le catalogue actuel de hyperevm_mainnet indique ws=false et ne liste pas eth_sendRawTransaction dans methods.allow. Utilisez BlockVectra pour les lectures ; le déploiement nécessite un RPC qui prend en charge la diffusion. Définissez DEPLOY_RPC_URL sur l'URL HTTP authentifiée de ce fournisseur. Ne supposez pas qu'il partage les limites de méthodes ou de plages de logs de BlockVectra. Vérifiez l'ID de chaîne sélectionné avant de signer.

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

Poursuivez avec le tutoriel de déploiement partagé Foundry ou Hardhat. Approvisionnez le déployeur en HYPE EVM et examinez les exigences d'architecture à deux blocs ci-dessous avant un déploiement volumineux.

HYPE, petits blocs et déploiements volumineux

Le guide officiel du réseau HyperEVM identifie HYPE comme le token de gas, avec 18 décimales (consulté le : 2026-10-07). Assurez-vous que le déployeur détient des HYPE sur HyperEVM ; un solde HyperCore seul ne constitue pas le solde de gas EVM. Suivez les instructions de transfert natif liées lors du déplacement de fonds.

Le guide de l'architecture à deux blocs décrit des petits blocs rapides et des grands blocs plus lents pour les transactions plus volumineuses (consulté le : 2026-10-07). Estimez d'abord le gas de déploiement. Pour les déploiements dépassant le budget des petits blocs, le déployeur doit être un utilisateur HyperCore existant et signer l'action Core {"type":"evmUserModify","usingBigBlocks":true} ; définir une limite de gas de transaction plus élevée seule ne sélectionne pas les grands blocs. Restaurez usingBigBlocks=false ensuite pour revenir aux petits blocs.

Sur un fournisseur qui les prend en charge, utilisez eth_usingBigBlocks pour vérifier le mode d'adresse et eth_bigBlockGasPrice pour les frais de base des grands blocs. La référence officielle JSON-RPC documente ces méthodes (consulté le : 2026-10-07). Vérifiez les méthodes du fournisseur choisi ; utilisez /v1/chains pour BlockVectra. Le déploiement minimal ci-dessus cible un petit contrat et ne modifie pas le mode de compte Core.

Données HyperCore et HyperEVM

Le RPC EVM sert les contrats, les reçus et les logs. Les données de trading et les actions d'HyperCore utilisent l'API Core. Les contrats peuvent lire l'état de Core via des précompilations et envoyer des actions via CoreWriter ; utilisez le guide d'interaction officiel lors de l'intégration de ces parcours (consulté le : 2026-10-07). Les logs EVM ne remplacent pas les requêtes de carnet d'ordres ou de positions Core.

Les transactions système d'HyperEVM (telles que les transferts d'HyperCore vers HyperEVM) ne sont pas incluses dans les réponses standard de eth_getBlockByNumber et sont fournies séparément par le RPC officiel via eth_getSystemTxsByBlockNumber et eth_getSystemTxsByBlockHash (voir la documentation JSON-RPC officielle, consulté le : 2026-10-07). Les données de blocs, de transactions et de Data API d'HyperEVM de BlockVectra n'incluent actuellement pas les transactions système ; utilisez ces deux méthodes RPC officielles directement lorsque vous avez besoin des données de transactions système.

Gérer l'erreur officielle 10055

Le guide officiel d'HyperEVM définit 10055 comme une erreur frontière entre Core et l'EVM, englobant les échecs de nonce, de fonds insuffisants, de hash en double et de remplacement sous-évalué (consulté le : 2026-10-07). Inspectez le message du RPC de diffusion avant de décider de la méthode de récupération :

  • Nonce : comparez eth_getTransactionCount avec vos transactions en attente ; sérialisez les soumissions d'un même déployeur et réconciliez son prochain nonce.
  • Fonds : vérifiez le solde HYPE EVM du déployeur par rapport à la valeur plus le coût en gas.
  • Hash en double : recherchez la transaction et le reçu existants avant de soumettre une autre transaction.
  • Frais de remplacement : vérifiez le nonce et les frais existants, puis appliquez la politique de remplacement du diffuseur ; répéter les mêmes octets n'augmente pas les frais.

Le code 10055 à lui seul ne justifie pas des nouvelles tentatives aveugles. Consultez les erreurs et leurs conseils de récupération séparément dans la référence des erreurs BlockVectra.

Limites de débit du RPC public officiel et 429

La documentation officielle sur les limites de débit d'Hyperliquid spécifie au maximum 100 requêtes JSON-RPC EVM par minute et par IP pour rpc.hyperliquid.xyz/evm. Sa documentation JSON-RPC limite également eth_getLogs à 50 blocs par requête et jusqu'à 4 topics. Consulté le : 2026-10-07.

Sur HTTP 429, suspendez les requêtes et respectez en priorité l'en-tête Retry-After (en secondes ou date HTTP). S'il est absent, utilisez un recul exponentiel avec gigue et un nombre maximal de tentatives, en réessayant le même segment inachevé. Réduisez la concurrence et la fréquence de scrutation, et découpez les requêtes de logs en segments respectant la limite de l'endpoint. Le découpage en segments seul n'élimine pas les limites de débit ; les clients partageant une même IP doivent coordonner leur cadence de requêtes.

Pour l'endpoint avec clé de BlockVectra, lisez max_logs_block_range, methods.allow et methods.deny de hyperevm_mainnet depuis GET /v1/chains au lieu d'appliquer l'étendue de blocs ou la limite de requêtes par minute du RPC public officiel. La cadence des requêtes est soumise séparément aux valeurs cu_per_sec, burst_cu de la clé et au plafond d'appels du forfait gratuit (voir la section suivante). En cas de 429, inspectez error.data.reason et retryable ; request_exceeds_burst nécessite des requêtes plus petites plutôt que des réessais inchangés avec temporisation.

Paramètres et règles de service de BlockVectra

BlockVectra dessert le mainnet HyperEVM via des endpoints JSON-RPC et REST Data API :

  1. Paramètres de chaîne et limites de logs : Depuis GET /v1/chains pour hyperevm_mainnet :
    • Identifiant de chaîne (Slug) : hyperevm_mainnet, Chain ID 999.
    • max_logs_block_range : Régie par le champ max_logs_block_range de GET /v1/chains. Une seule requête eth_getLogs peut couvrir au maximum ce nombre de blocs (toBlock − fromBlock + 1). Tout dépassement renvoie HTTP 200 avec le code d'erreur JSON-RPC -32602 (eth_getLogs block range too large: max <N> blocks), qui n'est pas facturé.
    • state_window_blocks : Régie par le champ state_window_blocks de GET /v1/chains. Les appels de lecture d'état (tels que eth_call et eth_getBalance) sont soumis à la fenêtre de rétention déclarée par ce champ (lorsqu'il vaut null, l'état complet est conservé sans limite de fenêtre glissante).
    • Politique des méthodes : Régie par methods.allow et methods.deny. Les méthodes EVM standard (eth_blockNumber, eth_getLogs, eth_call, eth_getBalance, eth_getBlockByNumber, eth_getTransactionReceipt, etc.) sont autorisées ; les méthodes de filtre et d'abonnement (eth_subscribe, eth_unsubscribe, eth_newFilter, eth_newBlockFilter) sont refusées, renvoyant -32601 (non facturé).
  2. Limites de débit du forfait gratuit et mise à niveau : Depuis GET /v1/plans :
    • free.max_calls_per_sec : jusqu'à 25 appels par seconde, partagés entre toutes les clés du compte, toutes les chaînes et la Data API.
    • Limites par défaut par clé : Chaque clé API dispose d'un bucket de CU (recharge cu_per_sec, capacité burst_cu — les valeurs par défaut sont 400 CU/s et un burst de 1,600 CU). Les méthodes sont mesurées selon les poids en Compute Units (CU).
    • Mise à niveau des limites : Après une recharge, la limite d'appels par seconde à l'échelle du compte est supprimée ; chaque clé reste soumise aux limites de débit en Compute Units (CU) et de burst. Pour les tarifs et unités de facturation actuels, consultez la page Tarifs.

Rétro-remplissage des logs historiques : eth_getLogs découpé et logique de nouvelle tentative

Lors de l'interrogation de logs historiques, les larges intervalles doivent être divisés en segments contigus bornés par le max_logs_block_range de la chaîne cible. Les stratégies de nouvelle tentative des clients doivent inspecter le champ retryable à l'intérieur des réponses d'erreur.

Évaluation de retryable dans les réponses d'erreur

Sur BlockVectra, les objets d'erreur JSON-RPC incluent une charge utile error.data contenant reason, docs_url et retryable (booléen) :

  • retryable: true : Conditions transitoires, notamment surcharge du service (overloaded), limite d'appels par seconde du forfait gratuit (free_plan_call_limit), synchronisation du nœud (node_syncing), ou amont indisponible (upstream_unavailable). Les clients doivent respecter l'en-tête Retry-After lorsqu'il est présent ou appliquer un recul exponentiel avec gigue.
  • retryable: false : Erreurs non transitoires, telles qu'une étendue de blocs dépassant les limites (-32602 / logs_range_too_large), des paramètres invalides (invalid_params), une clé API manquante (missing_api_key), ou une requête dépassant la capacité de burst (-32022 / request_exceeds_burst). Réessayer sans ajuster les paramètres ne réussira pas.

Voici la réponse renvoyée lorsqu'une clé API est omise :

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

Utiliser les endpoints de la Data API au lieu d'une analyse intensive par getLogs

Lorsqu'une application suit l'historique des transactions ou les mouvements de jetons pour une adresse spécifique, l'analyse via eth_getLogs nécessite d'émettre des requêtes séquentielles par segments bornées par max_logs_block_range et d'analyser les logs bruts d'événements Transfer.

La Data API de BlockVectra fournit des endpoints REST pré-indexés pour hyperevm_mainnet, prenant en charge des fenêtres allant jusqu'à 100 000 blocs avec pagination par curseur :

  1. Transactions d'adresse : GET /v1/data/hyperevm_mainnet/addresses/{address}/transactions
    • Paramètres : from_block (requis), to_block (requis), direction (facultatif : from, to, any, par défaut any), clamp (chaîne booléenne facultative, par défaut false ; lorsqu'elle est définie sur true, les fenêtres dépassant 100 000 blocs ou supérieures à as_of_block sont tronquées au lieu de renvoyer 409), limit (facultatif, max 500), cursor (jeton de pagination).
  2. Transferts de jetons d'adresse : GET /v1/data/hyperevm_mainnet/addresses/{address}/transfers
    • Paramètres : standard (requis : erc20 ou erc721 ; erc1155 ne peut pas être interrogé par adresse et renvoie 422 no_coverage), token (filtre de contrat de jeton facultatif), from_block (requis), to_block (requis), direction (facultatif : in, out, any), clamp (facultatif), limit, cursor.

Structure de la réponse

Les réponses utilisent des schémas d'enveloppe standard :

  • data : Tableau d'enregistrements. Les transactions incluent hash, block_number, block_timestamp, from, to, value, tx_index, gas_limit, gas_used et status. Les transferts incluent token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index et log_index (amount pour ERC-20, token_id pour ERC-721).
  • next_cursor : Jeton de pagination opaque renvoyé lorsque des enregistrements ultérieurs existent (absent sur la dernière page, pas null).
  • meta : Métadonnées contenant chain, chain_slug, chain_external_id, as_of_block, safe_block, finalized_block, coverage (full ou partial) et refreshed_at.

Exemple de code : requêtes 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"

Suivi en temps réel : scrutation des nouveaux blocs

Pour un transport HTTP, suivez les blocs par scrutation et récupérez les logs d'événements par segments consécutifs dans la limite de max_logs_block_range. Ne sélectionnez WebSocket que lorsque /v1/chains indique ws=true et l'entrée subscriptions requise. Pour la remise vers un récepteur HTTPS, utilisez le push webhook.

Pour tester le contrat Hello déployé, définissez LOG_ADDRESS sur son adresse. Envoyez ping() via le RPC de diffusion, puis rétro-remplissez le bloc du reçu à l'aide du script de rétro-remplissage de cette page. Poursuivez à partir du dernier segment terminé pour les nouveaux événements.

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

Flux de scrutation

  1. Émettez des appels légers périodiques vers eth_blockNumber pour inspecter la tête de chaîne la plus récente.
  2. Comparez le numéro de bloc renvoyé avec le lastSeenBlock précédemment traité.
  3. Si currentBlock > lastSeenBlock, découpez [lastSeenBlock + 1, currentBlock] en segments d'au plus max_logs_block_range. Ne persistez lastSeenBlock qu'après avoir traité avec succès chaque segment ; en cas d'échec, réessayez le segment inachevé. Dédupliquez par (blockHash, transactionHash, logIndex) et rejouez un chevauchement après reconnexion pour réconcilier les réorganisations.
  4. Les fonctions watchBlockNumber ou watchBlocks de viem implémentent nativement la scrutation HTTP sous un transport HTTP, permettant une personnalisation via le paramètre pollingInterval (comme 1000 ms).

Scruter les logs d'événements par segments bornés

Enregistrez sous le nom poll-logs.mjs à côté de network.mjs et viem-client.mjs. Définissez BLOCKVECTRA_API_KEY, LOG_ADDRESS et FROM_BLOCK, puis exécutez node poll-logs.mjs. Cet exemple fini échantillonne la tête 12 fois, à cinq secondes d'intervalle, et interroge chaque nouvelle plage par segments séquentiels. Une erreur interrompt le script avant d'avancer le segment en échec.

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

Chaque sortie enregistre un segment terminé. Pour reprendre, définissez FROM_BLOCK sur son to + 1 ; les consommateurs durables doivent enregistrer ensemble les événements et le curseur, dédupliquer et réconcilier les réorganisations comme décrit ci-dessus. Pour le code 429 ou d'autres défaillances réessayables, appliquez les conseils de recul temporisé au même segment inachevé.

Guides connexes

Prochaines étapes

Dernière mise à jour :

Sur cette page