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

Razones de Revert de Ethereum y Errores Personalizados: Decodificando por qué una Transacción o eth_call Falla

Aprende a decodificar razones de revert de Ethereum y errores personalizados de respuestas JSON-RPC, incluyendo Error(string), Panic(uint256) y selectores de errores personalizados.

TL;DR

Cuando una transacción de Ethereum o una llamada eth_call revierte, la respuesta JSON-RPC contiene una razón de revert codificada en ABI en el campo de datos de error. Este artículo explica cómo decodificar Error(string) (0x08c379a0), Panic(uint256) (0x4e487b71) y errores personalizados (selector de bytes4) usando ethers o viem, y cómo recuperar razones de revert de recibos de transacción con estado 0x0.

Respuesta Directa: Cómo Leer una Razón de Revert desde JSON-RPC

Cuando una transacción de Ethereum o una llamada eth_call falla, la razón del revert no se devuelve como un mensaje de texto plano. En su lugar, la EVM devuelve una cadena de bytes codificada en ABI dentro del objeto de error JSON-RPC. Para una llamada eth_call típica, la respuesta se ve así: {"error":{"code":3,"data":"0x08c379a0..."}} donde el campo data contiene la razón codificada. Para decodificarla, debes conocer el tipo de revert: Error(string) (selector 0x08c379a0), Panic(uint256) (selector 0x4e487b71) o un error personalizado (un selector de 4 bytes). Para errores personalizados, necesitas el ABI del contrato para mapear el selector al nombre del error y sus argumentos. Esta guía explica el mecanismo y proporciona un script ejecutable para decodificar los tres tipos.

Si estás solucionando problemas con un endpoint RPC, consulta nuestra guía de nodos RPC de Ethereum y precios de RPC para la configuración del endpoint. Para un contexto más amplio, el centro de aprendizaje de OnFinality cubre temas relacionados como tiempos de espera de RPC de Ethereum y límites de tasa.

  • Las razones de revert están codificadas en ABI, no son cadenas legibles por humanos.
  • El campo data del error JSON-RPC contiene la razón codificada.
  • Los errores personalizados requieren el ABI del contrato para decodificarse.
  • Los recibos de transacción con estado 0x0 indican un revert, pero la razón no se almacena en la cadena.

Mecanismo de Revert de la EVM y Codificación ABI

Cuando un contrato Solidity ejecuta revert(), require(false) o encuentra un error aritmético, la EVM revierte todos los cambios de estado y devuelve una razón al llamante. La razón se codifica según la especificación ABI. Para Error(string), la codificación es el selector de 4 bytes 0x08c379a0 seguido de un desplazamiento de 32 bytes a los datos de la cadena, luego la longitud de la cadena y los bytes UTF-8. Para Panic(uint256), el selector es 0x4e487b71 seguido de un código de pánico de 32 bytes. Los errores personalizados, introducidos en Solidity 0.8.4, se codifican como el selector de 4 bytes de la firma del error, opcionalmente seguido de argumentos codificados en ABI.

La especificación de ejecución de Ethereum documenta que un revert consume todo el gas y devuelve la razón al llamante. La documentación de Solidity sobre errores personalizados explica que los errores personalizados se identifican por su selector y pueden llevar argumentos. Por eso es imposible decodificar un error personalizado sin el ABI: solo ves un selector de 4 bytes como 0x9e8b2f3a.

Para una inmersión más profunda en la superficie JSON-RPC, las APIs de ejecución de Ethereum describen el formato de error estándar. Los proveedores pueden envolver el error de manera diferente, pero el campo data es la clave para decodificar.

  • Selector de Error(string): 0x08c379a0
  • Selector de Panic(uint256): 0x4e487b71
  • Error personalizado: selector de 4 bytes de la firma del error
  • Códigos de pánico: 0x01 (assert), 0x11 (desbordamiento/subdesbordamiento), 0x12 (división por cero), etc.

Superficie de Error JSON-RPC: eth_call y eth_sendRawTransaction

Cuando realizas una llamada eth_call que revierte, el nodo devuelve un objeto de error JSON-RPC. En geth, el código de error es 3 y el mensaje es execution reverted, con los datos del revert en el campo data. Otros clientes pueden usar códigos o mensajes diferentes, pero el campo data es estándar. Por ejemplo, una llamada eth_call fallida podría devolver:

Para eth_sendRawTransaction, la transacción se mina y el recibo muestra status: '0x0' si revirtió. Sin embargo, la razón del revert no se almacena en la cadena; debes re-simular la transacción con eth_call para recuperar la razón. Algunos proveedores ofrecen un método debug_traceTransaction para obtener la razón del revert, pero esto no es estándar y puede requerir acceso a un nodo de archivo.

