Skip to main content

Revert & Error Decoding

When a simulated transaction fails, understanding why it reverted is critical. The EVM simulator captures the revert data and the ProgramResultDecoder decodes it into human-readable error messages — including Solidity custom errors, require() messages, and panic codes.

How Reverts Work

Solidity has three ways to signal failure:

  1. require(condition, "message") — encodes as Error(string) with selector 0x08c379a0
  2. revert CustomError(params) — encodes as a custom error with its own 4-byte selector
  3. Panic codesPanic(uint256) with selector 0x4e487b71 for overflow, division by zero, etc.

The revert data is ABI-encoded and returned in execResult.ReturnData.

Prerequisites

dotnet add package Nethereum.EVM

Checking for Reverts

After executing a transaction, check Success first:

var executor = new TransactionExecutor(HardforkConfig.Default);
var execResult = await executor.ExecuteAsync(ctx);

if (!execResult.Success)
{
// Simple revert reason (if available)
Console.WriteLine($"Revert: {execResult.RevertReason}");
Console.WriteLine($"Error: {execResult.Error}");
}

RevertReason contains the decoded Error(string) message when the contract used require(). For custom errors, you need the decoder.

Decoding Custom Errors

The ProgramResultDecoder resolves custom error selectors against the ABI:

var decoder = new ProgramResultDecoder(abiStorage);
var decoded = decoder.Decode(execResult, callInput, chainId);

if (decoded.IsRevert && decoded.RevertReason != null)
{
var error = decoded.RevertReason;
Console.WriteLine($"Error name: {error.GetErrorName()}");
Console.WriteLine($"Message: {error.GetDisplayMessage()}");

if (error.Parameters != null)
{
foreach (var param in error.Parameters)
{
Console.WriteLine($" {param.Parameter.Name}: {param.Result}");
}
}
}

For example, an ERC-20 insufficient balance error decodes to:

Error name: ERC20InsufficientBalance
Message: ERC20InsufficientBalance(sender: 0xYour..., balance: 100, needed: 1000)

Decoding Revert Data Directly

You can also decode revert data from any source (not just simulation results):

var decoder = new ProgramResultDecoder(abiStorage);
var decodedError = decoder.DecodeRevert(revertData, chainId, contractAddress);

Console.WriteLine(decodedError.GetDisplayMessage());

This works with revert data from eth_call failures, eth_estimateGas errors, or transaction receipts.

DecodedError Properties

PropertyDescription
ErrorThe ErrorABI with name, selector, and parameter types
ParametersDecoded error parameters
MessageHuman-readable message (for standard Error(string))
IsStandardErrortrue for Error(string) and Panic(uint256)
IsDecodedtrue when the ABI was found and error was decoded
RawDataOriginal hex data (always available as fallback)

The GetDisplayMessage() method returns the best available description: the decoded message, error name, or raw data as fallback.

Common Error Selectors

These are the most common error selectors you will encounter:

SelectorErrorMeaning
0x08c379a0Error(string)require() with message
0x4e487b71Panic(uint256)Arithmetic overflow, division by zero
0xe450d38cERC20InsufficientBalanceNot enough tokens
0xfb8f41b2ERC20InsufficientAllowanceApproval too low
0xf4d678b8InsufficientBalance()Generic balance error
0x098fb561InsufficientInputAmount()DEX slippage
0x42301c23InsufficientOutputAmount()DEX slippage
0xbb55fd27InsufficientLiquidity()No liquidity in pool
0x203d82d8Expired()Deadline passed

Handling Errors in the Full Pipeline

In the complete simulation pipeline, check for errors at multiple levels:

// 1. Execution-level error (e.g., out of gas)
if (execResult.IsValidationError)
{
Console.WriteLine($"Validation error: {execResult.Error}");
return;
}

// 2. EVM revert with decoded error
if (!execResult.Success)
{
var decoded = decoder.Decode(execResult, callInput, chainId);

if (decoded.RevertReason != null)
{
Console.WriteLine($"Custom error: {decoded.RevertReason.GetDisplayMessage()}");
}
else
{
Console.WriteLine($"Revert: {execResult.RevertReason ?? execResult.Error ?? "Unknown"}");
}
return;
}

// 3. Success — extract state changes
var stateChanges = extractor.ExtractFromDecodedResult(decoded, executionState, userAddress);

Validation errors (IsValidationError = true) indicate pre-execution failures like insufficient balance for gas or nonce issues. These do not produce revert data — just an error string.

Panic Codes Reference

When the error selector is 0x4e487b71, the single uint256 parameter is a panic code:

CodeMeaning
0x00Generic compiler panic
0x01Assert failure
0x11Arithmetic overflow/underflow
0x12Division or modulo by zero
0x21Invalid enum conversion
0x22Incorrectly encoded storage byte array
0x31.pop() on empty array
0x32Array index out of bounds
0x41Too much memory allocated
0x51Calling zero-initialized function variable

The decoder handles panic codes automatically — GetDisplayMessage() returns a description like "Arithmetic overflow" rather than the raw code.

Next Steps