目录
-
- [什么是 Hardhat?](#什么是 Hardhat?)
- 搭建开发环境
-
- [安装 Node.js](#安装 Node.js)
- [安装 Hardhat](#安装 Hardhat)
- [Hardhat 的核心架构](#Hardhat 的核心架构)
- 开发我们的第一个智能合约
什么是 Hardhat?
Hardhat 是一个专为以太坊开发者设计的开发环境。它不仅提供了编译、部署、测试和调试智能合约的完整工具链,还拥有强大的插件生态系统,让开发者可以根据自己的需求定制开发流程。相比其他工具,Hardhat 的最大优势在于其出色的调试能力和灵活的架构设计。
搭建开发环境
在开始之前,我们需要先准备好开发环境。正所谓"工欲善其事,必先利其器",一个好的开发环境能让我们事半功倍。
安装 Node.js
Hardhat 基于 Node.js 构建,因此首先需要安装 Node.js。文章推荐使用 nvm (Node Version Manager) 来管理 Node.js 版本,这样可以轻松切换不同版本:
bash
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 了。创建一个新的项目目录:
bash
mkdir hardhat
cd hardhat
然后安装 Hardhat:
bash
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 目录:
bash
mkdir contracts
cd contracts
然后创建 Token.sol 文件,编写我们的智能合约代码:
solidity
//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];
}
}
这个合约虽然简单,但包含了智能合约的核心要素:
- 状态变量:存储合约的状态信息
- 事件:用于通知外部应用合约内部发生的变化
- 构造函数:合约部署时执行的初始化代码
- 函数:合约提供的功能接口
编译合约
编写完成后,使用以下命令编译合约:
bash
npx hardhat compile
编译成功后,Hardhat 会生成相应的 artifacts(合约的编译产物),这些文件将在后续的部署和测试中使用。
测试合约
没有经过测试的代码是不可靠的,智能合约尤其如此。让我们为合约编写测试用例。
首先创建测试目录:
bash
mkdir test
然后创建 Token.js 测试文件:
javascript
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);
});
});
运行测试:
bash
npx hardhat test
如果一切正常,测试应该会通过。这表明我们的合约在基本功能上是正确的。
调试合约
Hardhat 内置了 Hardhat Network,这是一个专门为开发设计的以太坊本地网络。它允许我们在本地部署合约、运行测试和调试代码,而无需连接到真实的区块链网络。
更令人兴奋的是,Hardhat 支持在 Solidity 代码中使用 console.log
!这在传统的区块链开发中是很难实现的。让我们来体验一下这个强大的功能。
首先,在合约中导入 console:
solidity
pragma solidity ^0.8.9;
import "hardhat/console.sol";
contract Token {
// ... 之前的代码 ...
}
然后在 transfer 函数中添加调试信息:
solidity
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);
}
现在,当我们运行测试时,就能在控制台看到详细的调试信息了。这对于排查问题和理解合约执行流程非常有帮助。