Hardhat开发

Hardhat 项目创建

安装hardhat

首先创建一个项目文件夹
然后使用yarn init命令初始化项目文件夹
接下来就可以直接使用yarn这个包管理工具安装也可以用npm安装

1
2
3
yarn add --dev hardhat
//or
npm install --save-dev hardhat

创建项目

使用
yarn hardhat
出现如下界面
image.png

选择创建项目然后一路y到底即可

项目开发

只要介绍一下各个文件夹的作用,我这里会议fundme的智能合约为例

项目环境配置

环境变量

首先的重重之重就是环境变量,在开发时会需要用到一些私密的接口以及密钥等不能向外界传播的数据,但是使用频次也比较高,于是我们就可以使用环境变量来操作这些数据。
要在项目中使用这个需要安装一个包
yarn add --dev dotenv
然后再项目中创建一个.env文件夹用于存放环境变量
image.png

hardhat-config.js

然后就是项目中至关重要的一个配置文件,如果没有配置好,那肯定是爆红少不了。
测试用的网络配置,插件的配置,以及测试使用的账户配置都在里面
image.png
具体的代码如下

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
require("@nomicfoundation/hardhat-toolbox");

require("hardhat-deploy");

require("dotenv").config();

require("@nomiclabs/hardhat-ethers");

require("@nomicfoundation/hardhat-verify");

require("hardhat-gas-reporter");



/** @type import('hardhat/config').HardhatUserConfig */

//将环境变量赋值给文件中的变量方便使用

const PRIVATE_KEY = process.env.SEPOLIA_PRIVATE_KEY;

const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL;

const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;

const COINMARKET_API_KEY = process.env.COINMARKET_API_KEY;

module.exports = {

  // solidity: "0.8.19",

  //这里是设置solidity的编译版本,通过如下方法可以设置多个版本,只需要一个的花上面的注释内容即可满足

  solidity: {

    compilers: [{ version: "0.8.8" }, { version: "0.6.6" }],

  },

  //网络配置,这个主要是公链的配置,本地网络的配置就和之前的ganache差不多,运行yarn hardhat node即可

  defaultNetwork: "hardhat",

  networks: {

    // hardhat: {

    //   chainId: 31337,

    //   // gasPrice: 130000000000,

    // },

    sepolia: {

      url: SEPOLIA_RPC_URL,

      accounts: [PRIVATE_KEY],

      chainId: 11155111,

      blockConfirmations: 6,

    },

    localhost: {

      url: "http://127.0.0.1:8545/",

      chainId: 31337,

    },

  },

  //用于查看合约的部署情况

  etherscan: {

    apiKey: ETHERSCAN_API_KEY,

  },

  //对合约的gas使用情况进行统计

  gasReporter: {

    enabled: false,

    outputFile: "gas-report.txt",

    noColors: true,

    currency: "USD",

    // coinmarketcap: COINMARKET_API_KEY,

    // token: "ETH",

  },

  //使用本地网络时,所用到的账户

  namedAccounts: {

    deployer: {

      default: 0,

    },

    user: {

      default: 1,

    },

  },

};

mocking

由于在使用本地网络时无法像在网络上一样得知加密货币与真实货币之间的汇率。
我们需要使用solidity搭建一个会在本地返回汇率的工具;
这个可以在github上找到这个
sol代码
可以将它直接复制到本地编译也可以通过import把它导入到本地
image.png
至于这个solidity的版本我并没有去尝试更多版本,毕竟这中版本引起的报错真的太难解决了。把这个合约代码写好之后就需要将它部署。因此还需要写一个部署脚本;
在部署我们的fundme合约之前部署它即可。
image.png
由于他们是按顺序执行的脚本所以我们将脚本编号;
部署的代码如下

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
const { network } = require("hardhat");

const {

  developmentChains,

  DECIMALS,

  INITIAL_ANSWER,

} = require("../helper-hardhat-config");



