Token-0x

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

Mint and Burn Do Not Emit Required ERC20 Transfer Events

Author Revealed upon completion

[M-01] Mint and Burn Do Not Emit Required ERC20 Transfer Events

Description

The ERC20 core implementation fails to emit mandatory Transfer events during mint and burn operations.

According to the ERC20 standard, minting must emit a Transfer event from the zero address (0x0) to the recipient, and burning must emit a Transfer event from the holder to the zero address. This contract performs balance and supply updates silently without generating these events.

This breaks ERC20 compliance and causes serious compatibility issues with wallets, indexers, analytics tools, and off-chain infrastructure that depend on events to track token activity.

The issue originates from the internal mint and burn implementations, which update storage directly via inline assembly without emitting logs.

// src/helpers/ERC20Internals.sol
function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
// ... validation omitted
let supply := sload(supplySlot)
sstore(supplySlot, add(supply, value))
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, add(accountBalance, value))
// Missing: emit Transfer(address(0), account, value)
}
}
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
// ... validation omitted
let supply := sload(supplySlot)
sstore(supplySlot, sub(supply, value))
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, sub(accountBalance, value))
// Missing: emit Transfer(account, address(0), value)
}
}

Risk

Likelihood: High

  • The mint and burn logic is used in core token functionality.

  • The contract is designed for inheritance.

  • No event emission exists anywhere in these functions.

Impact: Medium

  • Off-chain services cannot detect mint and burn activity.

  • Wallets and block explorers show incorrect balances.

  • Token trackers and DeFi protocols may malfunction.

  • Breaks ERC20 standard compliance.

Proof of Concept

  • The following test confirms that minting does not emit a Transfer event:

import {Vm} from "forge-std/Vm.sol";
function test_PoC_MintDoesNotEmitTransferEvent() public {
address user = makeAddr("user");
vm.recordLogs();
token.mint(user, 100e18);
Vm.Log[] memory logs = vm.getRecordedLogs();
bytes32 transferSig = keccak256("Transfer(address,address,uint256)");
bool found;
for (uint256 i = 0; i < logs.length; i++) {
if (logs[i].topics.length > 0 && logs[i].topics[0] == transferSig) {
found = true;
}
}
assertEq(found, false);
}
forge test --match-test test_PoC_MintDoesNotEmitTransferEvent -vvv
  • This confirms the contract does not emit the required ERC20 event.

Recommended Mitigation

  • Add standard ERC20 Transfer event emission inside _mint() and _burn().

function _mint(address account, uint256 value) internal {
// balance and supply updates...
+ emit Transfer(address(0), account, value);
}
function _burn(address account, uint256 value) internal {
// balance and supply updates...
+ emit Transfer(account, address(0), value);
}

Support

FAQs

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

Give us feedback!