區(qū)塊鏈全棧以太坊(六)Hardhat

一誉结、簡介

智能合約的開發(fā)鹅士、部署工具。

(一) 優(yōu)勢(shì):

  • 自動(dòng)提供一個(gè)虛擬區(qū)塊鏈以及虛擬私鑰惩坑。
  • 一系列開發(fā)工具掉盅,如單測也拜,自定義任務(wù)等完善的本地環(huán)境。
  • 非常適合運(yùn)行測試趾痘。

(二) Hardhat官方教程

[tutorial

  • 教程包含項(xiàng)目新建慢哈,合約編寫,單測扼脐,編譯部署等岸军。
  • 教程中的 npx 可以替換成yarn
  • 教程中的編譯部署和demo hardhat-simple-storage-fcc的部署方法不同

二、hardhat-simple-storage-fcc學(xué)習(xí)

demo源碼

(一) hardhat-ethers

hardhat-ethers 和 ethers 的區(qū)別瓦侮,前者知道 contracts文件架的存在艰赞、也知道合約已經(jīng)被編譯好了。

所以要這么引入:

// imports
const { ethers, run, network } = require("hardhat")

(二) 編譯合約

yarn hardhat compile
  • 與我們用 ethers js 和 solc js所做的事情非常相似肚吏。

  • 生成cache文件夾方妖,加速訪問solidity文件。

  • 生成 artifacts文件夾罚攀,包含所有我們編譯的代碼信息党觅。

yarn hardhat clearn
  • 刪除artifacts。
  • 清除緩存斋泄。

(三)部署合約

yarn hardhat run scripts/deploy.js
1.部署時(shí) 默認(rèn) hardhat 本地虛擬網(wǎng)絡(luò)杯瞻。
2.執(zhí)行該腳本時(shí),hardhat都會(huì)自動(dòng)為你重新編譯一次炫掐。


module.exports = {
  //如果沒有特別指定某個(gè)網(wǎng)絡(luò)魁莉,它都會(huì)自動(dòng)使用這個(gè)虛擬機(jī)的hardhat network,
  //并且這個(gè)虛擬的network,會(huì)自動(dòng)為你提供一個(gè)RPC URL 和一個(gè)私鑰
  //
  defaultNetwork: "hardhat",
}

1) 默認(rèn)網(wǎng)絡(luò)

有了這個(gè)網(wǎng)絡(luò)標(biāo)識(shí)募胃,在不同鏈旗唁,不同私鑰之間切換就會(huì)變得非常容易。

不傳痹束,默認(rèn)就是傳入hardhat

# npx 可以替換成yarn
yarn hardhat run scripts/deploy.js --network hardhat

2) sepolia網(wǎng)絡(luò)

添加一個(gè)網(wǎng)絡(luò)检疫,主要是三要數(shù): RPC URL、PRIVATE_KEY祷嘶、CHAINID
sepolia 的chainId: 11155111,
每一個(gè)EVM基礎(chǔ)網(wǎng)絡(luò)都有一個(gè)新的chainID

EVM網(wǎng)絡(luò): Solidity可以在其上面運(yùn)行屎媳。

這里使用了 alchemy 提供EVM節(jié)點(diǎn)。

const SEPOLIA_RPC_URL =
  process.env.SEPOLIA_RPC_URL ||
  "https://eth-sepolia.g.alchemy.com/v2/your-api-key"
const PRIVATE_KEY =
  process.env.PRIVATE_KEY ||
  "0x11ee3108a03081fe260ecdc106554d09d9d1209bcafd46942b10e02943effc4a"

module.exports = {
  defaultNetwork: "hardhat",
  networks: {
    hardhat: {},
    sepolia: {
      url: SEPOLIA_RPC_URL,
      accounts: [PRIVATE_KEY],
      chainId: 11155111,
    },
  },
}

.env添加配置:

必須有一個(gè)真實(shí)的測試網(wǎng)絡(luò)上的RPC_URL 以及私鑰论巍。

PRIVATE_KEY=234523425asdfasdfa
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-api-key

然后就可以運(yùn)行時(shí)指定:

