Overflow and Underflow in Solidity: A Deep Dive
Tags: blockchain, tutorial, programming, solidity
Published: 2022-07-06 07:02:54

If you have ever tried to operate on numbers and gotten a counter-intuitive result then you have most likely experienced an overflow or an underflow in solidity.

The concept of Overflow and Underflow in Solidity results from a fundamental concept in computer science. In this post, I intend to guide you through the fundamentals to help you understand the why behind it.

I assume you understand the basics of Solidity and the basics of how binary numbers work.

Copy and paste the code below on remix to replicate an Overflow and an Underflow.

Deploy the code and let's walk through the code.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.0;

contract OverflowAndUnderflow {

    uint8 public balance;
    
    function underflow() external{
        balance--;
    }

    function overflow() external{
        balance = 255;
        balance++;
    }
}
```

- **Line 2**: I used version **0.7.0** of solidity because overflow and underflow checking is implemented by default in versions **0.8.0** and above. Using that, the compiler would throw an error when we try to call the functions below. Moreover, It's advised you use 0.8.0 or above when writing production code so you won't have to use libraries like SafeMath or your implementation to do the checking. This is purely experimental.
- **Line 6**: I declared the variable **balance** which is set to **0** by default.
- **Line 8 to Line 10**: We have the **underflow** function. It uses **line 9** to decrease the value of balance by 1. Call this function once and then check the new value of the balance; it's **255**. i.e **0** minus **1** just gave us **255**. That's an underflow.
- **Line 12 to Line 15**: We have the **overflow** function. It sets the value of **balance** to **255**. And then, increments it by **1**. Intuitively, the new balance value should be **256** but it's **0**. That's an overflow.

Haven replicated an underflow and overflow situation. Let's go through the fundamentals.

## Fundamentals of Overflow and Underflow

Binary is at the core of how computers store and process data. An 8-bit memory would only be able to store at most 8-bit representations of any form of data at any given time.

Programming languages also try to emulate this pattern to handle storage more efficiently. For example, if you wanted to store data that you know for sure won't exceed the 8-bit space why then would you create a 256-bit storage space for it?  It's a waste of storage space among other disadvantages and that's why languages like solidity give us the ability to store data in different storage spaces based on data size. For integers, we could store integers from the 8 bits up to 256 bits in steps of  8 using **int8**/**uint8** to **int256**/**uint256** respectively.

Imagine declaring an unsigned integer and setting the storage to 8-bit storage as illustrated below in solidity.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.0;

contract IntegerDeclarion {

    uint8 public a;

}
```

But, at the time of assignment, an integer exceeding the 8-bit storage space is passed to the variable a. What happens then? That's what overflow and underflow are all about. Overflows and underflows are a result of data exceeding the predefined storage space created for them.

So, how do you determine the minimum and maximum integer a bit size can store?

Here's the simple formula:

- **For unsigned integers**: **0** to **(2 ^ n ) - 1** where **n** is the number of bits. For example, for 8 bits: **0** to **(2 ^ 8) - 1** = **0** to **(256) - 1 **= **0** to **255**

- **For signed integers**: **(-2 ^ n -1)** to **(2 ^ n - 1) - 1** where **n** is the number of bits. For example, for 8 bits: **(-2 ^ 8 -1)** to **(2 ^ 8 - 1) - 1** = **(-2 ^ 7)** to **(2 ^ 7) - 1** = **-128** to **127**


Or, using Solidity:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

