Root + Impact
Description
In Solidity 0.8+, arithmetic operations revert on overflow.
In Yul assembly, add() does NOT check for overflow — it silently wraps around.
_mint() uses unchecked add() for both totalSupply and balance.
function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
let supply := sload(supplySlot)
@> sstore(supplySlot, add(supply, value))
let accountBalance := sload(accountBalanceSlot)
@> sstore(accountBalanceSlot, add(accountBalance, value))
}
}
Risk
Likelihood:
Impact:
totalSupply can wrap to 0 while tokens exist
User balance can wrap to 0, losing all tokens
Breaks invariant: totalSupply == sum(balances)
Proof of Concept
function test_BUG_MintOverflowTotalSupply() public {
token.mint(alice, type(uint256).max);
uint256 supplyBefore = token.totalSupply();
console.log("Supply after first mint:", supplyBefore);
token.mint(bob, 1);
uint256 supplyAfter = token.totalSupply();
console.log("Supply after second mint:", supplyAfter);
}
Recommended Mitigation
function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
// ...
let supply := sload(supplySlot)
+ let newSupply := add(supply, value)
+ if lt(newSupply, supply) { revert(0, 0) } // Overflow check
- sstore(supplySlot, add(supply, value))
+ sstore(supplySlot, newSupply)
let accountBalance := sload(accountBalanceSlot)
+ let newBalance := add(accountBalance, value)
+ if lt(newBalance, accountBalance) { revert(0, 0) } // Overflow check
- sstore(accountBalanceSlot, add(accountBalance, value))
+ sstore(accountBalanceSlot, newBalance)
}
}
---
## Finding #4 — Mint Missing Event
**Titre :**
_mint() does not emit Transfer event violating ERC20 standard