akash11

ERC-20: The Standard That Powers Most Crypto Tokens

ERC-20 is a technical blueprint that defines how tokens work on Ethereum. It's a set of rules that token contracts must follow to ensure they can interact with wallets, exchanges, and other smart contracts seamlessly.

Published on 21 November, 2025

Categories:

SolidityToken

What ERC-20 Actually Is

ERC-20 stands for "Ethereum Request for Comments 20". Think of it as a standardized interface specification. When developers create a new token on Ethereum, following ERC-20 means their token will automatically work with existing infrastructure without requiring custom integrations.
The standard was proposed in November 2015 by Fabian Vogelsteller and became widely adopted because it solved a critical problem: before ERC-20, every token had different functions and names, making it impossible for wallets and exchanges to support them without custom code for each one.

The Six Core Functions

Every ERC-20 token must implement these six functions:

totalSupply()

Returns the total number of tokens that exist.
function totalSupply() public view returns (uint256)
Example: If a token has 1 million tokens created, this returns 1000000.

balanceOf(address)

Checks how many tokens a specific address holds.
function balanceOf(address account) public view returns (uint256)
Example: balanceOf(0x123...) might return 500 if that address owns 500 tokens.

transfer(address, uint256)

Sends tokens from your address to another address.
function transfer(address recipient, uint256 amount) public returns (bool)
Example: transfer(0xABC..., 100) sends 100 tokens to address 0xABC.

approve(address, uint256)

Gives permission to another address to spend tokens on your behalf. This is used for delegated transfers.
function approve(address spender, uint256 amount) public returns (bool)
Example: You approve a decentralized exchange contract to spend 50 of your tokens so it can execute trades for you.

allowance(address, address)

Checks how many tokens an address is allowed to spend on behalf of another address.
function allowance(address owner, address spender) public view returns (uint256)
Example: After approving 50 tokens, allowance(yourAddress, exchangeAddress) returns 50.

transferFrom(address, address, uint256)

Allows an approved address to transfer tokens on behalf of the owner.
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool)
Example: The exchange uses transferFrom(yourAddress, buyerAddress, 10) to complete a trade.

The Two Required Events

ERC-20 also mandates two events that must be emitted:

Transfer Event

Fired whenever tokens move from one address to another.
event Transfer(address indexed from, address indexed to, uint256 value)

Approval Event

Fired when the approve function is called.
event Approval(address indexed owner, address indexed spender, uint256 value)
These events allow applications to track token movements without constantly querying the blockchain.

How Token Transfers Work

Here's the typical flow when you send tokens:
User calls transfer()
         |
         v
Contract checks balance
         |
         v
Balance sufficient?
     /     \
   No       Yes
   |         |
Revert    Update balances
           |
           v
      Emit Transfer event

A Simple ERC-20 Implementation

Here's a minimal working example:
contract SimpleToken { string public name = "Simple Token"; string public symbol = "SMP"; uint8 public decimals = 18; uint256 private _totalSupply; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); constructor(uint256 initialSupply) { _totalSupply = initialSupply; _balances[msg.sender] = initialSupply; emit Transfer(address(0), msg.sender, initialSupply); } function totalSupply() public view returns (uint256) { return _totalSupply; } function balanceOf(address account) public view returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public returns (bool) { require(_balances[msg.sender] >= amount, "Insufficient balance"); _balances[msg.sender] -= amount; _balances[recipient] += amount; emit Transfer(msg.sender, recipient, amount); return true; } function approve(address spender, uint256 amount) public returns (bool) { _allowances[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { require(_balances[sender] >= amount, "Insufficient balance"); require(_allowances[sender][msg.sender] >= amount, "Allowance exceeded"); _balances[sender] -= amount; _balances[recipient] += amount; _allowances[sender][msg.sender] -= amount; emit Transfer(sender, recipient, amount); return true; } }

Optional Metadata Functions

While not required, most ERC-20 tokens include these:
function name() public view returns (string) function symbol() public view returns (string) function decimals() public view returns (uint8)
The decimals value determines how divisible your token is. Most tokens use 18 decimals (like Ether), meaning 1 token is represented as 1000000000000000000 in the contract's internal accounting.

Common Patterns and Use Cases

The Approve-TransferFrom Pattern

This two-step process is used extensively in DeFi:
  1. User approves a smart contract to spend their tokens
  2. Smart contract calls transferFrom when needed
Example: Uniswap requires approval before you can trade tokens. You approve once, then the Uniswap contract can execute multiple trades on your behalf.

Minting and Burning

While not in the base standard, many tokens add these functions:
function mint(address account, uint256 amount) public onlyOwner { _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); } function burn(uint256 amount) public { require(_balances[msg.sender] >= amount, "Insufficient balance"); _balances[msg.sender] -= amount; _totalSupply -= amount; emit Transfer(msg.sender, address(0), amount); }

Known Issues and Improvements

The Approval Race Condition

If you approve an address for 100 tokens, then want to change it to 50, there's a brief window where they could spend 150 tokens total. The fix is to set allowance to 0 first, then set the new amount.

Lost Tokens Problem

If you accidentally send ERC-20 tokens to a contract that doesn't handle them, those tokens are stuck forever. This led to the creation of ERC-223 and ERC-777 as improved alternatives.

No Transfer Notifications

Contracts don't get notified when they receive ERC-20 tokens, which can cause issues. You have to check balances manually.

Real World Examples

Popular ERC-20 tokens include USDT (Tether), USDC (USD Coin), LINK (Chainlink), and UNI (Uniswap). Each implements the same core functions, which is why you can store them all in the same wallet and trade them on the same exchanges.
ERC-20 became the foundation for the ICO boom in 2017-2018 and remains the dominant token standard on Ethereum today. Its simplicity and widespread adoption demonstrate how a well-designed standard can create network effects that benefit an entire ecosystem.

Copyright © 2025

Akash Vaghela