英文原文地址:https://rahulsethuram.medium.com/the-new-solidity-dev-stack-buidler-ethers-waffle-typescript-tutorial-f07917de48ae
原文發(fā)表日期:2020-02-09
【譯文省略了很多原作者抒情性文字,直接進入主題捏膨。】
本文相關代碼:Starter Kit repo
Hardhat (替代Truffle)
Hardhat 稱自己為“task runner for Ethereum smart contract developers”满粗。 在實踐中爱谁,Hardhat 將幫助你使用模板啟動 Solidity 項目晒喷,并提供測試及部署智能合約所需的所有腳手架。 之前访敌,使用 Truffle 初始化 truffle init
凉敲,編譯 truffle compile
,測試 truffle test
和部署 truffle migrate
功能來推動Solidity項目寺旺。
Hardhat的殺手級功能是堆棧跟蹤信息爷抓,使得在調試 Solidity 時,可以使用回退(revert)和console.log()阻塑。
Ethers.js (替代Web3.js)
Ethers.js 是一個Javascript SDK蓝撇,用于與以太坊區(qū)塊鏈進行交互。 我從使用Solidity開發(fā)以來陈莽,一直使用Web3.js渤昌。 當我第一次嘗試Ethers.js時,我對它如此簡單及API的出色程度感到震驚走搁。 我推薦曾經(jīng)使用Web3.js的任何人嘗試一下Ethers.js独柑。 它擁有針對錢包,帳戶和合約的所有必需功能朱盐,并且還具有一些簡潔的實用程序群嗤,例如ABICoder,HDNode兵琳,BigNumber狂秘,以及用于十六進制字符串,以太單位轉換和以太坊地址的各種格式化實用工具躯肌。
Waffle (替代Truffle 測試工具)
Ethereum Waffle 是以太坊智能合約的輕量級測試運行器者春。 Waffle內(nèi)置了一些非常不錯的測試工具函數(shù),例如用于以太坊地址清女,哈希和BigNumbers的Chai匹配器钱烟,Waffle使用原生Typescript,與Ethers.js配合非常好嫡丙。
譯者注:Chai 是一個斷言庫拴袭,使用鏈式結構進行斷言。
Typescript 無處不在
Typescript 最近很火曙博,這是有原因的拥刻。 對我而言,Typescript 的最大的改變是 IDE的集成父泳,它提供所有類屬性般哼,對象鍵吴汪,函數(shù)參數(shù)等的自動補全功能。熟悉Typescript之后蒸眠,我再也不會回過頭來編寫原始Javascript了漾橙。
上面提到的所有工具都可以與Typescript一起很好地工作,并且一旦完成所有設置楞卡,開發(fā)的體驗很夢幻霜运。
項目啟動(Project setup)
現(xiàn)在開始真正有趣的實踐! 在一個空文件夾中臀晃,運行以下命令初始化一個npm項目:
npm init
初始化過程中觉渴,需要多項目有一個簡單的設置,因為我們只是演練徽惋,可以隨意填。
安裝 Hardhat:
$ npm install --save-dev hardhat
譯者注座韵,如果npm 安裝慢险绘,本文的npm 命令都可以用cnpm替換
進行Hardhat項目引導:
$ npx hardhat
選擇"Create an empty hardhat.config.js"選項,意思是常見一個新的而不是參考一個樣例誉碴。
$ npx buidler
888 d8b 888 888
888 Y8P 888 888
888 888 888
88888b. 888 888 888 .d88888 888 .d88b. 888d888
888 "88b 888 888 888 d88" 888 888 d8P Y8b 888P"
888 888 888 888 888 888 888 888 88888888 888
888 d88P Y88b 888 888 Y88b 888 888 Y8b. 888
88888P" "Y88888 888 "Y88888 888 "Y8888 888
?? Welcome to Buidler v1.3.8 ??
? What do you want to do? …
Create a sample project
? Create an empty hardhat.config.js
Quit
創(chuàng)建一些目錄來保存項目文件宦棺,命令如下:
$ mkdir contracts test scripts
設置 Typescript
安裝Typescript需要的依賴項:
$ npm install --save-dev ts-node typescript @types/node @types/mocha
在根目錄中創(chuàng)建tsconfig文件:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "dist"
},
"include": ["./scripts", "./test"],
"files": [
"./hardhat.config.ts"
]
}
重命名hardhat配置文件,修改后綴并使其類型安全:
mv hardhat.config.js hardhat.config.ts
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/types";
const config: HardhatUserConfig = {};
export default config;
創(chuàng)建和編譯合約
現(xiàn)在可以開始編寫代碼:
在 contracts/ 目錄創(chuàng)建一個非常簡單的 Counter.sol 合約文件(當前使用的最新Solidity 版本是 0.6.8):
pragma solidity ^0.6.8;
import "hardhat/console.sol";
contract Counter {
uint256 count = 0;
event CountedTo(uint256 number);
function getCount() public view returns (uint256) {
return count;
}
function countUp() public returns (uint256) {
console.log("countUp: count =", count);
uint256 newCount = count + 1;
require(newCount > count, "Uint256 overflow");
count = newCount;
emit CountedTo(count);
return count;
}
function countDown() public returns (uint256) {
console.log("countDown: count =", count);
uint256 newCount = count - 1;
require(newCount < count, "Uint256 underflow");
count = newCount;
emit CountedTo(count);
return count;
}
}
在hardhat.config.ts中通過修改solidity.compilers.$.version 來設置Solidity版本:
譯者注黔帕,此處可能是舊版本才需要修改代咸,新版本config內(nèi)結構也不盡相同
hardhat 集成了編譯任務,因此編譯是小菜一碟:
> npx builder compile
Compiling...
Compiled 1 contract successfully
hardhat使用AMAZING 來對Solidity進行版本控制成黄。 切換版本很容易呐芥,hardhat會根據(jù)需要自動下載并安裝Solidity版本,您所需要做的就是在配置中進行更改奋岁。 hardhat團隊提供了很多選項進行設置思瘟!你可以通過將config文件中solidity key賦值為數(shù)組(array),而很簡單的編譯出不同solidity版本的項目闻伶。
使用Ethers和Waffle配置測試環(huán)境
現(xiàn)在滨攻,創(chuàng)建一個測試環(huán)境,安裝 Ethers, Waffle, 以及 Buidler 插件:
$npm install --save-dev ethers @nomiclabs/buidler-waffle ethereum-waffle
在tsconfig.json添加需要的類型定義:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"resolveJsonModule": true
},
"include": [
"./scripts",
"./test"
],
"files": [
"./hardhat.config.ts"
]
}
設置 hardhat.config.ts 以便使用 waffle 插件
import { HardhatUserConfig } from "hardhat/types";
import "@nomiclabs/hardhat-waffle";
const config: HardhatUserConfig = {
solidity: {
compilers: [{ version: "0.6.8", settings: {} }],
},
};
export default config;
設置 TypeChain
TypeChain 是一款非忱逗玻酷的工具光绕,可為智能合約提供完整的類型接口。 設置完成后畜份,我們可以在Typescript中獲得合約函數(shù)的類型提示诞帐!
可以通過我構建的Typechain插件使用,先安裝:
$ npm install --save-dev hardhat-typechain typechain ts-generator @typechain/ethers-v5
Builder配置文件中添加 typechain以配置插件:
import { HardhatUserConfig } from "hardhat/types";
import "@nomiclabs/hardhat-waffle";
import "hardhat-typechain";
const config: BuidlerConfig = {
solidity: {
compilers: [{ version: "0.6.8", settings: {} }],
},
};
export default config;
編譯
& npx hardhat compile
現(xiàn)在漂坏,進入目錄 typechain/
景埃,你可以看到一些生成的文件媒至,其中有一個是 Counter.d.ts
。這是主要的合同文件谷徙。
編寫和運行合約測試
編寫測試大多遵循Waffle語法拒啰,但有一個主要區(qū)別:ethers.provider
對象是從 hardhat
庫而不是 ethereum-waffle
庫導入的。
現(xiàn)在讓我們編寫一個測試完慧。 在 test/ 目錄中創(chuàng)建一個名為counter.ts的文件:
import { ethers } from "hardhat";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { Counter } from "../typechain/Counter";
chai.use(solidity);
const { expect } = chai;
describe("Counter", () => {
let counter: Counter;
beforeEach(async () => {
// 1
const signers = await ethers.getSigners();
// 2
const counterFactory = await ethers.getContractFactory(
"Counter",
signers[0]
);
counter = (await counterFactory.deploy()) as Counter;
await counter.deployed();
const initialCount = await counter.getCount();
// 3
expect(initialCount).to.eq(0);
expect(counter.address).to.properAddress;
});
// 4
describe("count up", async () => {
it("should count up", async () => {
await counter.countUp();
let count = await counter.getCount();
expect(count).to.eq(1);
});
});
describe("count down", async () => {
// 5
it("should fail", async () => {
// this test will fail
await counter.countDown();
});
it("should count down", async () => {
await counter.countUp();
await counter.countDown();
const count = await counter.getCount();
expect(count).to.eq(0);
});
});
});
代碼中幾處標記處解釋如下:
- 從Ethers獲取一寫預先存款的簽名器谋旦。
- 使用從 1 獲取的簽名器部署合約。 導入 Counter 類型屈尼,并將其作為 beforeEach 中部署的變量的類型册着。
- Waffle有一些有用的Chai匹配器可用于編寫合約測試,例如BigNumber匹配器和以太坊地址匹配器脾歧。 在這里查看所有內(nèi)容甲捏。
- 簡單計數(shù)測試,確保計數(shù)器正常工作鞭执。
- 此測試將失敗司顿,值得關注,等下會看到 Hardhat 的真正魔力兄纺。
讓我們運行測試大溜。
首先,我們將Hardhat配置為使用其hardhat
網(wǎng)絡估脆,該網(wǎng)絡提供了所有Solidity調試魔法:
import { HardhatUserConfig } from "hardhat/types";
import "[@nomiclabs/hardhat-waffle](http://twitter.com/nomiclabs/hardhat-waffle)";
import "hardhat-typechain";
const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
solidity: {
compilers: [{ version: "0.6.8", settings: {} }],
},
};
export default config;
現(xiàn)在運行測試:
$ npx hardhat test
注意在結果中有不尋常的內(nèi)容:
2 passing (1s)
1 failing
1) Counter
count down
should fail:
Error: VM Exception while processing transaction: revert Uint256 underflow
at Counter.countDown (contracts/Counter.sol:29)
它打印了Solidity 的輸出以及堆棧信息钦奋,顯示了觸發(fā)回退的行號!!! ??????
逐行注釋合約以查看觸發(fā)了哪個還原(revert)去猜測變量值的日子已經(jīng)一去不復返了。
譯者注:這里原作者稍微有點夸張疙赠,其實現(xiàn)在其他工具鏈也會給出 revert 原因付材。
部署合約
經(jīng)過測試后,開發(fā)周期的最后一步是部署合約棺聊。
第一步是將網(wǎng)絡配置添加到hardhat.config.ts文件伞租。 為此,本案例我們將使用rinkeby限佩,但您可以類似地添加任何網(wǎng)絡(如:mainnet)葵诈,配置很簡單:
import { HardhatUserConfig } from "hardhat/types";
import "[@nomiclabs/hardhat-waffle](http://twitter.com/nomiclabs/hardhat-waffle)";
import "hardhat-typechain";
const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
solidity: {
compilers: [{ version: "0.6.8", settings: {} }],
},
networks: {
hardhat: {},
rinkeby: {
url: `[https://rinkeby.infura.io/v3/${INFURA_API_KEY}`],
accounts: [RINKEBY_PRIVATE_KEY],
},
},
};
export default config;
我將Infura用作以太坊節(jié)點,不過你可以使用任何遠程以太坊節(jié)點祟同。 如果你之前沒有使用過 Infura作喘,請從Infura獲取API密鑰。
現(xiàn)在晕城,我們在 scripts 文件夾內(nèi)創(chuàng)建一個名為deploy.ts的部署腳本:
import { ethers } from "hardhat";
async function main() {
const factory = await ethers.getContract("Counter");
// If we had constructor arguments, they would be passed into deploy()
let contract = await factory.deploy();
// The address the Contract WILL have once mined
console.log(contract.address);
// The transaction that was sent to the network to deploy the Contract
console.log(contract.deployTransaction.hash);
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed();
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
部署非常簡單泞坦,現(xiàn)在運行這個腳本,我們在控制臺可以看到地址及交易hash.
$ npx hardhat run --network rinkeby scripts/deploy.ts
All contracts have already been compiled, skipping compilation.
0x01FF454Dd078dC7f3cd0905601d093b17E7B9CD7
0x2ae1444920ed76420fb69c9f2fc914c20956efc2ae05c94ab1ea53f224aa0930
我們可以前往 Etherscan 查看交易詳情砖顷。
這基本就是全部了贰锁,本文一步步進行創(chuàng)建項目測試赃梧、部署環(huán)境,他們都是類型安全的并且使用一些很酷的工具豌熄。
封裝一下
為了使一切保持干凈漂亮授嘀,讓我們編寫一些順手的NPM腳本。 將以下內(nèi)容添加到您的package.json中:
"scripts": {
"build": "npm run compile",
"compile": "npx buidler compile",
"test": "npx buidler test"
}
build
用于執(zhí)行合約編譯并生成TypeChain綁定
test
腳本運行合約測試锣险。
福利: 在Etherscan上驗證
Hardhat有一個超級方便的插件蹄皱,可用于在Etherscan上驗證合約,此任務其實比看起來要復雜芯肤。 Hardhat的工具可以為幫我們處理合約組合巷折,當我們導入了其他合約,例如使用了OpenZeppelin等庫的合約會非常方便崖咨。
安裝插件:
$ npm install --save-dev @nomiclabs/hardhat-etherscan
接下來锻拘,我們在hardhat.config.ts中添加所需的配置,前往Etherscan并從您的帳戶頁面獲取API密鑰:
import { HardhatUserConfig } from "hardhat/types";
import "[@nomiclabs/hardhat-waffle](http://twitter.com/nomiclabs/hardhat-waffle)";
import "hardhat-typechain";
const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
solidity: {
compilers: [{ version: "0.6.8", settings: {} }],
},
networks: {
hardhat: {},
rinkeby: {
url: `[https://rinkeby.infura.io/v3/${INFURA_API_KEY}`](https://rinkeby.infura.io/v3/$%7BINFURA_API_KEY%7D%60),
accounts: [RINKEBY_PRIVATE_KEY],
},
},
etherscan: {
// Your API key for Etherscan
// Obtain one at [https://etherscan.io/](https://etherscan.io/)
apiKey: ETHERSCAN_API_KEY,
},
};
export default config;
希望我們可以順手保存上一步的部署地址掩幢,因為這樣就可以簡單地運行插件提供的內(nèi)置命令來驗證合約:
$ npx buidler verify-contract --contract-name Counter --address 0xF0E6Ea29799E85fc1A97B7b78382fd034A6d7864
All contracts have already been compiled, skipping compilation.
Successfully submitted contract at 0xF0E6Ea29799E85fc1A97B7b78382fd034A6d7864 for verification on etherscan. Waiting for verification result...
Successfully verified contract on etherscan
非常簡單逊拍! 現(xiàn)在,在Etherscan上查看合約地址际邻,可以查看到完整的合約源代碼,并在網(wǎng)頁上讀寫合約芍阎。