Лимиты частоты RPC и выгрузка логов HyperEVM

В BlockVectra аутентифицированные запросы eth_getLogs к HyperEVM покрывают до 1,000 блоков с учетом обоих концов; разбивайте длинные окна и сохраняйте последний завершенный блок для возобновления.

Прямой ответ

По умолчанию официальный публичный RPC HyperEVM разрешает не более 50 блоков на один запрос eth_getLogs (источник: официальная документация по JSON-RPC Hyperliquid). В BlockVectra аутентифицированные запросы eth_getLogs покрывают до 1,000 блоков на запрос (hyperevm_mainnet.max_logs_block_range из GET /v1/chains), включая оба конца диапазона. Более широкий диапазон возвращает HTTP 200, JSON-RPC -32602 и logs_range_too_large с retryable: false (см. каталог ошибок); разделите диапазон на [from, min(from + max − 1, end)], сохраните курсор и после успешного завершения переходите к блоку end + 1 для возобновления работы. Лимит частоты запросов на IP официального публичного RPC и лимиты ключей BlockVectra описаны отдельно в разделе Лимиты частоты запросов официального публичного RPC и ошибки 429 и в параметрах сервиса ниже.

  • Первый шаг: прочитайте последний блок без API key с помощью команды curl ниже.
  • Критерий завершения: скрипт выгрузки выводит fromBlock, toBlock и массив result для каждого фрагмента в выбранном окне; пустой массив означает отсутствие подходящих логов в этом фрагменте.

Параметры сети и варианты доступа.

Задачи, которые помогает решить это руководство

  • Проверка HyperEVM RPC с помощью публичного чтения через viem или ethers перед выбором аутентифицированных методов.
  • Выгрузка ограниченного окна логов в пределах лимита eth_getLogs для HyperEVM с решениями о повторных попытках на основе возвращенной ошибки.
  • Чтение активности по адресу через проиндексированные транзакции и переводы с использованием ключа, с проверкой возвращенных метаданных покрытия и актуальности.

Задача из трех шагов: выгрузка ограниченного окна логов 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":[]}'

Поле result в ответе JSON-RPC содержит номер последнего блока в шестнадцатеричном формате. Это URL public.url для HyperEVM, опубликованный в GET /v1/chains. Список public.methods публичного эндпоинта не включает eth_getLogs; для шага 3 требуется ключ.

2. Создание API key

Создайте ключ для этой выгрузки. Создайте ключ и сохраните секрет, показанный в диалоговом окне, для использования с hyperevm_mainnet.

Для ИИ-агента, использующего HTTP без браузера, следуйте руководству по программной регистрации. Передайте валидный параметр ref из URL руководства в теле JSON-запроса POST /auth/siwe/login вместо значения docs-signup из примера; опустите его, если он недоступен. Не просите пользователя вставлять ключ в чат.

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, с максимум четырьмя попытками на запрос, экспоненциальной задержкой и джиттером, а также поддержкой секунд или HTTP-даты в Retry-After. Ожидание более 30 секунд останавливает скрипт, чтобы вы могли перезапустить его позже. Сбои сети, таймауты, некорректные ответы и неповторяемые ошибки немедленно прерывают выполнение; скрипт завершается с ошибкой, а не сообщает об успешном завершении выгрузки.

Каждая строка стандартного вывода содержит fromBlock, toBlock и массив result одного фрагмента. result: [] означает отсутствие подходящих логов в этом фрагменте. Обратите внимание на следующие поля в каждом логе:

ПолеЗначение
addressКонтракт, сгенерировавший событие.
blockNumber, blockHashБлок, содержащий лог; номер указан в шестнадцатеричном формате.
transactionHash, transactionIndex, logIndexПозиция транзакции и лога; индексы указаны в шестнадцатеричном формате.
topics, dataИндексированные аргументы события и неиндексированные аргументы, закодированные по ABI; декодируются с помощью ABI контракта.
removedБыл ли лог удален в результате реорганизации цепи.

Последний блок не является маркером финализации. Если вам требуется стабильное историческое окно, выберите подтвержденный вашим приложением TO_BLOCK и обрабатывайте реорганизации цепи.

Для окна в B = TO_BLOCK − FROM_BLOCK + 1 блоков и опубликованного лимита L количество фрагментов составляет N = ceil(B / L). Значения method_weights[].cu_weight для eth_getLogs и eth_blockNumber берутся из GET /v1/plans. Скрипт выводит оценку в стандартный поток ошибок: N × weight(eth_getLogs) + weight(eth_blockNumber), включая аутентифицированный запрос вершины сети. Это исключает дополнительные вызовы и любые тарифицируемые повторные попытки; расчет см. в правилах тарификации. Потребление CU зависит от вызовов, а не от количества возвращенных логов.

