Root + Impact
Description
-
In a standard ERC20, burning more tokens than the account balance should revert.
-
In Token-0x, _burn() does not verify the balance before subtracting, causing an underflow in Yul (which has no automatic overflow checks).
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
let accountBalanceSlot := keccak256(ptr, 0x40)
let accountBalance := sload(accountBalanceSlot)
@>
@> sstore(accountBalanceSlot, sub(accountBalance, value))
}
}
Risk
Likelihood:
Impact:
-
User balance wraps to type(uint256).max - difference
-
Attacker gains mass balance of tokens
-
Token supply accounting completely broken
Proof of Concept
function test_BUG_BurnUnderflowDemonstration() public {
token.mint(alice, 100e18);
uint256 balanceBefore = token.balanceOf(alice);
console.log("Balance before burn:", balanceBefore);
token.burn(alice, 101e18);
uint256 balanceAfter = token.balanceOf(alice);
console.log("Balance after burn:", balanceAfter);
}
Recommended Mitigation
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
// ...
let accountBalance := sload(accountBalanceSlot)
+ // Check balance before burning
+ if lt(accountBalance, value) {
+ mstore(0x00, shl(224, 0xe450d38c)) // ERC20InsufficientBalance
+ mstore(add(0x00, 4), account)
+ mstore(add(0x00, 0x24), accountBalance)
+ mstore(add(0x00, 0x44), value)
+ revert(0x00, 0x64)
+ }
sstore(accountBalanceSlot, sub(accountBalance, value))
}
}