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();
}
}
}