我从零开始构建 ERC-20 代币和 React dApp 完整 Web3 解析
发布: (2026年5月3日 GMT+8 14:22)
3 分钟阅读
原文: Dev.to
Source: Dev.to
我构建的内容
HarambeeCoin(HBC)是一种自定义 ERC‑20 代币。该 dApp 让你能够:
- 连接 MetaMask
- 查看你的余额
- 发送代币
- 查看完整的交易历史(发送和接收)
智能合约
我从零开始编写了 HarambeeCoin,未使用 OpenZeppelin,以便了解每一行代码。核心包括标准 ERC‑20 功能以及自定义的 mint(仅所有者)和 burn(任意持有者)。
function transfer(address to, uint256 amount) public returns (bool) {
require(balanceOf[msg.sender] >= amount, "Insufficient balance");
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
使用 Hardhat 进行测试
六个测试覆盖了关键路径。示例测试:
it("Should transfer tokens between accounts", async function () {
await harambeeCoin.transfer(addr1.address, 1000);
expect(await harambeeCoin.balanceOf(addr1.address)).to.equal(1000);
});
全部六个测试均通过。先进行测试让我在代币真正上链前捕获了一个余额检查的 bug。
将 React 连接到区块链
ethers.js v6 负责连接。下面的函数完成了整个 Web3 层的初始化:
import { ethers } from "ethers";
async function initWeb3() {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contractInstance = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
return { provider, signer, contractInstance };
}
contractInstance 的使用方式就像普通的 JavaScript 对象——调用其方法并 await 结果。
从事件获取交易历史
每一次 ERC‑20 转账都会触发 Transfer 事件。ethers.js 允许使用过滤器查询历史事件:
const sentFilter = contract.filters.Transfer(account, null);
const receivedFilter = contract.filters.Transfer(null, account);
const sentEvents = await contract.queryFilter(sentFilter, 0, "latest");
const receivedEvents = await contract.queryFilter(receivedFilter, 0, "latest");
这样即可在没有后端的情况下获得完整历史;区块链本身充当了数据库。
遇到的问题
- MetaMask 缓存 – 当 Hardhat 节点重启后,MetaMask 可能仍认为自己在旧链上。每次节点重启后,在 MetaMask 设置中删除并重新添加 Localhost 网络。
- 链 ID – Hardhat 默认是
31337,而不是1337。在配置 MetaMask 前,请先从节点输出中确认链 ID。 - ENS 解析 – 本地网络上 ENS 查询会失败。确保粘贴的任何地址以
0x开头,并且是完整的 42 位十六进制字符串。
接下来的计划
- 部署到 Sepolia 测试网
- 添加代币水龙头,让任何人都能获取 HBC
- 扩展 NFT 铸造功能
- 使用 HBC 代币实现 DAO 投票
试一试
GitHub: