智能合约开发框架--Hardhat
Hardhat是一个编译、部署、测试和调试以太坊应用的开发环境。它可以帮助开发人员管理和自动化构建智能合约和dApps过程中固有的重复性任务,并围绕这一工作流程轻松引入更多功能。这意味着hardhat在最核心的地方是编译、运行和测试智能合约。
一、Hardhat的优点
- Debug调试便捷。Hardhat内置有console.sol合约,可以轻松输出合约内你想要的日志信息;
- 插件功能强大。Hardhat拥有很多插件,这些插件可自定义开发,满足个性化功能;
- 兼容Truffle;
- 测试用例和部署脚本开发便捷。Hardhat集成了像ethers这样实用且强大的库,所有测试用例和部署脚本都可以通过库函数轻松实现;
二、Hardhat的安装
- Hardhat是一个node.js的工具包,所以需要先安装node.js环境和npm环境;
- 新建一个空的文件夹,运行npm init,初始化一个npm项目;
- 项目初始化后,运行npm install --save-dev hardhat 来安装hardhat;
三、Hardhat的使用
3.1 使用npx hardhat来创建Hardhat项目
按照指示创建示例项目,Hardhat会帮你生成示例合约,合约的测试用例和部署脚本;示例项目会要求安装hardhat-waffle
和hardhat-ethers
,以便让Hardhat与Waffle构建的测试兼容;
注:Hardhat会让提示你如何安装,但是,如果你错过了,你可以用npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers来安装它们。
3.2 使用npx hardhat运行任务
在项目文件夹中运行npx hardhat
,可以快速了解可用的命令和任务:
这是内置任务的列表,当你使用插件来添加更多的功能时,插件定义的任务也会在这里显示。
3.3 编译合约
接下来,如果你看一下contracts/目录
,应该可以找到Greeter.sol这个文件,这是一个生成的示例合约;要编译它,只需要运行: npx hardhat compile
编译后的工件默认保存在artifacts/
目录下,或者配置为任何其他路径(配置详情见第四部分)。初次编译后,Hardhat会在下次编译时尽量减少工作量。例如,如果你在上次编译后没有修改任何文件,那么什么也不会被编译。
3.4 测试合约
在test目录下,你可以看到有一个sample-test.js文件,里面的内容是在Hardhat内置测试网络部署一个Greeter.sol合约实例,然后对该实例进行调用,测试合约功能是否符合期望;要运行这个测试文件,只需要运行: npx hardhat test ./test/sample-test.js
如果不指定文件路径,则执行所有测试文件
'describe("", function() {});'内可包含多个异步调用,它的格式是'it("description", async function() {});';ethers.getContractFactory()会根据合约名字获取合约对象,deploy()能够带入合约构造函数的参数,await是异步标志,等待合约部署完成;合约部署完成后,可以调用合约的方法来完成相应的功能;expect是断言合约执行函数后的结果符合预期;如果希望对同一个合约实例,测试多个场景,可以通过'before("", async function() {});'构建合约实例,而后通过多个'it("", async function() {});'完成测试,如下:
这里可以通过contract.connect(otherUser)更换交易的发起账户,以满足测试通过不同账户发起交易。
3.5 部署合约
接下来,我们使用Hardhat脚本部署合约。 在 scripts/目录下
,你会发现
run scripts/sample-script.jssample-script.js文件,文件内容是部署Greeter合约,并输出部署好的合约的地址;要部署合约,只需要运行:npx hardhat
和测试脚本很类似,这里同样通过ethers这个库来获取合约对象并部署合约。因为hardhat默认是部署到内置的hardhat网络,所以如果需要部署到其他网络,如mainnet/rinkeby或自定义网络,需要配置自定义网络信息,如何配置请看第四部分。在配置好网络后,可以通过–-network来指定将要部署到的网络,如:npx hardhat run scripts/sample-test.js --network rinkeby;当然,除了部署,也可以在此部分进行一些合约方法调用来设置合约环境。
3.6 Hardhat内置网络
Hardhat在启动时,默认情况下总会启动一个Hardhat Network的内存实例,你也可以以独立的方式运行Hardhat Network,以便外部客户(可能是MetaMask,你的Dapp前端,或者一个脚本)可以连接到它。要以独立的方式运行Hardhat Network,运行npx hardhat node
这将暴露一个JSON-RPC接口链接到Hardhat网络。只要将钱包或应用程序连接到http://localhost:8545
就可以使用它。
3.7 日志输出
console.sol是一个标准的solidity合约,通过import "hardhat/console.sol"导入,它在view函数中起作用,但是在pure函数中不起作用,无论调用或交易是失败还是成功,都可以进行打印。console.log
最多支持4 个参数,支持以下类型,顺序不限:uint,string,address,bool;除了console.log()函数外,还有其他函数:
console.logInt(int i)
console.logBytes(bytes memory b)
console.logBytes1(bytes1 b)
console.logBytes2(bytes2 b)
...
console.logBytes32(bytes32 b)
注:console.sol 是用标准的Solidity实现,然后在Hardhat Network中会检测到这些输出。这使得在任何其他工具也可以编译(如Remix、Waffle或Truffle),仅意味在Remix/Truffle中编译不会报错;console.log()调用也可以在其他网络中运行,例如mainnet、kovan、ropsten等,但在这些网络中不起作用,但会花费少量的Gas。
四、 Hardhat配置
一般hardhat.config.js这个配置文件会在你项目的根目录下,hardhat启动时会读取这个配置文件。在这个文件中可配置的属性有:defaultNetwork,networks,solidity,paths,mocha。
示例文件:
defaultNetwork是默认使用的网络名,而这个网络名是定义在networks属性下的,networks下可定义多个网络;在networks下定义的网络需要包含网络的url和你想要使用的账户私钥信息,除此之外,还可以定义from(不指定时默认是第一个账户),chainId(会根据网络自动获取),gas(默认值:9500000),gasPrice(默认值:8000000000)等信息;
solidity属性定义了编译器版本,优化器是否启用等信息;
paths信息定义了文件路径;
mocha是一个异步测试框架,定义了超时时间;
hardhat.config.js文件可配置的属性很多,可参考hardhat文档;实际环境中,我们需要修改的属性很少,如下:
一般来说,只需要定义部署合约的网络信息,账户私钥信息,默认网络,以及编译器版本即可。
五、 用Hardhat在浏览器上开源合约代码
命令:
npx hardhat verify --contract contracts/要开源的合约文件.sol:要开源的合约类名 要开源的合约地址
例如:飞船合约Spacecraft.sol中的Spacecraft合约,合约的链上地址为 0x7861b6d04Db1518412edd564bE8AADF992Af0081:
npx hardhat verify --contract contracts/Spacecraft.sol:Spacecraft 0x7861b6d04Db1518412edd564bE8AADF992Af0081