如何建立一個Solidity智慧合約專案? - Oliver

banq發表於2021-06-15

本文是有關使用 VSCode、Hardhat、Waffle 和 ethers.js 建立 Solidity 專案教程。
有很多程式語言。但是僅僅為了建立智慧合約而建立的並不多。那些在區塊鏈上執行的程式。建立加密貨幣、NFT 或整個去中心化交易所 (DEX) 的程式。
如果您想使用 Solidity 進行智慧合約開發,那麼本文適合您。好吧,即使您以前從未聽說過 Solidity 和智慧合約,遵循本文也可能會激發您進行更深入的挖掘,甚至可能建立您自己的加密貨幣或 DEX。
本文是一份指南,旨在幫助您建立您的第一個 Solidity 專案。您將學習如何為 Solidity 設定 Visual Studio Code、安裝 Hardhat 作為本地開發環境、編寫 Solidity 的第一行、測試您的合約,然後將其部署到本地測試網。
 

設定
讓我們從設定開始,讓您準備好開始建立您的第一個專案!
先決條件
本文假設您已經安裝了以下工具:


為 Solidity 設定 Visual Studio Code
與任何其他編輯器相比,Visual Studio Code 有兩個巨大的優勢:
  1. 它非常受歡迎,背後有一個龐大的社群。
  2. 幾乎所有東西都有一個外掛。Solidity 在這裡沒有什麼特別之處,因此您只需單擊一下即可找到一個執行良好的 Solidity 外掛。

快速跳轉到Visual Studio Marketplace並安裝 Solidity 外掛。
你得到的是編譯器管理、語法高亮、片段支援、快速編譯、程式碼完成等等。它目前是使用 Visual Studio 程式碼進行 Solidity 開發的第一大外掛,因此您在這裡做出了可靠的選擇。
完成後,您可以開始建立專案。
 

建立專案
現在您已準備好初始化專案並安裝必要的依賴項。
初始化 NPM 專案
最突出的 Solidity 工具是基於 JavaScript 的。它在 Node 上執行並且需要一個 npm 專案。它基本上就像您過去可能做過的任何其他 Web 開發專案一樣。
因此,您必須做的第一件事是為您的新專案建立一個資料夾。所以,繼續建立一個新資料夾。我會打電話給我的solidity-project。
當你有那個資料夾時,跳轉到它並初始化一個新的 npm 專案:
npm init -y

這會為您建立一個基本的 package.json,如下所示。

{
  "name": "solidity-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Oliver Jumpertz",
  "license": "ISC"
}



 

安裝Hardhat
Hardhat 是本地的 Solidity 開發環境。它以 npm 包的形式提供,並帶有許多工具,使您能夠輕鬆地使用 Solidity 進行開發。它有一個任務執行器,為你管理 Solidity 編譯器版本,並帶有一個測試環境,允許你在本地測試你的合約。
Hardhat 有一個初始化命令,你可以用 npx 執行。所以繼續執行以下命令:
npx hardhat

您現在可以選擇建立一個示例專案,但現在使用空的Hardhat 配置,因為您將從頭開始構建您的專案。
執行該命令時,您的專案中應該有一個名為hardhat.config.js. 您可能已經知道此類檔案。幾乎所有的 JavaScript 工具都有配置檔案,Hardhat 也不例外。
它應該是這樣的:

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.7.3",
};


0.7.3遺憾的是有點過時了,所以將其更新為0.8.5(這是撰寫本文時 Solidity 的最新版本)
 

在本地安裝Hardhat
Hardhat 的 init 命令只為你建立了一個配置檔案,但它並沒有改變你的package.json. 這就是您現在需要手動安裝安全帽作為開發依賴項的原因。
執行以下命令:
npm install --save-dev hardhat

現在你已經在你的專案中安裝了安全帽。您可以隨時透過 npx 執行 Hardhat,但擁有專案本地版本通常是個好主意。這可以防止因更新和新版本中的意外更改而發生衝突。
 

建立資料夾結構
接下來,您需要建立 Solidity 專案的三個基本資料夾:

  1. contracts 是您的 Solidity 檔案所在的位置。
  2. scripts 是你儲存安全帽指令碼的地方(記得我說過它帶有任務執行器嗎?指令碼基本上是安全帽任務)。
  3. test 是您的合同測試登陸的地方。

