What ERC-20 Defines
ERC-20 is the Ethereum standard for fungible tokens, meaning every unit is identical and interchangeable, like currency. It specifies a common interface, a set of function signatures and events, that any compliant token contract must implement so that wallets, exchanges, and other contracts can interact with it uniformly. Because the interface is standardized, a single wallet can hold thousands of different ERC-20 tokens without custom code for each. The standard governs how balances are tracked, how transfers occur, and how one account can delegate spending authority to another.
Cricket analogy: Every run scored is identical and interchangeable, a run is a run, just as every ERC-20 unit is fungible and worth the same as any other.
Core Functions and Events
The mandatory ERC-20 functions are totalSupply (the total number of tokens), balanceOf(account) (an account's balance), transfer(to, amount) (move your own tokens), approve(spender, amount) and allowance(owner, spender) (delegated spending), and transferFrom(from, to, amount) (a spender moving someone else's tokens). Two events are required: Transfer, emitted on every token movement including mint and burn, and Approval, emitted when an allowance is set. Wallets and block explorers rely on these events to display balances and histories, so emitting them correctly is part of compliance, not an optional extra.
Cricket analogy: The scoreboard tracks each batsman's runs like balanceOf, the team total like totalSupply, and every scored run moving the tally like a transfer.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
// decimals() defaults to 18; 1 token = 10**18 base units
constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
_mint(msg.sender, initialSupply * 10 ** decimals());
}
}
// The interface every ERC-20 must satisfy (abbreviated):
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}The Approve and Allowance Pattern
Contracts cannot pull tokens from your wallet without permission, so ERC-20 uses a two-step delegation: you call approve(spender, amount) to authorize a spender up to a cap, and the spender later calls transferFrom to draw against that allowance. This underpins decentralized exchanges and staking contracts. It carries a well-known hazard, the approve race condition: changing a non-zero allowance to another non-zero value can let a watchful spender spend both the old and new amounts. The recommended mitigations are to set the allowance to zero first, or to use increaseAllowance and decreaseAllowance which adjust the value atomically.
Cricket analogy: A captain pre-authorizes a runner to complete runs on a batsman's behalf up to a limit; approve sets an allowance the spender draws against via transferFrom.
ERC-20 amounts are integers scaled by decimals(), which conventionally returns 18. A balance of 1,500000000000000000 (1.5 * 10**18) represents 1.5 tokens. There is no fractional type in the EVM, so front-ends divide by 10**decimals to display human-readable values.
Do not assume every ERC-20 returns true on transfer or reverts on failure, some non-standard tokens (famously USDT) do not. Use OpenZeppelin's SafeERC20 wrapper (safeTransfer, safeTransferFrom, forceApprove) to handle these inconsistencies safely when integrating arbitrary tokens.
- ERC-20 standardizes an interface for fungible, interchangeable tokens so tooling works across all of them.
- Core functions: totalSupply, balanceOf, transfer, approve, allowance, and transferFrom.
- The Transfer event fires on every movement (including mint/burn) and Approval fires when an allowance is set.
- approve plus transferFrom enables delegated spending used by DEXes and staking contracts.
- The approve race condition is mitigated by zeroing first or using increaseAllowance/decreaseAllowance.
- Amounts are integers scaled by decimals() (usually 18); the EVM has no native fractions.
- Use SafeERC20 to safely integrate non-standard tokens that misreport success.
Practice what you learned
1. What does 'fungible' mean in the context of ERC-20?
2. Which pair of functions implements delegated spending in ERC-20?
3. How is the approve race condition best avoided?
4. If decimals() returns 18, how are 2.5 tokens represented on-chain?
5. Why should you use SafeERC20 when integrating arbitrary tokens?
Was this page helpful?
You May Also Like
ERC-721 NFT Standard
The standard for non-fungible tokens on Ethereum, covering unique token IDs, ownership and metadata, safe transfers with receiver hooks, and minting.
Access Control Patterns
Techniques for restricting who can call privileged contract functions, from simple Ownable to role-based access control, safe ownership transfer, and timelocks.
Gas Optimization in Solidity
How the EVM prices computation and storage, and the practical patterns, storage packing, caching, calldata, unchecked math, that reduce transaction costs without sacrificing safety.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics