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?