module.exports = async ({ getNamedAccounts, deployments }) => {

  const { deploy, log } = deployments;

  const { deployer } = await getNamedAccounts();

  const chainId = network.config.chainId;



  if (developmentChains.includes(network.name)) {

    log("Local network detected! Deploying mocks..");

    await deploy("MockV3Aggregator", {

      contract: "MockV3Aggregator",

      from: deployer,

      log: true,

      /*下面是传入MockV3Aggregator中的构造参数*/

      args: [DECIMALS, INITIAL_ANSWER],

    });

    log("Mocks deployed!");

    console.log("-------------------------------------------");

  }

};



module.exports.tags = ["all", "mocks"];

讲到这个mocks的部署代码就不得不讲一下helper-hardhat-config.js中的网络设置了

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
const networkConfig = {

  11155111: {

    name: "sepolia",

    ethUsdPriceFeed: "0x694AA1769357215DE4FAC081bf1f309aDC325306",

  },

};

const developmentChains = ["hardhat", "localhost"];
//传递给mock的参数用来初始化feedprice
const DECIMALS = 8;

const INITIAL_ANSWER = 200000000000;

module.exports = {

  networkConfig,

  developmentChains,

  DECIMALS,

  INITIAL_ANSWER,

};

这个配置文件可以根据运行时包含的网络选项,从而获取相应的FeedPrice,后面module.exports中的变量可以在导入了这个包中的其他文件中使用

合约代码及脚本的编写

合约

Fundme

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//从用户获取资金

//撤出资金

//设置最小支付金额美元

/// @title Storage相关笔记

/// @author

/// @notice immutable和constant还有function中的变量均不会使用Storage中的空间,

///正常命名的变量都会使用storage中的地址,数组只会将数组长度存储在storage中,

///然后通过哈希函数操作得到地址存储位置

///此外constant变量会直接存储在字节码中

//SPDX-License-Identifier:MIT

pragma solidity ^0.8.8;



import "./PriceConvert.sol";

import "hardhat/console.sol";

error Fundme__NotOwner();



contract Fundme {

    //命名规则:immutable变量以i_price,storage变量以s_price;

    using PriceConvert for uint256;

    uint256 private constant MINIMUMCNY = 50 * 10 ** 18;



    address[] private s_funders;

    mapping(address => uint) private s_addresstoAmountfunded;

    address private immutable i_owner;

    AggregatorV3Interface private s_priceFeed;



    constructor(address priceFeedAddress) {

        // console.log("now we constructing");

        i_owner = msg.sender;

        s_priceFeed = AggregatorV3Interface(priceFeedAddress);

    }



    function fund() public payable {

        require(

            msg.value.GetConversionRate(s_priceFeed) >= MINIMUMCNY,

            "You need to speed more ETH"

        );

        s_funders.push(msg.sender);

        s_addresstoAmountfunded[msg.sender] = msg.value;

    }



    function Withdraw() public onlyOwner {

        for (

            uint256 funderIndex = 0;

            funderIndex < s_funders.length;

            funderIndex++

        ) {

            address funder = s_funders[funderIndex];

            s_addresstoAmountfunded[funder] = 0;

        }



        s_funders = new address[](0); //清空数组



        //call

        (bool callSuccess, ) = payable(msg.sender).call{

            value: address(this).balance

        }("");

        require(callSuccess, "call failed");

    }



    function cheaperWithdraw() public onlyOwner {

        address[] memory funders = s_funders;

        for (

            uint256 funderIndex = 0;

            funderIndex < funders.length;

            funderIndex++

        ) {

            address funder = funders[funderIndex];

            s_addresstoAmountfunded[funder] = 0;

        }



        s_funders = new address[](0); //清空数组



        //call

        (bool callSuccess, ) = payable(msg.sender).call{

            value: address(this).balance

        }("");

        require(callSuccess, "call failed");

    }



    //在其他函数前面加上这个修饰器的名字在函数运行前会先进行这里面的判断

    //"_;"表是其他代码放在修饰器最前面表示修饰器最后判断,放在最后面表示修饰器最现判断

    modifier onlyOwner() {

        // require(msg.sender==owner,Notowner());

        if (msg.sender != i_owner) {

            revert Fundme__NotOwner();

        }

        _;

    }



    function getOwner() public view returns (address) {

        return i_owner;

    }



    function getAddressToAmountFunded(

        address fundingAddress

    ) public view returns (uint256) {

        return s_addresstoAmountfunded[fundingAddress];

    }



    function getVersion() public view returns (uint256) {

        return s_priceFeed.version();

    }



    function getFunder(uint256 index) public view returns (address) {

        return s_funders[index];

    }



    function getPriceFeed() public view returns (AggregatorV3Interface) {

        return s_priceFeed;

    }

}

