Token-0x

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

Missing mint/burn Transfer events + balanceOf(0) reverts → Token-0x is invisible and bricks all DeFi protocols

Author Revealed upon completion

ERC20 standard requires: _mint() emits Transfer(address(0), to, amount)


_burn() emits Transfer(from, address(0), amount)


balanceOf(address(0)) returns 0 (used by Uniswap, Aave, Compound, etc.)

Description

  • The ERC20 standard mandates that minting increases total supply and emits a Transfer from zero, while burning decreases supply and emits a Transfer to zero, enabling accurate tracking by external tools.

  • In this implementation, _mint() and _burn() only update internal storage slots via assembly and completely ot the Transfer events, causing total supply to appear as 0 on all indexers and breaking DeFi integrations.

// helpers/ERC20Internals.sol
function _mint(...) internal {
// ... storage updates only
// @> missing: emit Transfer(address(0), account, value);
}
function _burn(...) internal {
// ... storage only
// @> missing: emit Transfer(account, address(0), value);
}
function _balanceOf(...) internal view {
assembly {
if iszero(owner) { revert(0, 0) } // @> reverts instead of returning 0
}
}

Risk

Likelihood:

  • Every mint/burn will lack events.

  • Every protocol that calls balanceOf(address(0)) will revert (Uniswap V2/V3, Aave, Compound, Balancer, etc.)


Impact:

  • Token appears dead (totalSupply = 0 on Etherscan, Dune, DeFiLlama, wallets.

  • Cannot be added to any major DEX or lending protocol — pools permanently brick.

Proof of Concept

function testCriticalBugs() public {

vm.expectEmit(true, true, false, true);

emit Transfer(address(0), address(1), 1e18);

token.mint(address(1), 1e18);

// FAILS — no event emitted

vm.expectRevert();

token.balanceOf(address(0));

// FAILS — reverts instead of returning 0

}

Recommended Mitigation

function _mint(...) internal {
// ... existing storage logic ...
+ emit Transfer(address(0), account, value);
}
function _burn(...) internal {
// ... existing storage logic ...
+ emit Transfer(account, address(0), value);
}
// In _balanceOf and _allowance assembly
- if iszero(owner) { revert(0, 0) }
+ if iszero(owner) { mstore(0x00, 0); return(0x00, 0x20) }

Support

FAQs

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

Give us feedback!