Handle smart contract reverts and decode custom error types using Nethereum (.NET). Use this skill whenever the user asks about contract errors, revert reasons, custom Solidity errors, SmartContractCustomErrorRevertException, error decoding, failed transaction reasons, or any revert handling in C#/.NET.
NuGet: Nethereum.Web3
dotnet add package Nethereum.Web3
Map Solidity custom errors to C# classes using [Error] and [Parameter] attributes.
using System.Numerics;
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Contracts;
// Solidity: error InsufficientBalance(address account, uint256 balance, uint256 required)
[Error("InsufficientBalance")]
public class InsufficientBalanceError
{
[Parameter("address", "account", 1)]
public string Account { get; set; }
[Parameter("uint256", "balance", 2)]
public BigInteger Balance { get; set; }
[Parameter("uint256", "required", 3)]
public BigInteger Required { get; set; }
}
// Solidity: error Unauthorized(address caller)
[Error("Unauthorized")]
public class UnauthorizedError
{
[Parameter("address", "caller", 1)]
public string Caller { get; set; }
}
try
{
var receipt = await contractHandler
.SendRequestAndWaitForReceiptAsync(transferFunction);
}
catch (SmartContractCustomErrorRevertException ex)
{
if (ex.IsCustomErrorFor<InsufficientBalanceError>())
{
var error = ex.DecodeError<InsufficientBalanceError>();
Console.WriteLine(
$"Insufficient: {error.Account} has {error.Balance}, needs {error.Required}");
}
else if (ex.IsCustomErrorFor<UnauthorizedError>())
{
var error = ex.DecodeError<UnauthorizedError>();
Console.WriteLine($"Unauthorized: {error.Caller}");
}
}
Code-generated contract services register all error types. Use FindCustomErrorException():
catch (SmartContractCustomErrorRevertException ex)
{
var typedException = myContractService.FindCustomErrorException(ex);
if (typedException != null)
{
Console.WriteLine($"Error: {typedException.DecodedError}");
Console.WriteLine($"Error ABI: {typedException.ErrorABI.Name}");
}
}
var errorTypes = new[] { typeof(InsufficientBalanceError), typeof(UnauthorizedError) };
catch (SmartContractCustomErrorRevertException ex)
{
var typed = SmartContractCustomErrorTypedFactory.CreateTypedException(ex, errorTypes);
if (typed is SmartContractCustomErrorRevertException<InsufficientBalanceError> balanceEx)
{
var error = balanceEx.CustomError;
Console.WriteLine($"Need {error.Required}, have {error.Balance}");
}
}
var errorReason = await web3.Eth.GetContractTransactionErrorReason
.SendRequestAsync(failedTransactionHash);
Console.WriteLine($"Transaction failed: {errorReason}");
var exception = new SmartContractCustomErrorRevertException(hexEncodedErrorData);
if (exception.IsCustomErrorFor<InsufficientBalanceError>())
{
var error = exception.DecodeError<InsufficientBalanceError>();
}
SmartContractCustomErrorRevertException -- base exception with ExceptionEncodedDataSmartContractCustomErrorRevertException<TError> -- generic typed with CustomError propertySmartContractCustomErrorRevertExceptionErrorDecoded -- decoded with DecodedError stringSmartContractCustomErrorTypedFactory -- factory for typed exceptions from error type listContractServiceBase.FindCustomErrorException() -- auto-decode using registered typesweb3.Eth.GetContractTransactionErrorReason -- error reason from mined failed tx| Task | Method |
|---|---|
| Check error type | ex.IsCustomErrorFor<TError>() |
| Decode to typed object | ex.DecodeError<TError>() |
| Decode to default string | ex.DecodeErrorToDefaultString(errorABI) |
| Auto-decode from service | contractService.FindCustomErrorException(ex) |
| Factory decode | SmartContractCustomErrorTypedFactory.CreateTypedException(ex, types) |
| Error from failed tx | web3.Eth.GetContractTransactionErrorReason.SendRequestAsync(hash) |