PriceFeed

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
//SPXD-License-Identifier:MIT

pragma solidity ^0.8.8;



import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";



library PriceConvert {

    function GetPrice(

        AggregatorV3Interface priceFeed

    ) internal view returns (uint256) {

        (, int price, , , ) = priceFeed.latestRoundData();

        return uint256(price * 1e10);

    }



    // function Getversion() internal view returns(uint256){

    // AggregatorV3Interface priceFeed=AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);

    // return priceFeed.version();

    //  }

    function GetConversionRate(

        uint256 ethAmount,

        AggregatorV3Interface priceFeed

    ) internal view returns (uint256) {

        uint256 ethprice = GetPrice(priceFeed);

        uint256 ethAmountInCn = (ethprice * ethAmount) / 1e18;

        return ethAmountInCn;

    }

}

脚本

mocks部署脚本

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
const { network } = require("hardhat");

const {

  developmentChains,

  DECIMALS,

  INITIAL_ANSWER,

} = require("../helper-hardhat-config");



module.exports = async ({ getNamedAccounts, deployments }) => {

  const { deploy, log } = deployments;

  const { deployer } = await getNamedAccounts();

  const chainId = network.config.chainId;



  if (developmentChains.includes(network.name)) {

    log("Local network detected! Deploying mocks..");

    await deploy("MockV3Aggregator", {

      contract: "MockV3Aggregator",

      from: deployer,

      log: true,

      /*下面是传入MockV3Aggregator中的构造参数*/

      args: [DECIMALS, INITIAL_ANSWER],

    });

    log("Mocks deployed!");

    console.log("-------------------------------------------");

  }

};



module.exports.tags = ["all", "mocks"];

Fundme部署脚本

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// async function deployFunc(hre){

// hre.getNamedAccounts()

// hre.deployments



// }

// module.exports.default=deployFunc

//上面的module.exports和下面的写法作用相同

// module.exports=async(hre)=>{

//     const{getNamedAccount,deployments}=hre;

// }



//const helperConfig=require("../helper-hardhat-config")

//const networkConfig=helperConfig.networkConfig

const {

  networkConfig,

  developmentChains,

} = require("../helper-hardhat-config");

const { network } = require("hardhat");

const { verify } = require("../utils/verify");



module.exports = async ({ getNamedAccounts, deployments }) => {

  const { deploy, log } = deployments;

  const { deployer } = await getNamedAccounts();

  const chainId = network.config.chainId;



  let ethUsdPriceFeedAddress;

  if (developmentChains.includes(network.name)) {

    const ethUsdAggregator = await deployments.get("MockV3Aggregator");

    ethUsdPriceFeedAddress = ethUsdAggregator.address;

  } else {

    ethUsdPriceFeedAddress = networkConfig[chainId]["ethUsdPriceFeed"];

  }

  const args = [ethUsdPriceFeedAddress];



  const fundMe = await deploy("Fundme", {

    from: deployer,

    args: args,

    log: true,

    waitConfirmations: network.config.blockConfirmations || 1,

  });

  console.log("fundme deployed");

  if (

    !developmentChains.includes(network.name) &&

    process.env.ETHERSCAN_API_KEY

  ) {

    //verify

    await verify(fundMe.address, args);

  }

  log("----------------------------------------------------");



  // const ethUsdPriceFeedAddress = networkConfig[chainId]["ethUsdPricceFeed"];

};

module.exports.tags = ["all", "fundme"];

在不部署之后需要对合约进行验证,即verify,但是在verify之前需要等待6各区块我们的部署的这个交易才能确定上链所以这里调用了hardhat.config中的blockConfirmations。

verify

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
const { run } = require("hardhat");

