Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: medium

Mint Overflow Leading to Supply and Balance Corruption

Author Revealed upon completion

Mint Overflow Leading to Supply and Balance Corruption

Description

  • The expected behavior of _mint() is to increase an account’s balance and the global _totalSupply by the specified value, ensuring proper token creation.

  • In the current implementation, these increments are performed inside Yul using unchecked add() operations. Since Yul does not include Solidity’s built-in overflow protection, minting values near uint256.max causes both the account balance and total supply to overflow and wrap to zero. This breaks ERC20 accounting and allows supply corruption.

function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
...
let supply := sload(supplySlot)
// @> Overflow here wraps totalSupply to zero or small values
sstore(supplySlot, add(supply, value))
...
let accountBalance := sload(accountBalanceSlot)
// @> Overflow here resets account balance due to wrapping
sstore(accountBalanceSlot, add(accountBalance, value))
}
}

Risk

Likelihood:

  • Overflow occurs whenever the minted amount approaches or exceeds the uint256 limit due to raw Yul arithmetic.

  • The function accepts arbitrary value inputs, making this overflow easy to trigger.

Impact:

  • Supply corruption: _totalSupply wraps around and becomes incorrect.

  • Balance corruption: user balances reset to zero after overflow.

Impact Level: High

Proof of Concept

A test that first mints uint256.max tokens and then mints one more token to trigger overflow. The overflow resets the balance back to zero, confirming the issue.

function test_mintOverflow() public {
uint256 amount = type(uint256).max;
address account = makeAddr("account");
token.mint(account, amount);
uint256 balance1 = token.balanceOf(account);
console.log("account balance after mint:", balance1);
uint256 extraAmountForOverflow = 1;
token.mint(account, extraAmountForOverflow);
uint256 balance2 = token.balanceOf(account);
console.log("account balance after overflow mint:", balance2);
assert(balance2 < balance1);
}
Console Output
[PASS] test_mintOverflow()
Logs:
account balance after mint:
115792089237316195423570985008687907853269984665640564039457584007913129639935
account balance after overflow mint: 0

Recommended Mitigation

Add explicit overflow checks prior to performing the arithmetic operations. If overflow is detected, revert early. Additionally, avoid using Yul for critical accounting unless necessary, as Solidity already provides safe arithmetic with built-in checks.

- sstore(supplySlot, add(supply, value))
+ if lt(add(supply, value), supply) {
+ // revert with appropriate custom error
+ revert(0, 0)
+ }
+ sstore(supplySlot, add(supply, value))
- sstore(accountBalanceSlot, add(accountBalance, value))
+ if lt(add(accountBalance, value), accountBalance) {
+ // revert with appropriate custom error
+ revert(0, 0)
+ }
+ sstore(accountBalanceSlot, add(accountBalance, value))

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!