Solidity and Remix

Solidity 的運作方式可以分解為三個主要步驟:編寫、編譯 和 執行。開發者能用人類能懂的語言寫程式,然後編譯器將其翻譯成機器語言,最終由各節點在去中心化的環境中執行。

1. 編寫 (Writing)

首先,開發者使用 Solidity 語言來編寫智能合約。這段程式碼包含了合約的邏輯、規則和變數,例如一個代幣合約如何處理轉帳、或一個拍賣合約如何管理出價。

2. 編譯 (Compiling)

寫好的 Solidity 程式碼是人類可讀的,但區塊鏈的底層虛擬機(例如 EVM,以太坊虛擬機)無法直接理解。因此,我們需要使用一個稱為 Solidity 編譯器 (solc) 的工具,將程式碼轉換成 EVM 能夠執行的 位元碼 (bytecode)。

這個位元碼是合約的機器語言,由一系列低階的指令(稱為 opcode)組成。編譯過程還會產生 ABI (Application Binary Interface),它就像是合約的公開介面說明書,告訴外界如何與合約互動。

3. 執行 (Executing)

一旦合約被編譯成位元碼,它就可以被部署到區塊鏈上。當用戶與合約互動(例如呼叫一個函式)時,這個過程會在所有運行區塊鏈網路的節點上發生。

  • 交易 (Transaction):如果互動會改變區塊鏈的狀態(例如發送代幣),那麼這會是一個交易。這筆交易會被發送到網路上,由礦工或驗證者打包到區塊中。
  • 節點運算:每個節點的底層虛擬機都會載入合約的位元碼,並執行其中的指令。節點EVM 會追蹤所有的狀態變動,並確保每個節點都得到相同的結果。這就是區塊鏈達成共識的關鍵。


Solidity範例

以下是Solidity可顯示Hello World的範例程式碼

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

contract HelloWorld {
    string public message;

    constructor() {
        message = "Hello World";
    }

    function updateMessage(string memory _newMessage) public {
        message = _newMessage;
    }
}

程式碼解說

  1. pragma solidity ^0.8.20;: 指定編譯器版本。這行告訴編譯器,這個合約需要使用 0.8.20 或更新,但不高於 0.9.0 的 Solidity 版本來編譯。
  2. contract HelloWorld { ... }: 這是定義一個智能合約的關鍵字。HelloWorld 是這個合約的名稱。
  3. string public message;:
    • string 是一種用於儲存文字的變數類型。
    • public 關鍵字很特別。當你將一個狀態變數設為 public 時,Solidity 會自動為你創建一個同名的 getter 函式。這意味著,任何人都可以透過呼叫 message() 這個函式來讀取 message 變數的值,而不需要我們自己手動寫一個 getMessage 的函式。
  4. constructor() { ... }: 建構函式,只在合約部署到區塊鏈時執行一次。在這裡,我們將 message 的初始值設定為 "Hello World"
  5. function updateMessage(string memory _newMessage) public { ... }:
    • updateMessage 是一個公共函式,任何人都可以呼叫它。
    • 它接受一個名為 _newMessage 的參數,類型是 string memorymemory 表示這個字串是儲存在記憶體中,只在函式執行期間存在。
    • 函式內部,我們將 message 的值更新為傳入的 _newMessage。由於這會改變區塊鏈的狀態,因此執行這個函式需要花費 Gas。

Remix IDE

Remix IDE (Integrated Development Environment,整合式開發環境) 是一個專為開發、測試和部署智能合約 (Smart Contracts) 而設計的強大開源工具。它最主要的優點是基於網頁,你不需要安裝任何東西,只需要一個瀏覽器就能開始使用。

將上述範例程式碼貼到 Remix IDE(一個線上開發環境)中,然後:

  1. 點擊左側的「Compile」標籤,編譯合約。
  2. 切換到「Deploy & Run Transactions」標籤,部署合約。
    • ENVIRONMENT選Remix VM,任一個都可以
    • ACCOUNT選任何一個都可以
    • 佈署完後會出現訊息,例如[vm]from: 0x5B3...eddC4to: HelloWorld.(constructor)value: 0 weidata: 0x608...e0033logs: 0hash: 0xcc6...0ac72
    • 展開訊息會看到contract address,例如0x7EF2e0048f5bAeDe046f6BF797943daF4ED8CB47
  3. 部署完成後,在Deployed Contracts下方看到合約的介面。
    • 每次佈署都會產生新的contract address,如果佈署多次會出現多個,選擇其中一個佈署的合約地址即可
  4. 展開合約,下方會出現一個新的區塊,顯示你合約中的所有 publicexternal 函式。
    • 紅色按鈕: 代表這個函式會 修改 區塊鏈的狀態 (需要花費 Gas)。
    • 藍色按鈕: 代表這個函式只會 讀取 區塊鏈的狀態 (不需要花費 Gas)。
  5. 藍色按鈕測試,可點擊 message 按鈕就會出現 0:string: Hello World
  6. 紅色按鈕測試,可在 updateMessage 函式旁的輸入框中,輸入想更改的新訊息(例如 "Hello Blockchain"),然後點擊 updateMessage 按鈕。可以注意到訊息提到花費了32263 gas
  7. 再次點擊 message 按鈕就會出現0:string: Hello Blockchain,會發現訊息已經被成功更新。

