合約開發全新技術棧:Buidler + Ethers + Waffle + Typescript

買賣虛擬貨幣
以太坊雖然還很年輕,但已經走了很長一段路。當我於2017年開始開發Solidity智慧合約和以太坊DApp時,Truffle[1]和Web3.js[2]是行業標準。這些都是很棒的工具。但是,有一些新的工具使開發流程變得更好。本文透過建立一個專案,來嘗試這些新工具來構建和測試智慧合約與DApp[3]。本文程式碼在Starter Kit repo[4],克隆下來,就可以在功能齊全的Typescript開發環境中進行開發,編譯,測試和部署。工具鏈Buidler (替代Truffle)Buidler[5] 稱自己為“以太坊智慧合約開發者的跑腿”(“task runner for Ethereum smart contract developers”)。在實踐中,Buidler將幫助我們使用模板啟動Solidity[6]專案,並提供測試及部署智慧合約所需的所有腳手架。之前,使用 Truffle 初始化(truffle init ),編譯(truffle compile ),測試(truffle test )和部署(truffle migrate )功能來推動Solidity專案。
Buidler的殺手級功能是堆疊跟蹤資訊,當您的Solidity 遇到回退(revert)和用console.log()進行除錯時,非常好用。Ethers.js (替代Web3.js)Ethers.js[7] 是一個Javascript SDK,用於與以太坊區塊鏈進行互動。我之前使用Solidity開發時,一直使用Web3.js。當我第一次嘗試Ethers.js時,我對它如此簡單及API的出色程度感到震驚。我推薦曾經使用Web3.js的任何人嘗試一下Ethers.js。它具有使用錢包,帳戶和合約的所有必需功能,並且還具有一些簡潔的實用程式,例如ABICoder,HDNode,BigNumber,以及用於十六進位制字串,以太單位轉換和以太坊地址的各種格式化實用工具。Ethers.js是一個Javascript SDK,用於與以太坊區塊鏈進行互動。當我開始進行Solidity開發時,我長期使用Web3.js。當我第一次嘗試Ethers時,我對它的設定如此簡單以及API的出色程度感到震驚。我敦促曾經使用Web3.js的任何人嘗試一下Ethers。它具有使用錢包,帳戶和合約的所有必需功能,並且還具有一些簡潔的實用程式,例如ABICoder,HDNode,BigNumber,以及用於十六進位制字串,以太單位和以太坊地址的各種格式化實用程式。Waffle (替代Truffle 測試工具)Ethereum Waffle[8] 是以太坊智慧合約的輕量級測試執行器。Waffle內建了一些非常不錯的測試工具函式,例如用於以太坊地址,雜湊和BigNumbers的Chai匹配器,Waffle使用原生Typescript,與Ethers.js配合非常好。
譯者注:Chai 是一個斷言庫,使用鏈式結構進行斷言。Typescript 無處不在Typescript 最近很火,這是有原因的。對我而言,Typescript 的最大的改變是 IDE的整合,它提供所有類屬性,物件鍵,函式引數等的自動補全功能。熟悉Typescript之後,我再也不會回過頭來編寫原始Javascript了。上面提到的所有工具都可以與Typescript一起很好地工作,並且一旦完成所有設定,開發的體驗很夢幻。專案啟動(Project setup)現在開始真正有趣的實踐!在一個空資料夾中,執行以下命令初始化一個npm專案:
npm init初始化過程中,需要多專案有一個簡單的設定,因為我們只是演練,可以隨意填。安裝 Buidler:$ npm install --save-dev @nomiclabs/buidler譯者注,如果npm 安裝慢,本文的npm 命令都可以用cnpm替換進行Buidler專案引導:
$ npx buidler選擇"Create an empty buidler.config.js"選項,意思是常見一個新的而不是參考一個樣例。$ npx buidler888               d8b      888 888888               Y8P      888 888888                        888 888
88888b.  888  888 888  .d88888 888  .d88b.  888d888888 "88b 888  888 888 d88" 888 888 d8P  Y8b 888P"888  888 888  888 888 888  888 888 88888888 888888 d88P Y88b 888 888 Y88b 888 888 Y8b.     88888888P"   "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 buidler.config.js Config file created 建立一些目錄來儲存專案檔案,如 contracts,test,scripts, 命令如下:$ mkdir contracts test scripts
設定 Typescript安裝Typescript需要的依賴項:$ npm install --save-dev ts-node typescript @types/node @types/mocha在根目錄中建立tsconfig檔案:{  "compilerOptions": {
    "target": "es5",    "module": "commonjs",    "strict": true,    "esModuleInterop": true,    "outDir": "dist"  },
  "include": ["./scripts", "./test"],  "files": [    "./buidler.config.ts"  ]}重新命名Builder配置檔案buidler.config.js,修改字尾並使其型別安全:
