freecoding.school100% FREE · NO SIGNUP
The EVM CoreISSUE #28 of 48

custom errors · revert data

SoliaVSThe Reentrancy Reaper
Solia saysCustom errors compile to a 4-byte selector plus ABI-encoded args — far cheaper than require(cond, "string message"), which embeds the full string in bytecode on revert.

Pre-0.8.4 the only way to revert with context was require(cond, "long message"). The string is embedded in the contract bytecode and re-encoded into revert data each call, costing real gas. Custom errors compile to a 4-byte selector (like a function call) plus ABI-encoded args, dropping the bytes-on-revert from dozens to a fixed handful.

They also carry structured data: revert InsufficientBalance(uint256 needed, uint256 actual) can be decoded by the frontend to display rich error messages. The demo compares the bytes-on-revert of a require-string vs the equivalent custom error.

Power-ups you unlock

The Reentrancy Reaper attacks — common mistakes

Boss battleCompare the calldata-on-revert bytes for require("...long string...") vs revert CustomError(needed, actual).

Example code

<!doctype html><html><head><meta charset="utf-8"></head>
<body style="background:#06040d;color:#e6e0ff;font-family:monospace;padding:20px"><pre id="o"></pre>
<script>
const errStr = 'ERC20: transfer amount exceeds balance';
const strLen = errStr.length;
// require("string"): Error(string) selector + offset(32) + length(32) + ⌈len/32⌉*32 bytes of string data
const stringDataBytes = Math.ceil(strLen / 32) * 32;
const requireBytes = 4 + 32 + 32 + stringDataBytes;
// custom error InsufficientBalance(uint256 needed, uint256 actual): 4 + 32 + 32
const customErrBytes = 4 + 32 + 32;
const savings = requireBytes - customErrBytes;
document.getElementById('o').textContent = [
  'message: "' + errStr + '"   (' + strLen + ' chars)',
  '',
  'require(cond, message) revert data:',
  '  4 (Error selector) + 32 (offset) + 32 (length) + ' + stringDataBytes + ' (string) = ' + requireBytes + ' bytes',
  '',
  'revert InsufficientBalance(uint256, uint256) revert data:',
  '  4 (selector) + 32 (needed) + 32 (actual) = ' + customErrBytes + ' bytes',
  '',
  'savings per revert: ' + savings + ' bytes (~' + Math.round(100*savings/requireBytes) + '%)',
  '  + structured args your frontend can decode'
].join('\n');
</script></body></html>
▶ Open the interactive comic issue
‹ Function Selectors · 4-Byte ClashingLibraries · Embedded Vs Linked · Using-For ›