El envoltorio específico del proveedor puede variar. Por ejemplo, algunos proveedores pueden incluir los datos del revert en un campo data en el nivel superior o usar un código de error diferente. Siempre inspecciona el objeto de error completo. Si estás usando un endpoint dedicado, consulta nuestro servicio de API para más detalles.

  • eth_call devuelve datos de revert en el campo data del objeto de error.
  • Los recibos de eth_sendRawTransaction con estado 0x0 indican un revert, pero la razón no se incluye.
  • Usa eth_call para simular la transacción y capturar la razón del revert.
  • Los códigos de error del proveedor pueden variar; confía en el campo data.
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 3,
    "message": "execution reverted",
    "data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a496e73756666696369656e740000000000000000000000000000000000000000"
  }
}

Decodificación Manual de Error(string) y Panic(uint256)

Para decodificar manualmente un revert de Error(string), puedes analizar los datos: toma los primeros 4 bytes para confirmar el selector 0x08c379a0, luego lee los siguientes 32 bytes como el desplazamiento (generalmente 0x20), luego los siguientes 32 bytes como la longitud de la cadena, y finalmente los bytes UTF-8. Para Panic(uint256), los primeros 4 bytes son 0x4e487b71, y los siguientes 32 bytes son el código de pánico como uint256.

Por ejemplo, los datos 0x08c379a0... con la cadena "Insufficient balance" se decodifican al mensaje. El código de pánico 0x11 indica un desbordamiento o subdesbordamiento aritmético. Los códigos de pánico de Solidity están documentados en los documentos de Solidity.

Aunque la decodificación manual es educativa, usar una biblioteca como ethers o viem es más confiable y maneja casos extremos.

  • Disposición de datos de Error(string): selector + desplazamiento + longitud + bytes de cadena.
  • Disposición de datos de Panic(uint256): selector + código de pánico de 32 bytes.
  • Códigos de pánico comunes: 0x01 (assert), 0x11 (desbordamiento), 0x12 (división por cero).

Decodificación de Errores Personalizados con ABI

Los errores personalizados son más complejos porque el selector de 4 bytes solo no te dice el nombre del error ni sus argumentos. Debes tener el ABI del contrato que incluya la definición del error. Por ejemplo, si tu contrato define error InsufficientBalance(uint256 available, uint256 required), el selector se calcula a partir de la firma InsufficientBalance(uint256,uint256). Para decodificar, necesitas hacer coincidir el selector con el ABI y luego decodificar los argumentos.

Bibliotecas como ethers.js y viem proporcionan funciones decodeErrorResult que toman el ABI y los datos del revert. Para ethers v6, puedes usar Contract.interface.parseError(data) o Interface.parseError. Para viem, usa decodeErrorResult de viem. Estas funciones manejan automáticamente la búsqueda del selector y la decodificación de argumentos.

Sin el ABI, solo puedes ver el selector. Esta es una limitación fundamental: los errores personalizados no se autodescriben. Siempre ten a mano el ABI de tu contrato para depurar.

  • El selector de error personalizado son los primeros 4 bytes de los datos del revert.
  • Usa el ABI del contrato para mapear el selector al nombre del error y sus argumentos.
  • ethers v6: contract.interface.parseError(data)
  • viem: decodeErrorResult({ abi, data })

Ejemplo Reproducible: Script de Node.js con viem

El siguiente script demuestra cómo decodificar razones de revert usando viem. Toma una URL RPC, una dirección de contrato y calldata (o una llamada a función) que provoque un revert. El script realiza una llamada eth_call y decodifica los datos de error. Reemplaza los marcadores de posición con tus propios valores.

La salida esperada para un revert de Error(string) sería Mensaje de error: Insufficient balance. Para un error personalizado, imprimiría el nombre del error y sus argumentos. El script asume que tienes un contrato que revierte; también puedes usar una muestra conocida como una transferencia de token que falla debido a saldo insuficiente.

Para probar con un contrato real, puedes usar un endpoint público como el proporcionado por OnFinality. Para una lista de endpoints públicos, consulta nuestra página de redes Ethereum.

  • El script usa decodeErrorResult de viem para manejar todos los tipos de error.
  • Debes proporcionar el ABI para errores personalizados.
  • El script imprime el error decodificado o el selector sin procesar si es desconocido.
import { createPublicClient, http, decodeErrorResult } from 'viem';
import { mainnet } from 'viem/chains';

const client = createPublicClient({
  chain: mainnet,
  transport: http('YOUR_RPC_URL')
});

