从零开始:以太坊ERC20代币开发详解**
以太坊作为全球领先的智能合约平台,其上发行的代币数量庞大,其中ERC20标准是最具影响力和广泛应用的代币标准之一,ERC20定义了一套统一的接口和规则,使得不同的代币可以在以太坊生态中无缝交互,例如在交易所交易、钱包存储、DeFi协议中使用等,本文将详细讲解ERC20代币的开发流程,包括核心概念、标准接口、智能合约编写、测试、部署以及注意事项。
什么是ERC20?
ERC20是“Ethereum Request for Comments 20”的缩写,即以太坊改进建议第20号,它不是一个具体的代币,而是一个技术标准,规定了以太坊上同质化代币(Fungible Token,即每个代币完全相同,可以互换)应遵循的规范,遵循ERC20标准的代币,其行为具有一致性,可以被各种以太坊钱包、交易所和应用识别和支持。
ERC20核心接口与事件
ERC20标准定义了必须实现的接口(函数)和推荐实现的事件,这些是开发ERC20代币合约的基础。

核心接口函数:
function name() public view returns (string): 返回代币的名称,"MyToken"。function symbol() public view returns (string): 返回代币的符号,"MTK",通常2-3个字符。function decimals() public view returns (uint8): 返回代币的小数位数,用于计算精度,18位小数意味着1个代币等于 10^18 个最小单位(wei类似)。function totalSupply() public view returns (uint256): 返回代币的总供应量。function balanceOf(address _owner) public view returns (uint256): 返回指定地址_owner拥有的代币数量。function transfer(address _to, uint256 _value) public returns (bool): 将调用者(msg.sender)的_value数量代币转移到地址_to,成功返回true。function transferFrom(address _from, address _to, uint256 _value) public returns (bool): 从地址_from转移_value数量代币到地址_to,调用者需要有来自_from的足够授权(通过approve)。function approve(address _spender, uint256 _value) public returns (bool): 授权地址_spender可以调用transferFrom从调用者(msg.sender)处转移最多_value数量的代币。function allowance(address _owner, address _spender) public view returns (uint256): 返回地址_spender被授权从地址_owner处转移的代币数量。
推荐事件:
event Transfer(address indexed from, address indexed to, uint256 value): 当代币发生转移时触发(包括铸造和销毁)。from为零地址表示铸造,to为零地址表示销毁。event Approval(address indexed owner, address indexed spender, uint256 value): 当approve函数被调用时触发,授权额度发生变化。
开发环境准备
在开始编写ERC20代币合约之前,需要准备以下开发环境:
- Node.js and npm/yarn: JavaScript运行时环境和包管理器。
- Truffle: 一个流行的以太坊开发框架,用于编译、测试和部署智能合约。
- Ganache: 一本本地的以太坊区块链,用于快速开发和测试,可以立即生成测试账户和提供以太坊。
- MetaMask: 一个浏览器钱包插件,用于与以太坊区块链交互,管理账户,以及在测试网上部署和测试合约。
- Solidity: 以太坊智能合约的编程语言,需要安装Solidity编译器(solc)。
安装步骤(简述):
npm install -g truffle
# 安装 Ganache (从官网下载桌面应用或命令行版本)
# 安装 MetaMask (浏览器扩展商店)
编写ERC20代币智能合约
我们可以使用Truffle提供的ERC20合约模板,或者从OpenZeppelin库导入经过审计的ERC20实现,后者更安全可靠。
使用OpenZeppelin库(推荐)
OpenZeppelin提供了一套安全的、标准化的智能合约组件,包括ERC20代币。

-
初始化项目:
mkdir my-erc20-token cd my-erc20-token npm init -y npm install @openzeppelin/contracts
-
编写合约: 在
contracts/目录下创建MyToken.sol文件。// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MyToken is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) { // 在部署时,向msg.sender(部署者)铸造1000000个代币,18位小数 _mint(msg.sender, 1000000 * 10**decimals()); } }SPDX-License-Identifier: 指定许可证。pragma solidity ^0.8.20;指定Solidity编译器版本。import "@openzeppelin/contracts/token/ERC20/ERC20.sol";导入OpenZeppelin的ERC20合约。contract MyToken is ERC20;声明我们的合约继承自ERC20。constructor: 构造函数,在合约部署时调用,我们在这里设置了代币的名称和符号,并通过_mint函数向部署者铸造初始代币。_mint是ERC20合约内部提供的铸造函数。
编译合约
在项目根目录下运行:
truffle compile
如果成功,会在 build/contracts/ 目录下生成编译后的JSON文件(ABI和字节码)。

测试合约
测试是确保智能合约正确性和安全性的重要环节。
-
编写测试脚本: 在
test/目录下创建myToken.test.js文件(使用JavaScript或TypeScript)。const MyToken = artifacts.require("MyToken"); contract("MyToken", (accounts) => { it("should put 1000000 MyToken in the first account", async () => { const myTokenInstance = await MyToken.deployed(); const balance = await myTokenInstance.balanceOf(accounts[0]); assert.equal(balance.toString(), "1000000000000000000000000", "1000000 tokens weren't in the first account"); }); it("should transfer tokens correctly", async () => { const myTokenInstance = await MyToken.deployed(); const sender = accounts[0]; const receiver = accounts[1]; const amount = web3.utils.toWei("100", "ether"); // 假设decimals是18 // 检查初始余额 let senderBalance = await myTokenInstance.balanceOf(sender); let receiverBalance = await myTokenInstance.balanceOf(receiver); assert.equal(senderBalance.toString(), "1000000000000000000000000"); assert.equal(receiverBalance.toString(), "0"); // 转移 await myTokenInstance.transfer(receiver, amount); // 检查转移后余额 senderBalance = await myTokenInstance.balanceOf(sender); receiverBalance = await myTokenInstance.balanceOf(receiver); assert.equal(senderBalance.toString(), "999900000000000000000000"); assert.equal(receiverBalance.toString(), "100000000000000000000"); }); }); -
运行测试:
truffle test
部署合约
部署合约到以太坊网络(可以是本地Ganache、测试网如Goerli或Sepolia,或主网)。
-
配置Truffle: 在
truffle-config.js(或truffle.js) 中配置网络。require('dotenv').config(); // 使用.env文件管理私钥等敏感信息 const { MNEMONIC, INFURA_API_KEY } = process.env; module.exports = { networks: { development: { host: "127.0.0.1", // Localhost (default: none) port: 7545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, goerli: { provider: () => new HDWalletProvider(MNEMONIC, `https://goerli.infura.io/v
