在区块链世界中,代币是数字资产的重要载体,而ERC20(Ethereum Request for Comments 20)是以太坊上最标准、应用最广泛的代币技术标准,它定义了一套统一的接口规范,使得代币可以在以太坊生态中无缝流转、交易和被钱包、交易所等工具识别,无论是初学者还是开发者,掌握ERC20代币开发都是进入区块链领域的重要一步,本文将带你从零开始,手把手教你开发一个符合ERC20标准的代币,涵盖环境搭建、代码编写、测试到部署的全流程。
ERC20标准核心规范
在开始开发前,我们需要先理解ERC20的核心要求,ERC20标准定义了以下必须实现的接口函数和事件:
必需接口函数
name():返回代币全称(如 "USD Coin")。symbol():返回代币符号(如 "USDC")。decimals():返回代币精度(小数位数,通常为18)。totalSupply():返回代币总供应量。balanceOf(address owner):查询指定地址的代币余额。transfer(address to, uint256 amount):向指定地址转移代币。transferFrom(address from, address to, uint256 amount):从指定地址转移代币(需授权)。allowance(address owner, address spender):查询授权额度。approve(address spender, uint256 amount):授权第三方地址转移代币。
必需事件
Transfer(address indexed from, address indexed to, uint256 value):代币转移时触发。Approval(address indexed owner, address indexed spender, uint256 value):授权时触发。
开发环境准备
在编写ERC20代币之前,我们需要搭建以下开发环境:
安装Node.js和npm
ERC20代币通常使用Solidity语言编写,而Solidity的开发依赖Node.js环境,从nodejs官网下载并安装LTS版本(建议v16+),安装完成后可通过终端运行以下命令验证:
node -v # 查看Node.js版本 npm -v # 查看npm版本
安装Hardhat
Hardhat是一个以太坊开发环境,支持编译、测试、部署Solidity合约,功能强大且易于上手,通过以下命令安装:
npm init -y # 初始化npm项目 npm install --save-dev hardhat
安装完成后,在项目目录下运行:
npx hardhat # 初始化Hardhat项目
根据提示选择"Create a JavaScript project"(JavaScript项目),并确认安装依赖(如@nomicfoundation/hardhat-toolbox)。
安装OpenZeppelin合约库
OpenZeppelin是一个开源的智能合约库,提供了经过审计的ERC20标准实现,可直接复用,避免重复造轮子和安全漏洞,安装命令:
npm install @openzeppelin/contracts
编写ERC20代币合约
创建合约文件
在Hardhat项目中,合约文件通常存放在contracts/目录下,创建一个名为MyToken.sol的文件:
touch contracts/MyToken.sol
编写合约代码
打开MyToken.sol,编写以下代码(基于OpenZeppelin的ERC20实现):
// 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) {
// 初始供应量:1亿代币,18位小数(ERC20默认)
_mint(msg.sender, 100000000 * 10**decimals());
}
}
代码解析:
SPDX-License-Identifier: MIT:声明MIT开源协议(可选但推荐)。pragma solidity ^0.8.20:指定Solidity编译器版本(0.8.20及以上,兼容性较好)。import "@openzeppelin/contracts/token/ERC20/ERC20.sol":导入OpenZeppelin的ERC20标准合约。contract MyToken is ERC20:定义MyToken合约,继承自ERC20,自动实现所有ERC20接口。constructor:构造函数,在合约部署时调用。_mint()是ERC20库中的内部函数,用于向部署地址(msg.sender)铸造代币,100000000 * 10**decimals()确保代币精度正确(如18位小数时,实际供应量为1亿*10^18)。
编译和测试合约
编译合约
在终端运行以下命令编译合约:
npx hardhat compile
编译成功后,合约的ABI(应用二进制接口)和字节码会生成在artifacts/contracts/MyToke中,这是后续部署和交互的关键文件。
编写测试脚本
测试是确保合约安全性的重要环节,在test/目录下创建myToken.test.js文件(JavaScript测试):
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("MyToken", function () {
let MyToken;
let myToken;
let owner;
let addr1;
let addr2;
beforeEach(async function () {
// 获取合约实例
MyToken = await ethers.getContractFactory("MyToken");
// 部署合约(参数为name和symbol)
myToken = await MyToken.deploy("My Token", "MTK");
await myToken.deployed();
// 获取测试账户
[owner, addr1, addr2] = await ethers.getSigners();
});
it("Should set the right name and symbol", async function () {
expect(await myToken.name()).to.equal("My Token");
expect(await myToken.symbol()).to.equal("MTK");
});
it("Should assign the initial supply to the owner", async function () {
const ownerBalance = await myToken.balanceOf(owner.address);
expect(ownerBalance).to.equal(100000000 * 10**18); // 1亿代币
});
it("Should transfer tokens between accounts", async function () {
// owner转账1000代币给addr1
await myToken.transfer(addr1.address, 1000 * 10**18);
const addr1Balance = await myToken.balanceOf(addr1.address);
expect(addr1Balance).to.equal(1000 * 10**18);
// addr1转账500代币给addr2
await myToken.connect(addr1).transfer(addr2.address, 500 * 10**18);
const addr2Balance = await myToken.balanceOf(addr2.address);
expect(addr2Balance).to.equal(500 * 10**18);
});
it("Should allow approved user to transferFrom", async function () {
// owner授权addr1使用1000代币
await myToken.approve(addr1.address, 1000 * 10**18);
const allowance = await myToken.allowance(owner.address, addr1.address);
expect(allowance).to.equal(1000 * 10**18);
// addr1通过transferFrom从owner账户转移500代币给addr2
await myToken.connect(addr1).transferFrom(owner.address, addr2.address, 500 * 10**18);
const addr2Balance = await myToken.balanceOf(addr2.address);
expect(addr2Balance).to.equal(500 * 10**18);
});
});
- 验证代币名称和符号是否正确。
- 验证初始供应量是否正确分配给部署者。
- 测试普通转账功能。
- 测试授权和
transferFrom功能。
运行测试:
npx hardhat test
如果所有测试通过,说明合约逻辑正确。
部署合约到以太坊网络
选择网络
ERC20代币可部署到以太坊主网、测试网(如Goerli、Sepolia)或本地网络(如Hardhat Network),这里以本地Hardhat Network为例,无需消耗真实ETH。
配置部署脚本
在scripts/目录下创建deploy.js文件:
async function main() {
// 获取合约工厂
const MyToken = await ethers.getContractFactory("MyToken");
// 部署合约(参数为name和symbol)
const myToken = await MyToken.deploy("My Token", "MTK");
// 等待部署完成
await my