yarn hardhat run scripts/deploy.js --network sepolia

(四) 部署后自動(dòng)驗(yàn)證

基于 etherscan api剿牺,編寫 的hardhat 插件,進(jìn)行自動(dòng)驗(yàn)證环壤。

效果等同于 etherscan 網(wǎng)頁上 對(duì)合約進(jìn)行 verify and publish 晒来。

1). hardhat-verify

插件安裝后,使用文檔

npm install --save-dev @nomicfoundation/hardhat-verify
yarn add --dev @nomicfoundation/hardhat-verify

2).etherscan apiKey

注冊(cè) etherscan--> Create API Key -->填入 .env文件郑现。

3.)命令行執(zhí)行測試(可選)

#npx 等于 yarn
npx hardhat verify --network mainnet DEPLOYED_CONTRACT_ADDRESS "Constructor argument 1"

4).程序化驗(yàn)證

對(duì)scripts/deploy.js 進(jìn)行適當(dāng)改造

a. 創(chuàng)建驗(yàn)證函數(shù)

const verify = async (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)
        }
    }
}

run允許我們運(yùn)行所有hardhat任務(wù)

b. 測試網(wǎng)絡(luò)時(shí)執(zhí)行驗(yàn)證

同 etherscan上 手工進(jìn)行 verify and publish 環(huán)節(jié)湃崩。
驗(yàn)證與發(fā)布

 // 如果在本地 hardhat網(wǎng)絡(luò)部署時(shí)荧降,去執(zhí)行etherscan是沒有意義的。
    // what happens when we deploy to our hardhat network?
    if (network.config.chainId === 11155111 && process.env.ETHERSCAN_API_KEY) {
        console.log("Waiting for block confirmations...")

        // Not functionable in version 6^ ethers ----->

        await simpleStorage.deploymentTransaction().wait(6)
        await verify(simpleStorage.target, [])
  
        //______________________________________________
    }

5). 與合約交互

const currentValue = await simpleStorage.retrieve()
console.log(`Current Value is: ${currentValue}`)

// Update the current value
const transactionResponse = await simpleStorage.store(7)
await transactionResponse.wait(1)
const updatedValue = await simpleStorage.retrieve()
console.log(`Updated Value is: ${updatedValue}`)

1.部署 在 hardhat:


yarn hardhat run scripts/deploy.js

執(zhí)行結(jié)果正常攒读,且沒有驗(yàn)證相關(guān)的信息朵诫。

2.部署 在 sepolia :

yarn hardhat run scripts/deploy.js --network sepolia

(五) 自定義hardhat任務(wù)

1)查看任務(wù)

$ yarn hardhat
yarn run v1.22.22
$ D:\code_solidity\hh-fcc\hardhat-simple-storage-fcc\node_modules\.bin\hardhat
Hardhat version 2.8.3

Usage: hardhat [GLOBAL OPTIONS] <TASK> [TASK OPTIONS]

GLOBAL OPTIONS:

  --config              A Hardhat config file.
  --emoji               Use emoji in messages.
  --help                Shows this message, or a task's help if its name is provided 
  --max-memory          The maximum amount of memory that Hardhat can use.
  --network             The network to connect to.
  --show-stack-traces   Show stack traces.
  --tsconfig            A TypeScript config file.
  --verbose             Enables Hardhat verbose logging
  --version             Shows hardhat's version.


AVAILABLE TASKS:

  block-number          Prints the current block number
  check                 Check whatever you need
  clean                 Clears the cache and deletes all artifacts
  compile               Compiles the entire project, building all artifacts
  console               Opens a hardhat console
  coverage              Generates a code coverage report for tests
  flatten               Flattens and prints contracts and their dependencies
  gas-reporter:merge
  help                  Prints this message
  node                  Starts a JSON-RPC server on top of Hardhat Network
  run                   Runs a user-defined script after compiling the project
  test                  Runs mocha tests
  verify                Verifies a contract on Etherscan or Sourcify

To get help for a specific task run: npx hardhat help [task]

Done in 3.19s.

2)block-number任務(wù)實(shí)現(xiàn)

1.任務(wù)源碼

