入门
安装
```
npm install --save-dev hardhat
npx hardhat
```
编译合约
```npx hardhat compile```
部署合约
- 编写js代码
const Token = await ethers.getContractFactory("合约名");// const hardhatToken = await Token.deploy(); await hardhatToken.deployed(); // 接下来可以直接调用合约方法 获取余额 const ownerBalance = await hardhatToken.balanceOf(address);
- 执行js代码
// node js文件路径 node scripts/index.js
常用操作
fork主网
- 命令的形式
// fork eth链 // npx hardhat node --fork 地址 npx hardhat node --fork https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 // fork指定块高度(锁定区块) // npx hardhat node --fork 地址 --fork-block-number 块高度 npx hardhat node --fork https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 --fork-block-number 115445
- 配置hardhat.config.js
networks: { hardhat: { forking: { url: "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", // okChain // blockNumber: 5115288, //指定fork的块高度(锁定区块) }, } },
重置Fork
你可以在运行时里操作Fork,如重置回全新的Fork状态、从另一个区块号Fork,或者通过调用hardhat_reset禁用Fork:
- 重新fork
await network.provider.request({ method: "hardhat_reset", params: [{ forking: { jsonRpcUrl: "https://eth-mainnet.alchemyapi.io/v2/<key>", blockNumber: 11095000 } }] })
- 禁用fork
await network.provider.request({ method: "hardhat_reset", params: [] })
挖矿
-
禁用挖矿
async function f6() { await hardhat.network.provider.send("evm_setAutomine", [false]); } f6()
-
启用区间挖掘
await network.provider.send("evm_setIntervalMining", [5000]);
-
手动挖矿
在hardhat.config.js没有配置手动挖矿 或 当自动挖矿被禁用时,可以执行手动挖矿(没有被禁用一样可以执行手动)async function f8() { await hardhat.network.provider.request({ method: "evm_mine", }); } f8()
获取pending中的交易列表
可以先禁用挖矿,再获取pending列表
const pendingBlock = await network.provider.send("eth_getBlockByNumber", [ "pending", false, ]);
删除和替换交易 hardhat_dropTransaction
- 删除交易
const txHash = "0xabc..."; await network.provider.send("hardhat_dropTransaction", [txHash]);
- 替换交易 直接发起一个交易,指定的交易的哈希为你先覆盖的哈希即可,就像在 Geth 中一样,新的 gas/fees 价格必须至少比当前交易的 gas 价格高 10%
设置下一个块的时间戳
```
const hardhat = require("hardhat");
hardhat.network.provider.request({
method: "evm_setNextBlockTimestamp",
params: [1640966400],
});
```
** 1640966400为你要设置的时间戳 场景:比如到点才能claim **
配置项 hardhat.config.js
networks: {
hardhat: {
chainId: 31337,//默认链id
from: account,//默认账户
gas: 'auto'|'3000',// gas费,auto自动或者一个具体的数值
gasPrice: 'auto'|'3000', // 类似gas
gasMultiplier: '1',用于乘以气体估计结果的数字,默认值1
accounts: { // 此字段可以配置为以下之一
mnemonic: //助记池 BIP39 定义的 12 或 24 个单词的助记词。默认值:"test test test test test test test test test test test junk"
initialIndex: // 要派生的初始索引。默认值:0
path:所有派生密钥的 HD 父级。默认值:"m/44'/60'/0'/0"。
count: 20,要派生的帐户数。默认值:20。
accountsBalance: 分配给每个派生帐户的余额(以 wei 为单位)的字符串。默认值:"10000000000000000000000"(10000 ETH)
},
blockGasLimit: gas 限制。默认值:30000000 (3gwei)
minGasPrice: 最低 gas 价格,默认0we
hardfork: 这个设置改变了安全帽网络的工作方式,在给定的硬分叉上模仿以太坊的主网。它必须是一个"byzantium","constantinople","petersburg","istanbul","muirGlacier","berlin"和"london"。默认值:"london"
throwOnTransactionFailures:控制安全帽网络是否引发交易失败的布尔值。如果此值为true,Hardhat Network 将在事务失败时抛出组合的 JavaScript 和 Solidity 堆栈跟踪。如果是false,它将返回失败的交易哈希。在这两种情况下,交易都被添加到区块链中。默认值:true
throwOnCallFailures:一个布尔值,用于控制 Hardhat Network 是否在调用失败时引发。如果此值为true,Hardhat Network 将在调用失败时抛出组合的 JavaScript 和 Solidity 堆栈跟踪。如果是false,它将返回调用的return data,其中可以包含还原原因。默认值:true
loggingEnabled:控制安全帽网络是否记录每个请求的布尔值。默认值:false对于进程内 Hardhat Network 提供者,true对于 Hardhat Network 支持的 JSON-RPC 服务器(即node任务)。
initialDate:设置区块链日期的可选字符串。有效值是Javascript 的日期时间字符串。默认值:如果不分叉另一个网络,则为当前日期和时间。当分叉另一个网络时,使用您分叉的区块的时间戳加上一秒。
allowUnlimitedContractSize:一个可选的布尔值,禁用EIP 170强加的合约大小限制。默认值:false
forking: {描述分叉配置的对象,可以具有以下字段:
url: 指向具有要分叉的状态的 JSON-RPC 节点的 URL。此字段没有默认值。必须为前叉提供它才能工作。
blockNumber:一个可选数字,用于固定要从哪个块中分叉。如果未提供值,则使用最新的块。
enabled: 一个可选的布尔值,用于打开或关闭 fork 功能。默认值:true如果url设置,false否则。
}
minGasPrice:gasPrice交易必须具有的最小值。如果"hardfork"是"london"或以后的,则此字段不得存在。默认值:"0"。
initialBaseFeePerGas: 第baseFeePerGas一个块的。请注意,在分叉远程网络时,“第一个块”是您分叉的块之后的那个。此字段必须不存在,如果"hardfork"不是"london"或更高之一。默认值:"1000000000"如果不分叉。在 fork 远程网络时,如果远程网络使用 EIP-1559,则第一个本地块将baseFeePerGas根据 EIP使用权限,否则"10000000000"使用
}
}
EVM方法
方法具体解析可以看以太坊虚拟机官方API说明 ethereum-json-rpc-api
标准方法
- eth_accounts 返回主账户,这个账户是在hardhat.config.js配置的私钥的账户地址
- eth_blockNumber 返回当前块高度
- eth_call
立刻执行一个新的消息调用参数 Object - 交易调用对象 from: DATA, 20 Bytes - 发送交易的原地址,可选 to: DATA, 20 Bytes - 交易目标地址 gas: QUANTITY - 交易可用gas量,可选。eth_call不消耗gas,但是某些执行环节需要这个参数 gasPrice: QUANTITY - gas价格,可选 value: QUANTITY - 交易发送的以太数量,可选 data: DATA - 方法签名和编码参数的哈希,可选 QUANTITY|TAG - 整数块编号,或字符串"latest"、"earliest"或"pending"
- eth_chainId 返回当前链id
- eth_coinbase 返回接收挖矿回报的账户地址
- eth_estimateGas 返回估计调用需要耗费的gas量
- eth_gasPrice 返回当前的gas价格
- eth_getBalance 获取指定账户余额
- eth_getBlockByHash 返回具有指定哈希的块
params: [ hash, true//为true时返回完整的交易对象,否则仅返回交易哈希 ]
- eth_getBlockByNumber 返回指定编号的块。
params: [ '0x1b4', // 整数块编号,或字符串"earliest"、"latest" 或"pending" true// 为true时返回完整的交易对象,否则仅返回交易哈希 ]
- eth_getBlockTransactionCountByHash 返回指定块内的交易数量,使用哈希来指定块
params: [ hash ]
- eth_getBlockTransactionCountByNumber 返回指定块内的交易数量,使用块编号指定块
params: [ '0xe8', // 232 ]
- eth_getCode 返回指定地址的代码
params: [ address, '0x2' //整数块编号,或字符串"latest"、"earliest" 或"pending" ]
- 如何调用
const hardhat = require("hardhat"); hardhat.network.provider.request({ method: 方法名, params: [参数], });
Hardhat 方法
- hardhat_addCompilationResult 添加有关已编译合同的信息
- hardhat_dropTransaction 从内存池中删除交易
- hardhat_impersonateAccount 模拟账户
async function f1(){ await hardhat.network.provider.request({ method: "hardhat_impersonateAccount", params: [被模拟的账户], }); const signer = await ethers.provider.getSigner(被模拟的账户) // 用模拟的账户给指定账户转账 await signer.sendTransaction({ to: "0x23FCB0E1DDbC821Bd26D5429BA13B7D5c96C0DE0", value: ethers.utils.parseEther("1.0"), }); console.log('success') // 取消模拟 await hardhat.network.provider.request({ method: "hardhat_stopImpersonatingAccount", params: [被模拟的账户], }); } f1().then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- 查询交易信息
async function UnsignedTransaction(hash) { const data = await hardhat.ethers.provider.getTransaction(hash) console.log('data', data) } //UnsignedTransaction(交易hash) UnsignedTransaction('0xa475cf601cb72f4435b22cec9e622eaee1c98f4bd5fa39d1d7e0e69c3f5f62f5')
- 修改账户余额
async function f2(){ await hardhat.network.provider.send("hardhat_setBalance", [ "0x23FCB0E1DDbC821Bd26D5429BA13B7D5c96C0DE0", Web3.utils.numberToHex(hardhat.ethers.utils.parseEther('123.12')),//十六进制,可以通过ethers.utils的转换接口 ]); } f2()
- Token转账
// 代币转账 async function f4(){ // 合约部署 const ERC20 = await ethers.getContractFactory("ERC20"); let erc20 = await ERC20.deploy(); erc20.deployed(); const accounts =await ethers.getSigners(); const beforeBalance = await erc20.balanceOf(accounts[1].address) // 设置转账数量 const amount = ethers.utils.parseEther('10') // 广播交易 const tx = await erc20.transfer(accounts[1].address, amount) // 等待交易上链 await tx.wait() // 查询目标地址余额 const afterBalance = await erc20.balanceOf(accounts[1].address) console.log('转帐前', beforeBalance.toString()) console.log('转帐后', afterBalance.toString()) } f4()
- ETH转账
// 代币转账 async function f5(){ const [owner] = await ethers.getSigners(); await owner.sendTransaction({ from: owner.address, to: "0xD528d6B7ff1f46417a7A7b2f2869Fe0CC8e9ed67", value: ethers.utils.parseEther("1.2"), }); // 查询目标地址余额 const balance = await hardhat.ethers.provider.getBalance('0xD528d6B7ff1f46417a7A7b2f2869Fe0CC8e9ed67') console.log(balance.toString()) } f5()
- 常见单位转换
// gas转换 num2wei hardhat.ethers.utils.parseUnits('300', 'gwei'), // ether转换 num2wei hardhat.ethers.utils.parseEther('1') // num 2 16hex Web3.utils.numberToHex('1') ethers.utils.hexValue(1)
编译配置
solidity: {
compilers: [
{
version: "0.5.1", // 编译器版本
settings: {
optimizer: {
enabled: true,
runs: 200
}
},
...其他配置