Lesson 13 of 25
Error Handling
require, revert, assert — handle errors gracefully and save users gas.
Intermediaterequire — Input Validation
require(condition, message) is the standard way to validate inputs and state. If the condition is false, execution reverts with the message and all gas (above a threshold) is returned to the caller.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract RequireDemo {
uint256 public balance;
address public owner;
constructor() { owner = msg.sender; }
function deposit(uint256 amount) public {
require(amount > 0, "Amount must be positive");
require(amount <= 1000 ether, "Too large");
balance += amount;
}
function withdraw(uint256 amount) public {
require(msg.sender == owner, "Not owner");
require(amount <= balance, "Insufficient funds");
balance -= amount;
}
}
revert — Custom Errors (0.8.4+)
Custom errors are more gas-efficient than string messages. Define them at the top of your contract, then use
revert CustomError().
Solidity
contract CustomErrors {
// Define custom errors (gas efficient — no string storage)
error NotOwner(address caller, address owner);
error InsufficientBalance(uint256 requested, uint256 available);
error ZeroAmount();
address public owner;
mapping(address => uint256) public balances;
constructor() { owner = msg.sender; }
function withdraw(uint256 amount) public {
if (msg.sender != owner)
revert NotOwner(msg.sender, owner);
if (amount == 0)
revert ZeroAmount();
if (balances[msg.sender] < amount)
revert InsufficientBalance(amount, balances[msg.sender]);
balances[msg.sender] -= amount;
}
}
assert — Invariant Checks
assert(condition) is used to check for internal bugs — conditions that should NEVER be false if the contract is correct. Unlike require, assert failures consume ALL remaining gas (indicating a critical bug).
Solidity
contract AssertDemo {
uint256 public totalSupply;
mapping(address => uint256) public balances;
function mint(address to, uint256 amount) public {
balances[to] += amount;
totalSupply += amount;
// This should always be true — if not, there's a critical bug
assert(totalSupply >= balances[to]);
}
}
try / catch — External Call Errors
Handle errors from external contract calls using
try/catch.
Solidity
interface IExternal {
function riskyOperation() external returns (uint256);
}
contract TryCatchDemo {
event Success(uint256 result);
event Failed(string reason);
function callExternal(address target) public {
try IExternal(target).riskyOperation() returns (uint256 result) {
emit Success(result);
} catch Error(string memory reason) {
emit Failed(reason); // revert with message
} catch {
emit Failed("Unknown error"); // panic or low-level revert
}
}
}