mv buidler.config.js buidler.config.ts修改buidler.config.ts:// buidler.config.tsimport { BuidlerConfig } from "@nomiclabs/buidler/config";const config: BuidlerConfig = {};export default config;
建立和編譯合約現在可以開始編寫程式碼:在 contracts/ 目錄建立一個非常簡單的 Counter.sol 合約檔案,當前使用的最新Solidity 版本是 0.6.8:pragma solidity ^0.6.8;import "@nomiclabs/buidler/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;  }}在buidler.config.ts中透過修改solc.version 來設定Solidity版本:import { BuidlerConfig } from "@nomiclabs/buidler/config";
const config: BuidlerConfig = {  solc: {    version: "0.6.8",  },}export default config;
Builder 整合了編譯任務,因此編譯是小菜一碟:> npx builder compileYou probably meant to type buidler. We got you.Compiling...Compiled 2 contracts successfullyBuidler使用AMAZING 來對Solidity進行版本控制。切換版本很容易,Buidler會根據需要自動下載並安裝Solidity版本,您所需要做的就是在配置中進行更改。Buidler團隊提供了很多選項進行設定!
使用Ethers和Waffle配置測試環境現在,建立一個測試環境,安裝 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": [
    "./buidler.config.ts",    "node_modules/@nomiclabs/buidler-ethers/src/type-extensions.d.ts",    "node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts"  ]}設定 buidler.config.ts 以便使用 Ethers 外掛(包括 buidler-waffle外掛):
import { BuidlerConfig, usePlugin } from "@nomiclabs/buidler/config";usePlugin("@nomiclabs/buidler-waffle");const config: BuidlerConfig = {  solc: {    version: "0.6.8"  }
};設定 TypeChainTypeChain[9] 是一款非常酷的工具,可為智慧合約提供完整的型別介面。設定完成後,我們可以在Typescript中獲得合約函式的型別提示!可以透過我構建的Typechain外掛使用,先安裝:$ npm install --save-dev buidler-typechain typechain ts-generator @typechain/ethers-v4 @typechain/truffle-v4 @typechain/web3-v1Builder配置檔案中新增 typechain以配置外掛:
import { BuidlerConfig, usePlugin } from "@nomiclabs/buidler/config";usePlugin("@nomiclabs/buidler-waffle");usePlugin("buidler-typechain");const config: BuidlerConfig = {  solc: {    version: "0.6.8"
  },  typechain: {    outDir: "typechain",    target: "ethers-v4"  }};
export default config;outDir 定義了產生檔案的目錄,生成目標的檔案指定匹配ethers 。給 tsconfig 新增型別:{  "compilerOptions": {    "target": "es5",
    "module": "commonjs",    "strict": true,    "esModuleInterop": true,    "outDir": "dist",    "resolveJsonModule": true  },
  "include": ["./scripts", "./test"],  "files": [    "./buidler.config.ts",    "node_modules/@nomiclabs/buidler-ethers/src/type-extensions.d.ts",    "node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts",    "node_modules/buidler-typechain/src/type-extensions.d.ts"
  ]}如果執行 npx buidler,則會在選單中看到新命令typechain。透過執行命令npx buidler typechain來生成型別檔案。現在在typechain/目錄中,您應該看到生成了一些檔案,其中最主要的是Counter.d.ts。這是合約對應的 Typescript 型別檔案,提供了型別安全測試所需的資訊!編寫和執行合約測試
