Solidity 入門與開發環境
學習目標
- 理解智能合約的本質與核心特性
- 掌握合約在以太坊上的運行原理(Transaction + EVM)
- 認識 Solidity 語言特點與開發工具鏈
- 編寫並部署第一個智能合約
前置知識
- 瞭解區塊鏈基本概念(區塊、交易、共識)
- 瞭解以太坊賬戶模型(EOA 與合約賬戶)
- 基本編程經驗(任意語言均可)
一、智能合約的根本性質
1.1 本質是契約(合同)
智能合約的英文是 Smart Contract,重點在 Contract(合同/契約)。它不僅僅是一段程序,更是一種跨組織、跨個體的信任關係的數字化表達。
傳統合同依賴法律體系和中介機構來保障執行,而智能合約依賴區塊鏈網絡和代碼邏輯來保障執行——Code is Law。
1.2 開發流程:定約、修約、廢約
智能合約的生命週期可以類比傳統合同:
| 階段 | 傳統合同 | 智能合約 |
|---|---|---|
| 定約 | 起草、簽署合同 | 編寫、部署合約(deploy) |
| 修約 | 簽訂補充協議 | 部署新版本合約(代理模式) |
| 廢約 | 終止合同 | 調用 selfdestruct 或停用邏輯 |
1.3 不可變性(Immutable)與開源
智能合約一旦部署到區塊鏈上,其代碼不可被修改——這就是不可變性。這意味著:
- 部署前必須經過充分的測試和審計
- 修改邏輯只能通過部署新合約或使用代理模式(Proxy Pattern)
- 合約代碼通常會在 Etherscan 等區塊瀏覽器上開源驗證,任何人都可以閱讀和審計
1.4 DeFi 說明商業契約,DAO 說明社會契約
智能合約的應用場景完美印證了它的「契約」本質:
- DeFi(去中心化金融):借貸、交易、保險等金融合約的數字化,體現的是商業契約關係。例如 Uniswap 的交易合約、Aave 的借貸合約。
- DAO(去中心化自治組織):投票、治理、提案等組織規則的數字化,體現的是社會契約關係。例如 MakerDAO 的治理合約。
二、合約運行原理
2.1 節點的雙重角色
以太坊網絡中的每個節點承擔兩個核心職責:
- 一側處理交易(Transaction):接收、驗證、廣播交易
- 一側運行 EVM(以太坊虛擬機):執行合約代碼,更新狀態
2.2 順序執行,無併發
這是理解智能合約的關鍵:
- Block 順序生成:區塊按照固定的時間間隔依次產生
- Transaction 順序執行:一個區塊內的交易按照固定順序逐個執行
- 沒有併發訪問:不存在多線程、不存在競態條件(Race Condition)
這與傳統後端開發截然不同——你不需要考慮鎖、信號量等併發控制問題,但也意味著性能受限於鏈的吞吐量。
2.3 Transaction 觸發 EVM 執行
合約的執行永遠是由一筆 Transaction 觸發的,理解 Transaction 的結構至關重要:
| 字段 | 說明 |
|---|---|
| from | 發起者地址(EOA 賬戶) |
| to | 目標地址(合約地址,或為空表示部署新合約) |
| value | 轉賬的 ETH 數量(單位 wei) |
| calldata | 調用數據(函數選擇器 + 參數編碼) |
關鍵理解:合約執行的背後是 Transaction 和 EVM。每次調用合約函數,本質上都是發送一筆交易,由 EVM 解析 calldata 並執行對應的函數邏輯。讀取操作(view/pure 函數)不需要發送交易,可以在本地節點直接執行。
三、Solidity 語言介紹
Solidity 是以太坊智能合約的主流開發語言,具有以下特點:
- 強類型:所有變量必須聲明類型,編譯時檢查類型安全
- 面向對象:支持合約繼承、接口、抽象合約
- 多重繼承:一個合約可以同時繼承多個父合約(採用 C3 線性化解決菱形繼承問題)
- 語法參照 C++:大括號語法、分號結尾,對有 C/C++/Java/JavaScript 經驗的開發者友好
- 專為 EVM 設計:編譯為 EVM 字節碼,運行在以太坊虛擬機上
四、開發環境與工具鏈
4.1 solc 編譯器
solc 是 Solidity 的官方編譯器,將 .sol 源文件編譯為 EVM 字節碼和 ABI(Application Binary Interface)。
# 安裝(macOS)
brew install solidity
# 編譯合約
solc --bin --abi MyContract.sol
4.2 Remix IDE
Remix 是最適合入門的智能合約開發工具:
- 在線版:直接在瀏覽器中使用,無需安裝
- 桌面版:Remix Desktop,提供離線開發能力
- 內置編譯器、部署工具、調試器
- 支持連接 MetaMask 部署到真實網絡
推薦入門階段使用 Remix,後續項目開發再遷移到框架。
4.3 開發框架
| 框架 | 語言 | 特點 |
|---|---|---|
| Hardhat | JavaScript/TypeScript | 當前主流,插件生態豐富,內置 Hardhat Network |
| Truffle | JavaScript | 老牌框架,社區成熟,逐漸被 Hardhat 取代 |
| Foundry | Solidity | 用 Solidity 寫測試,編譯速度極快 |
4.4 SDK(前端交互庫)
| 庫 | 特點 |
|---|---|
| ethers.js | 輕量、模塊化,當前推薦首選 |
| web3.js | 老牌庫,API 豐富,社區龐大 |
| viem | TypeScript 優先,性能優異,新興選擇 |
五、第一個合約:NumberStorage
下面編寫一個最簡單的智能合約,實現數字的存儲和讀取:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
contract NumberStorage {
uint public x;
function setX(uint px) public {
x = px;
}
function getX() public view returns(uint) {
return x;
}
function add(uint a, uint b) private pure returns(uint) {
return a + b;
}
}
代碼逐行解析
第 1 行:許可證聲明
// SPDX-License-Identifier: MIT
SPDX(Software Package Data Exchange)許可證標識符,聲明代碼的開源協議。從 Solidity 0.6.8 起,編譯器會對缺少此聲明的文件發出警告。常用值包括 MIT、GPL-3.0、UNLICENSED(不開源)。
第 2 行:版本聲明
pragma solidity >=0.8.2 <0.9.0;
指定編譯器版本範圍。pragma 是編譯器指令,告訴編譯器只有在版本滿足條件時才編譯此文件。
第 4 行:合約定義
contract NumberStorage {
contract 關鍵字定義一個合約,類似面向對象語言中的 class。
第 5 行:狀態變量
uint public x;
uint 是無符號整型(等同於 uint256),public 自動生成一個同名的 getter 函數。狀態變量存儲在區塊鏈上,是合約的持久化數據。
第 7-9 行:寫入函數
function setX(uint px) public {
x = px;
}
public 表示外部和內部都可以調用。修改狀態變量需要發送交易(消耗 Gas)。
第 11-13 行:讀取函數
function getX() public view returns(uint) {
return x;
}
view 表示只讀取狀態,不修改。調用 view 函數不消耗 Gas(本地執行)。
第 15-17 行:純計算函數
function add(uint a, uint b) private pure returns(uint) {
return a + b;
}
private:僅合約內部可調用pure:既不讀取也不修改狀態,純粹的計算函數
六、源文件結構
一個 Solidity 源文件的標準結構:
// 1. 許可證聲明
// SPDX-License-Identifier: MIT
// 2. 版本聲明
pragma solidity ^0.8.0;
// 3. import 語句(如有)
import "./OtherContract.sol";
// 4. 合約定義
contract MyContract {
// ...
}
七、版本問題
7.1 語義化版本號(Semantic Versioning)
Solidity 遵循語義化版本號:MAJOR.MINOR.PATCH
- MAJOR:不兼容的重大變更
- MINOR:向後兼容的功能新增
- PATCH:向後兼容的問題修復
7.2 版本聲明的寫法
pragma solidity ^0.8.0; // >=0.8.0 且 <0.9.0
pragma solidity >=0.8.2 <0.9.0; // 精確範圍
pragma solidity 0.8.20; // 僅限指定版本
7.3 重要版本變化
| 版本 | 重要變化 |
|---|---|
| 0.8.0 | 整型溢出默認拋出異常(不再需要 SafeMath) |
| 0.8.0 | ABIEncoderV2 成為默認編碼器 |
| 0.5.0 | 函數可見性必須顯式聲明(不再有默認的 public) |
| 0.6.0 | fallback 和 receive 替代匿名回退函數 |
特別注意 0.8.0 的整型溢出變化:低於 0.8.0 的版本中,
uint8(255) + 1的結果是0(取模運算),這是大量安全漏洞的根源。0.8.0 起默認會拋出異常,極大提升了安全性。
八、合約基本結構
一個完整的合約可能包含以下元素:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyContract {
// 1. 狀態變量 - 永久存儲在區塊鏈上
uint public count;
address public owner;
// 2. 事件 - 用於記錄日誌,前端可以監聽
event CountChanged(uint newCount);
// 3. 修飾符 - 函數執行的前置條件檢查
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// 4. 構造函數 - 合約部署時執行一次
constructor() {
owner = msg.sender;
}
// 5. 函數 - 合約的行為邏輯
function increment() public onlyOwner {
count += 1;
emit CountChanged(count);
}
}
| 元素 | 說明 |
|---|---|
| 狀態變量 | 合約的持久化數據,存儲在鏈上 |
| 函數 | 合約的行為,可讀取或修改狀態 |
| 修飾符(modifier) | 可復用的函數前置/後置檢查邏輯 |
| 事件(event) | 鏈上日誌,便於前端監聽和索引 |
| 構造函數(constructor) | 部署時執行一次,用於初始化 |
小結
本節我們瞭解了智能合約的本質是數字化的契約關係,理解了 Transaction + EVM 的執行模型,認識了 Solidity 語言和開發工具鏈,並編寫了第一個智能合約。下一節我們將深入學習 Solidity 的值類型系統。
本文為 Web3 區塊鏈系列第 3 篇,共 12 篇。
主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/7486
