Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Solución de problemas de RPC12 min de lectura

Tiempos de espera de RPC en Hyperliquid: límites de tasa, endpoints HyperEVM y reintentos confiables

Los tiempos de espera de RPC en Hyperliquid generalmente se deben al límite de ~100 req/min por IP del endpoint público de HyperEVM. Aprende a diagnosticar, evitar y manejar los tiempos de espera con caché, backoff y reintentos idempotentes.

TL;DR

Los tiempos de espera de RPC en Hyperliquid son típicamente causados por el estricto límite de tasa por IP (~100 req/min) del endpoint público de HyperEVM, que detiene o descarta solicitudes cuando se excede. Este artículo explica las dos pilas de Hyperliquid (API L1 info/exchange vs RPC HyperEVM), cómo diagnosticar tiempos de espera vs 429 vs órdenes rechazadas, y cómo construir clientes resilientes con caché, batching, tiempos de espera explícitos, backoff exponencial y reintentos idempotentes. Incluye un ejemplo ejecutable en Node.js y una lista de verificación para producción.

Respuesta directa: Por qué las solicitudes RPC de Hyperliquid agotan el tiempo

Los tiempos de espera de RPC en Hyperliquid ocurren porque el endpoint público de RPC de HyperEVM está limitado a una tasa estricta de alrededor de 100 solicitudes por minuto por IP (según informan Chainstack y otros proveedores). Cuando superas ese límite, las solicitudes se detienen o se descartan, lo que se manifiesta como tiempos de espera en el lado del cliente. Esto es separado de la API L1 info/exchange de Hyperliquid, que tiene sus propios límites de tasa y comportamiento diferente.

En esta guía, aprenderás la arquitectura detrás de las dos pilas de Hyperliquid, cómo diagnosticar si estás alcanzando un límite de tasa o un problema de red, y cómo construir un cliente resiliente que evite tiempos de espera y los maneje con gracia cuando ocurran.

  • El RPC público de HyperEVM está limitado a ~100 req/min por IP (reportado por el proveedor).
  • Exceder el límite causa que las solicitudes se detengan o se descarten, lo que lleva a tiempos de espera.
  • La API L1 info/exchange es separada y tiene límites diferentes.
  • Usa caché, batching y feeds WebSocket para mantenerse bajo los límites.
  • Implementa tiempos de espera explícitos, backoff exponencial y reintentos idempotentes.

Dos pilas, dos comportamientos de tiempo de espera diferentes

Hyperliquid opera dos interfaces distintas que a menudo se confunden: la API L1 nativa (endpoints info y exchange) y el RPC HyperEVM (un endpoint JSON-RPC compatible con Ethereum). Tienen diferentes límites de tasa, perfiles de latencia y características de tiempo de espera.

La API L1 info (por ejemplo, /info, /exchange) está diseñada para datos de mercado de alta frecuencia y colocación de órdenes. Utiliza una interfaz HTTP POST simple y tiene límites de tasa documentados (consulta la guía de límites de tasa de la API de Hyperliquid). El RPC HyperEVM, por otro lado, es un endpoint JSON-RPC estándar de Ethereum que admite eth_call, eth_getBalance y métodos similares. Está sujeto a un límite de tasa por IP mucho más estricto, a menudo citado como ~100 solicitudes por minuto.

Cuando superas el límite de HyperEVM, el endpoint puede devolver HTTP 429 o simplemente colgarse hasta que tu cliente agote el tiempo. En la práctica, muchos clientes ven tiempos de espera antes de que se devuelva un 429, porque el servidor pone en cola o descarta solicitudes bajo carga. Por eso 'tiempo de espera' es el síntoma más común, no 'límite de tasa excedido'.

  • API L1 info/exchange: límites de tasa separados, diseñada para datos de mercado y trading.
  • RPC HyperEVM: compatible con Ethereum, límite estricto por IP (~100 req/min).
  • Los tiempos de espera a menudo ocurren antes de que se devuelva un 429, debido a la cola/descartes de solicitudes.
  • La latencia de red y la proximidad geográfica también afectan la probabilidad de tiempo de espera.

