以太坊作为全球最大的智能合约平台,为代币发行提供了强大的基础设施,ERC20是以太坊上最常用、最标准的代币技术规范,它定义了一套接口(Interface),使得代币可以在以太坊生态中相互兼容,轻松被钱包、交易所等应用识别和集成,本教程将带你从零开始,一步步学习如何使用Solidity语言和开发工具,在以太坊上发行一枚属于自己的ERC20代币。
准备工作:开发环境搭建
在开始编写智能合约之前,你需要准备好以下开发环境:
- 代码编辑器:推荐使用Visual Studio Code(VS Code),并安装Solidity相关插件,如
Solidity by Juan Blanco或Hardhat for VS Code,以获得语法高亮、代码提示和编译支持。 - Node.js 和 npm:Node.js是一个JavaScript运行时环境,npm是Node.js的包管理器,许多以太坊开发工具(如Hardhat, Truffle)都依赖于它们,从Node.js官网下载并安装LTS版本。
- 以太坊客户端/钱包:你需要一个钱包来与以太坊网络交互,管理账户和测试ETH,推荐使用MetaMask,它是一个浏览器插件钱包,支持连接到各种以太坊测试网和主网。
- 测试网ETH:在以太坊主网上部署智能合约需要真实的ETH,并且成本较高,我们通常在测试网上进行开发和测试,你可以从水龙头(如Goerli测试网水龙头)获取免费的测试网ETH。
选择开发框架:Truffle 或 Hardhat
虽然你可以直接使用Solidity编写合约并通过solc(Solidity编译器)编译,但使用开发框架可以大大简化开发、测试和部署流程,目前最流行的两个框架是Truffle和Hardhat。
- Truffle:老牌框架,生态成熟,文档丰富,适合初学者。
- Hardhat:新兴框架,更现代化,性能更好,插件系统强大,社区活跃。
本教程将以Hardhat为例进行讲解,因为它在当前开发中更受欢迎。
安装Hardhat
- 创建一个新的项目目录:
mkdir my-erc20-token cd my-erc20-token
- 初始化npm项目(一路回车即可):
npm init -y
- 安装Hardhat:
npm install --save-dev hardhat
- 在项目目录下初始化Hardhat项目:
npx hardhat
选择
Create a basic sample project,然后按提示操作(接受默认配置即可),这将创建一个包含示例合约、测试脚本和配置文件的目录结构。
编写ERC20智能合约
Hardhat初始化后会创建一个contracts目录,里面有一个Lock.sol示例合约,我们将其删除,然后创建我们自己的ERC20代币合约,例如MyToken.sol。
在contracts目录下创建MyToken.sol文件,并编写以下代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
_mint(msg.sender, 1000000 * 10**decimals()); // 发行100万代币,默认18位小数
}
}
代码解析:
SPDX-License-Identifier: MIT:许可证标识符,声明合约的MIT许可证。pragma solidity ^0.8.9;:指定Solidity编译器版本,^0.8.9表示使用0.8.9或更高但不包括0.9.0的版本。import "@openzeppelin/contracts/token/ERC20/ERC20.sol";:导入OpenZeppelin库中的ERC20标准合约,OpenZeppelin提供了经过审计的安全的合约实现,强烈建议在开发中使用,避免重复造轮子和引入安全漏洞。contract MyToken is ERC20:定义一个名为MyTokencode>的合约,它继承自OpenZeppelin的
ERC20合约,因此自动获得了所有ERC20标准的属性和功能(如transfer,approve,transferFrom,balanceOf等)。constructor(string memory name, string memory symbol) ERC20(name, symbol):构造函数,在合约部署时调用,它接收代币名称(name)和代币符号(symbol)作为参数,并传递给父类ERC20的构造函数。_mint(msg.sender, 1000000 * 10**decimals());:_mint是ERC20合约内部定义的函数,用于铸造新代币,这里我们向合约部署者(msg.sender)发行100万代币。decimals()返回ERC20代币的小数位数,默认为18,所以10**decimals()表示1代币的最小单位(如1 ETH = 10^18 wei),100万代币实际上是1000000 * 10^18个最小单位。
安装OpenZeppelin合约库
我们需要安装OpenZeppelin的合约库才能导入:
npm install @openzeppelin/contracts
编译智能合约
在项目根目录下,运行以下命令编译合约:
npx hardhat compile
如果编译成功,Hardhat会在artifacts目录下生成编译后的合约字节码和ABI(应用程序二进制接口)。
编写测试脚本
为了确保我们的合约按预期工作,我们需要编写测试脚本,Hardhat使用Mocha作为测试框架,使用Chai作为断言库。
在test目录下创建myToken.test.js文件(或TypeScript版本.ts):
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("MyToken", function () {
it("Should deploy and mint tokens correctly", async function () {
const [owner] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MyToken");
const hardhatToken = await Token.deploy("My Awesome Token", "MAT");
await hardhatToken.deployed();
// 验证代币名称和符号
expect(await hardhatToken.name()).to.equal("My Awesome Token");
expect(await hardhatToken.symbol()).to.equal("MAT");
// 验证总供应量
const ownerBalance = await hardhatToken.balanceOf(owner.address);
expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
// 验证初始供应量是否为100万
expect(ownerBalance).to.equal(ethers.utils.parseUnits("1000000", 18));
});
});
运行测试
npx hardhat test
如果所有测试通过,说明你的合约逻辑基本正确。
部署智能合约到测试网
部署前,确保你的MetaMask已经连接到以太坊测试网(如Goerli),并且账户中有足够的测试网ETH。
-
配置网络:在Hardhat项目中,配置部署脚本,打开
scripts目录,删除deploy.js示例文件,创建新的部署脚本,例如deploy.js:async function main() { const MyToken = await ethers.getContractFactory("MyToken"); const myToken = await MyToken.deploy("My Awesome Token", "MAT"); await myToken.deployed(); console.log("MyToken deployed to:", myToken.address); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); -
配置Hardhat网络:打开
hardhat.config.js文件,添加测试网配置,你需要安装@nomicfoundation/hardhat-network和dotenv来管理环境变量。npm install --save-dev @nomicfoundation/hardhat-network dotenv
然后在项目根目录创建
.env文件,存放你的私钥(注意:私钥要妥善保管,不要泄露!)和测试网RPC URL:PRIVATE_KEY=你的MetaMask测试网账户私钥(不要带0x) GOERLI_RPC_URL=https://goerli.infura.io/v3/你的INFURA_PROJECT_ID修改
hardhat.config.js:require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config(); /** @type import('hardhat/config').HardhatUserConfig */