Environment (環境)

在 Remix IDE的Deploy & Run Transactions中,Environment可指定在哪個區塊鏈環境中執行你的合約,決定了你的合約會部署到哪裡。

  • Remix VM (Remix 虛擬機):這是 Remix 內建的模擬區塊鏈環境。
    • 優點: 區塊鏈的運作速度非常快,不需要連接到任何外部網路,所有交易都是即時的。你可以免費測試合約,不需要花費任何 Gas。
    • 缺點: 交易記錄只存在於瀏覽器中,刷新頁面後會消失。這是一個隔離的沙盒環境,不具備真實的區塊鏈功能。
  • Injected Provider:這個選項會連接你瀏覽器中的錢包擴充程式,例如 MetaMask
    • 優點: 讓你可以在真實的區塊鏈網路(如主網、測試網或本地網路)上部署和互動合約。你可以使用你的真實帳戶並花費真實的 Gas。
    • 缺點: 需要連接到網路,交易需要等待區塊確認時間,並且會消耗真實的 Gas。
  • Hardhat Provider :這選項讓你連接到本地端運行的開發環境。
    • 優點: 提供了更強大的開發和測試功能,適合進階開發者。
    • 缺點: 需要額外設定本地開發環境。

Account (帳戶)

在 Remix IDE的Deploy & Run Transactions中,Account用來選擇一個帳戶來部署或執行交易。

  • Remix VM 這裡會預設提供幾個帶有虛擬資金的測試帳戶,你可以任意選擇一個來使用,不用擔心花費。
  • Injected Provider 這個列表會顯示你連接的 MetaMask 錢包中所有可用的帳戶。

ABI

ABI (Application Binary Interface)就像是合約的「說明書」,用來定義合約中所有函式的公開介面。有了這個 ABI,外部應用程式(如 Remix IDE、MetaMask 錢包或任何 dApp)才能知道如何與你的智能合約互動。

Remix的Compile頁面下方可以產生ABI,分區說明如下

1. 建構函式 (Constructor)

{
  "inputs": [],
  "stateMutability": "nonpayable",
  "type": "constructor"
}
  • type: "constructor":這部分說明了這是一個建構函式,只在合約部署時執行一次。
  • inputs: []:空的陣列表示這個建構函式不接受任何參數。
  • stateMutability: "nonpayable":表示這個函式會修改區塊鏈的狀態(例如設定初始訊息),因此需要支付 Gas 費用。

2. updateMessage 函式

{
  "inputs": [
    {
      "internalType": "string",
      "name": "_newMessage",
      "type": "string"
    }
  ],
  "name": "updateMessage",
  "outputs": [],
  "stateMutability": "nonpayable",
  "type": "function"
}
  • type: "function":這是一個普通的函式。
  • name: "updateMessage":函式的名稱。
  • inputs:這個函式接受一個參數:
    • name: "_newMessage":參數的名稱。
    • type: "string":參數的類型是字串。
  • outputs: []:空的陣列表示這個函式不回傳任何值。
  • stateMutability: "nonpayable":表示這個函式會修改區塊鏈的狀態(更新訊息),因此需要支付 Gas 費用。

3. message 函式

{
  "inputs": [],
  "name": "message",
  "outputs": [
    {
      "internalType": "string",
      "name": "",
      "type": "string"
    }
  ],
  "stateMutability": "view",
  "type": "function"
}
  • type: "function":這是一個普通的函式。
  • name: "message":函式的名稱。
  • inputs: []:空的陣列表示這個函式不接受任何參數。
  • outputs:這個函式有一個回傳值:
    • type: "string":回傳值的類型是字串。
  • stateMutability: "view":這是最重要的部分。它表明這個函式是一個 只讀函式,它不會修改區塊鏈的狀態。因此,呼叫這個函式是 免費的,不需要支付 Gas 費用。