Lesson 10 of 23
In Progress

Unchecked Call Failures

Unchecked call failures can occur when a contract calls an external contract and does not properly handle the case where the external contract call fails. These failures can result in vulnerabilities and exploits in your contract, and it is important to properly handle them in order to ensure the security of your contract. In this article, we’ll explore what unchecked call failures are and how to prevent them in your smart contracts.

What are Unchecked Call Failures?

Unchecked call failures occur when a contract calls an external contract and does not properly handle the case where the external contract call fails. This can happen if the external contract call throws an exception, or if the external contract call returns a false value.

Here is an example of a contract that is vulnerable to unchecked call failures:

pragma solidity ^0.6.0;

import "./VulnerableContract.sol";

contract UncheckedCallFailures {
    VulnerableContract public target;

    function callExternal() public {
        target.doSomething();
    }
}

In this contract, the callExternal function calls the doSomething function in the VulnerableContract contract. If the doSomething function throws an exception or returns a false value, the callExternal function will not properly handle the failure and the contract will be vulnerable to unchecked call failures.

How to Prevent Unchecked Call Failures

There are several ways to prevent unchecked call failures in your smart contracts. One common method is to use the require function to check the return value of the external contract call and throw an exception if the call fails.

Here is an example of a contract that uses the require function to prevent unchecked call failures:

pragma solidity ^0.6.0;

import "./SafeContract.sol";

contract SafeFromUncheckedCallFailures {
    SafeContract public target;

    function callExternal() public {
        require(target.doSomething());
    }
}

In this contract, the callExternal function calls the doSomething function in the SafeContract contract and checks the return value using the require function. If the doSomething function returns a false value, the require function will throw an exception and prevent the contract from being vulnerable to unchecked call failures.

Another method to prevent unchecked call failures is to use the revert function to throw an exception when an external contract call fails.

Here is an example of a contract that uses the revert function to prevent unchecked call failures:

pragma solidity ^0.6.0;

import "./SafeContract.sol";

contract SafeFromUncheckedCallFailures {
    SafeContract public target;

    function callExternal() public {
        if (!target.doSomething()) {
            revert();
        }
    }
}

In this contract, the callExternal function calls the doSomething function in the SafeContract contract and checks the return value using an if statement. If the doSomething function returns a false value, the revert function is called and an exception is thrown, preventing the contract from being vulnerable to unchecked call failures.

Conclusion

Unchecked call failures can be a serious vulnerability in smart contracts if not properly addressed. By using the require function or the revert function, you can ensure that your contracts are not vulnerable to these attacks and maintain their security. It is important to properly handle external contract calls in order to prevent unchecked call failures and keep your smart contracts secure.

Exercises

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

Write a Solidity contract that defines a function named callExternal that calls a function named doSomething in an external contract and does not properly handle the case where the external contract call fails.

pragma solidity ^0.6.0;

import "./VulnerableContract.sol";

contract UncheckedCallFailures {
    VulnerableContract public target;

    function callExternal() public {
        target.doSomething();
    }
}

Write a Solidity contract that defines a function named callExternal that calls a function named doSomething in an external contract and uses the require function to prevent unchecked call failures.

pragma solidity ^0.6.0;

import "./SafeContract.sol";

contract SafeFromUncheckedCallFailures {
    SafeContract public target;

    function callExternal() public {
        require(target.doSomething());
    }
}

Write a Solidity contract that defines a function named callExternal that calls a function named doSomething in an external contract and uses the revert function to prevent unchecked call failures.

pragma solidity ^0.6.0;

import "./SafeContract.sol";

contract SafeFromUncheckedCallFailures {
    SafeContract public target;

    function callExternal() public {
        if (!target.doSomething()) {
            revert();
        }
    }
}

Modify the UncheckedCallFailures contract from Exercise 1 to use the require function to prevent unchecked call failures.

pragma solidity ^0.6.0;

import "./VulnerableContract.sol";

contract SafeFromUncheckedCallFailures {
    VulnerableContract public target;

    function callExternal() public {
        require(target.doSomething());
    }
}

Modify the UncheckedCallFailures contract from Exercise 1 to use the revert function to prevent unchecked call failures.

pragma solidity ^0.6.0;

import "./VulnerableContract.sol";

contract SafeFromUncheckedCallFailures {
    VulnerableContract public target;

    function callExternal() public {
        if (!target.doSomething()) {
            revert();
        }
    }
}