eth_estimateGas ejecuta una simulación contra un estado concreto de la cadena y devuelve el gas que consumió esa simulación, no un límite garantizado para su inclusión on-chain. Las estimaciones fallan por cuatro motivos principales: la deriva de estado entre la simulación y la inclusión, llamadas que tienen éxito en la simulación pero revierten en un bloque cambiado, diferencias entre proveedores a la hora de respetar el parámetro opcional de bloque y las anulaciones de estado, y la terminación anticipada en caso de revert, que informa del gas usado hasta el punto de fallo. Un límite seguro se deriva multiplicando la estimación por un factor de seguridad configurable, acotándolo contra el límite de gas del bloque y comprobando la asequibilidad frente al saldo del remitente. eth_call devuelve datos de retorno y revierte en caso de fallo, mientras que eth_estimateGas devuelve una cantidad y da error cuando la llamada revierte, por lo que ambas son herramientas de verificación complementarias. El suelo de gas intrínseco de 21000 más el coste de calldata y de creación de contrato debe incluirse en cualquier límite.
Qué devuelve realmente eth_estimateGas
La especificación JSON-RPC de Ethereum define eth_estimateGas como un método que genera y devuelve una estimación del gas necesario para que una transacción se complete. La palabra clave es estimación: el nodo ejecuta la llamada contra un estado concreto y devuelve el gas que consumió esa ejecución, lo que es una medición de una simulación, no una promesa sobre un bloque futuro. La especificación también documenta un parámetro de bloque opcional, por lo que la misma llamada puede evaluarse en latest, en un bloque histórico fijado o en pending.
Cuando pasas un objeto de transacción completo, la cantidad devuelta incluye el coste intrínseco de la transacción, que el Yellow Paper de Ethereum fija en 21000 gas para una transferencia simple más los costes de calldata y de creación de contrato. Cuando pasas solo un objeto de llamada sin un from ni un nonce, algunos clientes lo tratan como una llamada y pueden no añadir el coste intrínseco completo. Esta ambigüedad es una de las fuentes más comunes de un límite demasiado bajo, y es la razón por la que el objeto de parámetros que envías importa tanto como el número que recibes.
Como el resultado es una simulación, refleja el estado en el bloque que solicitaste. Si ese estado cambia antes de que se incluya tu transacción, la estimación puede ser incorrecta aunque el nodo haya respondido correctamente. Trata el número como una entrada para el cálculo del límite, no como el límite en sí.
El contrato del método que se usa en todo el artículo lo define la especificación JSON-RPC de Ethereum para eth_estimateGas y eth_call, la separación entre el límite de gas y los parámetros de comisión proviene de EIP-1559, y el suelo de gas intrínseco (21000 más el coste de calldata) se define en el Yellow Paper de Ethereum. Toma esos documentos como fuente de verdad y este artículo como el procedimiento operativo construido sobre ellos.
- Devuelve una cantidad de gas consumida por una ejecución simulada en un estado dado.
- Incluye los 21000 de gas intrínseco cuando se proporciona un objeto de transacción completo.
- Respeta un parámetro de bloque opcional, por lo que latest y un bloque fijado pueden diferir.
- No es una garantía de éxito on-chain; es la medición de una simulación.
Los cuatro motivos por los que una estimación de gas es incorrecta
La deriva de estado es la primera causa. La estimación se toma en el bloque N, pero tu transacción puede incluirse en el bloque N+3 después de que otras transacciones hayan cambiado saldos, allowances o el almacenamiento del contrato. Un swap que estimó 150000 gas en el bloque N puede necesitar más en el bloque N+3 si la ratio de un pool se movió y se ejecutó una rama distinta. Este es un comportamiento documentado del protocolo: la estimación solo es tan buena como el estado en el que se tomó.
La segunda causa es una llamada que tiene éxito en la simulación pero revierte en un bloque cuyo estado ya cambió. Una transacción que depende de otra transacción pendiente, como una aprobación que aún no se ha minado, estima contra un estado en el que la aprobación no existe. La simulación puede seguir teniendo éxito si el contrato tolera la falta de allowance, pero la transacción real puede revertir cuando la dependencia se resuelve de forma distinta.
La tercera causa son las diferencias entre proveedores. La especificación JSON-RPC de Ethereum documenta el parámetro de bloque opcional y los conjuntos de anulación de estado, pero si un proveedor concreto los respeta, y cómo trata un campo from ausente, varía según el proveedor. Algunos endpoints ignoran un bloque fijado y siempre estiman en latest; otros rechazan las anulaciones de estado. Confirma siempre el comportamiento de tu endpoint en lugar de asumir que la especificación está implementada por completo.
La cuarta causa es la terminación anticipada en caso de revert. Cuando la llamada simulada revierte, el nodo informa del gas consumido hasta el punto de revert, no del gas que necesitaría la ruta exitosa. Si tomas ese número como tu límite, estás presupuestando para un fallo, no para un éxito. Por eso el motivo del revert a menudo solo es visible a través de eth_call con los mismos parámetros.
- La deriva de estado entre la estimación y la inclusión cambia qué rama se ejecuta.
- Una llamada que depende de una transacción pendiente estima contra un estado que no existirá.
- El tratamiento del parámetro de bloque y de las anulaciones de estado varía según el proveedor.
- Un revert detiene la simulación antes de tiempo e informa del gas usado hasta el punto de fallo.
eth_call frente a eth_estimateGas: elegir la sonda correcta
La especificación JSON-RPC de Ethereum describe eth_call como un método que ejecuta inmediatamente una nueva llamada de mensaje sin crear una transacción, devolviendo los datos de retorno de la llamada. Si la llamada revierte, eth_call devuelve un error, y muchos clientes incluyen el motivo del revert en ese error. eth_estimateGas devuelve una cantidad, y también devuelve un error cuando la llamada revierte, pero el error se refiere a la estimación y no al valor de retorno.
Usa eth_call cuando necesites los datos de retorno, cuando quieras mostrar el motivo de un revert o cuando estés comprobando si una ruta tiene éxito en un estado dado. Usa eth_estimateGas cuando necesites una cantidad de gas para construir una transacción. En la práctica se usan ambos: estima para obtener un número de partida y luego llama con los mismos parámetros para confirmar que la ruta tiene éxito y capturar cualquier motivo de revert. El artículo sobre decodificar motivos de revert y errores personalizados de Ethereum cubre cómo convertir esos errores en mensajes legibles.
Un patrón útil es ejecutar primero eth_call con los parámetros exactos que pretendes enviar. Si revierte, corrige la llamada antes de dedicar tiempo a la estimación de gas. Si tiene éxito, ejecuta eth_estimateGas en el mismo bloque y compara. Una gran diferencia entre ambos es una señal de que la estimación se está tomando en un estado distinto o con valores por defecto diferentes.
- eth_call devuelve datos de retorno y da error en caso de revert, exponiendo el motivo.
- eth_estimateGas devuelve una cantidad de gas y da error cuando la llamada revierte.
- Ejecuta primero eth_call para validar la ruta y luego eth_estimateGas para dimensionar el límite.
- Compara ambos en el mismo bloque para detectar discrepancias de estado o de valores por defecto.
El suelo de gas intrínseco y el recargo por creación de contrato
El Yellow Paper de Ethereum define el gas intrínseco como el coste base que paga una transacción antes de que se ejecute cualquier código de contrato. Para una transferencia simple de valor son 21000 gas. La calldata añade un coste por byte, con un coste menor para los bytes cero y uno mayor para los bytes distintos de cero, y la creación de contrato añade un recargo sobre la base. Un límite seguro debe incluir todo esto, porque un límite por debajo del suelo intrínseco falla antes de que comience la ejecución.
Que eth_estimateGas incluya el coste intrínseco depende de los parámetros que pases. Cuando proporcionas un objeto de transacción completo con una dirección from y una dirección to, el nodo puede calcular el coste intrínseco e incluirlo. Cuando proporcionas un objeto de llamada simple, algunos clientes estiman solo el coste de ejecución. Este es un comportamiento documentado en la descripción de parámetros de la especificación, pero el efecto práctico varía según el proveedor, así que verifícalo con una transferencia simple conocida.
Una comprobación rápida de coherencia es estimar una transferencia simple a una cuenta de propiedad externa. Si el resultado se acerca a 21000, el endpoint está incluyendo el gas intrínseco. Si es muy inferior, solo estás viendo el coste de ejecución y debes añadir el suelo tú mismo. Esta única comprobación evita una gran clase de límites demasiado bajos.
- 21000 gas es el coste base de una transferencia simple.
- La calldata añade un coste por byte, con precios distintos para bytes cero y distintos de cero.
- La creación de contrato añade un recargo por encima del coste base y de calldata.
- Verifica si tu endpoint incluye el gas intrínseco estimando una transferencia simple.
Derivar un límite seguro a partir de la estimación
El primer paso es multiplicar la estimación por un factor de seguridad configurable. Un factor de 1,2 a 1,5 es un rango de partida habitual para transferencias simples y llamadas a contratos estables, mientras que las llamadas más dependientes del estado pueden necesitar más. El factor es una decisión de política, no una constante del protocolo, y debe ajustarse con tus propias mediciones en lugar de copiarse de un artículo.
El segundo paso es acotar el resultado contra el límite de gas del bloque. Un límite por encima del límite de gas del bloque nunca podrá incluirse, y un límite cercano a él es señal de que la estimación es incorrecta o de que la llamada es patológicamente costosa. Lee el límite de gas del bloque desde el último bloque y recorta tu límite derivado por debajo de él.
El tercer paso es comprobar la asequibilidad frente al saldo del remitente. Bajo EIP-1559, el límite de gas es el presupuesto y los parámetros de comisión son el precio, y el nodo comprueba que el remitente puede cubrir el límite multiplicado por maxFeePerGas más el value. Un límite generoso inmoviliza saldo aunque solo se cobre el gas usado, por lo que un límite seguro para la ejecución puede aun así hacer que una transacción no sea asequible. El artículo sobre el mercado de comisiones de EIP-1559 cubre el lado del precio, que este artículo mantiene deliberadamente separado del límite.
- Multiplica la estimación por un factor de seguridad configurable, normalmente de 1,2 a 1,5.
- Acota el límite derivado por debajo del límite de gas del bloque actual.
- Comprueba que el saldo cubre el límite por maxFeePerGas más el value.
- Mantén la decisión del límite separada de la decisión del precio de la comisión.
Un estimador ejecutable en Node.js con margen y verificación
El script siguiente llama a eth_estimateGas con un objeto de transacción completo en un bloque fijado, aplica un margen, acota el resultado contra el límite de gas del bloque y luego verifica la misma llamada con eth_call. Solo usa el fetch integrado disponible en Node.js moderno, por lo que no hay dependencias que instalar. Sustituye el endpoint por el tuyo propio de la guía de URL de RPC de Ethereum y selección de endpoints.
El script lee el límite de gas del bloque desde eth_getBlockByNumber en el mismo bloque fijado, de modo que el tope refleja el estado contra el que estimaste. También imprime la estimación bruta, el límite con margen y el límite acotado para que veas cada etapa. Si eth_call devuelve un error, el script imprime el motivo del revert, que es la forma más rápida de distinguir una llamada incorrecta de una estimación incorrecta.
const RPC_URL = process.env.RPC_URL || "https://your-endpoint.example";
const PINNED_BLOCK = process.env.BLOCK || "latest";
const SAFETY_FACTOR = Number(process.env.SAFETY_FACTOR || 1.3);
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 json = await res.json();
if (json.error) throw new Error(method + " -> " + JSON.stringify(json.error));
return json.result;
}
async function main() {
const tx = {
from: "0xYourSenderAddress",
to: "0xRecipientOrContract",
value: "0x0",
data: "0x",
maxFeePerGas: "0x3b9aca00",
maxPriorityFeePerGas: "0x3b9aca00"
};
const block = await rpc("eth_getBlockByNumber", [PINNED_BLOCK, false]);
const blockGasLimit = BigInt(block.gasLimit);
const estimateHex = await rpc("eth_estimateGas", [tx, PINNED_BLOCK]);
const estimate = BigInt(estimateHex);
const buffered = (estimate * BigInt(Math.round(SAFETY_FACTOR * 100))) / 100n;
const capped = buffered > blockGasLimit ? blockGasLimit : buffered;
console.log("raw estimate:", estimate.toString());
console.log("buffered limit:", buffered.toString());
console.log("capped limit:", capped.toString());
try {
const returnData = await rpc("eth_call", [tx, PINNED_BLOCK]);
console.log("eth_call ok, return data:", returnData);
} catch (err) {
console.log("eth_call reverted:", err.message);
}
}
main().catch((e) => { console.error(e.message); process.exit(1); });Una tabla de resultados para completar con tu propio endpoint
El comportamiento de los proveedores varía, así que la única forma fiable de saber cómo trata tu endpoint eth_estimateGas es medirlo. Ejecuta el script anterior con varios tipos de llamada en el mismo bloque fijado, repítelo luego en latest y registra los números. La tabla siguiente es una plantilla; complétala con tus propias mediciones en lugar de fiarte de cualquier cifra publicada.
Las filas más informativas son la transferencia simple y la llamada dependiente del estado. Una transferencia simple cercana a 21000 confirma que se incluye el gas intrínseco. Una llamada dependiente del estado que difiere entre un bloque fijado y latest confirma que se respeta el parámetro de bloque. Si las dos filas son idénticas para una llamada que sabes que depende del estado, puede que tu endpoint esté ignorando el parámetro de bloque.
- Columnas: tipo de llamada, parámetro de bloque, estimación bruta, límite con margen, límite acotado, resultado de eth_call.
- Filas: transferencia simple, transferencia ERC-20, swap dependiente del estado, creación de contrato.
- Repite cada fila en latest y en un bloque fijado para detectar el tratamiento del parámetro de bloque.
- Registra el límite de gas del bloque junto al límite acotado para tener contexto.
Solución de fallos comunes de eth_estimateGas
Un error -32000 execution reverted de eth_estimateGas significa que la llamada simulada falló. El método de estimación devuelve un error en lugar de un número en este caso, y el motivo del revert a menudo solo es visible a través de eth_call con los mismos parámetros. Ejecuta primero eth_call, captura el motivo y corrige la llamada antes de reintentar la estimación. La guía de depuración del error interno -32603 de JSON-RPC cubre la clase adyacente de errores del lado del nodo que no son reverts.
Un error gas required exceeds allowance suele significar que el saldo del remitente no puede cubrir el límite multiplicado por los parámetros de comisión más el value. Es un fallo de asequibilidad, no de ejecución. Reduce el límite si está inflado, o financia la cuenta. Recuerda que bajo EIP-1559 se comprueba el límite completo por adelantado aunque solo se cobre el gas usado.
Una estimación que devuelve el límite de gas del bloque para una ruta con bucle infinito es una señal de que la simulación alcanzó el tope. No envíes ese límite. En su lugar, inspecciona la lógica del contrato, confirma que el bucle termina y, si la ruta es realmente ilimitada, trátalo como un problema de diseño y no de gas. Un motivo de revert que solo es visible a través de eth_call es el cuarto caso común: la estimación informa de un fallo sin el motivo, mientras que eth_call lo muestra.
- -32000 execution reverted: ejecuta eth_call con los mismos parámetros para obtener el motivo.
- gas required exceeds allowance: comprueba el saldo frente al límite por maxFeePerGas más el value.
- Estimación igual al límite de gas del bloque: sospecha de un bucle ilimitado, no lo envíes.
- Motivo de revert ausente en la estimación: eth_call es la sonda que lo expone.
Limitaciones y compromisos de los límites con margen
Un límite generoso inmoviliza saldo. Bajo EIP-1559 el nodo comprueba que el remitente puede cubrir el límite multiplicado por maxFeePerGas más el value, por lo que un límite que duplica lo necesario reserva el doble de saldo durante la transacción. En cuentas que agrupan muchas transacciones, esto puede forzar la serialización o exigir un saldo mayor que el gasto real.
La estimación solo es tan buena como el estado en el que se tomó. Un bloque fijado ofrece reproducibilidad pero se queda obsoleto a medida que avanza la cadena; latest ofrece frescura pero no es reproducible. Ninguno resuelve el problema de dependencia en el que el resultado de tu transacción depende de otra transacción pendiente. En esos casos, considera ordenar tus transacciones explícitamente y volver a estimar después de que se resuelva cada dependencia.
Por último, el factor de seguridad es una política, no una garantía del protocolo. Un factor que es seguro para un contrato puede ser demasiado pequeño para otro, y un factor que es seguro hoy puede ser demasiado pequeño tras una actualización del contrato. Trata el factor como un parámetro ajustable que revisas con tus propias mediciones, y mantén la decisión del límite separada de la decisión del precio de la comisión para poder razonar sobre cada una de forma independiente.
- Un límite alto reserva saldo por adelantado aunque solo se cobre el gas usado.
- Las estimaciones en bloque fijado son reproducibles pero obsoletas; latest es fresco pero no reproducible.
- Las dependencias de transacciones pendientes no las resuelve ningún parámetro de bloque.
- El factor de seguridad es una política ajustable que necesita una nueva medición periódica.
Próximos pasos para el manejo de gas en producción
Empieza instrumentando tu estimador para registrar la estimación bruta, el límite con margen, el límite acotado y el gas realmente usado tras la inclusión. A lo largo de unos cientos de transacciones verás si tu factor de seguridad es demasiado ajustado o demasiado holgado, y podrás ajustarlo con evidencia en lugar de con conjeturas. El artículo sobre percentiles de recompensa de eth_feeHistory cubre el lado del precio del mismo pipeline.
Combina la lógica del límite con la gestión de nonces para que las transacciones dependientes se ordenen correctamente. La guía de gestión de nonces de la EVM explica cómo leer y secuenciar nonces, que es lo que da sentido a volver a estimar después de que se resuelva cada dependencia. Para la selección de endpoints y el failover, revisa la página de la red Ethereum y la descripción general del servicio de API, y consulta los precios de RPC si planeas ejecutar estimaciones de alto volumen. El hub de aprendizaje de OnFinality reúne en un solo lugar los artículos relacionados sobre comisiones, nonces y manejo de errores.
- Registra la estimación bruta, el límite con margen, el límite acotado y el gas realmente usado.
- Ajusta el factor de seguridad a partir de tus propios datos de inclusión.
- Secuencia las transacciones dependientes con una gestión de nonces correcta.
- Elige endpoints que respeten el parámetro de bloque y las anulaciones de estado en las que confías.