ERC-20 (Ethereum Request for Comments 20) is the technical standard for fungible tokens on Ethereum. Proposed by Fabian Vogelsteller and Vitalik Buterin in November 2015, it defines a common interface that enables tokens to be transferred, approved for spending by third parties, and queried for balance and supply information. ERC-20 is the most widely adopted token standard in blockchain, with thousands of implementations managing hundreds of billions of dollars in value.
The ERC-20 Interface
The standard defines six mandatory functions and two events. Every ERC-20 compliant token must implement this interface:
Core Functions
totalSupply()returns the total number of tokens in existence. This can be fixed at deployment or change dynamically through minting and burning.balanceOf(address account)returns the token balance of a specific address. The contract maintains an internal mapping from addresses to balances.transfer(address to, uint256 amount)moves tokens from the caller's account to the recipient. Returns a boolean indicating success. Emits aTransferevent.approve(address spender, uint256 amount)authorizes a third party (the spender) to transfer up to the specified amount from the caller's account. This is the first step in the two-step "approve and transfer" pattern.transferFrom(address from, address to, uint256 amount)allows an approved spender to transfer tokens from one account to another. The spender's allowance is decremented by the transfer amount. Emits aTransferevent.allowance(address owner, address spender)returns the remaining amount that the spender is authorized to transfer on behalf of the owner.
Events
Transfer(address indexed from, address indexed to, uint256 value)is emitted on every token transfer, including mints (from the zero address) and burns (to the zero address).Approval(address indexed owner, address indexed spender, uint256 value)is emitted when an allowance is set or changed.
Optional Metadata Functions
Most ERC-20 tokens also implement three optional functions that provide human-readable information:
name()returns the token's full name (e.g., "USD Coin").symbol()returns the ticker (e.g., "USDC").decimals()returns the number of decimal places the token uses.
How the Approval Mechanism Works
The two-step approve-then-transferFrom pattern is central to DeFi composability. When a user wants to interact with a protocol (for example, depositing USDC into Aave), the process works as follows:
- The user calls
approve(aavePool, 1000e6)on the USDC contract, authorizing the Aave pool to spend up to 1,000 USDC from their account. - The user calls the
deposit()function on the Aave pool contract. - Internally, the Aave pool calls
USDC.transferFrom(user, aavePool, 1000e6)to pull the tokens into the pool.
This two-transaction pattern is necessary because smart contracts cannot initiate transfers from a user's account without explicit authorization. The approval step is a separate on-chain transaction that costs gas.
Many users set "unlimited" approvals (type(uint256).max) to avoid repeated approval transactions. While convenient, this means the approved contract has perpetual authority to transfer all of the user's tokens. If the contract is compromised, the attacker can drain every approved wallet. Review and revoke unnecessary approvals regularly using tools like Revoke.cash.
The Decimals Convention
The EVM does not support floating-point numbers. To represent fractional token amounts, ERC-20 uses integer arithmetic with a decimals value that indicates where to place the decimal point for display purposes.
Most tokens use 18 decimals (matching ETH's wei denomination). A balance of 1000000000000000000 (10^18) represents 1.0 tokens. However, stablecoins like USDC and USDT use 6 decimals: a balance of 1000000 (10^6) represents 1.0 USDC.
Decimal Mismatch Risks
The lack of a universal decimal standard creates integration risks. Smart contracts that interact with multiple tokens must account for different decimal values. A contract that assumes 18 decimals when handling 6-decimal USDC will miscalculate amounts by a factor of 10^12. This type of error has caused multiple DeFi exploits and is a common audit finding.
Common Extensions
ERC-2612: Permit (Gasless Approvals)
ERC-2612 (also called ERC-20Permit) adds a permit() function that allows approvals via off-chain signatures instead of on-chain transactions. Instead of calling approve() in a separate transaction, the user signs a typed message (EIP-712) off-chain, and the protocol submits the signature alongside the action in a single transaction.
This reduces the interaction from two transactions to one, saving gas and improving user experience. Major tokens including DAI, USDC (V2), and most modern ERC-20 implementations support Permit.
ERC-20Votes: Governance Snapshots
ERC-20Votes extends ERC-20 with vote delegation and historical balance tracking via checkpoints. Token holders can delegate their voting power to another address (or themselves), and the contract records balance snapshots at each block. This prevents double-voting attacks where a user votes, transfers tokens to another wallet, and votes again.
Governance tokens like UNI, COMP, and ARB implement this extension.
Other Notable Extensions
- Burnable: Adds a
burn()function allowing token holders to permanently destroy their tokens, reducing total supply. - Mintable: Adds a
mint()function restricted to authorized addresses, allowing controlled supply expansion. - Pausable: Allows an admin to freeze all transfers in emergency situations. USDC and USDT both implement this, enabling Circle and Tether to blacklist addresses and freeze funds.
- Capped: Enforces a maximum supply that cannot be exceeded even through minting.
Notable ERC-20 Tokens
| Token | Type | Decimals | Supply Model |
|---|---|---|---|
| USDC | Stablecoin | 6 | Mint/burn by Circle |
| USDT | Stablecoin | 6 | Mint/burn by Tether |
| DAI | Stablecoin | 18 | Mint/burn by vaults |
| WETH | Wrapped ETH | 18 | Wrap/unwrap |
| UNI | Governance | 18 | Fixed (1B), vested |
| LINK | Utility | 18 | Fixed (1B) |
| AAVE | Governance | 18 | Fixed (16M) |
Security Considerations
The Approve Race Condition
The original ERC-20 standard contains a known race condition in the approve function. If a user has an existing approval of 100 tokens for a spender and wants to change it to 50, the following can occur:
- User submits
approve(spender, 50). - Before this transaction is mined, the spender front-runs by calling
transferFromto use the original 100-token allowance. - The user's
approve(spender, 50)transaction is mined, setting a new 50-token allowance. - The spender calls
transferFromagain for 50 tokens, having spent 150 total instead of the intended 50.
The standard mitigation is to set the allowance to 0 before setting it to the new value (two transactions), or to use increaseAllowance and decreaseAllowance functions provided by libraries like OpenZeppelin. ERC-2612 Permit also avoids this issue by using nonces.
Fee-on-Transfer and Rebasing Tokens
Some ERC-20 tokens deviate from standard behavior. Fee-on-transfer tokens deduct a percentage on each transfer, meaning the received amount is less than the sent amount. Rebasing tokens (like stETH) adjust all holder balances automatically to reflect staking rewards.
These non-standard behaviors can break DeFi protocols that assume the amount sent equals the amount received. Many protocols explicitly exclude such tokens or implement special handling.
Key Takeaway
ERC-20 is the foundational standard that makes the entire DeFi ecosystem interoperable. Its approve-transferFrom pattern enables composability between protocols, while extensions like Permit and Votes add governance and UX capabilities. However, decimal mismatches, unlimited approvals, and non-standard token behaviors remain persistent sources of risk that developers and users must actively manage.