Skip to content

Integer Overflow and Underflow

What are Overflows or Underflows?

In previous versions of Solidity (prior Solidity 0.8.x) an integer would automatically roll-over to a lower or higher number. If you would decrement 0 by 1 (0-1) on an unsigned integer, the result would not be -1, or an error, the result would simple be: MAX(uint).

For this example I want to use uint8. We haven't used different types of uint yet. In our previous example worked with uint256, but bear with me for a moment. Uint8 is going from 0 to 2^8 - 1. In other words: uint8 ranges from 0 to 255. If you increment 255 it will automatically be 0, if you decrement 0, it will become 255. No warnings, no errors. For example, this can become problematic, if you store a token-balance in a variable and decrement it without checking.

Let's do an actual example!

Sample Smart Contract

Create a new Smart Contrac with the name "RolloverExample.sol". We're going to use Solidity 0.7 for this example, as in Solidity 0.8 this behavior is different.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.0;

contract RolloverExample {
    uint8 public myUint8;

    function decrement() public {
        myUint8--;
    }

    function increment() public {
        myUint8++;
    }
}

Let's deploy the Smart Contract and see what happens if we call decrement.

Initially, myUint8 is 0:

If you press the "decrement" button, then the myUint8 is decremented by one. Let's see what happens, and also observe the Remix console:

The transaction works perfectly. No errors occur. If you retrieve the value for myUint8 then you see it's 255:

Increment Example

Try yourself what happens when you increment again. Does it roll over again without a warning?

Now you see one of the quirks with Solidity. It's not completely unique to Solidity, but definitely something to be aware of. This is where Libraries like SafeMath were invented, which you will see later.

But what happened in Solidity 0.8?

Solidity 0.8 Difference

In Solidity 0.8, the compiler will automatically take care of checking for overflows and underflows. Let's run the same example with Solidity 0.8. Create a new file and fill in the following Smart Contract:

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.0;

contract RolloverExample2 {
    uint8 public myUint8;

    function decrement() public {
        myUint8--;
    }

    function increment() public {
        myUint8++;
    }
}

Deploy the new version and try to hit "decrement". Now you will get an error in the Remix console:

Your variable "myUint8" will remain 0, because it cannot roll over anymore:

But what if you actually want to roll over? Then there is a new "unchecked" block you can wrap your variables around:

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.0;

contract RolloverExample2 {
    uint8 public myUint8;

    function decrement() public {
        unchecked {
            myUint8--;
        }
    }

    function increment() public {
        unchecked {
            myUint8++;
        }
    }
}

Then everything works again as you know it from previous Solidity versions. This is such an important quirks of Solidity that I wanted to bring it up early. Now, let's go back to some lighter topics.


Last update: April 17, 2021