// Ejemplo de fragmento ABI para un error personalizado
const abi = [
  {
    type: 'error',
    name: 'InsufficientBalance',
    inputs: [
      { name: 'available', type: 'uint256' },
      { name: 'required', type: 'uint256' }
    ]
  }
];

async function decodeRevert() {
  try {
    // Esta llamada revertirá; reemplaza con tu propia llamada a contrato
    await client.call({
      address: '0xContractAddress',
      data: '0x...' // calldata que provoca el revert
    });
  } catch (error) {
    const data = error.data; // o error.cause.data dependiendo de la versión de viem
    if (data) {
      const selector = data.slice(0, 10); // 0x + 4 bytes
      if (selector === '0x08c379a0') {
        // Decodificar Error(string) usando decodeErrorResult de viem con un ABI genérico
        const decoded = decodeErrorResult({ abi: ['error Error(string)'], data });
        console.log('Mensaje de error:', decoded.args[0]);
      } else if (selector === '0x4e487b71') {
        const decoded = decodeErrorResult({ abi: ['error Panic(uint256)'], data });
        console.log('Código de pánico:', decoded.args[0]);
      } else {
        // Intentar error personalizado con el ABI proporcionado
        try {
          const decoded = decodeErrorResult({ abi, data });
          console.log('Error personalizado:', decoded.errorName, decoded.args);
        } catch (e) {
          console.log('Selector de error personalizado desconocido:', selector);
        }
      }
    } else {
      console.log('Sin datos de revert en el error:', error);
    }
  }
}

decodeRevert();

Tabla de Resultados: Mapeo de Tipo de Error a Solución

La siguiente tabla resume escenarios comunes de revert y las soluciones recomendadas. Úsala como referencia rápida al depurar.

Tipo de ErrorSelectorCausa ComúnSolución
Error(string)0x08c379a0require/revert con mensajeInspeccionar mensaje; corregir condición
Panic(0x01)0x4e487b71fallo de assertVerificar invariantes
Panic(0x11)0x4e487b71Desbordamiento/subdesbordamiento aritméticoUsar SafeMath o comprobaciones de Solidity 0.8+
Panic(0x12)0x4e487b71División por ceroVerificar divisor
Error personalizadovaríaError de lógica de negocioDecodificar con ABI; inspeccionar argumentos
Fuera de gasN/ALímite de gas demasiado bajoAumentar gasLimit
Revert sin datosN/ARevert de bajo nivelVerificar lógica del contrato

Para errores de falta de gas, el error JSON-RPC puede no incluir datos de revert. En ese caso, necesitas aumentar el límite de gas y reintentar. Para más sobre tiempos de espera y gas, consulta nuestro artículo sobre tiempos de espera de RPC de Ethereum.

Limitaciones y Compensaciones

Decodificar razones de revert tiene varias limitaciones. Primero, los errores personalizados requieren el ABI del contrato; sin él, solo ves un selector. Segundo, algunos proveedores pueden truncar o envolver los datos del revert, especialmente para cadenas largas. Tercero, no todos los modos de fallo devuelven una razón de revert; por ejemplo, los errores de falta de gas pueden no incluir datos. Cuarto, los recibos de transacción con estado 0x0 no incluyen la razón del revert; debes re-simular la transacción.

Además, el formato de error JSON-RPC puede variar entre clientes y proveedores. Aunque el campo data es estándar, el código de error y el mensaje pueden diferir. Siempre registra el objeto de error completo para depurar.

Para depuración en producción, considera usar un endpoint RPC dedicado con datos de archivo para reproducir transacciones. Consulta nuestra guía sobre endpoints RPC públicos vs dedicados para más información.

  • Los errores personalizados necesitan ABI; de lo contrario, solo se ve el selector.
  • El proveedor puede envolver o truncar los datos del revert.
  • Los errores de falta de gas pueden no incluir datos de revert.
  • Los recibos con estado 0x0 no contienen la razón.

Próximos Pasos y Lecturas Adicionales

Ahora que entiendes cómo decodificar razones de revert, puedes aplicar este conocimiento para depurar tus contratos inteligentes y llamadas RPC. Para más solución de problemas específica de Ethereum, explora nuestra guía de nodos RPC de Ethereum y artículos relacionados sobre límites de tasa y tiempos de espera. Si necesitas consultar datos históricos, consulta Consulta de datos históricos de blockchain.

Para una comprensión completa de los endpoints RPC y precios, visita nuestras páginas de servicio de API y precios de RPC. El centro de aprendizaje de OnFinality ofrece muchas más guías.

Recuerda siempre probar con un nodo local o de testnet antes de depender de un endpoint público. ¡Feliz depuración!

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