Skip to main content

ErrorSignature

An ErrorSignature is a 4-byte identifier used for Solidity custom errors in revert data. It’s computed as the first 4 bytes of the Keccak-256 hash of the error signature, similar to function selectors.

Type Definition

const primitives = @import("primitives");
// ErrorSignature is a 4-byte array: [4]u8
const ErrorSignature = primitives.ErrorSignature.ErrorSignature;

Creating Error Signatures

From Signature String

const primitives = @import("primitives");
const insufficient = primitives.ErrorSignature.fromSignature(
    "InsufficientBalance(uint256,uint256)",
);
const hex = primitives.ErrorSignature.toHex(insufficient); // "0xcf479181"

From Hex String

const primitives = @import("primitives");
const sig = try primitives.ErrorSignature.fromHex("0xcf479181");

From Bytes

const primitives = @import("primitives");
const raw = [_]u8{ 0xcf, 0x47, 0x91, 0x81 };
const sig = try primitives.ErrorSignature.fromBytes(&raw);

Operations

Convert to Hex

const primitives = @import("primitives");
const hex = primitives.ErrorSignature.toHex(sig); // "0xcf479181"

Compare Signatures

const primitives = @import("primitives");
const s1 = primitives.ErrorSignature.fromSignature("Unauthorized()");
const s2 = primitives.ErrorSignature.fromSignature("Error(string)");
const equal = primitives.ErrorSignature.equals(s1, s2); // false

Standard Error Signatures

Built-in Errors

Solidity has two built-in error signatures:
const primitives = @import("primitives");
// Error(string) - Used by require()
const errorString = primitives.ErrorSignature.fromSignature("Error(string)");
// 0x08c379a0

// Panic(uint256) - Used by assert()
const panic = primitives.ErrorSignature.fromSignature("Panic(uint256)");
// 0x4e487b71

Panic Codes

When using assert() or encountering specific errors, Solidity reverts with Panic(uint256) and a code:
  • 0x00 - Generic panic
  • 0x01 - Assert failed
  • 0x11 - Arithmetic overflow/underflow
  • 0x12 - Division by zero
  • 0x21 - Invalid enum value
  • 0x22 - Invalid storage byte array access
  • 0x31 - Pop on empty array
  • 0x32 - Array out of bounds
  • 0x41 - Out of memory
  • 0x51 - Invalid internal function call

Custom Errors

Custom errors are more gas-efficient than require() with strings:
// Define custom error
error InsufficientBalance(uint256 available, uint256 required);

// Use in contract
if (balance < amount) {
  revert InsufficientBalance(balance, amount);
}
const primitives = @import("primitives");
const sig = primitives.ErrorSignature.fromSignature("InsufficientBalance(uint256,uint256)");
// 0xcf479181

Common Custom Errors

Access Control

const primitives = @import("primitives");
const unauthorized = primitives.ErrorSignature.fromSignature("Unauthorized()");
const onlyOwner = primitives.ErrorSignature.fromSignature("OnlyOwner()");
const invalidRole = primitives.ErrorSignature.fromSignature("InvalidRole(bytes32)");

Token Operations

const primitives = @import("primitives");
const insufficientBalance = primitives.ErrorSignature.fromSignature(
    "InsufficientBalance(uint256,uint256)",
);
const insufficientAllowance = primitives.ErrorSignature.fromSignature(
    "InsufficientAllowance(uint256,uint256)",
);
const invalidRecipient = primitives.ErrorSignature.fromSignature("InvalidRecipient(address)");

DeFi

const primitives = @import("primitives");
const slippageTooHigh = primitives.ErrorSignature.fromSignature(
    "SlippageTooHigh(uint256,uint256)",
);
const deadlineExpired = primitives.ErrorSignature.fromSignature("DeadlineExpired(uint256)");
const insufficientLiquidity = primitives.ErrorSignature.fromSignature("InsufficientLiquidity()");

Revert Data Structure

When a custom error is thrown, the revert data contains:
[selector (4 bytes)][encoded parameters]
Example for InsufficientBalance(1000, 2000):
0xcf479181                                                          // selector
0000000000000000000000000000000000000000000000000000000000003e8  // 1000
0000000000000000000000000000000000000000000000000000000000007d0  // 2000

Decoding Errors

Use error signatures to decode revert data:
const revertData = '0xcf479181...'; // from transaction
const selector = revertData.slice(0, 10); // '0xcf479181'

const errorSig = ErrorSignature.fromHex(selector);
// Identify: InsufficientBalance(uint256,uint256)

// Decode parameters from revertData.slice(10)

Signature Format

Error signatures must use canonical type names: ✅ Correct:
  • InsufficientBalance(uint256,uint256)
  • Unauthorized()
  • InvalidSwap(address,uint256,bytes)
❌ Incorrect:
  • InsufficientBalance(uint, uint) (should be uint256)
  • InsufficientBalance(uint256, uint256) (has spaces)
  • insufficientBalance(uint256,uint256) (wrong capitalization)
Note: Error names conventionally start with uppercase.

How Error Signatures Work

  1. Error Signature: Start with the canonical error signature
  2. Hash: Compute keccak256(signature)
  3. Truncate: Take the first 4 bytes
InsufficientBalance(uint256,uint256)
  ↓ keccak256
0xcf4791819f1b70c0f30aefb0f54ba2bc...
  ↓ slice(0, 4)
0xcf479181

Gas Efficiency

Custom errors are significantly more gas-efficient than require() with strings:
// ❌ Expensive (~24k gas)
require(balance >= amount, "Insufficient balance");

// ✅ Efficient (~1k gas)
if (balance < amount) revert InsufficientBalance(balance, amount);

Handling Errors

const primitives = @import("primitives");

// Given revert data bytes from an eth_call, first 4 bytes are error selector
fn matchesInsufficientBalance(revert_data: []const u8) bool {
    if (revert_data.len < 4) return false;
    const expected = primitives.ErrorSignature.fromSignature(
        "InsufficientBalance(uint256,uint256)",
    );
    const got = [_]u8{ revert_data[0], revert_data[1], revert_data[2], revert_data[3] };
    return primitives.ErrorSignature.equals(expected, got);
}

API Reference

Constructors

  • from(value: ErrorSignatureLike): ErrorSignatureType - Create from various inputs
  • fromHex(hex: string): ErrorSignatureType - Create from hex string
  • fromSignature(signature: string): ErrorSignatureType - Compute from error signature

Operations

  • toHex(sig: ErrorSignatureType): string - Convert to hex string
  • equals(a: ErrorSignatureType, b: ErrorSignatureType): boolean - Compare signatures

See Also