第一次学习Hardhat
目录
- 什么是 Hardhat?
- 搭建开发环境
- 安装 Node.js
- 安装 Hardhat
- Hardhat 的核心架构
- 开发我们的第一个智能合约
- 编写 Token 合约
- 编译合约
- 测试合约
- 调试合约
什么是 Hardhat?
Hardhat 是一个专为以太坊开发者设计的开发环境。它不仅提供了编译、部署、测试和调试智能合约的完整工具链,还拥有强大的插件生态系统,让开发者可以根据自己的需求定制开发流程。相比其他工具,Hardhat 的最大优势在于其出色的调试能力和灵活的架构设计。
搭建开发环境
在开始之前,我们需要先准备好开发环境。正所谓"工欲善其事,必先利其器",一个好的开发环境能让我们事半功倍。
安装 Node.js
Hardhat 基于 Node.js 构建,因此首先需要安装 Node.js。文章推荐使用 nvm (Node Version Manager) 来管理 Node.js 版本,这样可以轻松切换不同版本:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
nvm install 18
nvm use 18
nvm alias default 18
npm install npm --global # Upgrade npm to the latest version
使用 nvm 的好处是可以在不同项目间切换 Node.js 版本,避免版本冲突问题。这里选择 Node.js 18 是因为它是目前的长期支持版本,稳定性和兼容性都比较好。
安装 Hardhat
环境准备就绪后,我们就可以安装 Hardhat 了。创建一个新的项目目录:
mkdir hardhat
cd hardhat
然后安装 Hardhat:
npm install --save-dev hardhat
安装完成后,目录中会出现 node_modules
、package-lock.json
和 package.json
等文件,这表明 Hardhat 已经成功安装。
接下来,运行 npx hardhat
命令初始化项目配置。系统会提示你选择创建 Hardhat 配置文件,选择默认选项即可。
Hardhat 的核心架构
在深入开发之前,有必要了解一下 Hardhat 的核心设计理念。Hardhat 是围绕 task(任务) 和 plugins(插件) 这两个核心概念设计的。
每次从命令行运行 Hardhat 时,实际上都是在执行某个特定的任务。比如 npx hardhat compile
就是在运行编译任务。你可以通过运行 npx hardhat
来查看当前项目中所有可用的任务,或者使用 npx hardhat help [task]
来深入了解某个特定任务的使用方法。
Hardhat 的大部分功能都来自插件,这种设计让开发者可以自由选择需要的工具,而不必被一套固定的工具链所束缚。
开发我们的第一个智能合约
理论知识了解得差不多了,现在让我们动手开发第一个智能合约吧!
编写 Token 合约
我们来创建一个简单的 Token 合约。首先创建 contracts 目录:
mkdir contracts
cd contracts
然后创建 Token.sol 文件,编写我们的智能合约代码:
//SPDX-License-Identifier: UNLICENSED// Solidity files have to start with this pragma.
// It will be used by the Solidity compiler to validate its version.
pragma solidity ^0.8.9;// This is the main building block for smart contracts.
contract Token {// Some string type variables to identify the token.string public name = "My Hardhat Token";string public symbol = "MHT";// The fixed amount of tokens, stored in an unsigned integer type variable.uint256 public totalSupply = 1000000;// An address type variable is used to store ethereum accounts.address public owner;// A mapping is a key/value map. Here we store each account's balance.mapping(address => uint256) balances;// The Transfer event helps off-chain applications understand// what happens within your contract.event Transfer(address indexed _from, address indexed _to, uint256 _value);/*** Contract initialization.*/constructor() {// The totalSupply is assigned to the transaction sender, which is the// account that is deploying the contract.balances[msg.sender] = totalSupply;owner = msg.sender;}/*** A function to transfer tokens.** The `external` modifier makes a function *only* callable from *outside** the contract.*/function transfer(address to, uint256 amount) external {// Check if the transaction sender has enough tokens.// If `require`'s first argument evaluates to `false` then the// transaction will revert.require(balances[msg.sender] >= amount, "Not enough tokens");// Transfer the amount.balances[msg.sender] -= amount;balances[to] += amount;// Notify off-chain applications of the transfer.emit Transfer(msg.sender, to, amount);}/*** Read only function to retrieve the token balance of a given account.** The `view` modifier indicates that it doesn't modify the contract's* state, which allows us to call it without executing a transaction.*/function balanceOf(address account) external view returns (uint256) {return balances[account];}
}
这个合约虽然简单,但包含了智能合约的核心要素:
- 状态变量:存储合约的状态信息
- 事件:用于通知外部应用合约内部发生的变化
- 构造函数:合约部署时执行的初始化代码
- 函数:合约提供的功能接口
编译合约
编写完成后,使用以下命令编译合约:
npx hardhat compile
编译成功后,Hardhat 会生成相应的 artifacts(合约的编译产物),这些文件将在后续的部署和测试中使用。
测试合约
没有经过测试的代码是不可靠的,智能合约尤其如此。让我们为合约编写测试用例。
首先创建测试目录:
mkdir test
然后创建 Token.js 测试文件:
const { expect } = require("chai");describe("Token contract", function () {it("Deployment should assign the total supply of tokens to the owner", async function () {const [owner] = await ethers.getSigners();const Token = await ethers.getContractFactory("Token");const hardhatToken = await Token.deploy();const ownerBalance = await hardhatToken.balanceOf(owner.address);expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);});
});
运行测试:
npx hardhat test
如果一切正常,测试应该会通过。这表明我们的合约在基本功能上是正确的。
调试合约
Hardhat 内置了 Hardhat Network,这是一个专门为开发设计的以太坊本地网络。它允许我们在本地部署合约、运行测试和调试代码,而无需连接到真实的区块链网络。
更令人兴奋的是,Hardhat 支持在 Solidity 代码中使用 console.log
!这在传统的区块链开发中是很难实现的。让我们来体验一下这个强大的功能。
首先,在合约中导入 console:
pragma solidity ^0.8.9;import "hardhat/console.sol";contract Token {// ... 之前的代码 ...
}
然后在 transfer 函数中添加调试信息:
function transfer(address to, uint256 amount) external {require(balances[msg.sender] >= amount, "Not enough tokens");console.log("Transferring from %s to %s %s tokens",msg.sender,to,amount);balances[msg.sender] -= amount;balances[to] += amount;emit Transfer(msg.sender, to, amount);
}
现在,当我们运行测试时,就能在控制台看到详细的调试信息了。这对于排查问题和理解合约执行流程非常有帮助。