contract BitSize {

    // For Unsigned Integers
    uint8 public maxForUnsigned = type(uint8).max;

    uint8 public minForUnsigned = type(uint8).min;

    // For Signed Integers
    int8 public maxForSigned  = type(int8).max;

    int8 public minForSigned = type(int8).min;

}
```

To understand this better, think of it as how modulo arithmetic works. Values of arithmetic operations are wrapped around the range of numbers they can store. For example:

- **25 + 120** in **signed 8-bit(int8)** is **-111**. **25** plus **120** is **145** but the highest number that can be represented is **127** so instead of the next number being **128** it starts all over from the least value **-128** and counts up to **-127** then **-126**, **-125** up to **-111**


- **40 + 240** in **unsigned 8-bit(uint8)** is **24**. **40** plus **240** is **280** but the highest number that can be represented is **255** so instead of the next number being **256** it starts all over from the least value **0** then counts up to **1** then **2**, **3** up to **24**


- **0 - 1** in **unsigned 8-bit(uint8)** is **255**. **0** minus **1** is **-1** but the least number that can be represented is **0** so instead of the next number being **-1** it goes over to the top at **255**.


- **-128 - 5** in **signed 8-bit(int8)** is **123**. **-128** minus **5** is **-133** but the least number that can be represented is **-128** so instead of the following number being **-129** it starts all over from the top at **127** and counts down to **126** then **125**, **124** down to **123**

At this point, it is safe to say that the values in the range are like numbers on a clock and every number revolves around them.

Let's go a bit more in-depth by using binary numbers to explain the examples above.

Before we move on, here are a few things to take note of when solving in binary:

- In **signed binary arithmetic**, if the addition of **two positive numbers** gives a **-ve number** then an overflow/underflow has occurred.
- In **signed binary arithmetic**, if the addition of **two negative numbers** gives a **+ve number** then an overflow/underflow has occurred.
- In **unsigned binary arithmetic**, if there is a **carry from the MSB**(i.e for the **most significant bit**. it's the numbers on the left-most part of the binary digit. For example, in **00011001**, **0** is the most significant bit and in **10000000**, **1** is the most significant bit) then an overflow has occurred.
- In **signed binary arithmetic**, the MSB signifies if a number is a **-ve number** or **+ve number**. If the **MSB** is **0**, it's a **+ve number** while if the **MSB** is **1**, it's a **-ve number**.
- A simple trick to know if an overflow/underflow has occurred in **signed arithmetic** addition is if the **carry into the MSB** and **the carry out of the MSB** are different. If they are the same, an overflow/underflow has not occurred. For example, **1** & **1** and **0** & **0** means no overflow/underflow while **0** & **1** and **1** & **0** means there is an overflow/underflow. While for **unsigned arithmetic**, if there is a **carry out of the MSB** then there is an overflow/underflow.

The above is quite dense but I assure you you can understand the next part without understanding it. Feel free to go through it multiple times to understand it better but for now, you can just move to the next part.

The calculations:

- **25 + 120** in **signed 8-bit**: **25** is **00011001** and **120** is **01111000**. **00011001** + **01111000** is **10010001**. **10010001** is **-111** in **signed** binary arithmetic.
- **40 + 240** in **unsigned 8-bit**: **40** is **00101000** and **240** is **11110000**. **00101000** + **11110000** is **00011000**. **00011000** is **24** in **unsigned** binary arithmetic.
- **0 - 1** in **unsigned 8-bit**: **0** is **00000000** and **1** is **00000001**. **00000000** + **11111111**(i.e this is the **2's complement** of **00000001** which is **1**. We can't perform **0 - 1** rather we do **0 + (-1)** and **-1** is **11111111**) is **11111111**. **11111111** is **255** in **unsigned** binary arithmetic.
- **-128 - 5** in **signed 8-bit**: **-128** is **10000000** and **-5** is **11111011**. Using **-128 + (-5)** then **10000000** + **11111011** is **01111011**. **01111011** is 123 in **signed** binary arithmetic.

Use a [binary calculator](https://www.rapidtables.com/calc/math/binary-calculator.html) to try these out yourself. And, that's a wrap 

## Conclusion

As you might have noticed, this post is not about how to prevent an overflow or underflow rather it's about why you run into them. That being said, overflows and underflows are security threats and have been the cause of hacks in the past so make sure you prevent them by using solidity version 0.8.0 and above or using the SafeMath library when writing your solidity code.

This is quite complex to grasp right away so feel free to ask questions in the comment below. Please share if you found
this helpful. Happy learning.

This was originally posted [here](https://blockchaintotheworld.com/overflow-and-underflow-in-solidity/) on Blockchaintotheworld.com