tasks/block-number.js

const { task } = require("hardhat/config")

task("block-number", "Prints the current block number").setAction(
  // const blockTask = async function() => {}
  // async function blockTask() {}
  async (taskArgs, hre) => {
    const blockNumber = await hre.ethers.provider.getBlockNumber()
    console.log(`Current block number: ${blockNumber}`)
  }
)

module.exports = {}

2.導(dǎo)入:

hardhat.config.js

//導(dǎo)入后才能在yarn hardhat任務(wù)列表中顯示
require("./tasks/block-number")

3.輸出:

hardhat每次運(yùn)行都會(huì)重置,所以 blocknumber為0薄扁,測試網(wǎng)會(huì)是很大的數(shù)字剪返。

$ yarn hardhat block-number
yarn run v1.22.22
$ D:\code_solidity\hh-fcc\hardhat-simple-storage-fcc\node_modules\.bin\hardhat block-number
Current block number: 0
Done in 3.99s.

三、hardhat本地節(jié)點(diǎn)

1)啟動(dòng)本地網(wǎng)絡(luò)節(jié)點(diǎn)

與ganache完全相同邓梅,只不過運(yùn)行于我們的終端1里:

yarn hardhat node

Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

2)部署合約到本地節(jié)點(diǎn)

這個(gè)節(jié)點(diǎn) 并沒有運(yùn)行于 hardhat network脱盲,可以新建個(gè)終端2和它交互

1.添加本地網(wǎng)絡(luò)

把localhost的url和chainid (和hardhat一樣) 填上。
不需要象 sepolia 一樣傳入 accounts日缨,因?yàn)楸镜鼐W(wǎng)絡(luò)都是自己生成accounts钱反,類似ganache。

module.exports = {
  defaultNetwork: "hardhat",
  networks: {
    hardhat: {},
    sepolia: {
      url: SEPOLIA_RPC_URL,
      accounts: [PRIVATE_KEY],
      chainId: 11155111,
    },
    localhost: {
      url: "http://localhost:8545",
      chainId: 31337,
    },
  },
  ...
  }
yarn hardhat run scripts/deploy.js --network localhost

2.終端1

可以看到如下輸出:

從from 地址看出 使用了 account0匣距,進(jìn)行合約的部署
Account #0: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (10000 ETH)

web3_clientVersion
eth_chainId (2)
eth_accounts
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
  Contract deployment: SimpleStorage
  Contract address:    0x5fbdb2315678afecb367f032d93f642f64180aa3
  Transaction:         0x25cb0a6fcef2fda9e8e5041d3fa58d3ee6766a32f8ab49ce328b4ea853fe069e
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  Value:               0 ETH
  Gas used:            463682 of 463682
  Block #1:            0x51387bb4997512bf6bfc1a4a0eb6f459cd93ca9155562e25cf7fe882c03752a0

eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
eth_chainId
eth_call
  Contract call:       SimpleStorage#retrieve
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0x5fbdb2315678afecb367f032d93f642f64180aa3

eth_chainId
eth_estimateGas
eth_feeHistory
eth_sendTransaction
  Contract call:       SimpleStorage#store
  Transaction:         0x7705d6421a42c95a0e34825319c8d7d6d632512a564a27f94f91c343a652357f
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0x5fbdb2315678afecb367f032d93f642f64180aa3
  Value:               0 ETH
  Gas used:            43724 of 43724
  Block #2:            0x495b4190b154cc11387c09bf93e22e339eb19a441d1bcd3384263a2c04139fdd

eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
eth_chainId
eth_call
  Contract call:       SimpleStorage#retrieve
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0x5fbdb2315678afecb367f032d93f642f64180aa3

3.終端2

可以看到如下輸出:

Deploying contract...
Current Value is: 0
Updated Value is: 7
Done in 17.64s.

四面哥、hardhat控制臺(tái)

1)連入本地節(jié)點(diǎn)

#有時(shí)會(huì)有問題,不建議
#yarn hardhat console --network localhost
#改成如下
npx hardhat console --network localhost

