Solidity 教學: 基本介紹 Introduction - 智能合約、環境建置與版本控制

 

智能合約 (smart contract) 的概念最早由 Nick Szabo 於 1994 年提出,

作為區塊鏈的一部分,他是一個狀態機 (state machine) 的存在,

需要「交易 (transactions)」來更改狀態 (state) ,

同時也可以做任何的邏輯運算 ( logic operation) 。

 

本篇預計閱讀時間 15 分鐘,實作時間 10 分鐘。

 

在開始之前先來思考底下的練習題,閱讀後即可練習完成。

練習題

練習題 1

  • 創建一個 .sol 的檔案,寫出自己第一個智能合約印出 Hello World 並完成編譯。

  • 創建一個智能合約要能按下 get() Function 後會回傳 666!

練習題 2

  • 請問 Solidity 是編譯型語言還是直譯型語言?

練習題 3

佈署智能合約在以太坊區塊鏈上可視為何種行為:

  • A. 交易

  • B. 鑄造

  • C. 分岔

  • D. 挖礦

 

我們常常透過智能合約修改區塊鏈上的各種「 狀態 」,不管是佈署還是普通交易,甚至是呼叫函式,這些動作都需要透過交易和挖礦打包這兩個動作才能達到目的。同時其是圖靈完備的,所以可以解決任何的電腦問題。

在智能合約的佈署過程中,首先我們會撰寫智能合約程式碼,經過編譯器編譯之後獲得 bytecode,真正佈署的步驟其實是將 bytecode 打包到 EVM ,這個部分在未來章節會做解釋!

 

Solidity

 

Solidity 是目前針對以太坊虛擬機設計中,最知名的智能合約編輯語言。屬於編譯型語言,而非直譯型語言。最早在 2014 年由撰寫以太坊黃皮書的 Gavin James Wood 提出,設計參考了 ECMAScript,所以也會有人認為寫過 JavaScript 的工程師對 Solidity 較為上手。

撰寫智能合約並不單只有 Solidity,還有其他的智能合約語言像是:

  • Serpen: 基於 Python 的智能合約語言

  • LLL: 類似 Low-Level 的組合語言

  • Mutan: 已經被棄用的類似 Go 的語言

  • Vyper: 類似 Python 的實驗型語言

 

Solidity 的特性很大程度與 EVM、區塊鏈如何運作有關,例如以太坊底層並非像比特幣系統一樣是 UTXO(Unspent Transaction Output)的,其中 Transaction 被簡稱為 Tx。其架構更像是以帳戶為基礎的 Account Model,具有 State 。這使得合約本身也是一個特殊的 Account 型態。也因此每個地址都有 payable 與否的敘述,在編譯階段便定義了此物件是否可供支付。

在去中心化的區塊鏈網路上必須盡可能的減少人為決定的情況,所以 Solidity 的設計上強調了合約、函式、外部帳戶 EOA 的互動。每一個函數呼叫都會成為一筆交易,來去對狀態機、區塊鏈、EVM 進行互動。

 

環境建置 Remix IDE

 

Solidity 檔案的副檔名會是 .sol ,在這裡我們使用的整合開發環境(IDE)是 remix https://remix.ethereum.org/

Remix 可以用來作為編輯器、編譯器,同時我們也會在上面佈署和操作合約。同時他也是被以太坊基金會所開發與維護的。其他常用的開發工具還包含了 Truffle 與 Ganache,在最後幾章我們會介紹到它們!

為了避免版本或系統不同導致結果有所差異,在這邊提供我使用的環境在作業系統 Windows10 Pro x64, 瀏覽器 Google Chrome96.0.4664.110 (正式版本) (64 位元) 與編輯器 Visual Studio Code1.63.0 並整理如表 1-1 :

表 1-1

表 1-1

 

Remix 無須下載軟體和註冊帳戶,只要有瀏覽器便可直接開啟進行使用,只是因為存檔的機制和瀏覽器與內存有關,因此在上面撰寫完合約最好還是逕行存檔到本地端以免遺失。