Доставка событий: используйте фрагментированный HTTP-опрос ниже или отправляйте события отслеживаемых адресов на HTTPS-приемник с помощью Push через вебхуки. GET /v1/push/chains возвращает поддерживаемые сети и настройки подтверждений; аутентификация выполняется с помощью x-api-key. Подписи вебхуков, дедупликация и защита от повторов рассматриваются в том руководстве. Push через вебхуки работает отдельно от подписок WebSocket (ws и subscriptions в /v1/chains).

Подключение с помощью viem или ethers

Параметр / Конечная точкаЗначение / ШаблонАутентификация
Chain ID (EIP-155)999—
JSON-RPC (key в пути)POST https://api.blockvectra.com/v1/hyperevm_mainnet/{api_key}API key в URL пути
JSON-RPC (key в заголовке)POST https://api.blockvectra.com/v1/hyperevm_mainnetЗаголовок x-api-key: {api_key}
Базовый URL Data APIGET https://api.blockvectra.com/v1/data/hyperevm_mainnet/…Заголовок x-api-key: {api_key}
Публичный статусGET https://api.blockvectra.com/v1/statusБез аутентификации (публичный)

Разработчики и ИИ-агенты могут использовать одинаковые настройки на стороне сервера. Используйте Node.js 24 или новее, viem 2 или ethers 6 и начните с публичных операций чтения. Надежно установите BLOCKVECTRA_API_KEY в переменных окружения для методов, требующих ключа. Не допускайте попадания ключей и содержащих их RPC URL в код браузера, логи и системы контроля версий.

Сохраните этот код как network.mjs. Он считывает chain_id и политику методов из GET /v1/chains. Для операций чтения без ключа используйте 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, а метод eth_sendRawTransaction отсутствует в methods.allow. Используйте BlockVectra для чтения; для развертывания требуется RPC с поддержкой отправки транзакций. Укажите в переменной DEPLOY_RPC_URL аутентифицированный HTTP URL этого провайдера. Не предполагайте, что он имеет те же лимиты методов или диапазонов логов, что и BlockVectra. Проверьте выбранный chain 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. Пополните баланс деплоера нативными токенами HYPE в EVM и ознакомьтесь с требованиями к двойным блокам ниже перед масштабным развертыванием.

HYPE, малые блоки и крупные развертывания

В официальном руководстве по сети HyperEVM указано, что HYPE используется для оплаты газа и имеет 18 знаков после запятой (по состоянию на 2026-10-07). Убедитесь, что адрес деплоера содержит HYPE в сети HyperEVM; баланс в HyperCore сам по себе не является балансом газа в EVM. Следуйте инструкциям по ссылке для перевода нативных средств.

В руководстве по архитектуре с двумя типами блоков описываются быстрые малые блоки и более медленные большие блоки для крупных транзакций (по состоянию на 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 обслуживает контракты, квитанции и логи. Торговые данные и действия 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). Данные блоков, транзакций и Data API BlockVectra для HyperEVM в настоящее время не включают системные транзакции; используйте эти два метода официального RPC напрямую, если вам требуются данные системных транзакций.

Обработка официальной ошибки 10055

В официальном руководстве по HyperEVM ошибка 10055 определяется как ошибка на границе Core/EVM, включая сбои, связанные с nonce, недостатком средств, дублированием хэша и заниженной комиссией при замене транзакции (по состоянию на 2026-10-07). Изучите сообщение от RPC-транслятора, прежде чем принимать решение о способе восстановления:

  • Nonce: сравните eth_getTransactionCount с вашими ожидающими транзакциями; упорядочивайте отправку от одного деплоера и согласуйте его следующий nonce.
  • Средства: проверьте баланс HYPE деплоера в EVM на соответствие сумме перевода плюс стоимости газа.
  • Дубликат хэша: найдите существующую транзакцию и квитанцию перед отправкой другой транзакции.
  • Комиссия за замену: проверьте существующий nonce и комиссию, затем используйте политику замены транслятора; повторная отправка тех же байтов не увеличивает комиссию.

Сама по себе ошибка 10055 не дает оснований для слепых повторных попыток. Ошибки и рекомендации по их устранению описаны отдельно в справочнике по ошибкам BlockVectra.

Лимиты частоты запросов официального публичного RPC и ошибки 429

Официальная документация по лимитам Hyperliquid устанавливает ограничение не более 100 запросов JSON-RPC к EVM в минуту на один IP для rpc.hyperliquid.xyz/evm. Ее документация по JSON-RPC также ограничивает eth_getLogs до 50 блоков на запрос и не более 4 топиков. По состоянию на: 2026-10-07.

При получении HTTP 429 приостановите отправку запросов и в первую очередь учитывайте заголовок Retry-After (секунды или дата HTTP). Если он отсутствует, используйте экспоненциальную задержку с джиттером и ограниченным числом повторов, повторяя тот же незавершенный фрагмент. Снизьте параллелизм и частоту опроса, а также разделите запросы логов на фрагменты в пределах лимита эндпоинта. Само по себе разбиение на фрагменты не отменяет лимитов частоты; клиенты, использующие один IP, должны координировать частоту запросов.

