⛏️Informational Vulnerability 11: `require` vs `assert`

Introduction

In Solidity, ensuring the correctness of a smart contract is crucial. Error handling mechanisms such as assert() and require() are vital tools that developers use to enforce correctness, validate external inputs, and handle errors in contracts. This tutorial aims to provide a clear understanding of when and how to use assert() and require() effectively in Solidity contracts, aiding in writing secure and robust code.

Exploring assert() and require()

1. Syntax and Usage

assert(condition)

  • Utilized to handle internal errors and check invariants within a contract.

  • Consumes all remaining gas when the condition fails.

require(condition, message)

  • Primarily used for input validation and external contract call checks.

  • Refunds the remaining gas when the condition is not met.

  • Allows for an optional error message to be added, enhancing error clarity.

2. When to Use assert() vs require()

Using assert()

assert() should be used to handle conditions that must always be true. These are fundamental invariants of your contract, and if they fail, it typically signifies a bug in the contract.

Using require()

require() is suitable for handling runtime errors. It’s commonly used to validate user inputs, ensuring conditions are met before execution, and validating responses from external contract calls.

Best Practices and Guidelines

Using assert() for Invariants

Since assert() consumes all remaining gas, it’s best utilized for internal invariant checks where failure indicates a significant issue in the contract logic.

solidityCopy codepragma solidity ^0.8.0;

contract AssertExample {
    uint256 public storedData;

    function set(uint256 x) public {
        storedData = x;
    }

    function verifyInvariant() public view {
        assert(storedData != 0);
    }
}

Using require() for Input Validation and External Calls

require() is more flexible due to its ability to refund unused gas and provide error messages. It should be the primary choice for validating external inputs and conditions.

solidityCopy codepragma solidity ^0.8.0;

contract RequireExample {
    function transfer(address recipient, uint256 amount) public {
        require(recipient != address(0), "Invalid recipient");
        require(amount > 0, "Amount must be positive");
        // transfer logic here
    }
}

Conclusion

Choosing between assert() and require() should be a deliberate decision based on the nature of the condition being checked. Utilize assert() for invariants and internal error checks where failure represents a bug. On the other hand, employ require() for validating external inputs and handling runtime errors, taking advantage of its gas refund capabilities and descriptive error messages. By following these guidelines and understanding the implications of assert() and require(), developers can enhance the reliability and robustness of their Solidity contracts.

Last updated