Lesson 13 of 23
In Progress

Proper Handling of Exceptions and Failures

Smart contracts are self-executing contracts with the terms of the agreement between buyer and seller being directly written into lines of code. As a result, it is important for smart contract developers to properly handle exceptions and failures to ensure the smooth and secure operation of the contract.

In this article, we will explore the different types of exceptions and failures that can occur in smart contracts, and discuss best practices for handling them.

Types of Exceptions and Failures in Smart Contracts

There are several types of exceptions and failures that can occur in smart contracts:

Revert Exceptions

A revert exception occurs when a contract execution is invalidated and all changes made during the execution are rolled back. This can happen when a contract encounters an error or when a condition specified in the contract is not met.

For example, consider the following contract:

pragma solidity ^0.6.0;

contract RevertException {
    uint public balance;

    function withdraw(uint amount) public {
        require(amount <= balance, "Insufficient balance.");
        balance -= amount;
    }
}

In this contract, the withdraw function includes a require statement that checks if the amount being withdrawn is less than or equal to the current balance. If the amount is greater than the balance, the require statement will throw a revert exception, rolling back the contract execution and preventing the withdrawal from occurring.

Out of Gas Exceptions

An out of gas exception occurs when a contract execution runs out of gas, which is a measure of the computational resources required to execute the contract. Every Ethereum transaction has a gas limit, which specifies the maximum amount of gas that can be used to execute the transaction. If a contract execution exceeds the gas limit, it will throw an out of gas exception and the transaction will fail.

For example, consider the following contract:

pragma solidity ^0.6.0;

contract OutOfGasException {
    uint[] public data;

    function addData(uint _data) public {
        data.push(_data);
    }
}

Now that we have a better understanding of the different types of exceptions and failures that can occur in smart contracts, let’s discuss some best practices for handling them.

One of the best practices for handling exceptions in smart contracts is to use the revert function. The revert function allows you to throw an exception and revert all changes made during the contract execution.

pragma solidity ^0.6.0;

contract RevertFunction {
    uint public balance;

    function withdraw(uint amount) public {
        if (amount > balance) {
            revert("Insufficient balance.");
        }
        balance -= amount;
    }
}

In this contract, the withdraw function includes an if statement that checks if the amount being withdrawn is greater than the current balance. If the amount is greater, the contract throws a revert exception using the revert function and rolls back the execution.

Using the revert function is preferred over using the require function because it allows you to provide a custom error message, which can be helpful for debugging and tracking down issues.

Use the require Function in Moderation

While the revert function is generally preferred for handling exceptions in smart contracts, there may be cases where the require function is more appropriate. The require function is a built-in Solidity function that checks if a condition is true, and throws a revert exception if the condition is false.

However, it’s important to use the require function in moderation, as it can lead to issues if overused. For example, if you use the require function to check for every possible exception and failure, the contract may become unnecessarily complex and hard to read.

Instead, try to use the require function for critical checks that are essential for the correct operation of the contract, and use the revert function for less critical checks or for providing custom error messages.

Conclusion

Properly handling exceptions and failures is essential for the smooth and secure operation of smart contracts. By using the revert function and the require function in moderation, you can ensure that your contracts are robust and reliable.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Create a smart contract that has a function that accepts a string and throws a revert exception if the string is empty.

pragma solidity ^0.6.0;

contract RevertExceptionExercise {
    function checkString(string memory _str) public {
        require(_str.length > 0, "String must not be empty.");
    }
}

Create a smart contract that has a function that accepts an array and throws a revert exception if the array is too large (more than 10 elements).

pragma solidity ^0.6.0;

contract RevertExceptionExercise {
    function checkArraySize(uint[] memory _arr) public {
        require(_arr.length <= 10, "Array must not have more than 10 elements.");
    }
}

Create a smart contract that has a function that accepts a string and a uint and throws a revert exception if the string is empty and the uint is 0.

pragma solidity ^0.6.0;

contract RevertExceptionExercise {
    function checkInputs(string memory _str, uint _num) public {
        require(_str.length > 0 && _num > 0, "String must not be empty and uint must not be 0.");
    }
}

Create a smart contract that has a function that accepts a uint and throws an out of gas exception if the uint is too large (more than 1,000,000).

pragma solidity ^0.6.0;

contract OutOfGasExceptionExercise {
    function checkNumber(uint _num) public {
        if (_num > 1000000) {
            uint[] memory data = new uint[](_num);
        }
    }
}

Note: The above contract will throw an out of gas exception if the _num input is larger than 1,000,000 because the contract will try to create an array of that size, which will require more gas than the transaction’s gas limit.

Create a smart contract that has a function that accepts a bool and throws a revert exception if the bool is false.

pragma solidity ^0.6.0;

contract RevertExceptionExercise {
    function checkBoolean(bool _bool) public {
        require(_bool, "Bool must be true.");
    }
}