Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome back to our module on smart contract security.
In our last lesson, we tackled reentrancy attacks, a major vulnerability that stems from flawed interaction patterns. You learned how to secure your contracts using the crucial Checks-Effects-Interactions pattern. Today, we continue strengthening our defenses by focusing on another fundamental, yet historically devastating, class of bugs: integer overflow and underflow.
This topic is especially important for anyone building financial applications on the blockchain. In many backend languages you've used, like Java, an integer overflow might throw an explicit error or exception. However, in older versions of Solidity, the behavior was to silently "wrap around," a quirk that attackers have exploited to steal funds or break contract logic.
By the end of this lesson, you will be able to prevent integer overflow and underflow vulnerabilities, understanding both the classic SafeMath library solution and, more importantly, the built-in protections offered by modern Solidity versions.
1. What Are Integer Overflow and Underflow?
In computing, integer types have a fixed size, meaning they can only represent a finite range of numbers. For example, a uint8 (an 8-bit unsigned integer) can store values from 0 to 255.
- Integer Overflow: Occurs when you perform an operation that results in a number larger than the maximum value for that type. Instead of throwing an error, the number "wraps around" to the minimum value. For a
uint8, adding 1 to 255 results in 0. - Integer Underflow: Occurs when an operation results in a number smaller than the minimum value. For a
uint8, subtracting 1 from 0 results in 255.
Think of it like a car's odometer with a fixed number of digits. If it's maxed out at 999,999, driving one more mile makes it roll over to 000,000. This is an overflow.

