开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
之前我们介绍了基于 Ganache 测试网络和 Remix IDE 编写并部署智能合约的方法。今天我们学习一下使用 HardHat 完成智能合约的开发。
1. 概述
Hardhat是一个编译、部署、测试和调试以太坊应用的开发环境,它可以帮助开发人员管理和自动化构建智能合约和DApp的过程中的重复任务。
Hardhat 还内置了 Hardhat网络,这是为开发而设计的本地以太坊网络,它允许你部署合约、运行测试和调试代码。
Hardhat Runner 是与 Hardhat 交互的CLI命令,是一个可扩展的任务运行器。它是围绕任务和插件的概念设计的。每次你从CLI运行Hardhat时,你都在运行一个任务。例如,npx hardhat compile运行的是内置的compile任务。任务可以调用其他任务,允许定义复杂的工作流程。
接下来,我们将介绍完成以下操作:
- 为以太坊开发设置Node.js环境;
- 创建和配置 Hardhat 项目;
- 实现 Solidity 智能合约代币;
- 使用 Ethers.js 和 Waffle 为合约编写自动化测试;
- 使用 Hardhat EVM 通过
console.log()调试Solidity; - 将合约部署到 Hardhat EVM 和以太坊测试网。
2. 环境搭建
大多数以太坊库和工具都是用 JavaScript 编写的,Hardhat 也是如此,Hardhat 是建立 Node.js 之上的,因此在学习和安装 Hardhat 之前,请先了解并安装 Node.js> = 12.0。
项目创建
我们将使用 npm 命令行安装hardhat。打开一个新终端并运行以下命令:
mkdir hardhat-tutorial
cd hardhat-tutorial
npm init --yes
npm install --save-dev hardhat
✨安装 Hardhat 会安装一些以太坊的 npm 依赖包。
在安装Hardhat的目录下运行 npx hardhat,然后命令行显示如下:
$ npx hardhat
888 888 888 888 888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
Welcome to Hardhat v2.0.8
? What do you want to do? …
❯ Create a sample project
Create an empty hardhat.config.js
Quit
使用键盘选择 Create an empty hardhat.config.js,然后回车。
在运行 Hardhat 时,它将从当前工作目录开始搜索最接近的hardhat.config.js文件,这个文件通常位于项目的根目录下。
Hardhat 架构
Hardhat 是围绕 task(任务) 和 plugins(插件) 的概念设计的,它的大部分功能来自插件,你可选择你要使用的插件。
Tasks(任务)
每次你从命令行运行 Hardhat 时,你都在运行任务。例如 npx hardhat compile:正在运行compile任务。要查看项目中当前可用的任务,运行npx hardhat。 通过运行npx hardhat help [task],可以探索任何任务。
Plugins(插件)
Hardhat 不限制选择哪种工具,但是它确实内置了一些插件,所有这些插件也都可以覆盖。 大多数时候,使用给定工具的方法是将其集成到 Hardhat 中作为插件。
安装插件
在本项目中,我们将使用 Ethers.js 和 Waffle 插件,通过他们与以太坊进行交互并测试合约。要安装它们,请在项目目录中运行:
npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
并将 require("@nomiclabs/hardhat-waffle"); 添加到你的 hardhat.config.js 中,如下所示:
require("@nomiclabs/hardhat-waffle");
/**
*@type import('hardhat/config').HardhatUserConfig
*/module.exports = {
solidity: "0.8.17",
};
3. 编写和编译合约
接下来,我们将会创建一个简单的智能合约,功能是实现简单的代币转让。代币合约最常用于兑换或价值存储。这里,我们不深入讨论合约的 Solidity 源码,只介绍一些简单的规则:
- 代币有固定的发行总量,并且总量是无法更改。
- 所有发行总量都分配给了部署合约的地址。
- 任何人都可以接收代币。
- 任何人拥有代币的人都可以转让代币。
- 代币不可分割。 你只能交易整数个的代币。
✨你可能听说过
ERC20,它是以太坊中的代币标准。 DAI,USDC,MKR和ZRX之类的代币都遵循ERC20标准,使这些代币都可以与任何能处理ERC20代币的软件兼容。 为了简单起见,我们并不构建满足ERC20标准的代币。
编写合约
首先在项目根路径创建一个名为 contracts 的新目录,然后在目录内创建一个名为 Token.sol 的文件,最后将下面的代码粘贴到文件中
✨你可以使用 VSCode ,并在插件商城中添加相应的插件(Hardhat for Visual Studio Code)。
pragma solidity ^0.8.16;
// 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 = "MBT";
// 固定发行量
uint256 public totalSupply = 1000000;
// 存储代币的账户
address public owner;
// A mapping is a key/value map. Here we store each account balance.mapping(address => uint256) balances;
/**
* 合约构造函数
*
* The `constructor` is executed only once when the contract is created.
* The `public` modifier makes a function callable from outside the contract.
*/
constructor() public {
// The totalSupply is assigned to transaction sender, which is the account// that is deploying the contract.
balances[msg.sender] = totalSupply;
owner = msg.sender;
}
/**
* 代币转账.
*
* 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;
}
/**
* 读取某账号的代币余额
*
* 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];
}
}
✨
*.sol是 Solidity 合约文件的后缀,建议将文件名与其包含的合约名一致。
编译合约
要编译合约,你可以在终端中运行 npx hardhat compile 。 compile 任务是内置任务之一。
$ npx hardhat compile
Compiling...
Compiled 1 contract successfully
此时,合约已成功编译,可以运行了。