建立它們後,您就可以安裝更多依賴項了。
 

安裝Hardhat外掛
Hardhat主要透過外掛工作。包本身提供所有基本功能,而外掛處理更具體的任務,如測試。
現在是時候安裝更多依賴項了,因此執行以下命令來拉入依賴項:

npm install --save-dev @nomiclabs/hardhat-waffle @nomiclabs/hardhat-ethers ethereum-waffle chai  ethers solidity-coverage


這些是更多的依賴項,所以讓我們來看看它們現在是什麼或做什麼:
  1. @nomiclabs /hardhat-waffle:這是一個啟用waffle 支援的Hardhat外掛。
  2. @nomiclabs /hardhat-ethers:這是一個支援 ethers 的 Hardhat 外掛。
  3. ethereum-waffle:Waffle 是一個 Solidity 測試庫。它允許您使用 JavaScript 為您的合約編寫測試。
  4. chai : Chai 是一個斷言庫,提供諸如expect.
  5. ethers:這是一個流行的以太坊客戶端庫。它允許您與實現以太坊 API 的區塊鏈互動。
  6. solidity-coverage:這個庫在Istanbul的幫助下為您提供單元測試的覆蓋率報告。

 

啟用外掛
要使外掛變得活躍,您需要告訴 Hardhat 積極使用它們。不過,這項任務相對簡單。您只需要hardhat.config.js像這樣要求外掛:

require("@nomiclabs/hardhat-waffle");
require('solidity-coverage');

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.5",
};


現在hardhat知道啟用了哪些外掛並可以按需使用它們。但要真正使用它們,您首先需要建立一些命令。

 

新增命令
您的專案目前只知道一個命令:test,而這個命令甚至還沒有真正實現。是時候改變這一點了。讓我們新增命令來構建和測試您的智慧合約:

"build": "hardhat compile",
"test:light": "hardhat test",
"test": "hardhat coverage",


這些命令將執行以下操作:
  1. build:這個命令告訴安全帽從contracts資料夾中取出你的 Solidity 檔案並透過 Solidity 編譯器執行它們。
  2. test:light:這個呼叫 Waffle 來測試你的合約。
  3. test:這個呼叫 Waffle 並另外為您生成一份覆蓋率報告。覆蓋命令由solidity-coverage 外掛新增到Hardhat。


package.json修改後看起來像這樣:

{
  "name": "solidity-project",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "build": "hardhat compile",
    "test:light": "hardhat test",
    "test": "hardhat coverage",
  },
  "keywords": [],
  "author": "Oliver Jumpertz",
  "license": "ISC",
  "devDependencies": {
    "@nomiclabs/hardhat-ethers": "^2.0.2",
    "@nomiclabs/hardhat-waffle": "^2.0.1",
    "chai": "^4.3.4",
    "ethereum-waffle": "^3.3.0",
    "ethers": "^5.3.1",
    "hardhat": "^2.3.3",
    "solidity-coverage": "^0.7.16"
  }
}


現在您有了一個可以使用的基本專案。這意味著是時候在 Solidity 中實施您的第一個智慧合約了,這樣您就可以利用迄今為止投入到專案中的所有工作。
 

實施你的第一個智慧合約
是時候寫一些 Solidity 了!但是請不要期望太多。您將實施一個非常基本的合約,以便對 Solidity 有一個大致的瞭解。未來的文章肯定會涵蓋更復雜的合約。
您可以在下面看到智慧合約的完整程式碼。所以繼續,複製程式碼,並將其放入contracts/MyContract.sol.

// SPDX-License-Identifier: MIT //這是一個許可證標題。Solidity 鼓勵您許可甚至使您的程式碼開源,因為這與社群建立了信任。

//pragma是一個編譯器指令。它指出這個特定的原始檔至少需要 Solidity 編譯器的 0.7.0 版本,並且不能與 0.9.0 以後的編譯器版本一起使用。
//此語句的一件重要事情是:它僅對原始檔是本地的。當您匯入至少需要 Solidity 0.5.0 的檔案時,這根本不會影響您的檔案。您可能會在某些時候遇到編譯器錯誤,因此請務必檢查您引入的編譯器版本庫需要哪些。
pragma solidity >=0.7.0 <0.9.0;

