Every ERC-20 token contract must emit events when tokens move or when approvals change. Events are special logs stored on the blockchain that external applications — wallets, block explorers, dApps — can listen to and react to. They are the primary way off-chain systems know what happened inside a smart contract.
1// The two mandatory ERC-20 events23// Emitted on every token transfer (including mint and burn)4event Transfer(address indexed from, address indexed to, uint256 value);56// Emitted whenever approve() is called7event Approval(address indexed owner, address indexed spender, uint256 value);89// Inside the transfer function:10function _transfer(address from, address to, uint256 amount) internal {11_balances[from] -= amount;12_balances[to] += amount;13emit Transfer(from, to, amount); // This line creates the log14}
The 'indexed' keyword on event parameters is crucial. Indexed parameters become searchable topics in the transaction log. This means you can efficiently query the blockchain for 'all transfers TO my address' or 'all approvals FROM this wallet' without scanning every transaction. Non-indexed parameters are stored in the data portion of the log and are cheaper to emit but cannot be filtered on.
Key insight: When tokens are minted, the Transfer event is emitted with from = address(0). When burned, to = address(0). This is how block explorers detect mints and burns — they watch for transfers involving the zero address.
1// Listening for Transfer events using viem (used in this dApp)2import { parseAbiItem } from "viem";34const logs = await publicClient.getLogs({5address: tokenAddress,6event: parseAbiItem(7"event Transfer(address indexed from, address indexed to, uint256 value)"8),9args: { to: myAddress }, // Filter: only transfers TO me10fromBlock: 0n,11toBlock: "latest",12});1314// Each log contains: from, to, value, blockNumber, transactionHash15logs.forEach((log) => {16console.log(`Received ${log.args.value} tokens from ${log.args.from}`);17});
Events cost gas to emit (about 375 gas for the base log + 375 per indexed topic + 8 gas per byte of data), but they are much cheaper than storing the same data in contract state. This is why contracts emit events for historical records rather than keeping arrays of past transfers. Events are write-only from the contract's perspective — the contract itself cannot read its own past events.
Connect your wallet to mark this lesson as complete
Earn 15 EFFORT tokens