ERC-20是以太坊上最广泛使用的代币标准,定义了同质化代币的接口规范。以下是ERC-20标准的详细解析:
ERC-20标准概述
ERC-20代表"Ethereum Request for Comments 20",由Fabian Vogelsteller于2015年提出。它定义了一套标准接口,使不同代币能够在以太坊生态系统中互操作。
必须实现的方法
1. totalSupply()
返回代币的总供应量。
solidityfunction totalSupply() external view returns (uint256) { return _totalSupply; }
2. balanceOf(address account)
返回指定账户的代币余额。
solidityfunction balanceOf(address account) external view returns (uint256) { return _balances[account]; }
3. transfer(address recipient, uint256 amount)
从调用者账户转移代币到接收者账户。
solidityfunction transfer(address recipient, uint256 amount) external returns (bool) { _transfer(_msgSender(), recipient, amount); return true; }
4. allowance(address owner, address spender)
返回授权给spender的代币数量。
solidityfunction allowance(address owner, address spender) external view returns (uint256) { return _allowances[owner][spender]; }
5. approve(address spender, uint256 amount)
授权spender使用调用者的代币。
solidityfunction approve(address spender, uint256 amount) external returns (bool) { _approve(_msgSender(), spender, amount); return true; }
6. transferFrom(address sender, address recipient, uint256 amount)
使用授权额度从sender账户转移代币到recipient账户。
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; }
可选实现的方法
1. name()
返回代币名称。
solidityfunction name() external view returns (string memory) { return _name; }
2. symbol()
返回代币符号。
solidityfunction symbol() external view returns (string memory) { return _symbol; }
3. decimals()
返回代币的小数位数,通常为18。
solidityfunction decimals() external view returns (uint8) { return _decimals; }
事件(Events)
1. Transfer
代币转移时触发。
solidityevent Transfer(address indexed from, address indexed to, uint256 value);
2. Approval
授权时触发。
solidityevent Approval(address indexed owner, address indexed spender, uint256 value);
完整的ERC-20实现示例
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); } }
使用OpenZeppelin库
OpenZeppelin提供了经过审计的ERC-20实现,推荐在生产环境中使用。
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的应用场景
1. 去中心化金融(DeFi)
- 流动性提供
- 借贷协议
- 衍生品交易
2. 治理代币
- DAO投票
- 协议治理
- 社区激励
3. 稳定币
- USDT、USDC、DAI
- 法币抵押
- 算法稳定
4. 实用代币
- 平台访问权限
- 服务支付
- 奖励机制
ERC-20的局限性
1. 授权机制问题
- 需要两次交易(approve + transferFrom)
- 可能导致授权额度泄露
2. 缺乏批量操作
- 每次转移都需要单独交易
- Gas成本较高
3. 无法处理代币回调
- 不支持接收代币时的通知
- 可能导致代币丢失
其他ERC代币标准
ERC-721
非同质化代币(NFT)标准,每个代币都是唯一的。
ERC-1155
多代币标准,支持同质化和非同质化代币。
ERC-777
改进的ERC-20标准,支持代币回调和批量操作。
ERC-4626
金库代币标准,用于DeFi收益聚合器。
最佳实践
- 使用OpenZeppelin库:避免重复造轮子,使用经过审计的代码
- 添加访问控制:实现适当的权限管理
- 事件日志:记录所有重要操作
- 安全审计:在部署前进行专业审计
- 测试覆盖:确保充分的测试覆盖率
ERC-20标准是以太坊生态系统的基础,理解其工作原理对于开发区块链应用至关重要。