之后就可以在控制臺(tái)里執(zhí)行 deploy.js 里面的所有操作毅待,
不需要require(”hardhat“)這些導(dǎo)入操作尚卫,因?yàn)閔ardhat已經(jīng)自動(dòng)導(dǎo)入控制臺(tái)

臨時(shí)節(jié)點(diǎn)

npx hardhat console --network hardhat

測試節(jié)點(diǎn)

npx hardhat console --network sepolia

2)執(zhí)行操作

把之前deploy.js 里的部署操作尸红,依次復(fù)制進(jìn)控制臺(tái)如下:

$  npx hardhat console --network localhost
Welcome to Node.js v16.20.0.
Type ".help" for more information.
> const SimpleStorageFactory =  await ethers.getContractFactory("SimpleStorage")
undefined
> const simpleStorage = await SimpleStorageFactory.deploy()
undefined
> const currentValue = await simpleStorage.retrieve()
undefined
> currentValue
BigNumber { value: "0" }
>  const transactionResponse = await simpleStorage.store(7)
undefined
> currentValue = await simpleStorage.retrieve()
BigNumber { value: "7" }

五吱涉、單元測試

hardhat測試使用的是Mocha框架,一個(gè)基于js 的框架驶乾,用于運(yùn)行我們的測試。
你甚至可以直接用Solidity來編寫測試,如果你想的話循签。

主流的還是用js編寫測試代碼

1)源碼范例

test/test-deploy.js

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

// describe("SimpleStorage", () => {})
describe("SimpleStorage", function () {
  // let simpleStorageFactory
  // let simpleStorage
  let simpleStorageFactory, simpleStorage
  beforeEach(async function () {
    simpleStorageFactory = await ethers.getContractFactory("SimpleStorage")
    simpleStorage = await simpleStorageFactory.deploy()
  })

  it("Should start with a favorite number of 0", async function () {
    const currentValue = await simpleStorage.retrieve()
    const expectedValue = "0"
    // assert
    // expect
    assert.equal(currentValue.toString(), expectedValue)
    // expect(currentValue.toString()).to.equal(expectedValue)
  })
  it("Should update when we call store", async function () {
    const expectedValue = "7"
    const transactionResponse = await simpleStorage.store(expectedValue)
    await transactionResponse.wait(1)

    const currentValue = await simpleStorage.retrieve()
    assert.equal(currentValue.toString(), expectedValue)
  })

  // Extra - this is not in the video
  it("Should work correctly with the people struct and array", async function () {
    const expectedPersonName = "Patrick"
    const expectedFavoriteNumber = "16"
    const transactionResponse = await simpleStorage.addPerson(
      expectedPersonName,
      expectedFavoriteNumber
    )
    await transactionResponse.wait(1)
    const { favoriteNumber, name } = await simpleStorage.people(0)
    // We could also do it like this
    // const person = await simpleStorage.people(0)
    // const favNumber = person.favoriteNumber
    // const pName = person.name

    assert.equal(name, expectedPersonName)
    assert.equal(favoriteNumber, expectedFavoriteNumber)
  })
})

2)運(yùn)行測試

yarn hardhat test
#或者 
npx hardhat test

3)輸出如下測試:

$ yarn hardhat test
yarn run v1.22.22
$ D:\code_solidity\hh-fcc\hardhat-simple-storage-fcc\node_modules\.bin\hardhat test

  SimpleStorage
    √ Should start with a favorite number of 0
    √ Should update when we call store
    √ Should work correctly with the people struct and array


  3 passing (2s)

Done in 11.06s.

4)運(yùn)行指定測試

1.only()


it.only()
describe.only()

2.npx hardhat test --grep xx

https://ethereum.stackexchange.com/questions/113296/run-a-single-hardhat-test

只適用于 hardhat 2.9 以上

#單獨(dú)執(zhí)行這個(gè)測試 it("Should update when we call store"
npx hardhat test --grep store

5)gas reporter

1.安裝擴(kuò)展

yarn add hardhat-gas-reporter --dev

2.添加配置

hardhat.config.js