To see this behavior in action within a Hardhat testing environment, let's watch a video that demonstrates it clearly.
How to Hack Solidity Smart Contracts with Integer Overflow
This video from Dapp University provides a fantastic hands-on demonstration of what integer overflow is and how it behaves in Solidity code.
Please watch from 00:50 to 06:55. The video first explains the concept of unsigned integers and their maximum values, then uses a Hardhat test to show a uint8 variable overflowing back to zero. This practical example will make the abstract concept very concrete.
While the video uses a uint8 for a simple demonstration, the same principle applies to uint256, the most common integer type in Solidity. The maximum value is just astronomically larger (2^256 - 1), but it can still be overflowed, especially with multiplication or exponentiation.
2. How Overflow Vulnerabilities are Exploited
This "wrapping" behavior becomes a critical security risk when it's used to manipulate values like token balances or time locks. Imagine an ERC-20 token contract where an attacker could trigger an underflow to give themselves a near-infinite balance.
Let's look at how such an exploit works and introduce the historical solution: OpenZeppelin's SafeMath library.
Arithmetic Overflow and Underflow | Hack Solidity (0.6)
The 'Smart Contract Programmer' channel demonstrates a clever hack on a TimeLock contract, exploiting an overflow to bypass a waiting period. This perfectly illustrates the danger.
Watch the segment from 04:59 to 09:28. You don't need to follow every line of the attack code, but focus on the core idea: the attacker passes a very large, specially crafted number to the increaseLockTime function to make the lockTime variable overflow and reset to 0, allowing an immediate withdrawal.
3. The Evolution of Protection
The good news is that the Solidity language has evolved significantly to protect developers from this vulnerability.
Solution 1: The SafeMath Library (For Solidity < 0.8.0)
For many years, the standard way to prevent these attacks was to use OpenZeppelin's SafeMath library. This library doesn't provide new arithmetic operators; instead, it provides functions like add(), sub(), mul(), and div(). These functions perform the standard operation but include a check that causes the transaction to revert if an overflow or underflow would have occurred.
Here's how you would have used it:
// pragma solidity ^0.7.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract SafeContract {
using SafeMath for uint256;
uint256 public myValue = 1;
function safeAdd(uint256 _add) public {
// Instead of myValue = myValue + _add;
myValue = myValue.add(_add); // This will revert on overflow
}
}
Now, let's see how SafeMath is used to fix the TimeLock contract from the previous video.
Arithmetic Overflow and Underflow | Hack Solidity (0.6)
Let's return to the 'Smart Contract Programmer' video to see how SafeMath is used to patch the vulnerability.
Watch from 09:28 to 11:16. The key takeaway is how the simple + operator is replaced with the .add() function from SafeMath, which automatically prevents the overflow and stops the attack.
Solution 2: Built-in Checks (Solidity >= 0.8.0)
This is the most important takeaway for your work. Starting with Solidity version 0.8.0, the compiler automatically checks for overflow and underflow on all standard arithmetic operations. If an operation would result in an overflow or underflow, the transaction reverts.
This was a massive security improvement for the entire ecosystem and makes the SafeMath library unnecessary for most use cases in modern projects.
Let's see the powerful effect of simply changing the compiler version.
How to Hack Solidity Smart Contracts with Integer Overflow
We'll go back to the first video from Dapp University, which has an excellent demonstration of this built-in protection.
Watch from 10:44 to 11:41. The instructor changes the pragma version to 0.8.0 and re-runs the same test that previously passed due to overflow. You'll see the test now fails with an explicit error, proving the built-in protection is working.
For any new contract you write in this course, you will be using a Solidity version of 0.8.0 or higher, meaning you are protected by default.
Test your understanding!
You are reviewing a simple token contract written with pragma solidity ^0.7.5;. You see the following function:
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
balances[msg.sender] = balances[msg.sender] - _amount;
balances[_to] = balances[_to] + _amount;
}
- Identify the potential integer overflow vulnerability in this function.
- Describe two different ways to fix it.
Show answer
-
Vulnerability: The line
balances[_to] = balances[_to] + _amount;is vulnerable to an integer overflow. If the recipient's balance is already close to the maximum value of auint256, receiving a large_amountcould cause their balance to overflow and wrap around to a small number, effectively destroying their tokens. -
Two Fixes:
- The
SafeMathapproach: Import OpenZeppelin'sSafeMathlibrary and rewrite the lines using the safe functions:balances[msg.sender] = balances[msg.sender].sub(_amount);andbalances[_to] = balances[_to].add(_amount);. - The Modern approach: The best and simplest fix is to update the compiler version to
pragma solidity ^0.8.0;or higher. With this change, the standard+and-operators will automatically check for overflow/underflow and revert the transaction if it occurs, making the code safe without any other changes.
- The
A Quick Note on Gas Optimization: unchecked blocks
Since you're on a path to becoming an expert, it's good to know about advanced features. The automatic safety checks in Solidity 0.8.0+ add a small gas cost to every arithmetic operation. For code that runs in a loop or is called very frequently, developers might want to optimize this.
Solidity provides the unchecked { ... } block for this purpose. Any arithmetic inside an unchecked block will not be checked for overflow or underflow, reverting to the old, dangerous "wrapping" behavior.
// pragma solidity ^0.8.19;
function uncheckedExample(uint x, uint y) public pure returns (uint) {
// This is safe ONLY if you are 100% sure x + y cannot overflow.
unchecked {
return x + y;
}
}
You should use unchecked blocks with extreme caution. A common and relatively safe use case is for incrementing counters inside for loops where the loop's upper bound makes an overflow mathematically impossible. Otherwise, unless you have performed a rigorous analysis, it is best to rely on the default safety checks.
Conclusion
You've now covered another cornerstone of smart contract security. Understanding how low-level arithmetic works in the EVM is essential for writing secure and reliable code.
Key Takeaways:
- Integer Overflow/Underflow is when a number "wraps around" after exceeding the maximum or minimum value of its data type.
- In Solidity versions before 0.8.0, this was a silent failure and a major security hole. The standard solution was to use OpenZeppelin's
SafeMathlibrary. - In Solidity versions 0.8.0 and higher, overflow and underflow checks are automatically included by the compiler, causing the transaction to revert. This is a critical security feature and makes
SafeMathlargely obsolete for new projects.
Preview of the Next Lesson:
We've secured our contract against reentrancy and arithmetic errors. Next, we'll focus on permissions and control. You will learn how to implement ownership-based access control using OpenZeppelin's Ownable contract, a fundamental pattern for creating administrative functions like minting new tokens or pausing a contract.