使用 Remix 時雖然介面看似複雜,但已經相對 Truffle 或其他開發環境簡易許多。

首先我們打開 Remix IDE https://remix.ethereum.org/

 

圖1-1 Remix 介面

 

在 圖 1-1,點選 Workspaces 中檔案目錄中的小文件圖示「Create New File」便可新增一個檔案。

而左側依序為編譯器(Compiler)、 佈署與互動介面(Deploy & Run Transactions)、除錯器(Debugger)、統計分析(Solidity Static Analysis)、元件測試(Solidity Unit Testing)等功能。

最下方的「插頭」符號為擴充應用程式,如果找不到相關功能可在此搜尋並點選啟動即可;

而「齒輪」符號的按鈕則為 Remix 相關基本設定。

這邊我們只側重介紹編譯器(Compiler)、佈署與互動介面(Deploy & Run Transactions)兩個最常使用到的功能。

首先是在編譯器中:

  1. Compiler:我們可以自由選擇想要編譯的版本,這個部分需要跟我們於程式碼中宣告的版本符合。

  2. Language:在 Remix 中可編譯並執行的語言不只有 Solidity,但我們在這本書中只會使用到 Solidity 撰寫智能合約。

  3. EVM Version:以太坊虛擬機也有不同的版本迭代,通常都是使用預設的選項(compiler default)。

  4. Compiler Configuration:在這邊我們可以自行決定關於編譯器的設定檔,像是是否要自動編譯(每對檔案進行一次更改就會自動編譯,反之就是要手動編譯),是否要優化,是否要把警告訊息隱藏等。

 

圖 1-2 Remix 編譯介面

圖 1-2 Remix 編譯介面 

 

智能合約最佳化: https://docs.soliditylang.org/en/v0.8.11/internals/optimizer.html

再來是在執行介面中:

  1. Environment:我們可以選擇編譯環境,分別能夠選擇兩種不同版本的 Javascript Virtual Machine,這會模擬在瀏覽器上運行的狀態,這個部分在後來開發工具的相關章節會特別敘述。除了 VM 我們還能選擇Injected Web3,這個選項最常的結果是跳出 MetaMask 之類的節點媒介,並與之進行互動。最後是 Web3 Provider,常用狀況是與 Ganache或本機私網 http://localhost:8545 等節點直接溝通。
  2. Account:若是選擇 Javascript VM 則會有數十個虛擬帳戶供我們做使用。如果是選擇 Injected Web3 並連動到 MetaMask 的話互動帳戶就是我們在錢包裡面的帳戶。

  3. Gas Limit:註明 Gas 使用量限制,由於在 Gas 的使用量是與我們程式碼中使用到的 Operation 數量有最直接相關,因此我們可以透過 GasLimit 來確保我們的程式消耗的 Gas 在我們預期的範圍內。

  4. Value:轉入智能合約的金額,後方還有單位的選項欄。由於在以太坊中的合約也屬於帳戶的一種,自然可以有匯款的選項。

圖 1-3 Remix 佈署介面

圖 1-3 Remix 佈署介面 

選擇完已編譯的合約即可使用 Deploy 按鈕來進行佈署,就可以來寫第 1 個合約了!

版本控制

在合約的一開始我們必須先宣告版本,版本控制可以像以下的各種寫法,像是固定在 0.8.24 版。

pragma solidity ^0.8.24;

或者介於 0.4.16 和 0.9.0 之間。

pragma solidity >= 0.4.16 < 0.9.0;

現在最新的版本是 0.8.24,但每一個大版本都會有相對應的 breaking change 和汰除的語法,所以使用上還需要注意。尤其是在參考別人的程式碼 的時候,如果一昧地複製貼上可能會造成語法錯誤,進而出現非常大的損失。

Solidity v0.8.0 Breaking Changes

Solidity 最新版本 