為了把gas "Gwei" 轉(zhuǎn)換成 "USD", 需要去https://pro.coinmarketcap.com/申請(qǐng)這個(gè)key,配到.env.
如果key不合法,則usd 那一列 沒有數(shù)據(jù).

COINMARKETCAP_API_KEY=asdfasdfasdfasdfasdfasdfasdf

require("hardhat-gas-reporter")

module.exports = {
  gasReporter: {
      //這行可以注釋調(diào),不估算 gas
    enabled: true,
    currency: "USD",
    outputFile: "gas-report.txt",
    noColors: true,
      //這行可以注釋調(diào),不處理美元
    coinmarketcap: COINMARKETCAP_API_KEY,
      //可以估算多種EVM鏈的gas
      //token:"MATIC"
  },  
}

3.輸出報(bào)告

yarn hardhat test

在運(yùn)行完測試之后,它會(huì)自動(dòng)運(yùn)行這個(gè)"gas reporter",

并輸出報(bào)告在 gas-report.txt

6)solidity-coverage(HardHat插件)

npm install --save-dev solidity-coverage

添加到配置:

require("solidity-coverage")

執(zhí)行

yarn hardhat coverage

輸出:

--------------------|----------|----------|----------|----------|----------------|
File                |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
--------------------|----------|----------|----------|----------|----------------|
 contracts\         |      100 |      100 |      100 |      100 |                |
  SimpleStorage.sol |      100 |      100 |      100 |      100 |                |
--------------------|----------|----------|----------|----------|----------------|
All files           |      100 |      100 |      100 |      100 |                |
--------------------|----------|----------|----------|----------|----------------|

六级乐、Hardhat waffle

Hardhat waffle是一個(gè)用于waffle 測試框架的一個(gè)插件,有更多的測試,斷言功能.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市县匠,隨后出現(xiàn)的幾起案子风科,更是在濱河造成了極大的恐慌,老刑警劉巖乞旦,帶你破解...
    沈念sama閱讀 212,332評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贼穆,死亡現(xiàn)場離奇詭異,居然都是意外死亡兰粉,警方通過查閱死者的電腦和手機(jī)故痊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,508評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玖姑,“玉大人愕秫,你說我怎么就攤上這事慨菱。” “怎么了戴甩?”我有些...
    開封第一講書人閱讀 157,812評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵符喝,是天一觀的道長。 經(jīng)常有香客問我甜孤,道長协饲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,607評(píng)論 1 284
  • 正文 為了忘掉前任缴川,我火速辦了婚禮茉稠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘二跋。我一直安慰自己战惊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,728評(píng)論 6 386
  • 文/花漫 我一把揭開白布扎即。 她就那樣靜靜地躺著吞获,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谚鄙。 梳的紋絲不亂的頭發(fā)上各拷,一...
    開封第一講書人閱讀 49,919評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音闷营,去河邊找鬼烤黍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛傻盟,可吹牛的內(nèi)容都是我干的速蕊。 我是一名探鬼主播,決...
    沈念sama閱讀 39,071評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼娘赴,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼规哲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诽表,我...
    開封第一講書人閱讀 37,802評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤唉锌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后竿奏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袄简,經(jīng)...
    沈念sama閱讀 44,256評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,576評(píng)論 2 327
  • 正文 我和宋清朗相戀三年泛啸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绿语。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,712評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汞舱,靈堂內(nèi)的尸體忽然破棺而出伍纫,到底是詐尸還是另有隱情,我是刑警寧澤昂芜,帶...
    沈念sama閱讀 34,389評(píng)論 4 332
  • 正文 年R本政府宣布莹规,位于F島的核電站,受9級(jí)特大地震影響泌神,放射性物質(zhì)發(fā)生泄漏良漱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,032評(píng)論 3 316
  • 文/蒙蒙 一欢际、第九天 我趴在偏房一處隱蔽的房頂上張望母市。 院中可真熱鬧,春花似錦损趋、人聲如沸患久。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒋失。三九已至,卻和暖如春桐玻,著一層夾襖步出監(jiān)牢的瞬間篙挽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,026評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铺厨。 一個(gè)月前我還...
    沈念sama閱讀 46,473評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像煮落,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踊谋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,606評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容