ERC-20 is the most widely used token standard on Ethereum, defining the interface specification for fungible tokens. Here's a detailed analysis of the ERC-20 standard:
ERC-20 Standard Overview
ERC-20 stands for "Ethereum Request for Comments 20," proposed by Fabian Vogelsteller in 2015. It defines a set of standard interfaces that enable different tokens to interoperate within the Ethereum ecosystem.
Required Methods
1. totalSupply()
Returns the total supply of tokens.
solidityfunction totalSupply() external view returns (uint256) { return _totalSupply; }
2. balanceOf(address account)
Returns the token balance of a specified account.
solidityfunction balanceOf(address account) external view returns (uint256) { return _balances[account]; }
3. transfer(address recipient, uint256 amount)
Transfers tokens from the caller's account to the recipient's account.
solidityfunction transfer(address recipient, uint256 amount) external returns (bool) { _transfer(_msgSender(), recipient, amount); return true; }
4. allowance(address owner, address spender)
Returns the amount of tokens authorized for the spender.
solidityfunction allowance(address owner, address spender) external view returns (uint256) { return _allowances[owner][spender]; }
5. approve(address spender, uint256 amount)
Authorizes the spender to use the caller's tokens.
solidityfunction approve(address spender, uint256 amount) external returns (bool) { _approve(_msgSender(), spender, amount); return true; }
6. transferFrom(address sender, address recipient, uint256 amount)
Transfers tokens from the sender's account to the recipient's account using the authorized allowance.
solidityfunction transferFrom(address sender, address recipient, uint256 amount) external returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true; }
Optional Methods
1. name()
Returns the token name.
solidityfunction name() external view returns (string memory) { return _name; }
2. symbol()
Returns the token symbol.
solidityfunction symbol() external view returns (string memory) { return _symbol; }
3. decimals()
Returns the number of decimal places for the token, typically 18.
solidityfunction decimals() external view returns (uint8) { return _decimals; }
Events
1. Transfer
Triggered when tokens are transferred.
solidityevent Transfer(address indexed from, address indexed to, uint256 value);
2. Approval
Triggered when authorization occurs.
solidityevent Approval(address indexed owner, address indexed spender, uint256 value);
Complete ERC-20 Implementation Example
solidity// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract MyToken { string private _name; string private _symbol; uint8 private _decimals; uint256 private _totalSupply; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; constructor(string memory name_, string memory symbol_, uint256 initialSupply) { _name = name_; _symbol = symbol_; _decimals = 18; _totalSupply = initialSupply; _balances[msg.sender] = initialSupply; emit Transfer(address(0), msg.sender, initialSupply); } function name() external view returns (string memory) { return _name; } function symbol() external view returns (string memory) { return _symbol; } function decimals() external view returns (uint8) { return _decimals; } function totalSupply() external view returns (uint256) { return _totalSupply; } function balanceOf(address account) external view returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) external returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) external view returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) external returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][msg.sender]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, msg.sender, currentAllowance - amount); } return true; } function _transfer(address sender, address recipient, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[sender] = senderBalance - amount; _balances[recipient] += amount; } emit Transfer(sender, recipient, amount); } function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } }
Using OpenZeppelin Library
OpenZeppelin provides audited ERC-20 implementations, recommended for production use.
solidity// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyToken is ERC20, Ownable { constructor(uint256 initialSupply) ERC20("My Token", "MTK") { _mint(msg.sender, initialSupply); } function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } }
ERC-20 Use Cases
1. Decentralized Finance (DeFi)
- Liquidity provision
- Lending protocols
- Derivative trading
2. Governance Tokens
- DAO voting
- Protocol governance
- Community incentives
3. Stablecoins
- USDT, USDC, DAI
- Fiat-collateralized
- Algorithmic stability
4. Utility Tokens
- Platform access rights
- Service payments
- Reward mechanisms
ERC-20 Limitations
1. Authorization Mechanism Issues
- Requires two transactions (approve + transferFrom)
- May lead to authorization allowance leakage
2. Lack of Batch Operations
- Each transfer requires a separate transaction
- Higher Gas costs
3. No Token Callback Support
- Doesn't support notifications when receiving tokens
- May lead to token loss
Other ERC Token Standards
ERC-721
Non-Fungible Token (NFT) standard, each token is unique.
ERC-1155
Multi-token standard supporting both fungible and non-fungible tokens.
ERC-777
Improved ERC-20 standard with token callbacks and batch operations.
ERC-4626
Vault token standard for DeFi yield aggregators.
Best Practices
- Use OpenZeppelin Library: Avoid reinventing the wheel, use audited code
- Add Access Control: Implement proper permission management
- Event Logging: Record all important operations
- Security Audit: Conduct professional audits before deployment
- Test Coverage: Ensure adequate test coverage
The ERC-20 standard is the foundation of the Ethereum ecosystem, and understanding how it works is crucial for developing blockchain applications.