編寫測試大多遵循Waffle語法[10],但有一個主要區別:ethers.provider物件是從”@nomiclabs/buidler”庫而不是ethereum-waffle庫匯入的。現在讓我們編寫一個測試。在 test/ 目錄中建立一個名為counter.ts的檔案:import { ethers } from "@nomiclabs/buidler";import { Wallet } from "ethers";import chai from "chai";import { deployContract, solidity } from "ethereum-waffle";
import CounterArtifact from "../artifacts/Counter.json";import { Counter } from "../typechain/Counter";chai.use(solidity);const { expect } = chai;describe("Counter", () => {  let counter: Counter;
  beforeEach(async () => {    // 1    const signers = await ethers.signers();    // 2    counter = (await deployContract(      <Wallet>signers[0],
      CounterArtifact    )) as Counter;    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 () => {
      await counter.countDown();    });    it("should count down", async () => {      await counter.countUp();      await counter.countDown();      const count = await counter.getCount();
      expect(count).to.eq(0);    });  });});程式碼中幾處標記處解釋如下:1.從Ethers獲取一寫預先存款的簽名器。
2.使用從 1 獲取的簽名器部署合約。匯入 Counter 型別,並將其作為 beforeEach 中部署的變數的型別。3.Waffle有一些有用的Chai匹配器可用於編寫合約測試,例如BigNumber匹配器和以太坊地址匹配器。 在這裡[11]檢視所有內容。4.簡單計數測試,確保計數器正常工作。5.此測試將失敗,值得關注,等下會看到 Buidler 的真正魔力。讓我們執行測試。首先,我們將Buidler配置為使用其buidlerevm網路,該網路提供了所有Solidity除錯魔法:
import { BuidlerConfig, usePlugin } from "@nomiclabs/buidler/config";usePlugin("@nomiclabs/buidler-waffle");usePlugin("buidler-typechain");const config: BuidlerConfig = {  defaultNetwork: "buidlerevm",  solc: {    version: "0.6.8"  },
  typechain: {    outDir: "typechain",    target: "ethers-v4"  }};export default config;現在執行測試:
$ npx buidler test注意在結果中有不尋常的內容:2 passing (1s)  1 failing1) Counter       count down         should fail:
     Error: VM Exception while processing transaction: revert Uint256 underflow      at Counter.countDown (contracts/Counter.sol:29)它列印了Solidity 的輸出以及堆疊資訊,顯示了觸發回退的行號!!! 逐行註釋合約以檢視觸發了哪個還原(revert)去猜測變數值的日子已經一去不復返了。譯者注:這裡原作者稍微有點誇張,其實現在其他工具鏈也會給出 revert 原因。部署合約
經過測試後,開發週期的最後一步是部署合約。第一步是將網路配置新增到buidler.config.ts檔案。為此,本案例我們將使用rinkeby,但您可以類似地新增任何網路(如:mainnet),配置很簡單:import { BuidlerConfig, usePlugin } from "@nomiclabs/buidler/config";usePlugin("@nomiclabs/buidler-waffle");usePlugin("@nomiclabs/buidler-etherscan");usePlugin("buidler-typechain");const INFURA_API_KEY = "";
const RINKEBY_PRIVATE_KEY = "";const config: BuidlerConfig = {  defaultNetwork: "buidlerevm",  solc: {    version: "0.6.8"  },
  networks: {    rinkeby: {      url: `https://rinkeby.infura.io/v3/${INFURA_API_KEY}`,      accounts: [RINKEBY_PRIVATE_KEY]    }  },
  typechain: {    outDir: "typechain",    target: "ethers-v4"  }};export default config;我將Infura用作以太坊節點,不過你可以使用任何遠端以太坊節點。如果你之前沒有使用過 Infura,請從Infura[12]獲取API金鑰。
現在,我們在 scripts 資料夾內建立一個名為deploy.ts的部署指令碼:import { ethers } from "@nomiclabs/buidler";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);
  });部署非常簡單,現在執行這個指令碼,我們在控制檯可以看到地址及交易hash.$ npx buidler run --network rinkeby scripts/deploy.ts All contracts have already been compiled, skipping compilation.0x01FF454Dd078dC7f3cd0905601d093b17E7B9CD70x2ae1444920ed76420fb69c9f2fc914c20956efc2ae05c94ab1ea53f224aa0930
