🎓 A1Learner
🔍
0 of 48 lessons completed

Solidity Custom Errors

A custom error is a named failure that can carry data. A contract throws it with revert instead of a reason string.

A custom error carries data with the failure

The error names the two amounts involved. The contract throws it when the withdrawal is too large.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
error InsufficientBalance(uint256 have, uint256 want);

contract Vault {
    uint256 public balance;

    function deposit(uint256 amount) public {
        balance += amount;
    }

    function withdraw(uint256 amount) public {
        if (amount > balance) {
            revert InsufficientBalance(balance, amount);
        }
        balance -= amount;
    }
}

A contract-level error guards one caller

NotOwner lives inside Guarded. Only the owner passes the check, so a call from another address fails.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract Guarded {
    error NotOwner();

    address public owner;

    constructor() {
        owner = msg.sender;
    }

    function secret() public view returns (uint256) {
        if (msg.sender != owner) {
            revert NotOwner();
        }
        return 42;
    }
}

One error can be thrown from many functions

ZeroAmount is declared once. Both functions throw it when they receive zero.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
error ZeroAmount();

contract Steps {
    uint256 public total;

    function add(uint256 n) public {
        if (n == 0) {
            revert ZeroAmount();
        }
        total += n;
    }

    function subtract(uint256 n) public {
        if (n == 0) {
            revert ZeroAmount();
        }
        total -= n;
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
error ZeroAmount();
error InsufficientBalance(uint256 have, uint256 want);

contract Bank {
    uint256 public balance;

    function deposit(uint256 amount) public {
        if (amount == 0) {
            revert ZeroAmount();
        }
        balance += amount;
    }

    function withdraw(uint256 amount) public {
        if (amount == 0) {
            revert ZeroAmount();
        }
        if (amount > balance) {
            revert InsufficientBalance(balance, amount);
        }
        balance -= amount;
    }
}

Key points

  • Custom error: a named failure declared with the error keyword, with optional data fields.
  • Use revert with a custom error to report a failure and its data together.
  • A custom error can be declared at file level or inside a contract.
  • The same custom error can be thrown from many functions.
  • A custom error costs less gas than a long reason string.

In this example, Bank declares two errors at file level. Its deposit function throws ZeroAmount for a zero deposit. Its withdraw function throws ZeroAmount for zero and InsufficientBalance when the amount is larger than the balance.

Note

a custom error replaces a reason string, it does not replace the revert itself.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

When is a custom error the better choice over a reason string?