Create your own
Lesson illustration

Preventing Arithmetic Over/Underflow in Unchecked Blocks

Hello! Welcome to your fifth lesson in the "Advanced Solidity & Secure Development" module.

Introduction

In our previous lesson, we established the importance of access control, learning to implement and audit Ownable and AccessControl patterns to manage who can call sensitive functions.

Today, we address another critical area of smart contract security: arithmetic integrity. While modern programming languages often shield developers from low-level numerical quirks, the EVM's fixed-size integer types and the high stakes of financial applications make this a vital topic. An error in calculation isn't just a bug; it can lead to a catastrophic loss of funds.

Learning Outcome: By the end of this lesson, you will be able to identify and prevent arithmetic issues (overflow/underflow), particularly when using unchecked blocks for gas optimization.

We will explore what overflow and underflow are, how Solidity's behavior has evolved to mitigate them, and critically, how to use the unchecked keyword to save gas without reintroducing these dangerous vulnerabilities.

1. The Core Problem: Integer Overflow and Underflow

Given your background in electronics and radiophysics, you're likely familiar with how digital systems represent numbers using a fixed number of bits. A standard 8-bit register, for example, can only represent 256 distinct values (0 to 255). The EVM operates on similar principles, primarily using 256-bit unsigned integers (uint256).

When a calculation exceeds the maximum value a variable can hold, it "wraps around" to the minimum value. This is an overflow. Conversely, subtracting from the minimum value causes it to wrap around to the maximum. This is an underflow. Think of a car's odometer: if it's maxed out at 999,999, driving one more mile makes it roll over to 000,000.

This article provides an excellent, detailed explanation of the concept.

Ethereum Smart Contracts Vulnerabilities: Integer Overflow ...

This article from Dreamlab Technologies clearly defines arithmetic overflow and underflow in the context of the EVM's fixed-size data types.

Please read the first section, 'Arithmetic Underflow and Overflow Overview'. Focus on the definitions of uint and int, and the 'wrapping around' behavior illustrated by the diagrams.

2. The Evolution of Arithmetic Safety in Solidity

This "wrapping" behavior was the default in Solidity versions before 0.8.0. It was a notorious source of hacks, where attackers could manipulate balances or other critical values by intentionally causing an overflow or underflow.

To combat this, developers relied on external libraries, most famously OpenZeppelin's SafeMath, which provided functions that checked for these conditions before performing an operation.

A major security enhancement came with Solidity version 0.8.0. Since this version, the compiler automatically includes checks for overflow and underflow on all standard arithmetic operations (+, -, *, etc.). If an overflow or underflow occurs, the transaction reverts.

This change made SafeMath largely obsolete for projects using modern Solidity versions and significantly improved the baseline security of the ecosystem.

3. The Trade-off: Gas Optimization with unchecked Blocks

While automatic safety checks are a huge win for security, they aren't free. Each check consumes a small amount of gas. For operations inside a loop or in a function that is called very frequently, these costs can add up.

To give developers control over this trade-off, Solidity introduced the unchecked block. Any arithmetic inside an unchecked { ... } block will not be checked for overflow or underflow, reverting to the old "wrapping" behavior. This is a powerful tool for gas optimization, but it puts the responsibility for safety squarely back on you, the developer.

Let's watch a video that introduces this concept and demonstrates the potential gas savings.

Spend Less Gas - Solidity Gas Optimization [Deep Dive]

This video from Moralis for Developers provides a great introduction to unchecked arithmetic, explaining its purpose and showing a practical example of the gas savings.

Please watch the segment from 04:40 to 12:34. Pay attention to: The historical context of SafeMath. How to implement an unchecked block. The measured difference in gas cost. The final, crucial advice on when it is safe to use.

Now that you have the concept, let's look at some precise code patterns and rules for using unchecked safely.

12 Solidity Gas Optimization Techniques

This Alchemy article offers a concise, practical guide on exactly when and how to use unchecked blocks for gas optimization, complete with clear code examples.

Read section '10. Use unchecked arithmetic safely'. Focus on the distinction between safe and unsafe scenarios, and especially the pattern of using a require statement before an unchecked block.

To drive home the risk, let's look at a code example where using unchecked incorrectly reintroduces the classic vulnerability.

Ethereum Smart Contracts Vulnerabilities: Integer Overflow ...

Let's revisit the Dreamlab Technologies article to see a concrete example of how an unchecked block can be vulnerable.

In the 'Examples of the Vulnerability' section, find the code snippet labeled 'Image 8: Using unchecked'. This shows the old vulnerable overflow.sol contract adapted to Solidity 0.8.13, demonstrating that unchecked explicitly enables the dangerous behavior.

4. A Practical Checklist for Using unchecked

Based on what we've learned, here is a simple checklist to follow when considering an unchecked block. Only use it if you can confidently answer "yes" to one of these questions:

  1. Is this a loop counter that cannot realistically overflow?

    • Pattern: for (uint i = 0; i < array.length; ) { ... unchecked { ++i; } }
    • Reasoning: The loop is bounded by array.length. For a uint256 counter to overflow, the array would need to have 2^256 elements, which is physically impossible. This is the most common and safest use case.
  2. Have I placed a check immediately before the unchecked block that makes the operation mathematically safe?

    • Pattern: require(x >= y, "Error: Insufficient balance"); unchecked { result = x - y; }
    • Reasoning: The require statement explicitly prevents underflow. The operation within the unchecked block is therefore guaranteed to be safe.
  3. Is the operation safe due to the types involved?

    • Pattern: uint128 a = 100; uint128 b = 200; uint256 c; unchecked { c = a + b; }
    • Reasoning: The sum of two uint128 variables can never overflow a uint256. You are certain of the mathematical bounds.

When to AVOID unchecked:

  • Never wrap arithmetic that involves user-supplied inputs without strict validation before the block.
  • Be extremely cautious with multiplication (*) and exponentiation (**), as values can grow exponentially and overflow unexpectedly.

Conclusion

You've now explored one of the most classic and dangerous bug classes in smart contract development. Understanding the trade-off between the default safety of modern Solidity and the gas-saving potential of unchecked blocks is a mark of an advanced developer.

Key Takeaways:

  • Overflow/Underflow: Occur when arithmetic operations exceed the minimum or maximum value of a fixed-size integer type, causing the value to "wrap around."
  • Solidity >=0.8.0: Provides built-in protection by reverting transactions on overflow or underflow.
  • unchecked Blocks: A feature for gas optimization that disables these safety checks. It should be used with extreme care.
  • Safe Usage: The most common safe patterns for unchecked are for incrementing bounded loop counters and for operations that are preceded by an explicit require check.

Next Steps:

We've now covered reentrancy, access control, and arithmetic safety—three pillars of on-chain security. In our next lesson, we'll shift focus slightly to how smart contracts communicate information efficiently to the outside world. We will learn to use events with indexed parameters for efficient off-chain data querying, a crucial skill for building responsive dApp front-ends.

Can't find a good explanation? Sign up and we'll make it for you

Sign up