Solidity Error Handling: require, assert, and revert

Joshua ObafemiJoshua Obafemi
4 min read

Error handling lies at the core of writing secure and efficient smart contracts in Solidity. Whether you’re working on DeFi protocols, NFT marketplaces, or dApps, knowing how and when to use “require”, “assert”, and “revert” is crucial for preventing vulnerabilities and ensuring robust code execution.

Let’s see how these error-handling tools work, with a practical example of a contract checking which London football club deserves the title of "best" based on their UEFA Champions League (UCL) victories.

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

contract BestClub {
    mapping(string => bool) public hasWonUCL;

    constructor() {
        // Initialize clubs and their UCL status
        hasWonUCL["Chelsea"] = true;
        hasWonUCL["Arsenal"] = false;
        hasWonUCL["Tottenham"] = false;
        hasWonUCL["West Ham"] = false;
    }

    function checkBestClub(string memory club) public view returns (string memory) {
        // Use require to validate UCL victory status
        require(hasWonUCL[club], "Error: You must have won the UEFA Champions League!");
        return string(abi.encodePacked(club, " is the best club in London!"));
    }

    function checkBestClubRevert(string memory club) public view returns (string memory) {
        // Use revert for the same validation in a more complex conditional
        if (!hasWonUCL[club]) {
            revert("Error: You must have won the UEFA Champions League!");
        }
        return string(abi.encodePacked(club, " is the best club in London!"));
    }

    function internalInvariant() public pure {
        // Use assert to ensure a critical internal condition
        uint256 londonClubCount = 4; // Hypothetical number
        assert(londonClubCount > 0); // This should always be true
    }

    // Custom error for additional context
    error NotEligible(string clubName);

    function customErrorCheck(string memory club) public view {
        if (!hasWonUCL[club]) {
            revert NotEligible(club);
        }
    }
}

Validating Inputs and States with “require”

The “require” function is your go-to for validating external inputs and ensuring that the contract state aligns with expected conditions. If a condition fails, “require” reverts the transaction and allows for an optional error message.

When to Use “require”

  • Input Checks: Ensure function arguments meet specific criteria.

  • State Checks: Validate contract state before execution.

  • External Call Checks: Confirm successful external calls.

Example: Checking UCL Status

function checkBestClub(string memory club) public view returns (string memory) {
    require(hasWonUCL[club], "Error: You must have won the UEFA Champions League!");
    return string(abi.encodePacked(club, " is the best club in London!"));
}

Here, “require” ensures only clubs with a UCL title can be declared the "best." If you pass "Arsenal", the function reverts with the error:
Error: You must have won the UEFA Champions League!

Why “require”? It’s ideal for catching user errors early, providing meaningful feedback, and refunding unused gas, making it cost-effective.

Catching Critical Bugs with “assert”

“assert” is stricter, designed to enforce internal consistency and catch logic errors that should never occur. It’s used to verify invariants within your contract.

When to Use “assert”

  • Invariant Enforcement: Ensure certain conditions always hold true.

  • Bug Detection: Catch critical issues in contract logic.

Example: Verifying Club Count

function internalInvariant() public pure {
    uint256 londonClubCount = 4; // Hypothetical number
    assert(londonClubCount > 0); // This should always be true
}

If londonClubCount were ever less than or equal to zero, “assert” would fail, signaling a severe bug and consuming all remaining gas.

Why “assert”? Use it sparingly for logic that must not fail. When “assert” triggers, it indicates a fundamental issue requiring immediate attention.

Handling Complex Errors with “revert”

“revert” provides a more flexible way to handle errors, allowing for dynamic error messages and complex conditions. It explicitly halts execution and reverts all state changes.

When to Use “revert

  • Dynamic Validations: Handle multiple conditions with custom logic.

  • Custom Error Messages: Use detailed, contextual errors.

  • Fallback Scenarios: Provide a safety net for unexpected behavior.

Example: Validating Club Eligibility with “revert

function checkBestClubRevert(string memory club) public view returns (string memory) {
    if (!hasWonUCL[club]) {
        revert("Error: You must have won the UEFA Champions League!");
    }
    return string(abi.encodePacked(club, " is the best club in London!"));
}

For optimized error handling, Solidity allows custom errors:

error NotEligible(string clubName);

function customErrorCheck(string memory club) public view {
    if (!hasWonUCL[club]) {
        revert NotEligible(club);
    }
}

Why “revert”? It offers flexibility for complex error handling and is gas-efficient when paired with custom error types.

Comparing Solidity Error Handling Tools

Here’s a quick comparison to help you choose the right tool for the job:

Function

Purpose

Gas Refund

Error Message

Best Use Case

require

Validate external conditions

Refunds unused gas

Optional custom message

Input validation, state checks

assert

Enforce internal invariants

Consumes all gas

No message

Critical logic checks

revert

Dynamic error handling

Refunds unused gas

Optional custom message

Complex conditions, fallbacks

Custom Errors

Contextual error handling

Refunds unused gas

Defined in error type

Detailed, gas-efficient errors

Conclusion

Understanding and implementing effective error handling is crucial for building secure and user-friendly smart contracts. By mastering “require”, “assert”, and “revert”, you can ensure your contracts function reliably and protect against potential vulnerabilities.

Let’s keep the conversation going! Connect with me on Twitter, or LinkedIn.

0
Subscribe to my newsletter

Read articles from Joshua Obafemi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Joshua Obafemi
Joshua Obafemi

Software Developer || Web3 Advocate