0%

StudyRecord-Hardhat框架初步使用

Pre:

熟悉、回顾一下hardhat框架的使用,为后续erc20各类功能实现的学习做准备。

hardhat介绍:

Hardhat是一个编译、部署、测试和调试以太坊应用的开发环境。

它可以帮助开发人员管理和自动化构建智能合约和DApps过程中固有的重复性任务,并围绕这一工作流程轻松引入更多功能。

这意味着hardhat在最核心的地方是编译、运行和测试智能合约。

Hardhat内置了Hardhat网络,这是一个专为开发设计的本地以太坊网络。主要功能有Solidity调试,跟踪调用堆栈、console.log()和交易失败时的明确错误信息提示等。

Hardhat Runner是与Hardhat交互的CLI命令,是一个可扩展的任务运行器。它是围绕任务和插件的概念设计的。每次你从CLI运行Hardhat时,你都在运行一个任务。例如,npx hardhat compile运行的是内置的compile任务。任务可以调用其他任务,允许定义复杂的工作流程。用户和插件可以覆盖现有的任务,从而定制和扩展工作流程。

Hardhat的很多功能都来自于插件,而作为开发者,你可以自由选择想使用的插件。Hardhat不限制使用什么工具的,但它确实有一些内置的默认值。所有这些都可以覆盖

设置环境:

完整的安装步骤可参考refs里的链接进行安装,此处只记录部分的步骤

创建一个Hardhat项目:

1
2
3
4
5
6
7
8
9
10
11
12
# 创建文件夹、为项目根目录
mkdir demo
cd demo
# yarn初始化
yarn init --yes
yarn add -D hardhat
# 安装各类插件
yarn add -D hardhat-deploy hardhat-deploy-ethers ethers chai chai-ethers mocha @types/chai @types/mocha @types/node typescript ts-node dotenv
# 安装openzeppelin合约库
yarn add @openzeppelin/contracts
# 超时、github连接不上,可尝试加timeout参数
yarn add -D @nomiclabs/hardhat-etherscan --network-timeout 600000

根目录编辑tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"outDir": "dist"
},

"include": [
"hardhat.config.ts",
"./deploy",
"./test",
]
}

编写智能合约:

根目录编辑hardhat.config.ts文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// hardhat.config.ts
import {HardhatUserConfig} from 'hardhat/types';
import 'hardhat-deploy';
import 'hardhat-deploy-ethers';

const config: HardhatUserConfig = {
solidity: {
version: '0.7.6',
},
namedAccounts: {
deployer: 0,
},
paths: {
sources: 'src',
},
};
export default config;

根目录创建一个名为contracts的新目录,并在该目录内创建一个名为Token.sol的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Token.sol
// SPDX-License-Identifier: MIT
// The line above is recommended and let you define the license of your contract
// Solidity files have to start with this pragma.
// It will be used by the Solidity compiler to validate its version.
pragma solidity ^0.7.0;


