eth_sendRawTransaction devuelve errores de saldo y nonce cuando la transacción firmada no supera las comprobaciones previas al envío del nodo. El nodo verifica la firma, compara el nonce de la transacción con el nonce pendiente de la cuenta y comprueba que el saldo cubra gasLimit * tarifa efectiva + value más el mínimo de gas intrínseco. La cadena de error indica directamente qué comprobación falló: 'insufficient funds for gas * price + value' significa que el coste inicial supera el saldo, 'nonce too low' significa que el nonce ya se usó, 'nonce too high' significa que existe un hueco por delante y 'already known' significa que la transacción exacta ya está en el pool. Este artículo proporciona un diagnóstico ejecutable en Node.js que analiza los campos firmados, obtiene el saldo y el nonce pendiente, e imprime qué comprobación falló y por cuánto. También cubre modos de fallo como nodos retrasados, diferencias de mempool y transacciones en vuelo que hacen que un saldo sea correcto en latest pero insuficiente en pending.
Las comprobaciones previas al envío del nodo en orden
Cuando llamas a eth_sendRawTransaction, el nodo realiza una secuencia de comprobaciones antes de aceptar la transacción en su mempool. La especificación JSON-RPC de Ethereum define el contrato del método, pero el orden exacto y las cadenas de error son específicos del cliente y varían según el proveedor. En la práctica, la mayoría de los clientes siguen este orden: recuperación de la firma, validación del nonce contra el nonce pendiente de la cuenta y validación del saldo contra el coste inicial.
La primera comprobación es la recuperación de la firma. El nodo recupera la dirección del remitente a partir de la firma ECDSA y verifica que coincida con el formato esperado. Si esto falla, el error suele ser 'invalid sender' o 'invalid signature'. Esta comprobación no involucra saldo ni nonce, pero debe pasar antes de que se evalúen las demás.
La segunda comprobación es la validación del nonce. El nodo compara el nonce de la transacción con el nonce pendiente de la cuenta, que incluye las transacciones ya presentes en la mempool. Si el nonce de la transacción es menor que el nonce pendiente, el nodo devuelve 'nonce too low'. Si es mayor, el nodo devuelve 'nonce too high' porque existe un hueco. Si el nonce coincide pero la transacción exacta ya está en el pool, el nodo devuelve 'already known'.
La tercera comprobación es la validación del saldo. El nodo calcula el coste inicial como gasLimit multiplicado por la tarifa efectiva, más el value transferido. Para transacciones EIP-1559, la tarifa efectiva es maxFeePerGas, no maxPriorityFeePerGas. El nodo también aplica un mínimo de gas intrínseco de 21000 más los costes de calldata, según se define en el Yellow Paper de Ethereum. Si el saldo es insuficiente, el nodo devuelve 'insufficient funds for gas * price + value'.
Los contratos de los métodos están definidos por la especificación JSON-RPC de Ethereum para eth_sendRawTransaction, eth_getTransactionCount, eth_getBalance y eth_estimateGas. Los campos de tarifa cuyo producto con el límite de gas determina el coste inicial provienen de EIP-1559, y el mínimo de gas intrínseco (21000 más calldata) se define en el Yellow Paper de Ethereum.
- Recuperación de la firma: verifica la dirección del remitente a partir de la firma ECDSA.
- Validación del nonce: compara con el nonce pendiente de la cuenta obtenido de eth_getTransactionCount con el parámetro de bloque 'pending'.
- Validación del saldo: comprueba gasLimit * tarifaEfectiva + value contra el saldo de la cuenta.
- Mínimo de gas intrínseco: 21000 más costes de calldata, según el Yellow Paper.
La familia de insuficiencia: saldo por debajo del coste inicial
El error 'insufficient funds for gas * price + value' significa que el saldo de la cuenta es menor que el coste inicial que calcula el nodo. Para transacciones legacy, el coste inicial es gasLimit * gasPrice + value. Para transacciones EIP-1559, el nodo usa maxFeePerGas * gasLimit + value, aunque la tarifa efectiva real pueda ser menor. Esto se debe a que el nodo debe garantizar que el remitente pueda cubrir el coste máximo posible, tal como se especifica en EIP-1559.
Un error común es comprobar el saldo contra la tarifa efectiva (baseFee + maxPriorityFeePerGas) y concluir que la cuenta tiene suficiente. El nodo comprueba el máximo, por lo que una transacción con un maxFeePerGas alto puede fallar incluso cuando la tarifa efectiva sería asequible. La acción correctiva es reducir el maxFeePerGas o aumentar el saldo de la cuenta.
Otra variante es un saldo que cubre el value pero no la tarifa. Por ejemplo, si la cuenta tiene exactamente el value que se envía pero nada extra para gas, el nodo devuelve el mismo error de 'insufficient funds'. El diagnóstico debe separar el componente de value del componente de tarifa para identificar qué parte falta.
El mínimo de gas intrínseco también forma parte del coste inicial. Aunque el gasLimit esté configurado correctamente, el nodo exige que el saldo cubra al menos 21000 de gas más los costes de calldata. Una transacción con un gasLimit por debajo del mínimo intrínseco se rechaza antes de la comprobación de saldo, pero si el gasLimit está por encima del mínimo, el saldo debe cubrir el gasLimit * tarifaEfectiva completo.
- Legacy: coste inicial = gasLimit * gasPrice + value.
- EIP-1559: coste inicial = gasLimit * maxFeePerGas + value, no la tarifa efectiva.
- Mínimo de gas intrínseco: 21000 más costes de calldata, según el Yellow Paper.
- Acción correctiva: reducir maxFeePerGas o aumentar el saldo.
La familia de nonce: too low, too high y already known
Los errores de nonce son distintos y requieren acciones correctivas diferentes. 'nonce too low' significa que el nonce de la transacción es menor que el nonce pendiente de la cuenta. Esto ocurre cuando la transacción ya se ha minado, o cuando otro hueco se ha rellenado con otra transacción. La acción correctiva es volver a obtener el nonce pendiente y reenviar con el valor correcto.
'nonce too high' significa que el nonce de la transacción es mayor que el nonce pendiente, lo que indica que existe un hueco por delante. El nodo no aceptará la transacción hasta que se rellene el hueco. La acción correctiva es esperar a que se rellene el nonce faltante o enviar primero las transacciones que faltan.
'already known' significa que la transacción exacta ya está en la mempool. No es un error en el sentido tradicional; indica que el nodo ya ha visto la transacción antes. La acción correctiva es esperar la confirmación o reemplazar la transacción con una tarifa mayor si está atascada. La guía sobre replacement transaction underpriced y transacciones atascadas cubre la mecánica de reemplazo.
La distinción entre nonce pendiente y latest es crítica. eth_getTransactionCount con el parámetro de bloque 'pending' devuelve el nonce incluyendo las transacciones de la mempool, mientras que 'latest' devuelve el nonce del último bloque minado. Un nodo retrasado puede devolver un nonce 'latest' que va por detrás de la red, provocando un 'nonce too low' espurio si usas el parámetro equivocado. Usa siempre 'pending' para el envío.
- nonce too low: nonce ya usado o hueco rellenado; vuelve a obtener el nonce pendiente.
- nonce too high: existe un hueco por delante; rellénalo o espera.
- already known: la transacción exacta está en el pool; espera o reemplaza.
- Usa eth_getTransactionCount con 'pending' para el envío, no 'latest'.
# Read pending and latest nonce, then the balance, before resubmitting.
curl -s -X POST "$RPC_URL" -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionCount","params":["0xYourAddress","pending"]}'
# Compare against latest: a gap between pending and latest means in-flight transactions.
curl -s -X POST "$RPC_URL" -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":2,"method":"eth_getTransactionCount","params":["0xYourAddress","latest"]}'
# eth_getBalance at pending is the value the node checks against gas*price+value.
curl -s -X POST "$RPC_URL" -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":3,"method":"eth_getBalance","params":["0xYourAddress","pending"]}'Recalcular el coste inicial antes de reenviar
Antes de reenviar una transacción fallida, recalcula el coste inicial a partir de los campos firmados. Obtén el saldo de la cuenta con eth_getBalance y el nonce pendiente con eth_getTransactionCount. Después, analiza la transacción firmada para extraer gasLimit, maxFeePerGas (o gasPrice) y value. Calcula el coste inicial como gasLimit * maxFeePerGas + value para EIP-1559, o gasLimit * gasPrice + value para legacy.
Compara el coste inicial calculado con el saldo. Si el saldo es insuficiente, la diferencia es el déficit. Si el problema es el nonce, compara el nonce de la transacción con el nonce pendiente. Este recálculo evita bucles de reintento que culpan al nodo cuando la transacción en sí está mal formada.
La guía sobre gestión de nonce en EVM con eth_getTransactionCount proporciona contexto adicional sobre el seguimiento de nonces. Para la estimación de tarifas, la guía sobre estimar el precio del gas con eth_feeHistory explica cómo derivar un maxFeePerGas seguro. La guía sobre eth_estimateGas y cómo derivar un límite de gas seguro cubre la estimación del límite de gas.
- Obtén el saldo con eth_getBalance en 'pending'.
- Obtén el nonce con eth_getTransactionCount en 'pending'.
- Analiza los campos firmados: gasLimit, maxFeePerGas, value, nonce.
- Calcula el coste inicial y compáralo con el saldo.
Diagnóstico ejecutable en Node.js para errores de saldo y nonce
El siguiente script de Node.js analiza una transacción firmada, obtiene el saldo de la cuenta y el nonce pendiente, e imprime qué comprobación falló y por cuánto. Usa ethers.js para la decodificación RLP y las llamadas JSON-RPC. Reemplaza las constantes RPC_URL y RAW_TX por tus propios valores.
El script calcula el coste inicial tanto para transacciones legacy como EIP-1559, lo compara con el saldo y comprueba el nonce contra el nonce pendiente. Imprime un mensaje de diagnóstico para cada cadena de error, incluyendo el déficit calculado y la acción correctiva.
const { ethers } = require('ethers');
const RPC_URL = 'https://your-rpc-endpoint';
const RAW_TX = '0x...';
async function diagnose() {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const tx = ethers.Transaction.from(RAW_TX);
const from = tx.from;
const [balance, pendingNonce, latestNonce] = await Promise.all([
provider.getBalance(from, 'pending'),
provider.getTransactionCount(from, 'pending'),
provider.getTransactionCount(from, 'latest')
]);
const gasLimit = tx.gasLimit;
const value = tx.value;
const maxFee = tx.maxFeePerGas ?? tx.gasPrice;
const upfront = gasLimit * maxFee + value;
console.log('From:', from);
console.log('Balance:', ethers.formatEther(balance));
console.log('Pending nonce:', pendingNonce);
console.log('Latest nonce:', latestNonce);
console.log('Tx nonce:', tx.nonce);
console.log('Upfront cost:', ethers.formatEther(upfront));
if (balance < upfront) {
const shortfall = upfront - balance;
console.log('ERROR: insufficient funds for gas * price + value');
console.log('Shortfall:', ethers.formatEther(shortfall));
}
if (tx.nonce < pendingNonce) {
console.log('ERROR: nonce too low');
console.log('Expected nonce:', pendingNonce);
} else if (tx.nonce > pendingNonce) {
console.log('ERROR: nonce too high');
console.log('Gap ahead:', tx.nonce - pendingNonce);
}
}
diagnose().catch(console.error);Tabla de resultados para medir contra tu propio endpoint
Usa la siguiente tabla para registrar los resultados de tu diagnóstico contra tu propio endpoint. Rellena la cadena de error, el déficit calculado, el nonce pendiente, el nonce latest y la acción correctiva. Esta tabla te ayuda a rastrear patrones entre múltiples envíos e identificar si el problema está relacionado con el saldo o con el nonce.
Ejecuta el script de diagnóstico para cada transacción fallida y registra los valores. Si el déficit es positivo, el saldo es insuficiente. Si el nonce pendiente difiere del nonce latest, hay transacciones en vuelo. Si el nonce de la transacción es menor que el nonce pendiente, el nonce es demasiado bajo. Si es mayor, el nonce es demasiado alto.
- Cadena de error: el mensaje exacto devuelto por eth_sendRawTransaction.
- Déficit calculado: coste inicial menos saldo, si es positivo.
- Nonce pendiente: de eth_getTransactionCount en 'pending'.
- Nonce latest: de eth_getTransactionCount en 'latest'.
- Acción correctiva: reducir maxFeePerGas, aumentar el saldo o ajustar el nonce.
Modos de fallo: nodos retrasados, diferencias de mempool y transacciones en vuelo
Un nodo retrasado puede devolver un nonce 'latest' que va por detrás de la red, provocando un 'nonce too low' espurio si usas el parámetro de bloque equivocado. Usa siempre 'pending' para el envío. Si el nodo está significativamente retrasado, considera cambiar a otro endpoint. La guía sobre URL de RPC de Ethereum y selección de endpoints (RPC Assistant) cubre la selección de endpoints.
Las diferencias de mempool pueden provocar 'already known' en un endpoint y aceptación en otro. Esto ocurre porque distintos nodos tienen políticas de mempool y retrasos de propagación diferentes. Si ves 'already known' en un endpoint, prueba otro o espera a la propagación. La guía sobre depurar errores internos -32603 de JSON-RPC cubre el manejo de errores relacionados.
Un saldo correcto en 'latest' pero insuficiente en 'pending' indica una transacción en vuelo que aún no se ha minado. El saldo pendiente incluye deducciones por transacciones de la mempool, por lo que el saldo disponible es menor. Comprueba siempre el saldo en 'pending' antes de reenviar. Si el saldo es insuficiente en pending, espera a que se confirme la transacción en vuelo o reemplázala.
- Nodo retrasado: usa el nonce 'pending', no 'latest'.
- Diferencias de mempool: 'already known' puede variar según el endpoint.
- Transacciones en vuelo: el saldo en 'pending' es menor que en 'latest'.
- Acción correctiva: esperar, reemplazar o cambiar de endpoint.
Limitaciones y compensaciones: errores específicos del cliente e implicaciones de reintento
Las cadenas de error exactas son específicas del cliente y varían según el proveedor. Geth, Erigon, Nethermind y Besu pueden devolver mensajes ligeramente distintos para la misma condición subyacente. La especificación JSON-RPC de Ethereum define el contrato del método, pero no las cadenas de error. Trata siempre la cadena de error como una pista y verifica la condición subyacente con el script de diagnóstico.
Una diferencia de mempool entre nodos puede cambiar el error observado para los mismos bytes. Una transacción que es 'already known' en un nodo puede ser aceptada en otro. Esto tiene implicaciones de reintento e idempotencia: reintentar la misma transacción en un endpoint diferente puede tener éxito, pero también puede crear un duplicado si el primer endpoint finalmente la propaga. Usa un nonce único por transacción y monitoriza la confirmación.
El bucle de reintento debe ser idempotente. Antes de reenviar, recalcula el coste inicial y el nonce. Si la transacción ya está en el pool, espera la confirmación en lugar de reenviarla. Si el saldo es insuficiente, aumenta el saldo o reduce la tarifa. Si el nonce es incorrecto, vuelve a obtener el nonce pendiente y reenvía con el valor correcto.
- Las cadenas de error son específicas del cliente y varían según el proveedor.
- Las diferencias de mempool pueden cambiar el error observado para los mismos bytes.
- Los bucles de reintento deben ser idempotentes: recalcular antes de reenviar.
- Usa un nonce único por transacción y monitoriza la confirmación.
Lista de comprobación para errores de saldo y nonce
Cuando eth_sendRawTransaction falla, sigue esta lista de comprobación para identificar la causa raíz. Primero, analiza la transacción firmada para extraer el nonce, gasLimit, maxFeePerGas (o gasPrice) y value. Segundo, obtén el saldo de la cuenta y el nonce pendiente. Tercero, calcula el coste inicial y compáralo con el saldo. Cuarto, compara el nonce de la transacción con el nonce pendiente.
Si el error es 'insufficient funds for gas * price + value', comprueba si el saldo cubre el coste inicial. Si el saldo cubre el value pero no la tarifa, el déficit es el componente de la tarifa. Si el maxFeePerGas es alto, redúcelo o aumenta el saldo. Si el error es 'nonce too low', vuelve a obtener el nonce pendiente y reenvía. Si el error es 'nonce too high', rellena el hueco o espera. Si el error es 'already known', espera la confirmación o reemplaza la transacción.
Para contexto adicional sobre la estimación de tarifas, consulta la guía sobre estimar el precio del gas con eth_feeHistory. Para la estimación del límite de gas, consulta la guía sobre eth_estimateGas y cómo derivar un límite de gas seguro. Para la gestión de nonces, consulta la guía sobre gestión de nonce en EVM con eth_getTransactionCount.
- Analiza los campos firmados: nonce, gasLimit, maxFeePerGas, value.
- Obtén el saldo y el nonce pendiente.
- Calcula el coste inicial y compáralo con el saldo.
- Compara el nonce de la transacción con el nonce pendiente.
- Aplica la acción correctiva según la cadena de error.
Próximos pasos: monitorización, automatización y selección de endpoints
Después de resolver el error inmediato, considera automatizar el diagnóstico. Integra la lógica de recálculo en tu pipeline de envío de transacciones para detectar problemas de saldo y nonce antes de que lleguen al nodo. Monitoriza el nonce pendiente y el saldo a intervalos regulares para detectar transacciones en vuelo y déficits.
Para cargas de trabajo en producción, usa un endpoint RPC fiable. La guía sobre URL de RPC de Ethereum y selección de endpoints (RPC Assistant) cubre los criterios de selección de endpoints. La página de precios de RPC proporciona información sobre los planes de precios. La página de servicio de API describe las ofertas del servicio de API.
Para más guías de solución de problemas, visita el centro de aprendizaje de OnFinality. La página de la red Ethereum proporciona detalles específicos de la red. La guía sobre replacement transaction underpriced y transacciones atascadas cubre la clase de error de reemplazo, que es distinta de los errores de saldo y nonce aquí tratados.
- Automatiza el recálculo en tu pipeline de envío.
- Monitoriza el nonce pendiente y el saldo con regularidad.
- Usa un endpoint RPC fiable para producción.
- Revisa las guías relacionadas sobre reemplazo y estimación de tarifas.