vscode+solidity+hardhat 开发
环境安装
创建项目
- 初始化仓库
> yarn init
yarn init v1.22.22
question name (hardhat-simple-storage-fcc):
question version (1.0.0):
question description:
question entry point (index.js):
question repository url:
question author: yourself
question license (MIT):
question private:
success Saved package.json
Done in 15.23s.
执行成功后生成 package.json文件:
{
"name": "hardhat-simple-storage-fcc",
"version": "1.0.0",
"main": "index.js",
"author": "yourself",
"license": "MIT"
}
- 给项目添加
hardhat
> yarn add --dev hardhat
网络不好时需要很久,安装完后 package.json 文件变化:
{
"devDependencies": {
"hardhat": "^2.22.19"
}
}
hardhat配置初始化: 添加依赖及配置项.
> yarn 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.22.19
√ What do you want to do? · Create a JavaScript project
√ Hardhat project root: · D:\workspace\src\solidity\hh-fcc\hardhat-simple-storage-fcc
√ Do you want to add a .gitignore? (Y/n) · y
√ Do you want to install this sample project's dependencies with yarn (@nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-verify chai hardhat-gas-reporter solidity-coverage @nomicfoundation/hardhat-ignition @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-chai-matchers @nomicfoundation/hardhat-ethers ethers @typechain/hardhat typechain @typechain/ethers-v6 @nomicfoundation/hardhat-ignition-ethers)? (Y/n) · y
yarn add --dev "@nomicfoundation/hardhat-network-helpers@^1.0.0" "@nomicfoundation/hardhat-verify@^2.0.0" "chai@^4.2.0" "hardhat-gas-reporter@^1.0.8" "solidity-coverage@^0.8.0" "@nomicfoundation/hardhat-ignition@^0.15.0" "@nomicfoundation/hardhat-toolbox@^5.0.0" "@nomicfoundation/hardhat-chai-matchers@^2.0.0" "@nomicfoundation/hardhat-ethers@^3.0.0" "ethers@^6.4.0" "@typechain/hardhat@^9.0.0" "typechain@^8.3.0" "@typechain/ethers-v6@^0.5.0" "@nomicfoundation/hardhat-ignition-ethers@^0.15.0"
➤ YN0000: · Yarn 4.6.0
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + @nomicfoundation/hardhat-chai-matchers@npm:2.0.8, @nomicfoundation/hardhat-ethers@npm:3.0.8, @nomicfoundation/hardhat-ignition-ethers@npm:0.15.10, and 270 more.
➤ YN0000: └ Completed in 9s 519ms
➤ YN0000: ┌ Post-resolution validation
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @nomicfoundation/ignition-core (p3e4f1), requested by @nomicfoundation/hardhat-ignition-ethers.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @types/chai (p49220), requested by @nomicfoundation/hardhat-chai-matchers and other dependencies.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @types/mocha (p7fc22), requested by @nomicfoundation/hardhat-toolbox.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @types/node (p89ace), requested by @nomicfoundation/hardhat-toolbox.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide ts-node (p9b6d1), requested by @nomicfoundation/hardhat-toolbox and other dependencies.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide typescript (p0d7e8), requested by @nomicfoundation/hardhat-toolbox and other dependencies.
➤ YN0086: │ Some peer dependencies are incorrectly met by your project; run yarn explain peer-requirements <hash> for details, where <hash> is the six-letter p-prefixed code.
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0013: │ A package was added to the project (+ 2.89 MiB).
➤ YN0000: └ Completed in 6s 173ms
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0008: │ keccak@npm:3.0.4 must be rebuilt because its dependency tree changed
➤ YN0008: │ secp256k1@npm:4.0.4 must be rebuilt because its dependency tree changed
➤ YN0000: │ [Warning] The runtime detected new information in a PnP file; reloading the API instance (D:\workspace\src\solidity\hh-fcc\hardhat-simple-storage-fcc\.pnp.cjs)
➤ YN0000: └ Completed in 0s 875ms
➤ YN0000: · Done with warnings in 16s 654ms
Project created
See the README.md file for some example tasks you can run
Give Hardhat a star on Github if you're enjoying it!
https://github.com/NomicFoundation/hardhat
DEPRECATION WARNING
Initializing a project with npx hardhat is deprecated and will be removed in the future.
Please use npx hardhat init instead.
安装后再次执行 yarn hardhat 查看扩展的命令:
> yarn hardhat
Hardhat version 2.22.19
Usage: hardhat [GLOBAL OPTIONS] [SCOPE] <TASK> [TASK OPTIONS]
GLOBAL OPTIONS:
AVAILABLE TASKS:
check Check whatever you need
...
To get help for a specific task run: npx hardhat help [SCOPE] <TASK>
- 创建合约
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
contract SimpleStorage {
uint256 favoriteNumber;
struct People {
uint256 favoriteNumber;
string name;
}
// uint256[] public anArray;
People[] public people;
mapping(string => uint256) public nameToFavoriteNumber;
function store(uint256 _favoriteNumber) public {
favoriteNumber = _favoriteNumber;
}
function retrieve() public view returns (uint256) {
return favoriteNumber;
}
function addPerson(string memory _name, uint256 _favoriteNumber) public {
people.push(People(_favoriteNumber, _name));
nameToFavoriteNumber[_name] = _favoriteNumber;
}
}
- 编译合约
> yarn hardhat compile
Compiled 1 Solidity file successfully (evm target: paris).
- 安装
prettier插件
> yarn add --dev prettier prettier-plugin-solidity
➤ YN0000: · Yarn 4.6.0
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + prettier-plugin-solidity@npm:1.4.2, prettier@npm:3.5.2
➤ YN0000: └ Completed in 0s 484ms
➤ YN0000: ┌ Post-resolution validation
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @nomicfoundation/ignition-core (p3e4f1), requested by @nomicfoundation/hardhat-ignition-ethers.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @types/chai (p49220), requested by @nomicfoundation/hardhat-chai-matchers and other dependencies.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @types/mocha (p7fc22), requested by @nomicfoundation/hardhat-toolbox.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide @types/node (p89ace), requested by @nomicfoundation/hardhat-toolbox.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide ts-node (p9b6d1), requested by @nomicfoundation/hardhat-toolbox and other dependencies.
➤ YN0002: │ hardhat-simple-storage-fcc@workspace:. doesn't provide typescript (p0d7e8), requested by @nomicfoundation/hardhat-toolbox and other dependencies.
➤ YN0086: │ Some peer dependencies are incorrectly met by your project; run yarn explain peer-requirements <hash> for details, where <hash> is the six-letter p-prefixed code.
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0013: │ 2 packages were added to the project (+ 9.44 MiB).
➤ YN0000: └ Completed in 3s 766ms
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0000: └ Completed
➤ YN0000: · Done with warnings in 4s 519ms
- 部署合约
部署合约脚本:
// imports
const { ethers } = require('hardhat');
async function main() {
const simpleStorageFactory =
await ethers.getContractFactory('SimpleStorage');
console.log('Deploying SimpleStorage...');
const simpleStorageContract = await simpleStorageFactory.deploy();
await simpleStorageContract.waitForDeployment();
console.log(`Deployed contract to: ${simpleStorageContract.target}`);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
> yarn hardhat run ./scripts/deploy.js
hardhat network
hardhat 会提供默认网络, 附带 rpcurl 和 wallet 等必要元件.
module.exports = {
// defaultNetwork: 'hardhat',
solidity: '0.8.28',
};
也可以在部署命令中明确指定运行的网络(network):
yarn hardhat run ./scripts/deploy.js --network hardhat
- 设置网络
# 添加 `dotenv`
yarn add --dev dotenv
hardhat.config.js文件修改:
require('@nomicfoundation/hardhat-toolbox');
require('dotenv').config();
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL;
const PRIVATE_KEY = '0x' + process.env.PRIVATE_KEY;
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
defaultNetwork: 'hardhat',
networks: {
sepolia: {
url: SEPOLIA_RPC_URL,
accounts: [PRIVATE_KEY],
chainId: 11155111,
},
},
solidity: '0.8.28',
};
SEPOLIA_RPC_URL 是第三方机构提供的合约部署服务,如
metamask的为https://sepolia.infura.io/v3/YOUR-API-KEY,alchemy的为https://eth-sepolia.g.alchemy.com/v2/YOUR-API-KEY.根据实际需求自行申请.
使用hardhat-deploy 部署合约
- 安装
chainlink,非必要,只有需要获得代币价格时需要(ETH/USD).
yarn add --dev @chainlink/contracts
- 安装
hardhat-deploy
yarn add --dev hardhat-deploy
在hardhat.config.js中导入:
require("hardhat-deploy")
成功后hardhat命令行会多出deploy任务.
- 安装
@nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers
yarn add --dev @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers ethers
- 部署代码
helper-hardhat-config.js 代码:
const networkConfig = {
31337: {
name: "localhost",
},
// Price Feed Address, values can be obtained at https://docs.chain.link/data-feeds/price-feeds/addresses
11155111: {
name: "sepolia",
ethUsdPriceFeed: "0x694AA1769357215DE4FAC081bf1f309aDC325306",
},
}
const developmentChains = ["hardhat", "localhost"]
module.exports = {
networkConfig,
developmentChains,
}
00-deploy-mocks.js 代码:
const { network } = require("hardhat")
const { developmentChains } = require("../helper-hardhat-config")
const DECIMALS = "8"
const INITIAL_ANSWER = "200000000000" // 2000
module.exports = async ({ deployments, getNamedAccounts }) => {
const { deploy, log } = deployments
const { deployer } = await getNamedAccounts()
const chainId = network.config.chainId
// If we are on a local development network, we need to deploy mocks!
if (developmentChains.includes(network.name)) {
log("Local network detected! Deploying mocks...")
await deploy("MockV3Aggregator", {
contract: "MockV3Aggregator",
from: deployer,
log: true,
args: [DECIMALS, INITIAL_ANSWER],
})
log("Mocks Deployed!")
log("------------------------------------------------")
log(
"You are deploying to a local network, you'll need a local network running to interact"
)
log(
"Please run `npx hardhat console` to interact with the deployed smart contracts!"
)
log("------------------------------------------------")
}
}
module.exports.tags = ["all", "mocks"] // deplog 命令行中的 tags
MockV3Aggregator合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
// contracts/src/v0.8/shared/mocks/MockV3Aggregator.sol
import "@chainlink/contracts/src/v0.6/tests/MockV3Aggregator.sol";
- 部署
> yarn hardhat deploy --tags mocks
Compiling 1 file with 0.6.6
Solidity compilation finished successfully
Local network detected! Deploying mocks...
deploying "MockV3Aggregator" (tx: 0x2f60bd4cba5dffe33cd22380f4891cfadb7f13aad763bb084e8a1c3336b892f9)...: deployed at 0x5FbDB2315678afecb367f032d93F642f64180aa3 with 569635 gas
Mocks Deployed!
------------------------------------------------
You are deploying to a local network, you'll need a local network running to interact
Please run `npx hardhat console` to interact with the deployed smart contracts!
------------------------------------------------
执行
yarn hardhat node会执行所有的部署合约脚本.
与已有合约交互
- 编写
fund.js与已有合约交互
const { ethers, getNamedAccounts } = require("hardhat")
async function main() {
const { deployer } = await getNamedAccounts()
const fundMe = await ethers.getContract("FundMe", deployer)
console.log(`Got contract FundMe at ${fundMe.address}`)
console.log("Funding contract...")
const transactionResponse = await fundMe.fund({
value: ethers.utils.parseEther("0.1"),
})
await transactionResponse.wait()
console.log("Funded!")
const fundMeBalance = await fundMe.provider.getBalance(fundMe.address)
const fundMeBalanceInEther = ethers.utils.formatEther(fundMeBalance)
console.log(`FundMe balance: ${fundMeBalanceInEther}`)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
- 启动本地节点,部署
FundMe合约
> yarn hardhat node
Nothing to compile
Local network detected! Deploying mocks...
deploying "MockV3Aggregator" (tx: 0x2f60bd4cba5dffe33cd22380f4891cfadb7f13aad763bb084e8a1c3336b892f9)...: deployed at 0x5FbDB2315678afecb367f032d93F642f64180aa3 with 569635 gas
Mocks Deployed!
------------------------------------------------
You are deploying to a local network, you'll need a local network running to interact
Please run `npx hardhat console` to interact with the deployed smart contracts!
------------------------------------------------
chainId: 31337
----------------------------------------------------
Deploying FundMe and waiting for confirmations...
deploying "FundMe" (tx: 0x6a97ceb3049b38b6e4edcd8f4d92794fbbcdb62b3ece28825ff4be7e84e16cae)...: deployed at 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 with 1096977 gas
FundMe deployed at 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
----------------------------------------------------
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
Account #1: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 (10000 ETH)
Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
Account #2: 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc (10000 ETH)
Private Key: 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
Account #3: 0x90f79bf6eb2c4f870365e785982e1f101e93b906 (10000 ETH)
Private Key: 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
Account #4: 0x15d34aaf54267db7d7c367839aaf71a00a2c6a65 (10000 ETH)
Private Key: 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
Account #5: 0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc (10000 ETH)
Private Key: 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
Account #6: 0x976ea74026e726554db657fa54763abd0c3a0aa9 (10000 ETH)
Private Key: 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
Account #7: 0x14dc79964da2c08b23698b3d3cc7ca32193d9955 (10000 ETH)
Private Key: 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
Account #8: 0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f (10000 ETH)
Private Key: 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
Account #9: 0xa0ee7a142d267c1f36714e4a8f75612f20a79720 (10000 ETH)
Private Key: 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6
Account #10: 0xbcd4042de499d14e55001ccbb24a551f3b954096 (10000 ETH)
Private Key: 0xf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897
Account #11: 0x71be63f3384f5fb98995898a86b02fb2426c5788 (10000 ETH)
Private Key: 0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82
Account #12: 0xfabb0ac9d68b0b445fb7357272ff202c5651694a (10000 ETH)
Private Key: 0xa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1
Account #13: 0x1cbd3b2770909d4e10f157cabc84c7264073c9ec (10000 ETH)
Private Key: 0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd
Account #14: 0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097 (10000 ETH)
Private Key: 0xc526ee95bf44d8fc405a158bb884d9d1238d99f0612e9f33d006bb0789009aaa
Account #15: 0xcd3b766ccdd6ae721141f452c550ca635964ce71 (10000 ETH)
Private Key: 0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61
Account #16: 0x2546bcd3c84621e976d8185a91a922ae77ecec30 (10000 ETH)
Private Key: 0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0
Account #17: 0xbda5747bfd65f08deb54cb465eb87d40e51b197e (10000 ETH)
Private Key: 0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd
Account #18: 0xdd2fd4581271e230360230f9337d5c0430bf44c0 (10000 ETH)
Private Key: 0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0
Account #19: 0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199 (10000 ETH)
Private Key: 0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e
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.
web3_clientVersion
Contract deployment: MockV3Aggregator
Contract address: 0x5fbdb2315678afecb367f032d93f642f64180aa3
Transaction: 0x2f60bd4cba5dffe33cd22380f4891cfadb7f13aad763bb084e8a1c3336b892f9
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
Value: 0 ETH
Gas used: 569635 of 569635
Block #1: 0xe01b11ac8eb1061844456c9179f1b11e10c1b650ec7769b51aad43c1379a4e10
Contract deployment: FundMe
Contract address: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Transaction: 0x6a97ceb3049b38b6e4edcd8f4d92794fbbcdb62b3ece28825ff4be7e84e16cae
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
Value: 0 ETH
Gas used: 1096977 of 1096977
Block #2: 0x51151ed3ba29f736405864c7dbc498bba0f7cf5c7f3d4b522be874ecfc30a0cd
Contract deployment: FunWithStorage
Contract address: 0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0
Transaction: 0x474ff007295b40fb495744377d4689540b1d451396087b6d78cdea7b264186d1
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
Value: 0 ETH
Gas used: 253434 of 253434
Block #3: 0x202816719c9340efd77bf8f8e28a0e10ca454c5e51f06c6f994b0e0770d7a095
eth_chainId
eth_accounts
eth_chainId
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
Contract call: FundMe#fund
Transaction: 0x851a9775d49ce122a2322fa4537d5ab27e54bf3deac2702b9c95df149d7e7bc3
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Value: 0.1 ETH
Gas used: 104466 of 104466
Block #4: 0x3d67874aa549e60cfe5e9922c02ec18b12d0cf00c137a64b42cd6e1dd4a2df3a
eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
web3_clientVersion
eth_chainId
eth_accounts
eth_chainId
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
Contract call: FundMe#withdraw
Transaction: 0x589d82f64e1cde435396146da7a25c2671729473d878c2b29690057c080f2d11
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Value: 0 ETH
Gas used: 35622 of 46753
Block #5: 0xf72bf987b6214a20a957e63f75151cc465533bde7527d70a3de916f3193d9f92
eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
web3_clientVersion
eth_chainId
eth_accounts
eth_chainId
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
Contract call: FundMe#fund
Transaction: 0x2a0941a93c42f250e9441222eae63cb39dc2e290fd240713ed9b2c93e77d1a39
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Value: 0.1 ETH
Gas used: 104466 of 104466
Block #6: 0x7f28919a532fdd95a3d21a74327f848d1973a0c356dd4707fc48a0e29f7d9da3
eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
eth_chainId
eth_getBalance
web3_clientVersion
eth_chainId
eth_accounts
eth_chainId
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
Contract call: FundMe#fund
Transaction: 0x851cfbc1b42228e020af0b8693750826f21712d46e436791109f6bfa684953bf
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Value: 0.1 ETH
Gas used: 70266 of 70266
Block #7: 0x78e2d81458674078bee923e0a1b445f1424491a74e8d02c6c0b2ebf25731c14e
eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
eth_chainId
eth_getBalance
web3_clientVersion
eth_chainId
eth_accounts
eth_chainId
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
Contract call: FundMe#fund
Transaction: 0xd77c0f8ad2bf30ec9498e51555f56a7f95c0ec826f065f70a3dccaa47fcb1924
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xe7f1725e7734ce288f8367e1bb143e90bb3f0512
Value: 0.1 ETH
Gas used: 70266 of 70266
Block #8: 0x47a687c961da2c7ac255944d997d3c03e1da53c52307de87a868acbca9690da0
eth_chainId
eth_getTransactionByHash
eth_chainId
eth_getTransactionReceipt
eth_chainId
eth_getBalance
- 交互
> yarn hardhat run ./scripts/fund.js --network localhost
Got contract FundMe at 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
Funding contract...
Funded!
FundMe balance: 0.3
验证合约
require('@nomicfoundation/hardhat-verify');
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
etherscan: {
// Your API key for Etherscan
// Obtain one at https://etherscan.io/
apiKey: ETHERSCAN_API_KEY,
},
};
调用浏览器提供的 API 进行验证: https://docs.etherscan.io/sepolia-etherscan/api-endpoints/contracts
通过 hardhat 的插件进行验证: hardhat-verify
自定义验证函数:
// imports
const { run } = require('hardhat');
async function main() {
// others
// Verify contract
if (network.config.chainId === 11155111 && process.env.ETHERSCAN_API_KEY) {
console.log('Waiting for block confirmations...');
// Not functionable in version 6^ ethers ----->
await simpleStorageContract.deploymentTransaction().wait(6);
await verify(simpleStorageContract.target, []);
//______________________________________________
}
}
async function verify(contractAddress, args) {
console.log('Verifying contract...');
try {
await run('verify:verify', {
address: contractAddress,
constructorArguments: args,
});
} catch (error) {
if (error instanceof Error) {
if (error.message.toLowerCase().includes('already verified')) {
console.log('Contract already verified');
}
console.error('Failed to verify contract:', error.message || error);
} else {
console.error('Unexpected error:', error);
}
}
}
合约部署并验证:
> yarn hardhat run ./scripts/deploy.js --network sepolia
Deploying to network: sepolia
Deploying SimpleStorage...
Deployed contract to: 0xDB141Aa5fcccFd638f221e3009ba9E91Ca97eA59
Waiting for block confirmations...
Verifying contract...
Failed to verify contract: A network request failed. This is an error from the block explorer, not Hardhat. Error: Connect Timeout Error
Current favorite number: 0
Updated favorite number: 13
创建 hardhat task
类似golang服务的命令行.
- 添加任务
const { task } = require('hardhat/config');
task(
'block-number',
'Prints the current block number',
async (_, { ethers }) => {
const blockNumber = await ethers.provider.getBlockNumber();
console.log('Current block number: ', blockNumber);
},
);
// task('block-number', 'Prints the current block number').setAction(
// // async function blockNumber() {}
// // const blockNumber = async function() => {}
// async (_, hre) => {
// const blockNumber = await hre.ethers.provider.getBlockNumber();
// console.log(`Current block number: ${blockNumber}`);
// },
// );
task('accounts', 'Prints the list of accounts', async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
task('balance', "Prints an account's balance")
.addParam('account', "The account's address")
.setAction(async (taskArgs) => {
const balance = await ethers.provider.getBalance(taskArgs.account);
console.log(ethers.formatEther(balance), 'ETH');
});
- 导入
// hardhat.config.js
require('./tasks/balance');
require('./tasks/accounts');
require('./tasks/block-number');
- 执行
> yarn hardhat balance --account 0x28c03C558980988F858BD77B0Fd9F94826E99A15 --network sepolia
0.03849461495766039 ETH
> yarn hardhat block-number --network sepolia
Current block number: 7794057
> yarn hardhat accounts --network sepolia
0x28c03C558980988F858BD77B0Fd9F94826E99A15
hardhat 本地节点
使用 default hardhat 的网络配置,在本地启动一个节点(是一个独立的网络).
> yarn hardhat node
配置:
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
networks: {
localhost: {
url: 'http://127.0.0.1:8545',
// accounts: from hardhat.config
chainId: 31337,
},
},
};
hardhat 控制台
类似 mysql 服务的控制台,可按命令行与合约交互.
> yarn hardhat console
Welcome to Node.js v18.17.1.
Type ".help" for more information.
> const simpleStorageFactory = await ethers.getContractFactory('SimpleStorage');
undefined
> const simpleStorageContract = await simpleStorageFactory.deploy();
undefined
> await simpleStorageContract.waitForDeployment();
BaseContract {
target: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
interface: Interface {
fragments: [
[FunctionFragment],
[FunctionFragment],
[FunctionFragment],
[FunctionFragment],
[FunctionFragment]
],
deploy: ConstructorFragment {
type: 'constructor',
inputs: [],
payable: false,
gas: null
},
fallback: null,
receive: false
},
runner: HardhatEthersSigner {
_gasLimit: 30000000,
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
provider: HardhatEthersProvider {
_hardhatProvider: [LazyInitializationProviderAdapter],
_networkName: 'hardhat',
_blockListeners: [],
_transactionHashListeners: Map(0) {},
_eventListeners: []
}
},
filters: {},
fallback: null,
[Symbol(_ethersInternal_contract)]: {}
}
> await simpleStorageContract.retrieve();
0n
> await simpleStorageContract.store(100);
ContractTransactionResponse {
provider: HardhatEthersProvider {
_hardhatProvider: LazyInitializationProviderAdapter {
_providerFactory: [AsyncFunction (anonymous)],
_emitter: [EventEmitter],
_initializingPromise: [Promise],
provider: [BackwardsCompatibilityProviderAdapter]
},
_networkName: 'hardhat',
_blockListeners: [],
_transactionHashListeners: Map(0) {},
_eventListeners: []
},
blockNumber: 2,
blockHash: '0xc447e66f8f77a519c4cafa0b5ade88c79f59a74003cf2823d3c85e06ed18306c',
index: undefined,
hash: '0xf9830c6cb21deb814fa991c78fc164b05881b417aa662824cec262215e04e494',
type: 2,
to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
nonce: 1,
gasLimit: 30000000n,
gasPrice: 1769727927n,
maxPriorityFeePerGas: 1000000000n,
maxFeePerGas: 2539455854n,
maxFeePerBlobGas: null,
data: '0x6057361d0000000000000000000000000000000000000000000000000000000000000064',
value: 0n,
chainId: 31337n,
signature: Signature { r: "0x35ec79d17c634bc3f2d12a86825acbfba55438a9ab96d40606acc7c34227aff9", s: "0x7d2f300f8d874bd5f65d5195c9e9851c3afbc4bb0e985acc2d54a489d04d00ea", yParity: 1, networkV: null },
accessList: [],
blobVersionedHashes: null
}
> await simpleStorageContract.retrieve();
100n
测试合约
- 编写测试
const { assert, expect } = require('chai');
const { ethers } = require('hardhat');
// Vanilla Mocha test. Increased compatibility with tools that integrate Mocha.
// describe("SimpleStorage", () => {})
describe('SimpleStorage', function () {
let simpleStorageFactory;
let simpleStorageContract;
beforeEach(async function () {
// Get the contract factory
simpleStorageFactory = await ethers.getContractFactory('SimpleStorage');
simpleStorageContract = await simpleStorageFactory.deploy();
await simpleStorageContract.waitForDeployment();
});
it('Should start with a favorite number of 0', async function () {
const currentFavoriteNumber = await simpleStorageContract.retrieve();
// assert.equal(currentFavoriteNumber, 0);
// expect(currentFavoriteNumber == 10).to.be.false;
// expect(currentFavoriteNumber == 0).to.be.true;
expect(currentFavoriteNumber).to.equal(0);
});
it('Should update when we call store', async function () {
const newFavoriteNumber = 13;
let transactionResponse =
await simpleStorageContract.store(newFavoriteNumber);
await transactionResponse.wait(1);
const currentFavoriteNumber = await simpleStorageContract.retrieve();
assert.equal(currentFavoriteNumber, newFavoriteNumber);
});
it('Should work correctly with the people struct and array', async function () {
// Add a person
let expectPersionName = 'Alice';
let expectFavoriteNumber = '23';
let transactionResponse = await simpleStorageContract.addPerson(
expectPersionName,
expectFavoriteNumber,
);
await transactionResponse.wait(1);
// Check the person
const person = await simpleStorageContract.people(0);
assert.equal(person.name, expectPersionName);
assert.equal(person.favoriteNumber, expectFavoriteNumber);
});
});
it.only可限制只执行此标志的测试
- 执行测试
> yarn 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 (522ms)
# 匹配关键字测试
> yarn hardhat test --grep store
SimpleStorage
✔ Should update when we call store
1 passing (493ms)
-
unit test: locally- local hardhat
- forked hardhat
-
integration test: can be done on a testnet(LAST STOP!)
Gas Reporter
安装 hardhat-gas-reporter:
> yarn add hardhat-gas-reporter --dev
配置 gas-reporter, hardhat.config.js 文件:
gasReporter: {
enabled: true
// currency: "USD",
// outputFile: "gas-report.txt",
// Colors: true,
// coinmarketcap: process.env.COINMARKETCAP_API_KEY,
// token: 'MATIC', // 代币网络
}
COINMARKETCAP_API获取,可以将消耗的
gas转为对应的代币值.
这样,执行测试时会打印gas消耗报告:
> yarn 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
·-------------------------------|----------------------------|-------------|-----------------------------·
| Solc version: 0.8.28 · Optimizer enabled: false · Runs: 200 · Block limit: 30000000 gas │
································|····························|·············|······························
| Methods │
··················|·············|··············|·············|·············|···············|··············
| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
··················|·············|··············|·············|·············|···············|··············
| SimpleStorage · addPerson · - · - · 112546 · 2 · - │
··················|·············|··············|·············|·············|···············|··············
| SimpleStorage · store · - · - · 43724 · 2 · - │
··················|·············|··············|·············|·············|···············|··············
| Deployments · · % of limit · │
································|··············|·············|·············|···············|··············
| SimpleStorage · - · - · 562687 · 1.9 % · - │
·-------------------------------|--------------|-------------|-------------|---------------|-------------·
3 passing (2s)
测试覆盖率
安装 solidity-coverage:
> yarn add solidity-coverage --dev
hardhat.config.js 文件:
require('solidity-coverage');
测试覆盖检查:
> yarn hardhat coverage
Version
=======
> solidity-coverage: v0.8.14
Instrumenting for coverage...
=============================
> SimpleStorage.sol
Compilation:
============
Compiled 1 Solidity file successfully (evm target: paris).
Network Info
============
> HardhatEVM: v2.22.19
> network: hardhat
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 (99ms)
--------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
--------------------|----------|----------|----------|----------|----------------|
contracts\ | 100 | 100 | 100 | 100 | |
SimpleStorage.sol | 100 | 100 | 100 | 100 | |
--------------------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
--------------------|----------|----------|----------|----------|----------------|
> Istanbul reports written to ./coverage/ and ./coverage.json
solidity tools
solhint: 是solidity linter, 可以发现潜在的代码问题.
> yarn solhint contracts/FundMe.sol
contracts/FundMe.sol
5:1 warning global import of path @chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name) no-global-import
6:1 warning global import of path ./PriceConverter.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name) no-global-import
22:5 warning Immutable variables name are set to be in capitalized SNAKE_CASE
immutable-vars-naming
23:5 warning Variable name must be in mixedCase
var-name-mixedcase
24:5 warning Variable name must be in mixedCase
var-name-mixedcase
25:5 warning Variable name must be in mixedCase
var-name-mixedcase
53:9 warning Use Custom Errors instead of require statements
custom-errors
75:9 warning Provide an error message for require
reason-string
75:9 warning Use Custom Errors instead of require statements
custom-errors
92:9 warning Provide an error message for require
reason-string
92:9 warning Use Custom Errors instead of require statements
custom-errors
✖ 11 problems (0 errors, 11 warnings)
断点调试
- 加断点
- Debug 栏 选择
JavaScript Debug Terminal - 在打开的命令行窗口执行
yarn hardhat test
配置命令脚本
package.json 文件:
{
"name": "hardhat-fund-me-fcc",
"scripts": {
"test": "hardhat test",
"test:staging": "hardhat test --network sepolia",
"lint": "solhint 'contracts/**/*.sol'",
"lint:fix": "solhint 'contracts/**/*.sol' --fix",
"format": "prettier --write .",
"coverage": "hardhat coverage"
}
}
配置后测试命令可简化为:
> yarn test
FundeMe
constructor
chainId: 31337
√ sets the aggregator address correctly
fund
√ Fails if you don't send enough ETH
√ Updates the amount funded data structure
√ Adds funder to array of funders
withdraw
√ withdraws ETH from a single funder
GasCost: 101613356109152
GasUsed: 77744
GasPrice: 1307025058
√ is allows us to withdraw with multiple funders
√ Only allows the owner to withdraw
7 passing (1s)