contract MyContract {
    string private name;

    constructor(string memory _name) {
        name = _name;
    }

    //這是一個setter方法。更準確地說,它是一個需要交易的函式,因為它修改name了合約的屬性。該public關鍵字標記,這個函式是從外部訪問。
         //交易是被放入區塊的交易。它們是礦工實際開採的。這也意味著:寫入區塊鏈的函式需要花錢才能執行!
    function changeName(string memory _name) public {
        name = _name;
    }

    //這是一個getter方法。它不需要事務,因為它只讀取資料。該public關鍵字使得這個功能從外部訪問。該view關鍵字標記此功能為只讀。而returns關鍵字標記在它之後來到括號內的返回型別。
         //只讀方法不需要任何費用。
    function getName() public view returns (string memory) {
        return name;
    }
}

現在你已經實現了你的第一個智慧合約,是時候編譯它了。
執行以下命令:
npm run build

您現在應該看到如下所示的輸出:

Compiling 1 file with 0.8.5
Compilation finished successfully



如果您仔細觀察您的專案,您可能會注意到artifacts已經建立了一個新資料夾。這是dist/out資料夾或您在常用專案中所稱的任何內容。在此資料夾中,您會發現另外兩個資料夾:contracts和MyContract.sol。在後者中,您會找到兩個檔案,名為MyContract.dbg.json和MyContract.json。特別是最後一個很有趣,因為它包含你的智慧合約的 ABI 和位元組碼。這是 Solidity 編譯器的輸出。
如果您有興趣,可以進一步檢視或跟隨以瞭解接下來如何測試您的合同。
 

測試你的合約
未經測試的軟體總是有意外失敗的風險。智慧合約也不例外。但這正是我們包含 Waffle 和 chai 的目的,用於編寫測試以驗證您的合約是否按應有的方式工作。尤其是編寫可以實際更新的智慧合約並不簡單(區塊鏈上的所有內容都是不可變的,甚至是您的程式碼),因此測試更為重要。
Waffle 使您可以不使用 Solidity 本身而是使用 JavaScript 來測試您的智慧合約,所有這些都在您可能已經熟悉的環境中進行。在底層,Waffle 使用 mocha 作為測試執行器,describe並it為 JavaScript 測試提供眾所周知的結構。
檢視以下程式碼,然後將其複製到一個新檔案中test/MyContract.test.js:

const { expect } = require("chai");

describe("MyContract", () => {
  it("should return its name", async () => {
   //這是一些魔法開始的地方。還記得你是如何加入[url=https://hashnode.com/@nomiclabs]@nomiclabs[/url] /hardhat-ethers 的嗎?該外掛將以太幣全域性注入到您的測試中,因此您不必匯入它。
        //指示 ethers 查詢您的智慧合約並建立一個工廠,以便您以後可以例項化它。
    const MyContract = await ethers.getContractFactory("MyContract");
   //此行呼叫智慧合約的建構函式。您在智慧合約的建構函式中實現的所有內容現在都已執行。
    const myContract = await MyContract.deploy("My Contract");

    await myContract.deployed();
    expect(await myContract.getName()).to.equal("My Contract");
  });
  it("should change its name when requested", async () => {
    const MyContract = await ethers.getContractFactory("MyContract");
    const myContract = await MyContract.deploy("My Contract");

    await myContract.changeName("Another Contract");
    expect(await myContract.getName()).to.equal("Another Contract");
  });
});
 

執行你的測試
既然您的合同已包含測試,現在是執行它們並檢視它們是否成功的時候了。
執行以下命令:
npm run test

您應該在終端中看到與此類似的輸出:

> solidity-project@1.0.0 test
> hardhat coverage


Version
=======
> solidity-coverage: v0.7.16

Instrumenting for coverage...
=============================

> MyContract.sol

Compilation:
============

Compiling 1 file with 0.8.5
Compilation finished successfully

Network Info
============
> HardhatEVM: v2.3.3
> network:    hardhat



  MyContract
      should return its name (174ms)
      should change its name when requested (106ms)


  2 passing (283ms)

