Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Guías de red y protocolo12 min de lectura

Estimación del precio del gas con eth_feeHistory: percentiles de tarifa base y tarifa prioritaria

Aprende a leer eth_feeHistory y construye un estimador de gas EIP-1559 robusto utilizando percentiles de tarifa base y tarifa prioritaria.

TL;DR

Después de la bifurcación de Londres, las tarifas de transacción de Ethereum se dividieron en una tarifa base determinista y una tarifa prioritaria opcional. El método RPC eth_feeHistory devuelve las tarifas base por bloque y, cuando se solicita, los percentiles de tarifa prioritaria de las transacciones incluidas. Este artículo explica cómo leer esos datos y combinarlos en una estimación robusta del precio del gas que evite pagar de más o depender de un único oráculo eth_gasPrice.

El mercado de tarifas EIP-1559 en la práctica

Desde la bifurcación de Londres, el precio de las transacciones de Ethereum ya no es una subasta de gasPrice única. Cada bloque tiene un baseFeePerGas que se determina algorítmicamente según el gas utilizado en el bloque anterior. Esta tarifa base se quema, no se paga a los mineros, y puede ajustarse hacia arriba o hacia abajo en un máximo del 12.5% por bloque mientras intenta mantener el uso de gas del bloque cerca de un objetivo. Los usuarios luego agregan una tarifa prioritaria opcional (a menudo llamada propina) para incentivar a los validadores a incluir su transacción. La tarifa total está limitada por el maxFeePerGas que establezcas, y la parte prioritaria está limitada por maxPriorityFeePerGas.

La especificación oficial de la API de ejecución de Ethereum define el método eth_feeHistory, que devuelve un historial de tarifas base y ratios de uso de gas para un rango de bloques. Cuando solicitas rewardPercentiles, el cliente escanea las transacciones incluidas en esos bloques y devuelve las tarifas prioritarias pagadas en los percentiles solicitados. Este es el material bruto para un estimador de gas basado en datos.

Para una visión general rápida de la red Ethereum y sus endpoints RPC, consulta la página de red de Ethereum.

Leyendo la respuesta de eth_feeHistory

El método eth_feeHistory toma tres parámetros: blockCount (el número de bloques a obtener), newestBlock (el número de bloque más alto o la etiqueta latest), y rewardPercentiles (una matriz de percentiles, por ejemplo, [25, 50, 75, 90]). La respuesta contiene tres campos clave: baseFeePerGas (una matriz de tarifas base para cada bloque, más una adicional para el siguiente bloque), gasUsedRatio (la proporción de gas utilizado respecto al límite de gas para cada bloque), y reward (una matriz de matrices, cada una conteniendo la tarifa prioritaria en los percentiles solicitados para ese bloque).

El campo reward solo se completa cuando solicitas rewardPercentiles. Si lo omites, el cliente devuelve una matriz vacía. Además, la forma en que los clientes calculan estos percentiles puede variar: algunos escanean todas las transacciones, otros muestrean, y los proveedores de RPC públicos pueden limitar la profundidad del historial o incluso omitir los datos de recompensa para reducir la carga. Este es un comportamiento documentado que varía según el cliente y el proveedor; siempre prueba con tu endpoint específico.

La matriz baseFeePerGas tiene un elemento más que el número de bloques solicitados. El último elemento es la tarifa base del bloque que seguiría al bloque más reciente, asumiendo que no hay cambio en el uso de gas. Esto es útil para proyectar la tarifa base del siguiente bloque.

Construyendo un estimador simple con Node.js

El siguiente script llama a eth_feeHistory para los últimos 10 bloques, imprime las tarifas base y las proporciones de uso de gas, y luego calcula un par sugerido de maxFeePerGas y maxPriorityFeePerGas. El método de estimación es deliberadamente simple: toma el percentil 90 de la tarifa prioritaria del historial y agrega un margen del 12.5% a la tarifa base promedio para tener en cuenta posibles aumentos.

Puedes ejecutar este script con cualquier endpoint RPC de Ethereum, incluido uno público o tu propio nodo. Reemplaza YOUR_RPC_URL con tu endpoint. El script utiliza la API fetch disponible en Node.js 18+.

const RPC_URL = 'YOUR_RPC_URL';

async function rpc(method, params) {
  const res = await fetch(RPC_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
  });
  const data = await res.json();
  if (data.error) throw new Error(data.error.message);
  return data.result;
}

