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 進行權限控制。」
本日總結
在本章中,你學到了:
- ✅ 資料型別:uint、address、string、mapping、struct
- ✅ 函式修飾器:public/external/internal/private,pure/view/payable
- ✅ 自訂 Modifier:onlyOwner、whenNotPaused 等
- ✅ 事件 (Events):紀錄區塊鏈日誌,前端可監聽
- ✅ 繼承與 OpenZeppelin:組合標準合約
- ✅ 錯誤處理:require、revert、assert
下一章,我們將發行自己的 ERC-20 代幣!