最好的方式就是在使用各種程式碼或教學的時候,都回去 Solidity 的官方文件查看當前用途或版本的資訊。

還有一點需要說明的是在某些編譯器版本中需要著名 License,不然可能會報錯!

例如:

//SPDX-License-Identifier: MIT

絕大部分的 License 都是 MIT 或 GPL 3.0。

 

Hello World & First Contract

 

首先我們在檔案目錄中創建一個名為 Hello.sol 的檔案。並把以下程式碼複製貼上到檔案中。

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
contract HelloWorld {
    string public helloworld = "Hello World!";
}

撰寫完程式碼之後到編譯器的地方按下「Compile Hello.sol」就可以將整個檔案進行編譯。

在這邊我們還可以看見下方有幾個選項可以選,分別是Compilation Details、ABI、Bytecode。其實後兩者已經存在在 CompilationDetails 中了,根據合約的安全性我們不該把這些資料提供給其他人,因為只要有了這些資料便可以直接與我們的合約進行開發者等級的互動。

選擇完已編譯的合約(也就是 Helloworld – Hello.sol)即可使用 Deploy 按鈕來進行佈署。佈署完即可看見圖 1-4 的訊息:

 

圖 1-4 佈署資訊 

 

首先我們可以看到佈署的合約會擁有自己的一串地址,還有一個名為「helloworld」的按鈕,按下去之後會回傳 "Hello World!" 字串。我們要如何理解 Solidity 的運作模式呢:可以把 set() 視做 input,把 get()視做 output。當然這邊的 set() 和 get() 是我們自己定義的函式,想要取其他的名字也可以!

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

contract FirstContract {
    uint public myVar;

    function get() public view returns (uint) {
        return myVar;
    }
    function set(uint x) public {
    myVar = x;
    }
}

這邊先偷跑一下之後的內容,在 get() 這個函數裡面:

  • public 代表這個函數的可視性(Visibility),意思就是說我們可以在 Remix interface 跟它互動

  • view 的意思則為這個函數是唯讀(read-only)的。

  • returns 就像我們已經認識的其他程式語言一樣,這個函數會回傳一個型別為 uint 的值

而這裡的 set() 和 get() 就分別為 writing function 和 read function,writing function 會對現行的狀態變數進行修改,而 read function 可去察看現存的狀態變數的值。

同時可視性為 public 的字串變數 helloworld 也會自動被設置一個 getter function 讓我們能夠在 Remix 的使用者介面與其互動(看見當前變數值)。這些內容在未來相關章節會有更深入的敘述。

註解

為了有效地提升程式碼的可讀性,我們常常會在其中加上註解來讓其他夥伴更理解我們在撰寫這段語法的目標與意義。

在 Solidity 可以使用 // 來達到單一行註解的功效,使用 /* ~ */ 來達到區塊註解的功效。

區塊註解表示我們想要註解的內容可以是隔行的,變成一個不會被編譯和執行的閱讀區塊。

pragma solidity ^0.8.11;
contract Account{
  // HI
  /*Hello
  */
}

 

練習題解答

練習題 1

  • 創建一個 .sol 的檔案,並寫出自己第一個智能合約印出 Hello World 並完成編譯。
//SPDX-License-Identifier: MIT 

pragma solidity ^0.8.11; 

contract HelloWorld {   

string public helloworld = "Hello World!"; 

}
  • 創建一個智能合約要能按下 get() Function 後會回傳 666!
pragma solidity ^0.8.11;

contract myContract {

function get() public view returns (uint) {

return 666;

    }

}

程式碼因編輯器問題容易跑版,歡迎參考以下圖片。

此題有多種寫法,也可以把範例中的 myVar 初始化 666 後再回傳 myVar 都是可行的。

練習題 2

  • Solidity 是 編譯型語言,必須要經過編譯器將程式語言轉為 bytecode 提供 EVM 才能使智能合約真正運作。

練習題 3

佈署智能合約在以太坊區塊鏈上可視為何種行為:

  • A. 交易