async function estimateGas() {
  const blockCount = 10;
  const newestBlock = 'latest';
  const rewardPercentiles = [25, 50, 75, 90];

  const history = await rpc('eth_feeHistory', [blockCount, newestBlock, rewardPercentiles]);

  console.log('Base fees per block:');
  console.log(history.baseFeePerGas);
  console.log('Gas used ratios:');
  console.log(history.gasUsedRatio);
  console.log('Reward percentiles per block:');
  console.log(history.reward);

  // Compute average base fee from the first blockCount entries (exclude the extra next-block estimate)
  const baseFees = history.baseFeePerGas.slice(0, blockCount).map(hex => parseInt(hex, 16));
  const avgBaseFee = baseFees.reduce((a, b) => a + b, 0) / baseFees.length;

  // Take the 90th percentile priority fee from the last block that has data
  const lastRewards = history.reward[history.reward.length - 1];
  const p90Priority = lastRewards ? parseInt(lastRewards[3], 16) : 0; // index 3 corresponds to 90th percentile

  // Add 12.5% headroom to base fee for possible increase
  const projectedBaseFee = Math.ceil(avgBaseFee * 1.125);
  const maxPriorityFeePerGas = p90Priority;
  const maxFeePerGas = projectedBaseFee + maxPriorityFeePerGas;

  console.log(`\nSuggested maxPriorityFeePerGas: ${maxPriorityFeePerGas}`);
  console.log(`Suggested maxFeePerGas: ${maxFeePerGas}`);
}

estimateGas().catch(console.error);

Salida esperada y cómo interpretarla

Cuando ejecutes el script, verás una salida similar a la siguiente (los valores son ejemplos, no mediciones):

Las tarifas base están en wei (hexadecimal). La proporción de gas utilizado te indica qué tan lleno estaba cada bloque: una proporción superior a 0.5 significa que la tarifa base probablemente aumentará en el siguiente bloque, mientras que por debajo de 0.5 significa que disminuirá. Los percentiles de recompensa muestran las tarifas prioritarias pagadas por las transacciones en cada percentil. Por ejemplo, el valor del percentil 90 significa que el 90% de las transacciones pagaron esa propina o menos.

Para validar el estimador, puedes registrar tus propios resultados en una tabla como esta:

Rango de bloquesTarifa base promedio (Gwei)Propina percentil 90 (Gwei)maxFee sugerido (Gwei)Tarifa base real del siguiente bloque (Gwei)
10 bloques201.523.520.1
20 bloques192.023.419.8

Base fees per block:
["0x3b9aca00", "0x3b9aca00", ...]
Gas used ratios:
[0.5, 0.6, ...]
Reward percentiles per block:
[["0x3b9aca00", "0x4a817c80", ...], ...]

Suggested maxPriorityFeePerGas: 1000000000
Suggested maxFeePerGas: 2000000000

Elección de percentiles y horizonte

La elección de rewardPercentiles y el número de bloques a obtener depende de tu tolerancia al tiempo de confirmación versus el costo. Un enfoque común es usar los percentiles 25, 50, 75 y 90. Si necesitas inclusión rápida, usa la propina del percentil 90; si estás dispuesto a esperar, el percentil 50 podría ser suficiente.

El horizonte (blockCount) debe reflejar las condiciones recientes de la red. Un horizonte corto (por ejemplo, 5-10 bloques) reacciona rápidamente a los cambios pero puede ser ruidoso. Un horizonte más largo (por ejemplo, 50-100 bloques) suaviza los picos a corto plazo pero puede quedarse atrás durante la congestión. Para la mayoría de las aplicaciones, 10-20 bloques es un equilibrio razonable.

Recuerda que la tarifa base puede cambiar hasta un 12.5% por bloque. Si estás enviando una transacción que podría no ser minada de inmediato, debes proyectar la tarifa base sobre el número esperado de bloques hasta la inclusión. Un método simple es tomar la tarifa base promedio de los últimos N bloques y agregar un margen de seguridad, como se muestra en el script.

Evitando contar dos veces la tarifa base

Un error común es agregar el percentil de tarifa prioritaria a la tarifa base actual y establecer eso como maxFeePerGas. Sin embargo, las tarifas prioritarias reportadas por eth_feeHistory son las propinas que se pagaron además de la tarifa base en ese momento. Si la tarifa base ha aumentado desde entonces, la tarifa total real pagada por esas transacciones históricas fue mayor, pero la propina sola es lo que necesitas agregar a la tarifa base proyectada.

En otras palabras, tu maxFeePerGas debe ser la tarifa base proyectada (con margen) más tu tarifa prioritaria elegida. La tarifa prioritaria es la propina que estás dispuesto a pagar, independiente de la tarifa base. Esta es la forma correcta de presupuestar transacciones EIP-1559.

