Handle Errors in Solidity With Require and Revert

A good programmer is someone that can imagine and handle all the cases that will happens during the execution of the program. For this, Solidity offers different functions that we’ll cover in this article: require and revert.

Require

The require Solidity function guarantees validity of the condition(s) passed as parameter that cannot be detected before execution. It checks inputs, contract state variables and return values from calls to external contracts.

Solidity manages errors by using state-reverting exceptions. These exceptions revert all modifications done to the state in the current call, including all sub-calls. Additionally, it can show an error to the caller if a string is passed as parameter.

Require is convenient for checking inputs of a function especially in modifiers for example. A common use case for example is to check if the caller of the function is really the owner of the smart contract:

 modifier onlyOwner() {
        require(msg.sender == owner);
        _;
 }

or in the case of an ERC20 token, checking if the address sending tokens has enough tokens to perform the transfer:

 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
        require(numTokens <= balances[msg.sender]);
        balances[msg.sender] = balances[msg.sender].sub(numTokens);
        balances[receiver] = balances[receiver].add(numTokens);
        emit Transfer(msg.sender, receiver, numTokens);
        return true;
}

The require function allows you to pass a second parameter to describe the error that will be seen by the user as a string. For eg:

require(msg.sender == owner, "Caller is not the owner");

If we execute the transaction from a different account than the owner of the smart contract, the following will happen on Remix logs.

When the conditions of a require function are not met, all the changes made inside of the transaction are reverted and the remaining gas is returned to the user.

Revert

Revert is quite similar to require but does only accept one optional parameter: a string explaining why the revert happened. The revert function stops the execution of the smart contract, revert all changes that were made during the execution and return the remaining gas to the caller.

Revert can be used instead of require when it makes it easier to read the condition on why it fails. For example we could write the two previous examples like this:

 modifier onlyOwner() {
        if (msg.sender != owner) {
            revert("Caller is not the owner");
        }
        _;
 }

or in the case of the ERC20 transfer function:

function transfer(address receiver, uint256 numTokens) public override returns (bool) {
        if (numTokens > balances[msg.sender]) {
            revert("Not enough tokens to transfer");
        }
        balances[msg.sender] = balances[msg.sender].sub(numTokens);
        balances[receiver] = balances[receiver].add(numTokens);
        emit Transfer(msg.sender, receiver, numTokens);
        return true;
}

In conclusion you can see that Solidity offers everything you need to handle any cases that your contract will meet during execution. We always advice you to think about who is calling your code, where are you transferring value to and from who, are the inputs correct and is the output of an external function correct?

Leave a Reply

Your email address will not be published. Required fields are marked *