Back to Lessons
Lesson 05

ERC-20 Events: Transfer & Approval

+15EFFORT
6 sections
events()
01
text

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.

02
code
solidity
1// The two mandatory ERC-20 events
2
3// Emitted on every token transfer (including mint and burn)
4event Transfer(address indexed from, address indexed to, uint256 value);
5
6// Emitted whenever approve() is called
7event Approval(address indexed owner, address indexed spender, uint256 value);
8
9// Inside the transfer function:
10function _transfer(address from, address to, uint256 amount) internal {
11 _balances[from] -= amount;
12 _balances[to] += amount;
13 emit Transfer(from, to, amount); // This line creates the log
14}
03
text

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.

04
note
Key Insight

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.

05
code
javascript
1// Listening for Transfer events using viem (used in this dApp)
2import { parseAbiItem } from "viem";
3
4const logs = await publicClient.getLogs({
5 address: tokenAddress,
6 event: parseAbiItem(
7 "event Transfer(address indexed from, address indexed to, uint256 value)"
8 ),
9 args: { to: myAddress }, // Filter: only transfers TO me
10 fromBlock: 0n,
11 toBlock: "latest",
12});
13
14// Each log contains: from, to, value, blockNumber, transactionHash
15logs.forEach((log) => {
16 console.log(`Received ${log.args.value} tokens from ${log.args.from}`);
17});
06
text

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.

Complete

Connect your wallet to mark this lesson as complete

Earn 15 EFFORT tokens