Para una inmersión más profunda en la simulación de transacciones y anulaciones de estado, consulta nuestra guía sobre anulaciones de estado y simulación con eth_call.

Cuándo usar eth_maxPriorityFeePerGas

Muchos clientes proporcionan un método de conveniencia eth_maxPriorityFeePerGas que devuelve una propina sugerida basada en las condiciones recientes de la red. Este es un atajo rápido, pero no incluye la tarifa base. Aún necesitas establecer maxFeePerGas tú mismo, típicamente agregando la propina sugerida a una tarifa base proyectada.

Usar eth_maxPriorityFeePerGas puede ser un respaldo razonable si no quieres implementar el muestreo de percentiles tú mismo, pero es menos transparente y puede no reflejar tus requisitos específicos de inclusión. Para una billetera de producción, podrías combinar ambos: usar eth_feeHistory para la proyección de la tarifa base y eth_maxPriorityFeePerGas como verificación de cordura.

Limitaciones y compensaciones

La tarifa base no es un oráculo de precios para la inclusión. Una tarifa base baja no garantiza que tu transacción sea incluida si la red está congestionada; aún necesitas una tarifa prioritaria competitiva. Además, el límite de cambio del 12.5% por bloque se aplica a bloques consecutivos, pero a lo largo de muchos bloques la tarifa base puede moverse significativamente, por lo que tu proyección debe tenerlo en cuenta.

Diferentes tipos de transacción requieren un manejo diferente. Las transacciones heredadas de tipo 0 usan un solo gasPrice que cubre tanto la tarifa base como la propina. Las transacciones de tipo 2 (EIP-1559) usan maxFeePerGas y maxPriorityFeePerGas. Si estás enviando una transacción heredada, no puedes usar eth_feeHistory directamente; necesitas estimar un gasPrice que sea lo suficientemente alto para cubrir la tarifa base más una propina.

Las reorganizaciones pueden cambiar el historial que obtuviste. Si un bloque se reorganiza, la tarifa base y las recompensas en ese bloque pueden cambiar. Para aplicaciones críticas, es posible que desees esperar algunas confirmaciones antes de confiar en los datos.

Los proveedores de RPC públicos pueden tener diferentes límites en blockCount o pueden no soportar rewardPercentiles en todas las redes. Algunas redes, como ciertas capas 2, pueden no implementar EIP-1559 o tener mecanismos de tarifas diferentes. Siempre consulta la documentación de tu red específica.

Solución de problemas comunes con eth_feeHistory

Si encuentras problemas, aquí hay problemas comunes y soluciones:

  • Campos de recompensa vacíos: Esto sucede cuando no solicitas rewardPercentiles o cuando el cliente/proveedor no soporta el muestreo de percentiles. Intenta agregar rewardPercentiles a tu solicitud, o usa un proveedor de RPC diferente.

  • HTTP 415 Unsupported Media Type: Algunas redes o proveedores pueden no soportar eth_feeHistory en absoluto. Consulta la documentación de la red o usa un método alternativo como eth_gasPrice como respaldo.

  • Límites de blockCount: Algunos proveedores limitan el número de bloques que puedes solicitar en una sola llamada. Si obtienes un error, reduce blockCount y haz múltiples llamadas si es necesario.

  • Hex vs decimal: Recuerda que todos los valores numéricos en la respuesta están codificados en hexadecimal. Usa parseInt con base 16 para convertir.

Para más sobre la fiabilidad de RPC, consulta nuestras guías sobre tiempos de espera y reintentos de RPC de Ethereum y latencia y rendimiento de RPC de Ethereum.

Próximos pasos y lecturas adicionales

Ahora que entiendes cómo leer eth_feeHistory, puedes construir un estimador más sofisticado que se adapte a las condiciones de la red. Considera integrar esto en tu billetera o relé para reducir el sobrepago. Para una comprensión más amplia del RPC de Ethereum, explora el centro de aprendizaje de OnFinality y la documentación del servicio de API.

Si estás eligiendo un proveedor de nodos de Ethereum, nuestra guía para elegir un nodo RPC de Ethereum puede ayudarte a evaluar características como el soporte de eth_feeHistory. También revisa precios de RPC para entender las implicaciones de costo de las llamadas de alta frecuencia.

Para temas relacionados, consulta nuestras guías sobre gestión de nonce EVM con eth_getTransactionCount y anulaciones de estado y simulación con eth_call.

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