Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: low
Valid

Missing `Transfer` Event in `_mint` Violates ERC20 Standard and Breaks Off-chain Tracking

Root + Impact

Description

  • Per EIP-20 (ERC-20), implementations MUST emit a Transfer(address indexed from, address indexed to, uint256 value) event when tokens are minted, with from == address(0) for mint operations. Emitting this event enables wallets, block explorers and indexers (e.g., Etherscan, The Graph) to detect supply changes and token transfers.

  • The _mint implementation updates totalSupply and the recipient balance using inline assembly but does not emit the required Transfer(address(0), to, value) event (no logN call implemented). As a result, on-chain state is updated but off-chain indexers and wallets will not register the mint activity.

function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
// ... update totalSupply slot
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, add(accountBalance, value))
@> // ROOT CAUSE: No log3 (or other logN) call to emit Transfer(address(0), account, value)
}
}

Risk

Likelihood:

  • Every time _mint is called, the balance and totalSupply change but no Transfer event is emitted.

  • Any public or internal function that relies on _mint for emissions (airdrop, rewards, admin mint) will exhibit this behavior 100% of the time.

Impact:

  • Broken indexing: Block explorers, analytics dashboards, and subgraphs that depend on Transfer logs will not detect minted tokens or supply changes.

  • Standard violation: The token is not fully ERC‑20 compliant, which can block listings or integrations with protocols that enforce strict EIP‑20 behaviour.

  • User confusion: A user’s balance increases after a mint, but their wallet or explorer shows no corresponding Transfer in the activity history, making behavior look suspicious or buggy.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Test, console} from "forge-std/Test.sol";
import {Vm} from "forge-std/Vm.sol";
import {ERC20} from "../src/ERC20.sol";
contract ERC20Mock is ERC20 {
constructor() ERC20("Test", "TST") {}
function mint(address to, uint256 amount) public { _mint(to, amount); }
}
contract MintEventTest is Test {
ERC20Mock public token;
address public alice = makeAddr("alice");
function setUp() public {
token = new ERC20Mock();
}
function test_mint_missing_event() public {
vm.recordLogs();
// Action: Mint tokens
token.mint(alice, 100);
// Retrieve recorded logs
Vm.Log[] memory entries = vm.getRecordedLogs();
// Assertion: Balance updated
assertEq(token.balanceOf(alice), 100);
// Assertion: NO event emitted — demonstrates defect
assertEq(entries.length, 0, "Critical: Transfer event missing on mint");
}
}

Run:

forge test --match-test test_mint_missing_event -vvvv

Output:

Ran 1 test for test/MissingEventTest.t.sol:MissingEventTest
[PASS] test_mint_missing_event() (gas: 61935)
Logs:
Events emitted: 0

Recommended Mitigation

- sstore(accountBalanceSlot, add(accountBalance, value))
+ sstore(accountBalanceSlot, add(accountBalance, value))
+
+ // Emit Transfer(address(0), account, value)
+ mstore(0x00, value)
+ log3(
+ 0x00, 0x20,
+ 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef,
+ 0x0,
+ account
+ )
Updates

Lead Judging Commences

gaurangbrdv Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

missing event

missing event emission

Support

FAQs

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

Give us feedback!