-----------------|----------|----------|----------|----------|----------------|
File             |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
-----------------|----------|----------|----------|----------|----------------|
 contracts/      |      100 |      100 |      100 |      100 |                |
  MyContract.sol |      100 |      100 |      100 |      100 |                |
-----------------|----------|----------|----------|----------|----------------|
All files        |      100 |      100 |      100 |      100 |                |
-----------------|----------|----------|----------|----------|----------------|

> Istanbul reports written to ./coverage/ and ./coverage.json

恭喜!您的測試透過 - 是時候考慮如何部署您的合約了。
 

部署你的合約
Solidity 不是在每臺機器上執行的語言。它在作為以太坊節點一部分的以太坊虛擬機器中執行。您的程式碼需要部署到區塊鏈才能使用。
Hardhat 附帶了一個任務執行器,這對自動化此任務有很大幫助。這些任務只不過是按需執行的 JavaScript 檔案。
複製此程式碼並將其放入一個新檔案中scripts/deployMyContract.js:

async function main() {
  const MyContract = await ethers.getContractFactory("MyContract");
  const myContract = await MyContract.deploy("My Contract");

  console.log("My Contract deployed to:", myContract.address);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });


您可能會發現一些您之前在測試中已經見過的行。是的,您可以在此處使用您在測試中使用的相同例項化邏輯。
永遠不要直接將您的合約部署到世界各地是一種很好的做法。很高興,Hardhat 附帶了一個本地測試網,您可以在其中測試您的合約並在本地使用它們。
將以下指令碼新增到您的package.json:

"deploy:local": "hardhat run --network localhost scripts/deployMyContract.js"


該命令使用 Hardhat 執行您的指令碼並將目標網路定義為 localhost。
接下來,您需要一個本地測試網,以便您可以在某個地方部署您的合約。
將另一個指令碼新增到您的package.json:
"local-testnet": "hardhat node",

package.json現在應該類似:

{
  "name": "solidity-project",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "build": "hardhat compile",
    "test:light": "hardhat test",
    "test": "hardhat coverage",
    "deploy:local": "hardhat run --network localhost scripts/deployMyContract.js",
    "local-testnet": "hardhat node"
  },
  "keywords": [],
  "author": "Oliver Jumpertz",
  "license": "ISC",
  "devDependencies": {
    "@nomiclabs/hardhat-ethers": "^2.0.2",
    "@nomiclabs/hardhat-waffle": "^2.0.1",
    "chai": "^4.3.4",
    "ethereum-waffle": "^3.3.0",
    "ethers": "^5.3.1",
    "hardhat": "^2.3.3",
    "solidity-coverage": "^0.7.16"
  }
}


您已準備好在本地部署合約所需的一切。現在開啟一個新終端(選項卡)並執行以下命令:
npm run local-testnet
輸出應如下所示:

> solidity-project@1.0.0 local-testnet
> hardhat node

Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========
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

Hardhat 為您啟動了一個本地以太坊網路並列印出所有現有帳戶,包括他們的私鑰。在你問之前:不,你不能把所有的以太幣轉移到主網上。抱歉,這只是用來玩和測試的假 Ether。
現在切換回原始終端(選項卡)並執行:
npm run deploy:local
這應該建立這樣的輸出:

> solidity-project@1.0.0 deploy:local
> hardhat run --network localhost scripts/deployMyContract.js

Compiling 1 file with 0.8.5
Compilation finished successfully
My Contract deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
`

恭喜,您已成功將第一個智慧合約部署到本地測試網!
 

結論
就是這樣。您已經建立了一個本地開發環境,實現了您的第一個智慧合約,對其進行了測試,然後在本地進行了部署。
這個設定非常可靠,因為它帶有維護良好的庫和工具。它也很容易使用並且可以很好地擴充套件。您還可以非常輕鬆地擴充套件它,這要歸功於 Hardhat 任務和 npm 的靈活性,可以在需要時簡單地新增更多指令碼。
你可以從這裡繼續玩。看看你還能創造什麼。向該專案新增更多合約應該相對簡單。
未來我肯定會涵蓋更復雜的智慧合約,從 ERC20,超過 ERC721,到 ERC1155,可升級合約(代理模式),甚至可能是完整的 DEX。所以請繼續關注更多。

 

相關文章