ERC-20 代币标准
页面最后更新: 2026年2月15日
简介
什么是代币?
代币可以在以太坊中表示任何东西:
- 在线平台中的信誉积分
- 游戏中一个角色的技能
- 金融资产类似于公司股份的资产
- 像美元一样的法定货币
- 一盎司黄金
- 以及更多...
以太坊的这种强大特点必须以强有力的标准来处理,对吗? 这正是 ERC-20 发挥其作用的地方! 此标准允许开发者构建可与其他产品和服务互相操作的代币应用程序。 ERC-20 标准还可用于为提供附加功能。
什么是 ERC-20?
ERC-20 提出了一个同质化代币的标准,换句话说,它们具有一种属性,使得每个代币都与另一个代币(在类型和价值上)完全相同。 例如,一个 ERC-20 代币就像以太币一样,意味着一个代币会并永远会与其他代币一样。
前提条件
正文
ERC-20(以太坊意见征求 20)由 Fabian Vogelsteller 提出于 2015 年 11 月。这是一个能实现智能合约中代币的应用程序接口标准。
ERC-20 的功能示例包括:
- 将代币从一个帐户转到另一个帐户
- 获取帐户的当前代币余额
- 获取网络上可用代币的总供应量
- 批准一个帐户中一定的代币金额由第三方帐户使用
如果智能合约实施了下列方法和事件,它可以被称为 ERC-20 代币合约,一旦部署,将负责跟踪以太坊上创建的代币。
方法
1function name() public view returns (string)2function symbol() public view returns (string)3function decimals() public view returns (uint8)4function totalSupply() public view returns (uint256)5function balanceOf(address _owner) public view returns (uint256 balance)6function transfer(address _to, uint256 _value) public returns (bool success)7function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)8function approve(address _spender, uint256 _value) public returns (bool success)9function allowance(address _owner, address _spender) public view returns (uint256 remaining)显示全部事件
1event Transfer(address indexed _from, address indexed _to, uint256 _value)2event Approval(address indexed _owner, address indexed _spender, uint256 _value)示例
让我们看看如此重要的一个标准是如何使我们能够简单地检查以太坊上的任何 ERC-20 代币合约。 我们只需要合约的应用程序二进制接口 (ABI) 来创造一个 ERC-20 代币界面。 下面我们将使用一个简化的应用程序二进制接口,让例子变得更为简单。
Web3.py 示例
首先,请确保您已安装 Web3.pyopens in a new tab Python 程序库:
1pip install web31from web3 import Web3234w3 = Web3(Web3.HTTPProvider("https://cloudflare-eth.com"))56dai_token_addr = "0x6B175474E89094C44Da98b954EedeAC495271d0F" # DAI7weth_token_addr = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" # 包装的以太币 (WETH)89acc_address = "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11" # Uniswap V2: DAI 21011# 这是 ERC-20 代币合约的简化版合约应用程序二进制接口 (ABI)。12# 它只会公开以下方法:balanceOf(address)、decimals()、symbol() 和 totalSupply()13simplified_abi = [14 {15 'inputs': [{'internalType': 'address', 'name': 'account', 'type': 'address'}],16 'name': 'balanceOf',17 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}],18 'stateMutability': 'view', 'type': 'function', 'constant': True19 },20 {21 'inputs': [],22 'name': 'decimals',23 'outputs': [{'internalType': 'uint8', 'name': '', 'type': 'uint8'}],24 'stateMutability': 'view', 'type': 'function', 'constant': True25 },26 {27 'inputs': [],28 'name': 'symbol',29 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}],30 'stateMutability': 'view', 'type': 'function', 'constant': True31 },32 {33 'inputs': [],34 'name': 'totalSupply',35 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}],36 'stateMutability': 'view', 'type': 'function', 'constant': True37 }38]3940dai_contract = w3.eth.contract(address=w3.to_checksum_address(dai_token_addr), abi=simplified_abi)41symbol = dai_contract.functions.symbol().call()42decimals = dai_contract.functions.decimals().call()43totalSupply = dai_contract.functions.totalSupply().call() / 10**decimals44addr_balance = dai_contract.functions.balanceOf(acc_address).call() / 10**decimals4546# DAI47print("===== %s =====" % symbol)48print("Total Supply:", totalSupply)49print("Addr Balance:", addr_balance)5051weth_contract = w3.eth.contract(address=w3.to_checksum_address(weth_token_addr), abi=simplified_abi)52symbol = weth_contract.functions.symbol().call()53decimals = weth_contract.functions.decimals().call()54totalSupply = weth_contract.functions.totalSupply().call() / 10**decimals55addr_balance = weth_contract.functions.balanceOf(acc_address).call() / 10**decimals5657# WETH58print("===== %s =====" % symbol)59print("Total Supply:", totalSupply)60print("Addr Balance:", addr_balance)显示全部已知问题
ERC-20 代币接收问题
截至 2024 年 6 月 20 日,因该问题损失的 ERC-20 代币价值至少达 83,656,418 美元。 请注意,纯粹的 ERC-20 实现很容易出现此问题,除非您在标准之上实施下面列出的一组额外限制。
当 ERC-20 代币被发送到并非为处理 ERC-20 代币而设计的智能合约时,这些代币可能会永久丢失。 出现这种情况的原因是,接收合约无法识别或回应所传入的代币,而且 ERC-20 标准中也没有通知接受合约所传入代币的机制。 导致这一问题的主要原因包括:
- 代币转移机制
- ERC-20 代币使用 transfer 或 transferFrom 函数进行转移
- 当用户使用这些函数将代币发送到合约地址时,无论接收合约是否是为处理它们而设计,代币都会被转移
- 缺乏通知
- 接收合约不会收到已向其发送代币的通知或回调
- 如果接收合约缺乏处理代币的机制(例如,回退函数或专门用于处理代币接收的函数),则代币实际上会卡在合约的地址中
- 无内置处理
- ERC-20 标准不包含用于接收待实现合约的强制函数,导致许多合约无法正确管理传入的代币
潜在解决方案
虽然无法通过 ERC-20 完全避免此问题,但有一些方法可以显著降低最终用户遭遇代币损失的可能性:
- 最常见的问题是当用户将代币发送到代币合约地址本身时(例如,将 USDT 存入 USDT 代币合约的地址)。 建议限制
transfer(..)函数,以撤销此类转账尝试。 考虑在transfer(..)函数的实现中添加require(_to != address(this));检查。 - 通常,
transfer(..)函数并非为向合约存入代币而设计。approve(..) 和transferFrom(..)模式是向合约存入 ERC-20 代币的替代方法。 可以通过限制 transfer 函数以禁止用其向任何合约存入代币,但这可能会破坏与那些假设可以用trasnfer(..)` 函数向合约存入代币的合约的兼容性(例如,Uniswap 流动性池)。 - 必须预设ERC-20代币可能会意外地转入您的合约,即便该合约理应不接收任何的代币。 接收的人没有办法阻止或拒绝意外的存款。 建议实现一个功能,允许提取出那些被意外转入的ERC-20代币。
- 考虑使用替代的代币标准。
为解决此问题,出现了一些替代标准,例如 ERC-223 或 ERC-1363。
扩展阅读{#further-reading}
- EIP-20:ERC-20 代币标准opens in a new tab
- OpenZeppelin - 代币opens in a new tab
- OpenZeppelin - ERC-20 实现opens in a new tab
- Alchemy - Solidity ERC20 代币指南opens in a new tab