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

type ErrorSignatureType = Uint8Array & {
  readonly [brand]: "ErrorSignature";
  readonly length: 4;
};

Creating Error Signatures

From Signature String

import * as ErrorSignature from '@tevm/primitives';

const insufficientBalance = ErrorSignature.fromSignature(
  'InsufficientBalance(uint256,uint256)'
);

console.log(ErrorSignature.toHex(insufficientBalance));
// '0xcf479181'

From Hex String

const sig = ErrorSignature.fromHex('0xcf479181');

From Bytes

const bytes = new Uint8Array([0xcf, 0x47, 0x91, 0x81]);
const sig = ErrorSignature.from(bytes);

Operations

Convert to Hex

const hex = ErrorSignature.toHex(signature);
// '0xcf479181'

Compare Signatures

const sig1 = ErrorSignature.fromSignature('Unauthorized()');
const sig2 = ErrorSignature.fromSignature('Error(string)');
const equal = ErrorSignature.equals(sig1, sig2); // false

Standard Error Signatures

Built-in Errors

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

// Panic(uint256) - Used by assert()
const panic = 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 sig = ErrorSignature.fromSignature('InsufficientBalance(uint256,uint256)');
// 0xcf479181

Common Custom Errors

Access Control

const unauthorized = ErrorSignature.fromSignature('Unauthorized()');
const onlyOwner = ErrorSignature.fromSignature('OnlyOwner()');
const invalidRole = ErrorSignature.fromSignature('InvalidRole(bytes32)');

Token Operations

const insufficientBalance = ErrorSignature.fromSignature(
  'InsufficientBalance(uint256,uint256)'
);
const insufficientAllowance = ErrorSignature.fromSignature(
  'InsufficientAllowance(uint256,uint256)'
);
const invalidRecipient = ErrorSignature.fromSignature('InvalidRecipient(address)');

DeFi

const slippageTooHigh = ErrorSignature.fromSignature(
  'SlippageTooHigh(uint256,uint256)'
);
const deadlineExpired = ErrorSignature.fromSignature('DeadlineExpired(uint256)');
const insufficientLiquidity = 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

try {
  await contract.transfer(recipient, amount);
} catch (error) {
  if (error.data) {
    const selector = error.data.slice(0, 10);

    const insufficientBalanceSig = ErrorSignature.fromSignature(
      'InsufficientBalance(uint256,uint256)'
    );

    if (selector === ErrorSignature.toHex(insufficientBalanceSig)) {
      // Handle insufficient balance error
      console.log('Not enough balance');
    }
  }
}

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