// This is the main building block for smart contracts.
contract Token {
// Some string type variables to identify the token.
// The `public` modifier makes a variable readable from outside the contract.
string public name = "My Hardhat Token";
string public symbol = "MBT";

// 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 balance.
mapping(address => uint256) balances;

/**
* Contract initialization.
*
* The `constructor` is executed only once when the contract is created.
*/
constructor(address _owner) {
// The totalSupply is assigned to transaction sender, which is the account
// that is deploying the contract.
balances[_owner] = totalSupply;
owner = _owner;
}

/**
* 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;
}

/**
* 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];
}
}

编译:

终端运行

1
yarn hardhat compile

输出

1
2
3
4
yarn run v1.22.10
$ /Users/cool-erc20/demo/node_modules/.bin/hardhat compile
Compiled 1 Solidity file successfully
✨ Done in 5.25s.

部署脚本:

在能够测试或部署合约之前,你需要设置部署脚本,以便在测试和准备部署时使用。部署脚本让你可以专注于合约的最终形式,设置它们的参数和依赖关系,并确保你的测试的是将要部署的内容。部署脚本也省去了重复部署的烦恼。

经历过手工测试之后,才知道用脚本测试有多必要…

根目录下创建一个名为deploy的新目录,并创建一个名为001_deploy_token.ts的新文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 001_deploy_token.ts
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre;
const {deploy} = deployments;

const {deployer, tokenOwner} = await getNamedAccounts();

await deploy('Token', {
from: deployer,
args: [tokenOwner],
log: true,
});
};
export default func;
func.tags = ['Token'];

编辑hardhat.config.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// hardhat.config.ts
import {HardhatUserConfig} from 'hardhat/types';
import 'hardhat-deploy';
import 'hardhat-deploy-ethers';

const config: HardhatUserConfig = {
solidity: {
version: '0.7.6',
},
namedAccounts: {
deployer: 0,
tokenOwner: 1,
},
paths: {
sources: 'src',
},
};
export default config;

终端上运行

1
yarn hardhat deploy

此次部署是在内存中的hardhat网络中进行的。

输出

1
2
3
4
5
6
$ yarn hardhat deploy
yarn run v1.22.10
$ /Users/cool-erc20/demo/node_modules/.bin/hardhat deploy
Nothing to compile
deploying "Token" (tx: 0x453cf83db75329816a62d6079aa22f694036d339106344b7e1864bcb344aa49d)...: deployed at 0x5FbDB2315678afecb367f032d93F642f64180aa3 with 483242 gas
✨ Done in 5.60s.

测试合约:

在项目根目录下创建一个名为test的新目录,并创建一个名为Token.test.ts的新文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Token.test.ts
import {expect} from "./chai-setup";

import {ethers, deployments, getNamedAccounts} from 'hardhat';

describe("Token contract", function() {
it("Deployment should assign the total supply of tokens to the owner", async function() {
await deployments.fixture(["Token"]);
const {tokenOwner} = await getNamedAccounts();
const Token = await ethers.getContract("Token");
const ownerBalance = await Token.balanceOf(tokenOwner);
const supply = await Token.totalSupply();
expect(ownerBalance).to.equal(supply);
});
});

test目录下创建chai-setup.ts

1
2
3
4
5
// chai-setup.ts
import chaiModule from 'chai';
import {chaiEthers} from 'chai-ethers';
chaiModule.use(chaiEthers);
export = chaiModule;

在终端上运行

1
npx hardhat test

输出

1
2
3
4
5
6
7
8
$ npx hardhat test


Token contract
✔ Deployment should assign the total supply of tokens to the owner (947ms)


1 passing (958ms)

常用命令合集:

记录一些hardhat框架的常用命令,后面方便复查。

编译:

1
yarn hardhat compile

部署

1
2
3
4
# 部署在hardhat网络
yarn hardhat deploy
# 部署在配置文件对应的测试网
yarn hardhat --network bsc_testnet deploy

测试:

1
2
3
4
5
6
# 测试整个test文件夹里的script,hardhat环境
yarn hardhat test
# 测试整个test文件夹里的script,指定测试网
yarn hardhat --network bsc_testnet test
# 测试单个script
yarn hardhat --network bsc_testnet test test/transfer.ts

fork主网:

chainlist里找一个rpc链接
hardhat.config.ts加上配置

1
2
3
4
5
6
7
8
9
10
11
12
networks: {
hardhat: {
mining: {
auto: true,
interval: 1000
}
},
forking: {
url: "https://bsc-mainnet.nodereal.io/v1/64a9df0874fb4a93b9d0a3849de012d3",

}
},
1
2
3
4
5
6
# 这个命令会将当前最新的一个区块数据 fork 到本地,也就是说现在本地 node 链中存在的就是这个区块的数据。
npx hardhat node --fork https://bsc-mainnet.nodereal.io/v1/64a9df0874fb4a93b9d0a3849de012d3
# 也可以指定区块号进行 fork
npx hardhat node --fork https://bsc-mainnet.nodereal.io/v1/64a9df0874fb4a93b9d0a3849de012d3 --fork-block-number 19906400
# 单元测试在本地 node 中执行,需要指定网络localhost
yarn hardhat --network localhost test test/PancakeSyrupPoolStrategy.test.ts

验证合约源码:

1
2
# example:合约源代码、部署网络、合约的地址
yarn hardhat verify --contract contracts/JToken.sol:JToken --network bsc_testnet 0x582cA468bcE2a68D1147D5411814256E52590103

Summary:

构建一个hardhat项目,一般需要以下几个步骤

  • 环境安装

  • 创建hardhat项目

  • 智能合约编写和编译 compile

  • 编写部署脚本 deploy

  • 验证合约源码

  • 编写测试脚本 test

Inspire:

search或构建一个适合某类项目的hardhat脚手架,就不用每次都一步步setup了。

refs: