Free snapshot slots open this week info@inversez.com
DetectionVulnerabilitiesToolsAuditsPricingBlogFree monitoring assessmentContact
Home/Vulnerabilities/Integer overflow and underflow
SWC-101 · Typically high

Integer overflow and underflow

Before Solidity 0.8.0, arithmetic wrapped silently: subtracting 1 from 0 in a uint256 produced the maximum possible value. Since 0.8.0 the compiler inserts overflow checks by default, which removed most of this class. It still appears in older code, inside unchecked blocks, and anywhere assembly is used.

Vulnerable code

Do not ship this
// pragma solidity ^0.7.0 — no built-in checks
function transfer(address to, uint256 amt) public {
    balance[msg.sender] -= amt;    // underflows to a huge number
    balance[to] += amt;
}

// Or, in modern Solidity, the same bug reintroduced:
function deduct(uint256 amt) public {
    unchecked {
        balance[msg.sender] -= amt;   // checks disabled here
    }
}

Why it breaks

In the first function there is no balance check at all. Calling it with an amount larger than your balance underflows the subtraction, wrapping your balance to a number near 2256. You now hold effectively infinite tokens. The second version compiles under Solidity 0.8.x but the unchecked block explicitly turns the compiler's protection off, restoring the original bug.

Watch the attack run

The same bug as a stepped sequence. Play it, or walk through with the arrow keys.

The fix

Corrected
// pragma solidity ^0.8.0
function transfer(address to, uint256 amt) public {
    require(balance[msg.sender] >= amt, "insufficient balance");
    balance[msg.sender] -= amt;    // reverts on underflow anyway
    balance[to] += amt;
}

// If you use unchecked for gas, prove the bound first:
for (uint256 i; i < len; ) {
    // ... i cannot overflow because len is bounded by array length
    unchecked { ++i; }
}

The invariant

The fix above is what an audit gives you. This is what monitoring gives you: the thing that should always be true once the contract is live, stated precisely enough to check every block.

InvariantEVM · integer overflow

Total supply never decreases on a mint, and no single balance ever exceeds total supply.

invariant  totalSupply(t) >= totalSupply(t-1)  on mint
invariant  max(balanceOf(a)) <= totalSupply()
Backtest: not yet run. This entry carries the reasoning, not the evidence. When the invariant has been replayed against the real transactions around the exploit, the firing block, how far ahead it fired, and its false-positive rate over a clean control period will be published here, along with the recording of the run.

How to test for it

A finding you cannot reproduce is an opinion. Write the test before you write the fix, watch it fail, then make it pass.

function testCannotUnderflow() public {
    vm.expectRevert();
    token.transfer(bob, 1);   // caller holds 0
}

// Better: fuzz it. Foundry will search the input space for you.
function testFuzzTransfer(uint256 amt) public {
    amt = bound(amt, 0, type(uint256).max);
    if (amt > token.balanceOf(address(this))) vm.expectRevert();
    token.transfer(bob, amt);
}

Where it has caused real losses

Several ERC-20 tokens in 2018 carried a 'batchTransfer' overflow that let an attacker mint astronomical balances by passing a crafted array. Exchanges suspended trading on the affected tokens. The underlying bug was a multiplication that overflowed before the balance check ran.

What we check during review

Two places this still bites in 2026. Downcasting: uint128(someUint256) truncates silently rather than reverting, so a value above the smaller type's range wraps. Use OpenZeppelin's SafeCast. And assembly blocks, where none of the compiler's checks apply at all: every arithmetic operation in Yul is unchecked by definition.

Want us to check your contract for this?

A free monitoring assessment covers one contract up to 200 lines, manually reviewed, findings back within 72 hours. This class is on the checklist for every review we run.

Free monitoring assessment

Found this useful?

We publish these because the alternative, asking you to trust us, is worth less. Send a contract and we’ll apply the same thinking to your code.

  • Reply written by the auditor who read your code
  • No sales sequence, no drip campaign, no retargeting
  • We’ll tell you if you don’t need a paid audit yet
  • Report published only with your written permission

Request your snapshot

We read every submission. If it doesn’t fit the free tier we’ll say so and quote you instead, no obligation.