---
# System prepended metadata

title: Solidity 教學
tags: [Solidity]

---

# Solidity 教學

Solidity是一種高級編程語言，專門用於在以太坊平台上編寫智能合約。它是一種靜態類型語言，語法上類似於JavaScript、C++和Python。Solidity被設計來支持創建各種類型的去中心化應用程序（DApps），尤其是那些需要複雜邏輯和功能的合約。
Solidity提供強大的靈活性和多功能性，支持複雜的智能合約開發。它提供了豐富的特性，如繼承、多重繼承、函數重載等，適合開發大型和複雜的DApp，與之相比Vyper更加注重簡單性和安全性。它的語法較簡單，刪除了許多可能導致複雜性和安全漏洞的特性，如函數重載、修飾符和內聯組件等。

#### Vyper VS Solidity

| **比較項目**        | **Vyper**                          | **Solidity**                        |
|-------------------|------------------------------------|------------------------------------|
| **語言目的**       | 簡單性和安全性                      | 多功能性和靈活性                    |
| **語法風格**       | 類似於Python                       | 類似於JavaScript和C++              |
| **開發狀態**       | 還在開發中，語法較為簡單            | 已經成熟，擁有廣泛的功能和工具支持 |
| **安全性**         | 注重安全性，排除一些複雜特性       | 支持複雜邏輯，但可能引入安全風險   |
| **迴圈支持**       | 嚴格限制（不支持或僅限特定場景）    | 完整支持各種迴圈語句                |
| **可變性**         | 嚴格控制變量的可變性               | 允許更多靈活的變量處理方式          |
| **編譯器和工具**   | 較少的工具支持                     | 擁有完整的開發工具鏈和IDE支持       |
| **常見用途**       | 簡單、安全的智能合約               | 各種類型的智能合約，包括複雜DApp   |
| **社區資源**       | 社區相對較小，資源較少             | 擁有廣大的開發者社區和豐富的資源   |
| **第三方套件**     | 限制使用第三方庫                   | 可以使用多種第三方庫和標準庫       |
| **錯誤處理**       | 簡化的錯誤處理機制                 | 提供完整的錯誤處理和回退機制       |
| **學習曲線**       | 對於有Python經驗的開發者較為友好    | 對於有JavaScript或C++經驗的開發者友好 |


## 開始使用 Solidity
### 安裝開發環境
為了開始編寫 Solidity 智能合約，您需要設置一個開發環境。這裡有幾個常見的選項：

| **Truffle**        | **Ganache**                          | **Remix IDE** |
|-------------------|-----------------------|------------------------------------|
| ![image](https://hackmd.io/_uploads/SkST0rStA.png)| ![image](https://hackmd.io/_uploads/B1LvAHSK0.png) | ![image](https://hackmd.io/_uploads/SkfM18BK0.png)|
1. Truffle：Truffle 是一個綜合開發框架，提供了創建、編譯、測試和部署智能合約的工具。Truffle 是基於 EVM（以太坊虛擬機），其目標之一是讓智慧合約的開發變得更簡單、更容易實現
    * 編譯、連結、部署和管理智能合約的二進位。
    * 用於快速開發的自動化合約測試。
    * 可編寫腳本且可擴展的部署和遷移框架。
    * 用於直接合約通訊的互動式控制台。
    * 在 Truffle 環境中執行腳本的外部腳本執行器。
3. Ganache：Ganache 是一個本地的區塊鏈模擬器，允許您在本地測試合約，而不需要連接到真實的以太坊網絡。Ganache 有兩個不同的版本：Ganache UI 和名為 ganache-CLI 的 CLI。這兩個版本均可在 Linux、Mac 和 Windows 上使用
4. Remix IDE：這是一個基於瀏覽器的開發環境，無需安裝任何軟件，適合初學者使用。Remix 提供了編譯、部署和調試合約的完整工具。

![image](https://hackmd.io/_uploads/H1xpmrSFA.png)

### 選擇編輯器
除了使用 Remix，您還可以選擇其他文本編輯器來編寫 Solidity 代碼，如：
* VS Code (Visual Studio Code)：支持多種插件，可以提供代碼補全和調試功能。
* Sublime Text：輕量且高效，適合喜歡簡潔界面的開發者。
* Atom：由 GitHub 開發，也支持多種插件。


## Solidity 基礎概念
### 智能合約

智能合約（Smart Contract）是一段部署在區塊鏈上的代碼，它自動執行並強制合約條款，而不需要第三方的干預。這些合約在執行過程中具有以下關鍵特性：

* **不可變性**：一旦智能合約被部署到區塊鏈上，其代碼和規則無法更改，確保合約的條款始終保持一致。這一特性是通過區塊鏈的不可變記錄和分佈式共識機制來實現的。每次的交易和合約操作都會永久記錄在區塊鏈中，無法被篡改。

* **透明性**：合約的所有操作都是公開的，任何人都可以查看合約的代碼和執行結果。這種透明性使得所有參與方都能信任合約的運行方式，因為一切都是公開且無法更改的。

* **自動執行**：智能合約中的條款一旦達成，合約就會自動執行對應的操作，而無需人工介入。這種自動化特性消除了中介的需要，大大減少了交易成本和延遲。智能合約可以觸發一系列的動作，例如資產轉移、資訊更新等，根據合約條款的設定自動執行。

* **去中心化**：智能合約在去中心化的區塊鏈網絡上運行，這意味著它們不依賴於單一的伺服器或實體來執行或控制。即使某個節點發生故障，合約仍能在其他節點上運行，這增加了合約的可靠性和抗審查性。

* **可編程性**：開發者可以根據需求編寫複雜的邏輯和條件，使智能合約能夠處理各種不同的業務場景，從簡單的資產轉移到複雜的去中心化應用（DApp）。這使得智能合約成為區塊鏈技術的基石，支持了許多創新應用的發展。

### Solidity 語法基礎

Solidity 是一種專為開發智能合約而設計的編程語言。它的設計目標是使得智能合約的開發過程直觀且高效，特別是在以太坊區塊鏈上運行。以下是 Solidity 語法的一些關鍵特徵和功能：

* **靜態類型語言**：Solidity 是靜態類型的，這意味著每個變量的類型（如整數、字符串、布爾值等）都必須在編譯時確定。這有助於在代碼執行前檢查錯誤，增強了代碼的安全性和穩定性。靜態類型還有助於優化編譯器的性能，因為編譯器可以更好地理解代碼結構並進行優化。

* **類似 JavaScript 和 C++ 的語法**：Solidity 的語法受 JavaScript 和 C++ 的影響，因此對於有這些語言背景的開發者來說，入門相對容易。例如，像 if-else 條件語句、for 循環和函數定義在 Solidity 中的表達方式都與 JavaScript 和 C++ 類似。

* **合約（Contract）**：合約是 Solidity 的基本結構單元，類似於面向對象編程中的類（Class）。一個合約可以包含狀態變量、函數、事件和其他子合約等。每個合約都是一個獨立的程式碼單位，可以被部署到區塊鏈上運行，並與其他合約或帳戶交互。

* **函數修飾符**：Solidity 提供了多種函數修飾符，用來控制函數的行為和可見性。例如，public 修飾符使函數可以被合約內外部訪問，而 private 則限制函數只能在合約內部訪問。view 和 pure 修飾符用來指示函數是否會改變區塊鏈的狀態或僅進行計算。

* **事件（Events）**：事件是 Solidity 中的一個特殊功能，用於在區塊鏈上記錄操作或狀態變更，並通知外部監聽器（如 DApp 前端）。事件可以被外部應用程式監聽並用作觸發操作的依據。這是一個有效的方式來跟蹤合約中的重要操作，如資產轉移、狀態更新等。

* **繼承（Inheritance）**：Solidity 支持合約的繼承，這使得開發者可以創建可重用的合約結構。例如，可以創建一個基本合約，其中包含通用的功能和變量，然後其他合約可以繼承這個基礎合約並添加額外的功能或覆蓋現有功能。這種繼承特性大大提高了代碼的可重用性和模組化設計。

* **庫（Libraries）**：Solidity 支持庫的使用，庫是一些可以被多個合約共享的程式碼片段。庫中的函數可以直接在合約中調用，而不需要通過繼承。這種設計有助於減少合約的代碼重複和增強代碼的組織結構。

* **錯誤處理**：Solidity 提供了多種方式來處理錯誤，如 require、assert 和 revert。這些語句允許開發者在代碼中指定條件檢查，如果條件不滿足，則終止執行並回滾狀態變更。這對於保證智能合約的安全性和正確性非常重要。

##  Solidity 基本結構
### 智能合約範例
這是一個簡單的 Solidity 智能合約範例，展示了如何使用 Solidity 編寫和管理合約中的狀態變量及其操作：
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleStorage {
    uint256 storedData;

    function set(uint256 x) public {
        storedData = x;
    }

    function get() public view returns (uint256) {
        return storedData;
    }
}
```
在這個範例中，我們定義了一個名為 SimpleStorage 的智能合約。這個合約的功能十分簡單：它允許用戶存儲一個數據（整數）並隨後檢索該數據。

1. 狀態變量：
uint256 storedData;：這是一個狀態變量，用於存儲數據。狀態變量存儲在區塊鏈的永久存儲（Storage）中，這意味著它的值在合約執行期間保留不變，除非被明確修改。

1. set 函數：
function set(uint256 x) public { storedData = x; }：這個函數接受一個參數 x，並將其值存儲在 storedData 中。set 函數是一個公共函數，這意味著它可以由任何人調用，從而修改合約的狀態（即 storedData 的值）。

1. get 函數：
function get() public view returns (uint256) { return storedData; }：這個函數返回當前存儲在 storedData 中的值。get 函數被標記為 view，這意味著它不會改變合約的狀態，只是讀取狀態。它返回一個 uint256 類型的值。

### 關鍵詞解釋
以下是合約中一些重要關鍵詞的詳細解釋：

1. pragma solidity ^0.8.0;：
pragma 指令用來指定 Solidity 編譯器的版本。這行代碼表明該合約應該使用編譯器版本 0.8.0 或更高，但不包括 0.9.0。使用這種版本控制可以防止在不同編譯器版本下運行時出現意外行為或錯誤。
^ 符號表示允許使用指定版本及其後續的小版本更新。這樣的規範可以保證代碼的穩定性，避免不兼容的語法和特性帶來的風險。

1. contract：
contract 關鍵詞用於定義一個智能合約。合約是 Solidity 中的基本單位，類似於面向對象編程中的類（class）。一個合約可以包含狀態變量、函數、事件、修飾符等。所有的邏輯和數據都包含在合約的上下文中。

1. uint256：
uint256 是 Solidity 中最常用的數字類型之一，表示無符號的 256 位整數。無符號表示這個數字類型只能表示非負數，範圍從 0 到 2^256-1。
使用 uint256（而不是 int 或其他類型）是因為在區塊鏈上，確保數據安全性和節省存儲空間至關重要。uint256 通常是 Solidity 開發中處理數字計算時的首選。

1. public：
public 是一個可見性修飾符，用於修飾合約中的變量和函數。當函數或變量被標記為 public 時，它們可以被任何人或其他合約訪問和調用。
在函數中，public 允許外部用戶調用該函數來執行特定操作（如 set 函數）。在狀態變量中，public 允許外部合約和用戶直接讀取該變量的值，並且 Solidity 會自動為這些變量生成一個 "getter" 函數。

1. view：
view 是一個函數修飾符，表示該函數不會修改區塊鏈的狀態，只能讀取數據。這意味著函數只能檢索和返回數據，而不會執行任何寫入操作。
使用 view 函數時，不需要花費任何以太幣（ETH）來執行，因為它不會觸發任何狀態變更。這種函數非常適合用來檢查合約的當前狀態或計算不需要修改狀態的值。

## 智能合約的函數和變量
### 狀態變量和函數
**狀態變量**：這些變量存儲在區塊鏈上，每個合約可以有多個狀態變量，用於持久化數據。它們類似於其他編程語言中的類屬性。
**函數**：函數是合約的行為單位，可以用來改變狀態變量或執行計算。每個函數可以有可見性修飾符（如 public、private）來控制訪問權限。

### 可見性修飾符
**public**：表示函數或變量是公開的，任何人或合約都可以訪問。這是默認的修飾符，但為了明確通常還是會顯式聲明。
**private**：僅限於合約內部訪問，其他合約和外部用戶無法訪問。
**internal**：類似於 private，但允許繼承的合約訪問。這是 Solidity 中的默認可見性。
**external**：僅允許外部訪問，不能被合約內部其他函數調用。
### 狀態修飾符
**view**：表示函數不會修改合約的狀態，只能讀取狀態變量。
**pure**：表示函數不會訪問或修改合約的狀態，通常用於不依賴合約狀態的計算。

## 編譯和部署
### 在 Remix 中編譯和部署
Remix 是開始編寫和測試 Solidity 智能合約的便捷工具：
1. 打開 Remix IDE。
1. 創建一個新的文件，粘貼 Solidity 代碼。
1. 選擇適當的編譯器版本，然後點擊 "Compile" 按鈕來編譯合約。
1. 部署合約：選擇 "JavaScript VM" 作為運行環境，然後點擊 "Deploy" 按鈕。

### 在本地環境中編譯和部署
使用 Truffle 和 Ganache 進行本地開發和測試：
    * 使用 Truffle 創建一個新項目：truffle init。
    * 編寫和編譯智能合約：truffle compile。
    * 使用 Ganache 啟動本地區塊鏈：ganache-cli。
    * 部署合約到本地區塊鏈：truffle migrate。

## 合約互動和測試
### 使用 Web3.js
Web3.js 是一個 JavaScript 庫，用於與以太坊區塊鏈交互。它提供了與智能合約互動的 API，包括調用合約的函數、檢查帳戶餘額、發送交易等。

範例代碼：
```javascript=
const Web3 = require('web3');
const web3 = new Web3('http://localhost:8545');
const contractAddress = '0x...'; // 部署後的合約地址
const abi = [ /* ABI */ ]; // 合約的 ABI
const contract = new web3.eth.Contract(abi, contractAddress);

// 調用合約函數
contract.methods.get().call().then(console.log);

```

### 測試智能合約
測試智能合約的過程通常包括以下步驟：
1. 單元測試：測試合約中每個函數的獨立功能。Truffle 提供了 Mocha 測試框架來撰寫單元測試。
1. 集成測試：測試合約的整體功能和各個部分的互動。
1. 安全測試：測試合約是否存在潛在的安全漏洞，如重入攻擊和溢出問題。

## 安全考量
### 常見的安全漏洞
* 重入攻擊：攻擊者利用合約在完成狀態變更之前重複進行外部調用。防止方法包括使用 checks-effects-interactions 模式和 ReentrancyGuard 合約。
* 溢出和下溢：由於整數的限制，操作可能超出其範圍。可以使用 SafeMath 庫來防止這類問題。
* 隨機性：區塊哈希和時間戳並不是真正的隨機數，不應用作隨機數生成器。
### 安全開發實踐
* 代碼審計：聘請第三方進行代碼審計，找出潛在的安全漏洞。
* 使用安全庫：使用經過驗證的開源庫，如 OpenZeppelin，來提高合約的安全性。
* 限制使用低級函數：避免使用 call、delegatecall 等低級函數，因為它們可能引入潛在的安全風險。