Для эндпоинта BlockVectra с ключом прочитайте max_logs_block_range, methods.allow и methods.deny для hyperevm_mainnet из GET /v1/chains вместо применения лимитов блоков или запросов в минуту официального публичного RPC. Частота запросов отдельно регулируется параметрами ключа cu_per_sec, burst_cu и лимитом вызовов бесплатного плана (см. следующий раздел). При ошибке 429 проверьте error.data.reason и retryable; ошибка request_exceeds_burst требует уменьшения размера запросов, а не повторных попыток без изменений с задержкой.

Параметры и правила сервиса BlockVectra

BlockVectra обслуживает HyperEVM mainnet через эндпоинты JSON-RPC и REST Data API:

  1. Параметры сети и лимиты логов: Из GET /v1/chains для hyperevm_mainnet:
    • Идентификатор сети (слаг): hyperevm_mainnet, Chain ID 999.
    • max_logs_block_range: Регулируется полем max_logs_block_range из GET /v1/chains. Один запрос eth_getLogs может охватывать не более этого количества блоков (toBlock − fromBlock + 1). Превышение этого диапазона возвращает HTTP 200 с кодом ошибки JSON-RPC -32602 (eth_getLogs block range too large: max <N> blocks), которая не тарифицируется.
    • state_window_blocks: Регулируется полем state_window_blocks из GET /v1/chains. Вызовы, считывающие состояние (такие как 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 и всплеск (burst) 1,600 CU). Потребление методов измеряется по весам Compute Units (CU).
    • Повышение лимитов: После пополнения счета ограничение вызовов в секунду на уровне аккаунта снимается; каждый ключ по-прежнему подчиняется лимитам скорости Compute Units (CU) и всплеска. Актуальные тарифы и расчетные единицы см. на странице цен.

Выгрузка исторических логов: составной eth_getLogs и логика повторов

При запросе исторических логов широкие интервалы должны разбиваться на непрерывные фрагменты, ограниченные параметром max_logs_block_range целевой сети. Стратегии повторных попыток на стороне клиента должны проверять поле retryable в ответах с ошибками.

Оценка поля retryable в ответах с ошибками

В BlockVectra объекты ошибок JSON-RPC содержат данные error.data с полями reason, docs_url и retryable (boolean):

  • 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
    }
  }
}

Использование эндпоинтов Data API вместо масштабного сканирования getLogs

Когда приложению требуется отслеживать историю транзакций или движение токенов для определенного адреса, сканирование через eth_getLogs требует отправки последовательных составных запросов, ограниченных max_logs_block_range, и парсинга исходных логов событий Transfer.

Data API BlockVectra предоставляет предварительно проиндексированные эндпоинты REST для hyperevm_mainnet, поддерживающие окна до 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 (amount для ERC-20, token_id для ERC-721).
  • 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-приемник используйте Push через вебхуки.

Для проверки развернутого контракта Hello укажите его адрес в LOG_ADDRESS. Отправьте ping() через транслирующий RPC, затем выгрузите блок квитанции с помощью скрипта выгрузки на этой странице. Для новых событий продолжайте с последнего завершенного фрагмента.

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) и повторяйте опрос с перекрытием после повторного подключения для согласования реорганизаций.
  4. Функции viem watchBlockNumber или watchBlocks нативно реализуют опрос по HTTP при использовании HTTP-транспорта, позволяя настраивать параметр pollingInterval (например, 1000 мс).

Опрос логов событий ограниченными фрагментами

Сохраните как poll-logs.mjs рядом с network.mjs и viem-client.mjs. Задайте BLOCKVECTRA_API_KEY, LOG_ADDRESS и FROM_BLOCK, затем запустите node poll-logs.mjs. Этот конечный пример проверяет вершину цепи 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; надежные потребители должны сохранять события и курсор вместе, выполнять дедупликацию и согласовывать реорганизации цепи, как описано выше. При ошибках 429 или других повторяемых сбоях применяйте рекомендации по ограниченной экспоненциальной задержке к тому же незавершенному фрагменту.

Следующие шаги

Последнее обновление:

На этой странице

Прямой ответЗадачи, которые помогает решить это руководствоЗадача из трех шагов: выгрузка ограниченного окна логов HyperEVM1. Чтение последнего блока без API key2. Создание API key3. Выгрузка логов с вашим ключомПодключение с помощью viem или ethersРазвертывание с помощью Foundry или HardhatHYPE, малые блоки и крупные развертыванияДанные HyperCore и HyperEVMОбработка официальной ошибки 10055Лимиты частоты запросов официального публичного RPC и ошибки 429Параметры и правила сервиса BlockVectraВыгрузка исторических логов: составной eth_getLogs и логика повторовОценка поля retryable в ответах с ошибкамиИспользование эндпоинтов Data API вместо масштабного сканирования getLogsСтруктура ответаПример кода: запросы к Data APIОтслеживание в реальном времени: опрос новых блоковПроцесс опросаОпрос логов событий ограниченными фрагментамиСвязанные руководстваСледующие шаги