Cover photo

Overflow, Underflow, and Safe Math in Solidity

What is Overflow and Underflow?

Integer types in Solidity have fixed size. For example, uint8 is 8-bit long, so the number with type uint8 is in the range from 0 to 2**8 - 1 (255).

Imagine there’s a number valued 255 with type uint8, what will happen when we add it by 1, or the number is 0 and we sub it by 1. So when we do arithmetic operations, we will run into two exceptions: the former is called overflow and the latter is called underflow.

There are two modes when performing arithmetic operations: wrapping (or unchecked) and checked. And checked mode is the default.

  • checked - Will revert on over- and underflow

  • unchecked - Will wrap on over- and underflow

Here, wrapping means we add a number valued 255 with type uint8 by 1, it will go down to value 0, like a clock goes from 23:59 to 00:00.

All arithmetic operations revert on over- and underflow by default. If you want to wrap the operation results, you can use unchecked :

contract A {
    function test(uint x, uint y) public returns (uint) {
        // This will revert on overflow
        uint a = x + y;
        // This will revert on underflow
        uint b = x - y;
        // This will wrap on overflow
        uint c;
        unchecked {
            c = x + y;
        }
        return c;
    }
}

Bitwise operators do not perform over- and underflow checks.

You can call functions in unchecked block, but these functions don’t inherit the property.

Safe Math

Before Solidity version 0.8, the compiler won’t check over- and underflow, instead of wrapping it, so we need to do the checks by manual. e.g.

library SafeMath {
    function add(uint a, uint b) internal returns (uint) {
        unchecked {
            uint c = a + b;
            require(c >= a, 'err: overflow');
            return c;
        }
    }
}

After Solidity 0.8, the compiler will check over- and underflow, so we don’t need SafeMath library anymore.