async function verify(contractAddress, args) {

  console.log("Verifying contract...");

  try {

    await run("verify:verify", {

      address: contractAddress,

      constructorArguments: args,

    });

  } catch (e) {

    if (e.message.toLowerCase().includes("already verified")) {

      console.log("Already Verified!");

    } else {

      console.log(e);

    }

  }

}



module.exports = { verify };

测试

单元测试

这个测试是在本地运行的,花了很多时间用来修改因为版本问题出现的报错,注意事项写在代码的注释里了,不再赘述

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
const { deployments, ethers, getNamedAccounts } = require("hardhat");

const { assert, expect } = require("chai");

const {

  experimentalAddHardhatNetworkMessageTraceHook,

} = require("hardhat/config");

const { developmentChains } = require("../../helper-hardhat-config.js");



//describe这里的async要去掉要不然无法运行

!developmentChains.includes(network.name)

  ? describe.skip

  : describe("fundme", function () {

      let fundMe;

      let deployer;

      let mockV3Aggregator;

      let sendValue = ethers.parseEther("1");

      beforeEach(async function () {

        // console.log(sendValue);

        //两种获取

        //const accounts=await ethers.getSigners()

        //const accountZero =accounts[0]

        deployer = (await getNamedAccounts()).deployer;

        await deployments.fixture(["all"]);

        fundMe = await ethers.getContract("Fundme", deployer);

        mockV3Aggregator = await ethers.getContract(

          "MockV3Aggregator",

          deployer

        );

      });

      describe("constractor", function () {

        it("sets the aggregator addresses correctly", async function () {

          const response = await fundMe.getPriceFeed();

          //这里因为版本问题需要把address改成target或者getAddress()

          assert.equal(response, mockV3Aggregator.target);

        });

      });

      describe("fund", function () {

        it("Fails if you don't send enough ETH", async function () {

          await expect(fundMe.fund()).to.be.revertedWith(

            "You need to speed more ETH"

          );

        });

        it("update the amount funded data structure", async function () {

          await fundMe.fund({ value: sendValue });

          const response = await fundMe.getAddressToAmountFunded(deployer);

          assert.equal(response.toString(), sendValue.toString());

        });

        it("Adds s_funder to array of getFunder", async function () {

          await fundMe.fund({ value: sendValue });

          const s_funder = await fundMe.getFunder(0);

          assert.equal(s_funder, deployer);

        });

      });

      describe("withdraw", function () {

        beforeEach(async function () {

          await fundMe.fund({ value: sendValue });

        });



        it("Withdraw ETH from a single founder", async function () {

          const startingFundMeBalance = await ethers.provider.getBalance(

            fundMe.getAddress()

          );

          //console.log(startingFundMeBalance);

          const startingDeployBalance = await ethers.provider.getBalance(

            deployer

          );

          //console.log(startingDeployBalance);

          const transactionResponse = await fundMe.Withdraw();

          //这里原本是fundMe.provider.getBalance但是v6变成了现在这样

          const transactionReceipt = await transactionResponse.wait();

          //通过调试得知这个不是effectivegasPrice而是gasPrice

          const { gasUsed, gasPrice } = transactionReceipt;

          const gasCost = gasUsed * gasPrice;

          //console.log(gasCost);

          const endingFundMeBalance = await ethers.provider.getBalance(

            fundMe.getAddress()

          );

          //console.log(endingFundMeBalance);

          const endingDeployerBalance = await ethers.provider.getBalance(

            deployer

          );

          assert.equal(endingFundMeBalance, 0);

          assert.equal(

            (startingFundMeBalance + startingDeployBalance).toString(),

            (endingDeployerBalance + gasCost).toString()

          );

        });

        it("allows us to withdraw with multiple getFunder", async function () {

          const accounts = await ethers.getSigners();

          for (let i = 1; i < 6; i++) {

            const fundMeConnectedContract = await fundMe.connect(accounts[i]);

            await fundMeConnectedContract.fund({ value: sendValue });

          }

          const startingFundMeBalance = await ethers.provider.getBalance(

            fundMe.target

          );

          const startingDeployerBalance = await ethers.provider.getBalance(

            deployer

          );

          const transactionResponse = await fundMe.Withdraw();

          const transactionReceipt = await transactionResponse.wait();

          const { gasUsed, gasPrice } = transactionReceipt;

          const gasCost = gasUsed * gasPrice;

          const endingFundMeBalance = await ethers.provider.getBalance(

            fundMe.getAddress()

          );

          const endingDeployerBalance = await ethers.provider.getBalance(

            deployer

          );

          assert.equal(endingFundMeBalance, 0);

          assert.equal(

            (startingFundMeBalance + startingDeployerBalance).toString(),

            (endingDeployerBalance + gasCost).toString()

          );

          //这里的getFunder改成了gets_funder,但是不改好像没问题

          await expect(fundMe.getFunder(0)).to.be.reverted;

          for (i = 1; i < 6; i++) {

            assert.equal(

              await fundMe.getAddressToAmountFunded(accounts[i].address),

              0

            );

          }

        });

        it("Only allows the owner to withdraw", async function () {

          const accounts = await ethers.getSigners();

          const attacker = accounts[1];

          const attackerConnectedContract = await fundMe.connect(attacker);

          //这里需要修改revertedWith("FundMe__NotOwner")编程下面的样子,可能是因为这是用户自定义错误的问题吧

          await expect(

            attackerConnectedContract.Withdraw()

          ).to.be.revertedWithCustomError(fundMe, "Fundme__NotOwner");

        });

      });

      describe("cheaperwithdraw", function () {

        beforeEach(async function () {

          await fundMe.fund({ value: sendValue });

        });



        it("cheaperWithdraw ETH from a single founder", async function () {

          const startingFundMeBalance = await ethers.provider.getBalance(

            fundMe.getAddress()

          );

          //console.log(startingFundMeBalance);

          const startingDeployBalance = await ethers.provider.getBalance(

            deployer

          );

          //console.log(startingDeployBalance);

          const transactionResponse = await fundMe.cheaperWithdraw();

          //这里原本是fundMe.provider.getBalance但是v6变成了现在这样

          const transactionReceipt = await transactionResponse.wait();

          //通过调试得知这个不是effectivegasPrice而是gasPrice

          const { gasUsed, gasPrice } = transactionReceipt;

          const gasCost = gasUsed * gasPrice;

          //console.log(gasCost);

          const endingFundMeBalance = await ethers.provider.getBalance(

            fundMe.getAddress()

          );

          //console.log(endingFundMeBalance);

          const endingDeployerBalance = await ethers.provider.getBalance(

            deployer

          );

          assert.equal(endingFundMeBalance, 0);

          assert.equal(

            (startingFundMeBalance + startingDeployBalance).toString(),

            (endingDeployerBalance + gasCost).toString()

          );

        });

        it("allows us to cheaperwithdraw with multiple getFunder", async function () {

          const accounts = await ethers.getSigners();

          for (let i = 1; i < 6; i++) {

            const fundMeConnectedContract = await fundMe.connect(accounts[i]);

            await fundMeConnectedContract.fund({ value: sendValue });

          }

          const startingFundMeBalance = await ethers.provider.getBalance(

            fundMe.target

          );

          const startingDeployerBalance = await ethers.provider.getBalance(

            deployer

          );

          const transactionResponse = await fundMe.cheaperWithdraw();

          const transactionReceipt = await transactionResponse.wait();

          const { gasUsed, gasPrice } = transactionReceipt;

          const gasCost = gasUsed * gasPrice;

          const endingFundMeBalance = await ethers.provider.getBalance(

            fundMe.getAddress()

          );

          const endingDeployerBalance = await ethers.provider.getBalance(

            deployer

          );

          assert.equal(endingFundMeBalance, 0);

          assert.equal(

            (startingFundMeBalance + startingDeployerBalance).toString(),

            (endingDeployerBalance + gasCost).toString()

          );

          //这里的getFunder改成了gets_funder,但是不改好像没问题

          await expect(fundMe.getFunder(0)).to.be.reverted;

          for (i = 1; i < 6; i++) {

            assert.equal(

              await fundMe.getAddressToAmountFunded(accounts[i].address),

              0

            );

          }

        });

        it("Only allows the owner to cheaperwithdraw", async function () {

          const accounts = await ethers.getSigners();

          const attacker = accounts[1];

          const attackerConnectedContract = await fundMe.connect(attacker);

          //这里需要修改revertedWith("FundMe__NotOwner")编程下面的样子,可能是因为这是用户自定义错误的问题吧

          await expect(

            attackerConnectedContract.Withdraw()

          ).to.be.revertedWithCustomError(fundMe, "Fundme__NotOwner");

        });

      });

    });