我們可以前往 Etherscan[13] 檢視交易詳情。這基本就是全部了,本文一步步進行建立專案測試、部署環境,他們都是型別安全的並且使用一些很酷的工具。封裝一下為了使一切保持乾淨漂亮,讓我們編寫一些順手的NPM指令碼。將以下內容新增到您的package.json中:"scripts": {  "build": "npm run compile && npx buidler typechain",
  "compile": "npx buidler compile",  "test": "npx buidler test"}build 用於執行合約編譯並生成TypeChain繫結test 指令碼執行合約測試。福利: 在Etherscan上驗證
Buidler有一個超級方便的外掛,可用於在Etherscan上驗證合約,此任務其實比看起來要複雜。Buidler的工具可以為幫我們處理合約組合,當我們匯入了其他合約,例如使用了OpenZeppelin等庫的合約會非常方便。安裝外掛:$ npm install --save-dev @nomiclabs/buidler-etherscan然後,新增到tsconfig.json以確保我們的Typescript環境使用此外掛:{  "compilerOptions": {
    "target": "es5",    "module": "commonjs",    "strict": true,    "esModuleInterop": true,    "outDir": "dist",    "resolveJsonModule": true
  },  "include": ["./scripts", "./test"],  "files": [    "./buidler.config.ts",    "node_modules/@nomiclabs/buidler-ethers/src/type-extensions.d.ts",    "node_modules/@nomiclabs/buidler-etherscan/src/type-extensions.d.ts",
    "node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts",    "node_modules/buidler-typechain/src/type-extensions.d.ts"  ]}接下來,我們在buidler.config.ts中新增所需的配置,前往Etherscan[14]並從您的帳戶頁面獲取API金鑰:import { BuidlerConfig, usePlugin } from "@nomiclabs/buidler/config";usePlugin("@nomiclabs/buidler-waffle");
usePlugin("@nomiclabs/buidler-etherscan");usePlugin("buidler-typechain");const INFURA_API_KEY = "";const RINKEBY_PRIVATE_KEY = "";const ETHERSCAN_API_KEY = "";const config: BuidlerConfig = {  defaultNetwork: "buidlerevm",
  solc: {    version: "0.6.8"  },  networks: {    rinkeby: {      url: `https://rinkeby.infura.io/v3/${INFURA_API_KEY}`,
      accounts: [RINKEBY_PRIVATE_KEY]    }  },  etherscan: {    // The url for the Etherscan API you want to use.    url: "https://api-rinkeby.etherscan.io/api",
    // Your API key for Etherscan    // Obtain one at https://etherscan.io/    apiKey: ETHERSCAN_API_KEY  },  typechain: {    outDir: "typechain",
    target: "ethers"  }};export default config;希望我們可以順手儲存上一步的部署地址,因為這樣就可以簡單地執行外掛提供的內建命令來驗證合約:$ npx buidler verify-contract --contract-name Counter --address 0xF0E6Ea29799E85fc1A97B7b78382fd034A6d7864All 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非常簡單!現在,在Etherscan[15]上檢視合約地址,可以檢視到完整的合約原始碼,並在網頁上讀寫合約。最後的想法在整個使用過程中,Buidler的開發者體驗給我留下了深刻的印象。它具備很多酷炫功能,並且他們計劃構建更多更酷的東西。除了Solidity堆疊跟蹤,該團隊還計劃推出另一個急需的智慧合約除錯功能:console.log!。我會繼續密切關注這個專案,並盡我所能為其生態系統做出貢獻。
如果您需要任何幫助,請執行Telegram上的Buidler Support group[16],這是迅速解決問題的好資源。Nomic Labs團隊經常在外面閒逛,並且反應迅速。請繼續關注有關全棧dapp開發和工具的更多後續帖子!References[1] Truffle: https://learnblockchain.cn/docs/truffle/[2] Web3.js: https://learnblo

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;