一誉结、簡介
智能合約的開發(fā)鹅士、部署工具。
(一) 優(yōu)勢(shì):
- 自動(dòng)提供一個(gè)虛擬區(qū)塊鏈以及虛擬私鑰惩坑。
- 一系列開發(fā)工具掉盅,如單測也拜,自定義任務(wù)等完善的本地環(huán)境。
- 非常適合運(yùn)行測試趾痘。
(二) Hardhat官方教程
- 教程包含項(xiàng)目新建慢哈,合約編寫,單測扼脐,編譯部署等岸军。
- 教程中的 npx 可以替換成yarn
- 教程中的編譯部署和demo hardhat-simple-storage-fcc的部署方法不同
二、hardhat-simple-storage-fcc學(xué)習(xí)
(一) 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è)插件,有更多的測試,斷言功能.