集成测试

因为是在线上环境测试,这里比较容易出现的问题就是钱包里的钱不够,你需要把那个parseETH里面的数字改成0.1个eth

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
const { getNamedAccounts, ethers, network } = require("hardhat");

const { developmentChains } = require("../../helper-hardhat-config.js");

const {

  isCallTrace,

} = require("hardhat/internal/hardhat-network/stack-traces/message-trace");

const { assert } = require("chai");



developmentChains.includes(network.name)

  ? describe.skip

  : describe("Fundme", function () {

      let fundMe;

      let deployer;

      //这里原本时ethers.utils.parseEther

      const sendValue = ethers.parseEther("0.05");

      beforeEach(async function () {

        deployer = (await getNamedAccounts()).deployer;

        fundMe = await ethers.getContract("Fundme", deployer);

      });

      it("allows people to fund and withdraw", async function () {

        await fundMe.fund({ value: sendValue });

        await fundMe.cheaperWithdraw();

        const endingBalance = await ethers.provider.getBalance(fundMe.target);

        assert.equal(endingBalance.toString(), "0");

      });

    });

其他

快速脚本

这里我在跟课程的时候写的,但是并不知道有什么用

fundme

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
const { getNamedAccount, ethers, getNamedAccounts } = require("hardhat");



async function main() {

  const { deployer } = await getNamedAccounts();

  const fundMe = await ethers.getContract("Fundme", deployer);

  console.log("Funding Contract...");

  const transactionResponse = await fundMe.fund({

    value: ethers.parseEther("0.1"),

  });

  await transactionResponse.wait(1);

  console.log("funded");

}