Diagnóstico de tiempos de espera vs 429 vs órdenes rechazadas

Antes de corregir los tiempos de espera, necesitas identificar correctamente lo que está sucediendo. Un tiempo de espera es cuando tu cliente deja de esperar una respuesta. Un 429 es una respuesta explícita de límite de tasa. Una orden rechazada es un rechazo a nivel de aplicación (por ejemplo, margen insuficiente, precio inválido). Cada uno requiere una respuesta diferente.

Aquí hay una lista de verificación de diagnóstico:

  1. Verifica el código de estado HTTP: 429 significa límite de tasa; 5xx significa error del servidor; tiempo de espera significa sin respuesta.

  1. Inspecciona el cuerpo de la respuesta: Hyperliquid puede devolver un error JSON con un código y mensaje.

  1. Monitorea tu tasa de solicitudes: registra marcas de tiempo y cuenta las solicitudes por minuto por IP.

  1. Prueba con un curl simple al endpoint público para ver si responde en absoluto.

  1. Compara la latencia desde diferentes regiones: usa una herramienta como ping o un servicio geo-distribuido para ver si la proximidad importa.

  • Tiempo de espera: sin respuesta dentro de la ventana de tiempo de espera de tu cliente.
  • 429: respuesta explícita de límite de tasa (HTTP 429).
  • Orden rechazada: error a nivel de aplicación, a menudo con un código de razón.
  • Usa registro y métricas para distinguir estos casos.

Mantenerse bajo el límite de tasa: caché, batching y WebSockets

La forma más efectiva de evitar tiempos de espera es mantenerse bajo el límite de tasa por IP. Para el RPC HyperEVM, esto significa reducir el número de solicitudes que haces. Las estrategias incluyen:

Caché: almacena en caché las respuestas para datos que no cambian con frecuencia (por ejemplo, saldos de tokens, estado de contratos). Usa un TTL corto (por ejemplo, 5-10 segundos) para equilibrar la frescura.

Batching: JSON-RPC admite solicitudes por lotes. Combina múltiples llamadas eth_call o eth_getBalance en una sola solicitud HTTP. Esto cuenta como una solicitud contra el límite de tasa.

Usa feeds WebSocket: para datos de mercado, usa los feeds WebSocket nativos de L1 (por ejemplo, allMids, l2Book) en lugar de consultar el RPC EVM. Esto reduce drásticamente el número de solicitudes.

Para la API L1 info, se aplican principios similares: usa los endpoints /info con parámetros apropiados para obtener todos los datos en una llamada, y usa suscripciones WebSocket para actualizaciones en tiempo real.

  • Almacena en caché datos inmutables o que cambian lentamente.
  • Agrupa múltiples llamadas JSON-RPC en una sola solicitud.
  • Prefiere feeds WebSocket para datos de mercado.
  • Usa los endpoints /info nativos para datos L1.
  • Monitorea tu tasa de solicitudes para mantenerte bajo los límites.

Ejemplo ejecutable: bucle de solicitudes limitado con backoff e idempotencia

