The Underlying Issue
Table of Contents
In Solidity, developers can perform external calls using various methods, like
transfer(). However, each of these has a different behavior when it comes to error handling:
call()return a boolean value (
false) indicating the success or failure of the operation. A common pitfall is failing to check this return value. If the external call fails, these methods will not revert the entire transaction but will simply return
transfer(), conversely, reverts the entire transaction if the external call fails, serving as a more failsafe method for transferring Ether.
Failure to properly handle these return values or understand the nuances between these methods can lead to vulnerabilities in the smart contract, opening up opportunities for exploits.
The Lotto Contract
Let’s look at the example below:
The core issue lies in where the contract uses
send() without checking its return value. If the external call fails due to a variety of possible reasons (e.g., gas limitations, malicious fallback functions), the variable
payedOut would still be set to
This essentially allows anyone to withdraw the remaining funds using the
withdrawLeftOver() function even if the winner has not actually received their reward.
- Use Transfer Over Send: Whenever possible, use
send()since the former reverts the transaction if the external call fails.
- Check Return Values: Always validate the return value of
call()functions to take appropriate actions if they return
- Adopt a Withdrawal Pattern: Employing a withdrawal pattern ensures that the end-user will call a separate function to complete the transaction, thus allowing the contract to handle external call failures more gracefully.
- Implement Event Logging: By using events, contracts can log specific activities. If a function like send() or call() fails, this event can be recorded. This will provide transparency and traceability.
- Gas Limit Considerations: Always ensure that there’s adequate gas for operations. Though send() and transfer() automatically use a stipend of 2300 gas, other operations might require more.
The unchecked return values vulnerability may seem trivial, but it can lead to significant consequences, including fund loss.
Developers should not underestimate the power of return values and should adopt best practices to safeguard their contracts against such vulnerabilities.