main()

  .then(() => process.exit(0))

  .catch((error) => {

    console.error(error);

    process.exit(1);

  });

withdraw

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
const { getNamedAccounts, ethers } = require("hardhat");



async function main() {

  const { deployer } = await getNamedAccounts();

  const fundMe = await ethers.getContract("Fundme", deployer);

  console.log("Funding...");

  const transactionResponse = await fundMe.Withdraw();

  await transactionResponse.wait(1);

  console.log("Got it back!");

}



main()

  .then(() => process.exit(0))

  .catch((error) => {

    console.error(error);

    process.exit(1);

  });

vscode的js调试功能

在vscode的左边选择调试然后打开一个js调试终端
image.png
然后在调试终端中运行脚本即可

如果需要查看特定的数据可以直接打开调试控制台输入对应的变量名即可
image.png

上传到github

先要在github创建仓库
Pasted image 20230803215437.png

在github上创建好仓库后
在终端输入
git init -b main
初始化项目文件夹、
Pasted image 20230803215529.png

其中的绿色的是需要上传的,灰色的是在.gitignore中的
使用git status可以查看git的暂存状态

Pasted image 20230803215839.png

使用
git add .
即可进行暂存
暂存之后的文件就是使用git status展现的如下形式

Pasted image 20230803220016.png

接下来使用
git commit -m 'first commit'
这次再使用gitstatus就没有东西了

Pasted image 20230803220326.png

然后使用
git remote add origin https://github.com/guhuihey/web3test.git
连接仓库并命名为origin
使用
git remote -v
可以查看仓库情况

Pasted image 20230803220824.png
最后通过
git push origin main
上传到github既可,当然因为这一步会有一个身份验证,如果无法验证可以创建一个github token以达成目的

依赖包的安装

1
yarn add --dev @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers ethers @nomiclabs/hardhat-etherscan @nomiclabs/hardhat-waffle chai ethereum-waffle hardhat hardhat-contract-sizer hardhat-deploy hardhat-gas-reporter prettier prettier-plugin-solidity solhint solidity-coverage dotenv @chainlink/contracts

Hardhat开发
http://example.com/2023/08/04/Hardhat开发/
Author
ghh
Posted on
August 4, 2023
Licensed under