
在這篇文章中,我們會從 0 基礎開始,逐步介紹智能合約的概念、Solidity 語法,以及如何部署你的第一個智能合約。 適合對區塊鏈開發有興趣但還沒有 Solidity 經驗的開發者!
Solidity 是一種面向智能合約的編程語言,專門用於在 Ethereum(以太坊) 和其他 EVM(Ethereum Virtual Machine)兼容的區塊鏈上開發去中心化應用(DApps)。
它受到了 JavaScript、Python 和 C++ 的影響,語法簡潔且適合初學者入門。
智能合約(Smart Contract)是一種 自動執行的合約,當滿足特定條件時,程式會自動執行約定的內容,無需中間人。
例如:
當然不僅僅是這種與金融有關的產業,任何可以去中心化的應用都可以採用智能合約,好比說有些產業為了更好的紀錄各個生產鏈上下的資料,便可以用智能合約,公正無私的紀錄資料。
目前最簡單的方式是使用線上編輯器 **Remix IDE**:
如果你想在本地開發,可以使用:
另外你會需要一個加密貨幣錢包來與合約互動(放心,在本教學中不會花到任何一毛錢)


1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 // 儲存訊息的變數
6 string public message;
7
8 // 建構函式,合約部署時會自動執行
9 constructor() {
10 message = "Hello, Solidity!";
11 }
12
13 // 設定新的訊息
14 function setMessage(string memory _newMessage) public {
15 message = _newMessage;
16 }
17}
181// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
31// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5
6}
71// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 // 儲存訊息的變數
6 string public message;
7
8 // 建構函式,合約部署時會自動執行
9 constructor() {
10 message = "Hello, Solidity!";
11 }
12}
131// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 // 儲存訊息的變數
6 string public message;
7
8 // 建構函式,合約部署時會自動執行
9 constructor() {
10 message = "Hello, Solidity!";
11 }
12
13 // 設定新的訊息
14 function setMessage(string memory _newMessage) public {
15 message = _newMessage;
16 }
17}
18







