Token-0x

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

[M-02] Missing Transfer Event in _burn Function (Non-Compliant with ERC20 Standard)

Author Revealed upon completion

Description

  • The _burn function does not emit the required Transfer event when tokens are burned. According to the ERC20 standard, any token burning must emit a Transfer event with from set to the sender address and to set to the zero address. Without this event, external observers cannot track token burning, breaking interoperability with wallets, block explorers, and other smart contracts.

Root Cause

The function uses Yul assembly for gas optimization but omits the emission of the Transfer event after updating the token balances and total supply.


function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0x96c6fd1e))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let balanceSlot := _balances.slot
let supplySlot := _totalSupply.slot
let supply := sload(supplySlot)
sstore(supplySlot, sub(supply, value))
mstore(ptr, account)
mstore(add(ptr, 0x20), balanceSlot)
let accountBalanceSlot := keccak256(ptr, 0x40)
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, sub(accountBalance, value))
// <@ Missing Transfer event emission: Transfer(account, address(0), value)
}

Risk

Likelihood:

  • Every burn operation fails to emit the required event, affecting all burns.

  • The absence of events is immediately apparent to any external observer or tool that relies on events.


Impact:

  • · Broken ERC20 Compliance: The token does not fully comply with the ERC20 standard, which may cause integration issues with exchanges, wallets, and other DeFi protocols.

  • Lack of Transparency: External parties cannot track burning activities, leading to a loss of transparency and auditability.

  • Supply Tracking Issues: Without burn events, external tools cannot accurately track the circulating supply or calculate burn rates.

  • Interoperability Issues: Smart contracts that rely on Transfer events to react to token burning (e.g., deflationary token trackers, reward calculations) will not function correctly.

Proof of Concept

Recommended Mitigation

Emit a Transfer event with from set to the account and to set to address(0) after updating balances:

function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0x96c6fd1e))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let balanceSlot := _balances.slot
let supplySlot := _totalSupply.slot
let supply := sload(supplySlot)
sstore(supplySlot, sub(supply, value))
mstore(ptr, account)
mstore(add(ptr, 0x20), balanceSlot)
let accountBalanceSlot := keccak256(ptr, 0x40)
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, sub(accountBalance, value))
+ mstore(0x00, value) // Store value in memory
+ log3(
+ 0x00, // data offset
+ 0x20, // data size (32 bytes for uint256)
+ 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, // Transfer event signature
+ account, // from = sender
+ 0x00 // to = address(0)
+ )
}

Support

FAQs

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

Give us feedback!