A continuación se muestra un script Node.js autónomo que demuestra cómo interactuar con el RPC HyperEVM respetando los límites de tasa. Incluye un limitador de tasa simple, backoff exponencial y una protección de idempotencia para el envío de órdenes (aunque el envío de órdenes está en la API L1, el patrón se aplica). El script usa el endpoint público (https://api.hyperliquid.xyz) para L1 y (https://api.hyperliquid.xyz/evm) para RPC EVM.

El script hace lo siguiente:

  • Define un limitador de tasa que permite un número configurable de solicitudes por minuto.

  • Implementa una función fetchWithRetry que reintenta en tiempo de espera o 429 con backoff exponencial.

  • Muestra un ejemplo de envío de orden idempotente usando un ID de orden proporcionado por el cliente.

Ejecútalo con Node.js (v18+). Mostrará el resultado de una llamada simple eth_blockNumber y un envío de orden simulado.

  • Limitador de tasa: token bucket para hacer cumplir las solicitudes por minuto.
  • Backoff exponencial: reintento con retraso creciente (por ejemplo, 1s, 2s, 4s).
  • Idempotencia: incluye un ID de orden único del cliente para prevenir órdenes duplicadas.
  • Nunca reintentes automáticamente la colocación de órdenes sin idempotencia.
// hyperliquid-rpc-timeout-example.js
// Ejecutar con: node hyperliquid-rpc-timeout-example.js

const https = require('https');

// Configuración
const EVM_RPC_URL = 'https://api.hyperliquid.xyz/evm';
const L1_API_URL = 'https://api.hyperliquid.xyz';
const RATE_LIMIT_PER_MINUTE = 90; // mantenerse bajo el límite de ~100
const TIMEOUT_MS = 5000;
const MAX_RETRIES = 3;

// Limitador de tasa simple de token bucket
class RateLimiter {
  constructor(ratePerMinute) {
    this.rate = ratePerMinute / 60; // por segundo
    this.tokens = ratePerMinute;
    this.lastRefill = Date.now();
  }

  async waitForToken() {
    while (true) {
      const now = Date.now();
      const elapsed = (now - this.lastRefill) / 1000;
      this.tokens = Math.min(this.rate, this.tokens + elapsed * this.rate);
      this.lastRefill = now;
      if (this.tokens >= 1) {
        this.tokens -= 1;
        return;
      }
      await sleep(100);
    }
  }
}

const limiter = new RateLimiter(RATE_LIMIT_PER_MINUTE);

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function postJson(url, body) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify(body);
    const options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(data)
      },
      timeout: TIMEOUT_MS
    };
    const req = https.request(url, options, (res) => {
      let responseBody = '';
      res.on('data', chunk => responseBody += chunk);
      res.on('end', () => {
        resolve({ status: res.statusCode, body: responseBody });
      });
    });
    req.on('timeout', () => {
      req.destroy(new Error('Request timed out'));
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

async function fetchWithRetry(url, body, idempotencyKey = null) {
  let attempt = 0;
  while (attempt <= MAX_RETRIES) {
    await limiter.waitForToken();
    try {
      const headers = {};
      if (idempotencyKey) headers['X-Idempotency-Key'] = idempotencyKey;
      const res = await postJson(url, body);
      if (res.status === 429) {
        // Limitado por tasa, reintentar con backoff
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Limitado por tasa (429). Reintentando en ${delay}ms`);
        await sleep(delay);
        attempt++;
        continue;
      }
      if (res.status >= 500) {
        // Error del servidor, reintentar
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Error del servidor (${res.status}). Reintentando en ${delay}ms`);
        await sleep(delay);
        attempt++;
        continue;
      }
      return JSON.parse(res.body);
    } catch (err) {
      if (err.message === 'Request timed out') {
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Tiempo de espera. Reintentando en ${delay}ms`);
        await sleep(delay);
        attempt++;
        continue;
      }
      throw err;
    }
  }
  throw new Error('Máximo de reintentos excedido');
}

async function main() {
  // Ejemplo 1: Llamada RPC EVM simple (eth_blockNumber)
  console.log('Obteniendo el número de bloque actual del RPC HyperEVM...');
  const blockNumber = await fetchWithRetry(EVM_RPC_URL, {
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  });
  console.log('Número de bloque (hex):', blockNumber.result);

  // Ejemplo 2: Envío de orden simulado con clave de idempotencia
  // En producción, usa el endpoint L1 /exchange con un payload firmado.
  console.log('\nSimulando envío de orden con idempotencia...');
  const orderPayload = {
    action: {
      type: 'order',
      orders: [{
        a: 1, // índice de activo
        b: 100, // precio
        s: '0.1', // tamaño
        r: false, // solo reducir
        t: { limit: { tif: 'Gtc' } }
      }]
    },
    nonce: Date.now(),
    signature: '0x...' // sería una firma real
  };
  const idemKey = `order-${Date.now()}`;
  try {
    const result = await fetchWithRetry(L1_API_URL + '/exchange', orderPayload, idemKey);
    console.log('Respuesta de la orden:', result);
  } catch (err) {
    console.error('La orden falló después de los reintentos:', err.message);
  }
}

main().catch(err => {
  console.error('Error fatal:', err);
  process.exit(1);
});

// Salida esperada (forma):
// Obteniendo el número de bloque actual del RPC HyperEVM...
// Número de bloque (hex): 0x123456
// Simulando envío de orden con idempotencia...
// Respuesta de la orden: { status: 'ok', response: { type: 'order', ... } }

Fallos comunes y soluciones

Incluso con un diseño cuidadoso, puedes encontrar problemas. Aquí hay modos de fallo comunes y cómo solucionarlos:

  1. Tiempo de espera en eth_call: Esto a menudo sucede cuando el RPC EVM está bajo carga. Reduce el número de solicitudes eth_call almacenando en caché los resultados o usando llamadas por lotes. Si necesitas datos en tiempo real, considera usar los feeds WebSocket de L1.

  1. 429 Demasiadas solicitudes: Esto es explícito. Implementa backoff exponencial y respeta el encabezado Retry-After si está presente. Además, reduce tu tasa de solicitudes.

  1. Rechazo de orden debido a problemas de nonce o firma: Esto no es un tiempo de espera sino un error de aplicación. Asegúrate de que tu nonce sea único y tu firma sea correcta. Usa claves de idempotencia para evitar órdenes duplicadas.

  1. Latencia de red desde regiones distantes: Si estás lejos de los servidores de Hyperliquid, la latencia puede causar tiempos de espera. Usa un proveedor con caché de borde global o implementa un nodo más cercano al exchange.

  • Tiempos de espera en eth_call: caché y batching.
  • 429: backoff y reducir la tasa.
  • Rechazo de orden: verificar nonce y firma.
  • Latencia: usar proveedores geo-distribuidos o colocalización.

Compensaciones y limitaciones

Si bien las estrategias anteriores ayudan, hay compensaciones:

El almacenamiento en caché introduce desactualización. Para datos de mercado, unos segundos de retraso pueden ser aceptables, pero para datos de libro de órdenes, no lo son. Usa feeds WebSocket para datos en tiempo real.

El batching aumenta la complejidad. Necesitas mapear las respuestas a las solicitudes, y algunos endpoints pueden no admitir batching.

Los reintentos pueden amplificar la carga. Si muchos clientes reintentan simultáneamente, pueden causar una manada atronadora. Usa jitter en tu backoff.

El límite de ~100 req/min es reportado por el proveedor y puede variar. Siempre prueba tus límites reales.

Para carga de producción sostenida, se recomienda un endpoint dedicado o un nodo colocalizado. Consulta Precios de RPC y Servicio de API para opciones.

  • Caché: compensación entre frescura y límite de tasa.
  • Batching: complejidad y compatibilidad.
  • Reintentos: usar jitter para evitar la manada atronadora.
  • Límites de tasa: varían según el proveedor; prueba los tuyos.
  • Producción: considerar endpoints dedicados.

Próximos pasos y lecturas adicionales

Ahora que entiendes los tiempos de espera de RPC en Hyperliquid, puedes construir aplicaciones más confiables. Aquí hay algunos próximos pasos:

Revisa la guía de límites de tasa de la API de Hyperliquid para una inmersión profunda en los límites de L1.

Explora los endpoints RPC de Hyperliquid (Asistente de RPC) para encontrar el endpoint adecuado para tu caso de uso.

Consulta la página de red de Hyperliquid para detalles de red.

Si necesitas una solución gestionada, consulta nuestro Servicio de API y Precios de RPC.

Para más guías de solución de problemas, visita el centro de aprendizaje de OnFinality.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar