Solidity 智慧合約語言實戰

Solidity 是 Ethereum 生態系中最主流的智慧合約語言。它的語法類似 JavaScript,但由於智慧合約運行在區塊鏈上,有許多獨特的概念。

Solidity 資料型別

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

contract DataTypes {
    // === 基本型別 ===
    bool public isActive;           // true / false
    uint256 public count;           // 無號整數(0 到 2^256-1)
    int256 public balance;          // 有號整數
    address public owner;           // 以太坊地址(20 bytes)
    bytes32 public hash;            // 32 bytes 資料
    string public name;             // 字串
    
    // === 列舉 (Enum) ===
    enum Status { Pending, Active, Inactive, Banned }
    Status public userStatus;
    
    // === 結構 (Struct) ===
    struct User {
        string name;
        uint256 age;
        address wallet;
        bool isVerified;
    }
    
    User public defaultUser;
    
    // === 陣列 ===
    uint256[] public numbers;       // 動態陣列
    uint256[10] public fixedArray;  // 固定大小陣列
    User[] public users;            // 結構陣列
    
    // === 映射 (Mapping) — 最常用的資料結構 ===
    mapping(address => uint256) public balances;      // 地址 → 餘額
    mapping(uint256 => address) public idToAddress;   // ID → 地址
    mapping(address => mapping(uint256 => bool)) public hasClaimed;  // 雙層映射
    
    constructor() {
        owner = msg.sender;  // msg.sender = 呼叫合約的地址
    }
}

函式修飾器

contract FunctionModifiers {
    address public owner;
    bool public paused;
    
    constructor() {
        owner = msg.sender;
    }
    
    // === 可見性修飾器 ===
    function publicFunction() public {}     // 任何人都可以呼叫
    function externalFunction() external {} // 只能從外部呼叫(更省 Gas)
    function internalFunction() internal {} // 只有合約內部可以呼叫
    function privateFunction() private {}   // 只有這個函式內部可以呼叫
    
    // === 狀態修飾器 ===
    function pureFunction() public pure returns (uint256) {
        // pure:不讀取也不修改區塊鏈狀態
        return 42;
    }
    
    function viewFunction() public view returns (address) {
        // view:讀取但不修改區塊鏈狀態
        return owner;
    }
    
    function payableFunction() public payable {
        // payable:可以接收 ETH
    }
    
    // === 自訂修飾器 (Modifier) — 最常用的功能 ===
    modifier onlyOwner() {
        require(msg.sender == owner, "你不是合約擁有者");
        _;  // 繼續執行原函式
    }
    
    modifier whenNotPaused() {
        require(!paused, "合約已暫停");
        _;
    }
    
    function withdraw() public onlyOwner {
        // 只有擁有者可以執行
        payable(owner).transfer(address(this).balance);
    }
    
    function togglePause() public onlyOwner {
        paused = !paused;
    }
}

事件 (Events)

事件是 Solidity 中非常重要的功能。當事件被觸發時,它會被記錄在區塊鏈的日誌中,前端可以監聽這些事件:

contract EventsExample {
    // 定義事件
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Deposit(address indexed user, uint256 amount, uint256 timestamp);
    event UserRegistered(address indexed user, string name);
    
    mapping(address => uint256) public balances;
    
    function deposit() public payable {
        balances[msg.sender] += msg.value;
        
        // 觸發事件
        emit Deposit(msg.sender, msg.value, block.timestamp);
    }
    
    function transfer(address _to, uint256 _amount) public {
        require(balances[msg.sender] >= _amount, "餘額不足");
        
        balances[msg.sender] -= _amount;
        balances[_to] += _amount;
        
        // 觸發轉帳事件
        emit Transfer(msg.sender, _to, _amount);
    }
}

繼承 (Inheritance)

Solidity 支援多重繼承,這讓你可以組合使用 OpenZeppelin 的標準合約:

// 基礎合約
contract Ownable {
    address public owner;
    
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    constructor() {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "New owner is zero address");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

// 繼承 Ownable
contract MyToken is Ownable {
    string public name;
    string public symbol;
    
    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }
    
    function mint(address to, uint256 amount) public onlyOwner {
        // 只有擁有者可以鑄造
    }
}

錯誤處理

contract ErrorHandling {
    // === require(最常用,剩餘 Gas 會退還) ===
    function checkRequire(uint256 _value) public pure returns (uint256) {
        require(_value > 0, "值必須大於 0");
        require(_value < 1000, "值必須小於 1000");
        return _value * 2;
    }
    
    // === revert(手動觸發錯誤) ===
    function checkRevert(uint256 _value) public pure returns (uint256) {
        if (_value == 0) {
            revert("值不能為 0");
        }
        return 100 / _value;
    }
    
    // === assert(用於內部錯誤,不應發生) ===
    function checkAssert(uint256 _value) public pure returns (uint256) {
        uint256 result = _value * 2;
        assert(result >= _value);  // 乘法不應減少數值
        return result;
    }
}

單位與全域變數

contract UnitsGlobals {
    // === 以太幣單位 ===
    uint256 public weiUnit = 1 wei;
    uint256 public gweiUnit = 1 gwei;      // = 10^9 wei
    uint256 public etherUnit = 1 ether;     // = 10^18 wei
    
    // === 時間單位 ===
    uint256 public oneMinute = 1 minutes;
    uint256 public oneHour = 1 hours;
    uint256 public oneDay = 1 days;
    uint256 public oneWeek = 1 weeks;
    
    // === 全域變數 ===
    function globalVariables() public view returns (
        address, uint256, uint256, uint256
    ) {
        return (
            msg.sender,     // 呼叫者的地址
            block.timestamp, // 當前區塊的時間戳
            block.number,    // 當前區塊的編號
            block.chainid    // 當前鏈的 ID(1 = Ethereum 主網)
        );
    }
}

使用 Vibe Coding 寫智慧合約

🔥 【Solidity 詠唱範例】 「請幫我寫一個眾籌 (Crowdfunding) 智慧合約: 1. 設定募資目標(如 10 ETH)與截止時間。 2. 任何人在截止前都可以捐款。 3. 如果截止時達到目標,擁有者可以提領資金。 4. 如果未達目標,捐款者可以退款。 5. 捐款和退款時都要觸發事件。 6. 使用 OpenZeppelin 的 Ownable 進行權限控制。」

本日總結

在本章中,你學到了:

  1. 資料型別:uint、address、string、mapping、struct
  2. 函式修飾器:public/external/internal/private,pure/view/payable
  3. 自訂 Modifier:onlyOwner、whenNotPaused 等
  4. 事件 (Events):紀錄區塊鏈日誌,前端可監聽
  5. 繼承與 OpenZeppelin:組合標準合約
  6. 錯誤處理:require、revert、assert

下一章,我們將發行自己的 ERC-20 代幣!

會員專屬免費教學

本章節為註冊會員專屬的免費開放內容!請先登入或註冊會員,即可立即解鎖閱讀。

立即登入 / 註冊