深入理解 ERC-20 代币智能合约

Posted by FXE 加密实验室 on August 28, 2025

在以太坊生态系统中,ERC-20 无疑是最为重要的智能合约标准之一。它已成为以太坊区块链上所有可替换代币(Fungible Token)实现的技术规范,为开发者提供了一套统一的规则。

什么是 ERC-20 标准?

ERC-20 定义了一系列通用规则,所有基于以太坊的可替换代币都应遵循这些规则。这一标准使得各类开发者能够准确预测新代币在更广泛的以太坊系统中的运行方式。

采用 ERC-20 标准大大简化和减轻了开发者的工作负担。开发者无需在每次新代币发布时重新设计基础功能,只要代币遵循这一标准,就能确保其兼容性和可预测性。

ERC-20 核心功能解析

以下以接口形式展示了 ERC-20 必须实现的功能。如果您对接口概念不熟悉,可以将其理解为一份规定了必须实现的功能清单。

pragma solidity ^0.6.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    
    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, 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);
}

查询函数详解

总供应量查询

function totalSupply() external view returns (uint256);

此函数返回已存在的代币总数量,是一个不会修改合约状态的查询函数。需要特别注意的是,Solidity 不支持浮点数,因此大多数代币采用 18 位小数表示。例如,1 个代币会表示为 1000000000000000000。但并非所有代币都使用 18 位小数,这在处理不同代币时需要特别注意。

余额查询

function balanceOf(address account) external view returns (uint256);

返回指定地址(account)所拥有的代币数量。此函数同样不会修改合约状态。

津贴查询

function allowance(address owner, address spender) external view returns (uint256);

ERC-20 标准允许一个地址向另一个地址授予支出津贴。此查询函数返回 spender 仍被允许从 owner 账户中提取的代币数量。默认情况下应返回 0。

操作函数解析

转账功能

function transfer(address recipient, uint256 amount) external returns (bool);

将指定数量的代币从函数调用者地址(msg.sender)转移到接收者地址。此函数会触发后续定义的 Transfer 事件,并在转账成功时返回 true。

授权功能

function approve(address spender, uint256 amount) external returns (bool);

设置 spender 可以从函数调用者(msg.sender)余额中转移的代币津贴数量。此函数会触发 Approval 事件,并返回津贴是否设置成功。

授权转账功能

function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

使用津贴机制将指定数量的代币从发送方转移到接收方。转移的数量将从调用者的津贴中扣除。此函数会触发 Transfer 事件。

事件定义

转账事件

event Transfer(address indexed from, address indexed to, uint256 value);

当一定数量的代币(value)从 from 地址发送到 to 地址时,会触发此事件。在铸造新代币的情况下,转账通常来自 0x00..0000 地址;而在销毁代币时,转账则指向 0x00..0000 地址。

授权事件

event Approval(address indexed owner, address indexed spender, uint256 value);

当 owner 批准 spender 使用一定数量的代币(value)时,会触发此事件。

ERC-20 代币基础实现

以下是一个简单的 ERC-20 代币基础实现代码:

pragma solidity ^0.8.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    
    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, 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);
}

contract ERC20Basic is IERC20 {
    string public constant name = "ERC20Basic";
    string public constant symbol = "ERC";
    uint8 public constant decimals = 18;
    
    mapping(address => uint256) balances;
    mapping(address => mapping (address => uint256)) allowed;
    
    uint256 totalSupply_ = 10 ether;
    
    constructor() {
        balances[msg.sender] = totalSupply_;
    }
    
    function totalSupply() public override view returns (uint256) {
        return totalSupply_;
    }
    
    function balanceOf(address tokenOwner) public override view returns (uint256) {
        return balances[tokenOwner];
    }
    
    function transfer(address receiver, uint256 numTokens) public override returns (bool) {
        require(numTokens <= balances[msg.sender]);
        balances[msg.sender] = balances[msg.sender] - numTokens;
        balances[receiver] = balances[receiver] + numTokens;
        emit Transfer(msg.sender, receiver, numTokens);
        return true;
    }
    
    function approve(address delegate, uint256 numTokens) public override returns (bool) {
        allowed[msg.sender][delegate] = numTokens;
        emit Approval(msg.sender, delegate, numTokens);
        return true;
    }
    
    function allowance(address owner, address delegate) public override view returns (uint) {
        return allowed[owner][delegate];
    }
    
    function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
        require(numTokens <= balances[owner]);
        require(numTokens <= allowed[owner][msg.sender]);
        
        balances[owner] = balances[owner] - numTokens;
        allowed[owner][msg.sender] = allowed[owner][msg.sender] - numTokens;
        balances[buyer] = balances[buyer] + numTokens;
        emit Transfer(owner, buyer, numTokens);
        return true;
    }
}

除了上述基础实现,👉探索更多高级实现方案也是提升开发技能的重要途径。

常见问题

ERC-20 标准的主要优势是什么? ERC-20 标准的主要优势在于其统一性和互操作性。它确保了不同代币之间的兼容性,使代币能够无缝集成到钱包、交易所和其他智能合约中。开发者无需为每个新代币重新设计基础功能,大大提高了开发效率。

所有以太坊代币都必须遵循 ERC-20 标准吗? 不是必须的。虽然 ERC-20 是最流行的标准,但开发者也可以创建不遵循此标准的代币。不过,非标准代币可能无法与所有钱包、交易所和智能合约兼容,这会限制其应用范围。

ERC-20 代币中的小数位是什么意思? 小数位决定了代币的可分割程度。18 位小数意味着 1 个代币可以被分为 10^18 个最小单位。这种设计允许处理极小的交易金额,同时保持数学计算的精确性。

Transfer 和 TransferFrom 函数有什么区别? Transfer 函数直接从调用者地址向目标地址转移代币,而 TransferFrom 函数允许被授权的第三方地址从代币所有者账户中转移代币。后者实现了代币支出的委托机制,是许多去中心化应用的基础。

如何处理 ERC-20 交易失败的情况? ERC-20 标准中的函数通常返回布尔值来指示操作成功与否。在实际应用中,开发者应该始终检查返回值,并实现适当的错误处理机制。此外,利用事件日志可以跟踪交易状态和调试问题。

为什么需要 Approval 机制? Approval 机制允许代币所有者授权第三方(如去中心化交易所)在不超过指定限额的情况下管理其部分代币。这样既方便了各种金融应用的开发,又保证了用户资产的安全性。