1bool public myBool = true;
21uint256 public myUint = 123;
2int public myInt = -123;
31address public myAddress = 0x1234567890123456789012345678901234567890;
2myAddress.balance // 取得該錢包目前剩餘的存款1string public myString = "Hello";
2
3myString.length; //error
4myString[0]; //error1uint[] public numbers; // 動態大小的陣列
2uint[5] public fixedSizeNumbers; // 大小固定為 5 的陣列
3
4numbers.push(123);1struct Person {
2 string name;
3 uint age;
4}
5
6Person public alice = Person("Alice", 30);
71enum Status { Pending, Shipped, Completed, Rejected }
2
3Status public currentStatus;
4在 Solidity 中的變數根據其宣告位置可以分為 三大類:
定義:
定義:
定義:
常見 Global 變數:
msg.sender呼叫合約的發送者地址
msg.value交易發送的 ETH 數量
block.timestamp當前區塊的時間戳
block.number當前區塊號
gasleft()剩餘 Gas 數量
tx.origin原始交易發起者
address(this).balance合約帳戶的 ETH 餘額
除了變數的類型(local、state、global),Solidity 也提供了 三種資料儲存方式
適用於陣列 (array)、結構 (struct)、字串 (string)、映射 (mapping) 等參考型別 (Reference Types)。
storage表示這個資料永遠儲存於區塊鏈上頭,**改變會消耗 Gas,**剛剛提到的 State 變數就屬於這一類
memory表示這個資料只是短暫出現在記憶體上,**交易結束後消失,**剛剛提到的 Local 變數就屬於這一類
calldata只讀記憶體(交易輸入)外部函式參數專用,不允許修改external 函式的輸入參數
眼尖的同學應該發現,如果你修改了區塊鏈的資料是要花錢的!
不僅如此,在以太坊上,每當你執行一筆交易(例如呼叫智能合約、轉帳等),都需要向網路支付一筆 交易手續費。
這筆費用的計算方式並不是固定的,基本上對區塊鏈做越多操作會越貴。
而以太坊使用一種叫做 Gas 的單位來衡量交易在網路上執行所需的運算資源。
舉例:
在智能合約中,大多數操作都會消耗 Gas,尤其是對區塊鏈「狀態 (state)」造成變化的操作。
下面列出一些常見「高 Gas 消耗」的動作:
storage 做 寫入 或 更新。storage 更新類似,只要有寫入都會消耗較多 Gas。push() 也會往合約的永續狀態中寫入新元素。call、delegatecall 或 interface 呼叫其他合約函式,需要額外的 Gas 來執行被呼叫的邏輯。emit Event 會產生一筆鏈上日誌 (Log),也會消耗 Gas。(一般比寫 storage 便宜,但還是要考量頻繁記錄事件帶來的額外開銷)storage、呼叫其他合約、更複雜的邏輯,Gas 量會隨迴圈次數成倍增加。view 或 pure 函式,不會執行交易,也不會在鏈上留下任何記錄,因此不需支付額外 Gas。view 或 pure 函式,仍然要計入該交易整體的執行成本——只是相對來說,只有計算的成本,但沒有對 storage 寫入的成本。memory 或 calldata 中的操作只會產生交易執行中計算的 Gas,通常比修改 storage 要便宜許多,因為它不需要永久寫入區塊鏈。注意: 雖然「讀取數據」或「單純做計算」比寫入便宜很多,但「執行交易」本身仍有基礎費用,因為必須產生交易並在鏈上被礦工/驗證者驗證。
storage 的寫入
memory、calldata 中處理就盡量不要寫進 storage。memory 中計算好,再一次性寫回 storage,或只在需要時更新 storage。mapping 在查詢方面通常比 array 高效(尤其針對索引操作);storage 寫入便宜且可以提供鏈上日誌。optimizer(如設置 runs = 200 或其他數值)。1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 string public result = "";
6
7 constructor() {
8 int256 val = 1;
9 if (val == 0) {
10 result = "Value is zero";
11 } else if (val == 1) {
12 result = "Value is one";
13 } else {
14 result = "Value is something else";
15 }
16 }
17}
181// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 uint public sum = 0;
6
7 constructor() {
8 uint max = 100;
9 for (uint i = 0; i < max; i++) {
10 sum += i;
11 }
12 }
13}
141// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 uint public sum = 0;
6
7 constructor() {
8 uint max = 100;
9 uint i = 0;
10 while (i < max) {
11 sum += i;
12 i ++;
13 }
14 }
15}
16函數就像是一個魔法盒子,我們可以把一系列的指令放進這個盒子裡,替這個盒子取一個名字
然後以後只要叫這個名字,就可以讓這些指令執行。
在 Solidity 中為了更方便使用者開發,我可以替函式加上一些設定
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 uint public count;
6
7 // 這是一個 public 函式
8 function increment() public {
9 count += 1;
10 }
11}
121// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 uint public count;
6
7 // 外部無法直接訪問該變數
8 // 請注意!該資料仍然公開於區塊鏈上頭,仍然可以爬取到該變數
9 uint private innerCount;
10
11 // 這是一個 public 函式
12 function increment() public {
13 _increment(); // ✅ 合約內部可存取
14 }
15
16 // 這是一個 private 函式,習慣命名上最前面加上 _ 來表示這是 private
17 function _increment() private {
18 count += 1;
19 }
20}
211// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract HelloWorld {
5 uint public count;
6
7 // 這是一個 public 函式
8 function increment() public {
9 this.externalIncrement(); // 內部調用必須透過 `this`
10 }
11
12 // 這是一個 external 函式
13 function externalIncrement() external {
14 count += 1;
15 }
16}
171// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract ViewExample {
5 uint public number = 42; // 狀態變數
6
7 // 這個函式只是讀取狀態變數,並沒有改變它
8 function getNumber() public view returns (uint) {
9 return number;
10 }
11}
121// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract PureExample {
5 // 這個函式純粹進行計算,沒有存取合約內的狀態變數
6 function add(uint a, uint b) public pure returns (uint) {
7 return a + b;
8 }
9}
101// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract PayableExample {
5 uint public balance;
6
7 // 這個函式允許用戶發送 ETH 到合約
8 function deposit() public payable {
9 balance += msg.value; // msg.value 是發送的 ETH 數量
10 }
11
12 // 這個函式回傳合約內的 ETH 餘額
13 function getBalance() public view returns (uint) {
14 return address(this).balance;
15 }
16}
171// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.0;
3
4contract PayableExample {
5 uint public balance;
6
7 function withdraw(uint _amount) public {
8 // _amount 須小於 balance 的金額,否則報錯
9 require(_amount < balance, "合約中資金不足");
10 balance -= _amount;
11 }
12
13}
141function doSomething() public {
2 if (someConditionNotMet()) {
3 revert("Condition not met");
4 }
5 // ...
6}
71function testAssert(uint _x) public pure {
2 assert(_x != 0); // _x 絕對不能為 0
3}
4