智能合約語言
以太坊的一大優勢在於,可以使用對開發者相對友善的語言來編寫智能合約。如果你對 Python 或任何大括號語言 (curly-bracket language) (opens in a new tab) 有經驗,你可以找到語法熟悉的語言。
兩個最活躍且持續維護的語言是:
- Solidity
- Vyper
Remix IDE 提供了一個全面的開發環境,用於建立和測試 Solidity 與 Vyper 合約。嘗試使用瀏覽器中的 Remix IDE (opens in a new tab) 開始編寫程式碼。
更有經驗的開發者可能也會想使用 Yul(一種用於以太坊虛擬機 (EVM) 的中介語言),或是 Yul 的擴充語言 Yul+。
如果你很好奇,並且喜歡協助測試仍在積極開發中的新語言,你可以嘗試使用 Fe,這是一種目前仍處於起步階段的新興智能合約語言。
先決條件
具備程式語言(特別是 JavaScript 或 Python)的基礎知識,有助於你理解智能合約語言之間的差異。我們也建議你在深入探討語言比較之前,先了解智能合約的概念。智能合約簡介。
Solidity
- 用於實作智能合約的物件導向高階語言。
- 受 C++ 影響最深的大括號語言。
- 靜態型別(變數的型別在編譯時就已確定)。
- 支援:
- 繼承(你可以擴充其他合約)。
- 函式庫(你可以建立可重複使用的程式碼,並從不同的合約中呼叫——就像其他物件導向程式語言中靜態類別裡的靜態函式)。
- 複雜的使用者自訂型別。
重要連結
- 文件 (opens in a new tab)
- Solidity 語言入口網站 (opens in a new tab)
- Solidity 範例 (opens in a new tab)
- GitHub (opens in a new tab)
- Solidity Gitter 聊天室 (opens in a new tab)(橋接至 Solidity Matrix 聊天室 (opens in a new tab))
- 備忘單 (opens in a new tab)
- Solidity 部落格 (opens in a new tab)
- Solidity 推特 (opens in a new tab)
範例合約
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.7.0;
contract Coin {
// 「public」關鍵字讓變數
// 可以從其他合約存取
address public minter;
mapping (address => uint) public balances;
// 事件允許客戶端對特定的
// 您宣告的合約變更做出反應
event Sent(address from, address to, uint amount);
// 建構子程式碼只會在合約
// 建立時執行
constructor() {
minter = msg.sender;
}
// 將一定數量的新建代幣發送到一個地址
// 只能由合約建立者呼叫
function mint(address receiver, uint amount) public {
require(msg.sender == minter);
require(amount < 1e60);
balances[receiver] += amount;
}
// 發送一定數量的現有代幣
// 從任何呼叫者到一個地址
function send(address receiver, uint amount) public {
require(amount <= balances[msg.sender], "Insufficient balance.");
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Sent(msg.sender, receiver, amount);
}
}
這個範例應該能讓你對 Solidity 合約語法有初步的了解。如需函式與變數的更詳細說明,請參閱文件 (opens in a new tab)。
Vyper
- 具備 Python 風格的程式語言
- 強型別
- 編譯器程式碼精簡且易於理解
- 高效的位元組碼生成
- 刻意減少功能(相較於 Solidity),旨在讓合約更安全且更容易稽核。Vyper 不支援:
- 修飾符 (Modifiers)
- 繼承
- 行內組合語言 (Inline assembly)
- 函式多載 (Function overloading)
- 運算子多載 (Operator overloading)
- 遞迴呼叫
- 無限長度迴圈
- 二進位定點數 (Binary fixed points)
如需更多資訊,請閱讀 Vyper 的設計理念 (opens in a new tab)。
重要連結
- 文件 (opens in a new tab)
- Vyper 範例 (opens in a new tab)
- 更多 Vyper 範例 (opens in a new tab)
- GitHub (opens in a new tab)
- Vyper 社群 Discord 聊天室 (opens in a new tab)
- 備忘單 (opens in a new tab)
- Vyper 的智能合約開發框架與工具
- VyperPunk - 學習保護與駭入 Vyper 智能合約 (opens in a new tab)
- Vyper 開發中心 (opens in a new tab)
- Vyper 精選智能合約範例 (opens in a new tab)
- Awesome Vyper 精選資源 (opens in a new tab)
範例
# 公開拍賣
# 拍賣參數
# 受益人從最高出價者那裡收到資金
beneficiary: public(address)
auctionStart: public(uint256)
auctionEnd: public(uint256)
# 拍賣的當前狀態
highestBidder: public(address)
highestBid: public(uint256)
# 在結束時設為 true,不允許任何更改
ended: public(bool)
# 追蹤已退款的出價,以便我們可以遵循提款模式
pendingReturns: public(HashMap[address, uint256])
# 建立一個簡單的拍賣,競標時間為 `_bidding_time`
# 秒,代表
# 受益人地址 `_beneficiary`。
@external
def __init__(_beneficiary: address, _bidding_time: uint256):
self.beneficiary = _beneficiary
self.auctionStart = block.timestamp
self.auctionEnd = self.auctionStart + _bidding_time
# 使用與此交易一起發送的
# 價值對拍賣進行出價。
# 只有在未贏得拍賣的情況下
# 才會退還該價值。
@external
@payable
def bid():
# 檢查競標期是否已結束。
assert block.timestamp < self.auctionEnd
# 檢查出價是否夠高
assert msg.value > self.highestBid
# 追蹤前一個最高出價者的退款
self.pendingReturns[self.highestBidder] += self.highestBid
# 追蹤新的最高出價
self.highestBidder = msg.sender
self.highestBid = msg.value
# 提取先前退還的出價。這裡使用提款模式
# 是為了避免安全問題。如果退款直接
# 作為 bid() 的一部分發送,惡意出價合約可能會阻擋
# 這些退款,從而阻擋新的更高出價進入。
@external
def withdraw():
pending_amount: uint256 = self.pendingReturns[msg.sender]
self.pendingReturns[msg.sender] = 0
send(msg.sender, pending_amount)
# 結束拍賣並將最高出價發送
# 給受益人。
@external
def endAuction():
# 這是一個很好的準則,將與
# 其他合約互動的函式(即它們呼叫函式或發送以太幣)
# 結構化為三個階段:
# 1. 檢查條件
# 2. 執行動作(可能會改變條件)
# 3. 與其他合約互動
# 如果這些階段混合在一起,另一個合約可能會回呼
# 到當前合約中並修改狀態,或導致
# 效果(以太幣支付)被執行多次。
# 如果內部呼叫的函式包含與外部
# 合約的互動,它們也必須被視為與
# 外部合約的互動。
# 1. 條件
# 檢查是否已達到拍賣結束時間
assert block.timestamp >= self.auctionEnd
# 檢查此函式是否已被呼叫
assert not self.ended
# 2. 效果
self.ended = True
# 3. 互動
send(self.beneficiary, self.highestBid)
這個範例應該能讓你對 Vyper 合約語法有初步的了解。如需函式與變數的更詳細說明,請參閱文件 (opens in a new tab)。
Yul 與 Yul+
如果你是以太坊的新手,且尚未用智能合約語言編寫過任何程式碼,我們建議從 Solidity 或 Vyper 開始。只有在你熟悉智能合約安全最佳實務以及使用 EVM 的具體細節後,再考慮研究 Yul 或 Yul+。
Yul
- 以太坊的中介語言。
- 支援 EVM 與 Ewasm (opens in a new tab)(一種以太坊風格的 WebAssembly),其設計目的是成為這兩個平台皆可使用的共同基礎。
- 適合用於高階最佳化階段,能同時為 EVM 與 Ewasm 平台帶來同等效益。
Yul+
- Yul 的低階、高效率擴充語言。
- 最初是為樂觀 Rollup 合約所設計。
- Yul+ 可以被視為 Yul 的實驗性升級提案,為其新增了多項功能。
重要連結
範例合約
以下簡單範例實作了一個次方函式。可以使用 solc --strict-assembly --bin input.yul 進行編譯。該範例應儲存在 input.yul 檔案中。
{
function power(base, exponent) -> result
{
switch exponent
case 0 { result := 1 }
case 1 { result := base }
default
{
result := power(mul(base, base), div(exponent, 2))
if mod(exponent, 2) { result := mul(base, result) }
}
}
let res := power(calldataload(0), calldataload(32))
mstore(0, res)
return(0, 32)
}
如果你對智能合約已經非常有經驗,可以在這裡 (opens in a new tab)找到以 Yul 實作的完整 ERC-20 範例。
Fe
- 用於以太坊虛擬機 (EVM) 的靜態型別語言。
- 受到 Python 與 Rust 的啟發。
- 旨在易於學習——即使是對以太坊生態系不熟悉的開發者也能輕鬆上手。
- Fe 的開發仍處於早期階段,該語言於 2021 年 1 月發布了 Alpha 版本。
重要連結
- GitHub (opens in a new tab)
- Fe 發布公告 (opens in a new tab)
- Fe 2021 路線圖 (opens in a new tab)
- Fe Discord 聊天室 (opens in a new tab)
- Fe 推特 (opens in a new tab)
範例合約
以下是以 Fe 實作的簡單合約。
type BookMsg = bytes[100]
contract GuestBook:
pub guest_book: map<address, BookMsg>
event Signed:
book_msg: BookMsg
pub def sign(book_msg: BookMsg):
self.guest_book[msg.sender] = book_msg
emit Signed(book_msg=book_msg)
pub def get_msg(addr: address) -> BookMsg:
return self.guest_book[addr].to_mem()
如何選擇
就如同其他程式語言一樣,這主要取決於為合適的工作選擇合適的工具,以及個人的偏好。
如果你還沒有嘗試過任何一種語言,以下是一些可以考慮的因素:
Solidity 有什麼優點?
- 如果你是初學者,市面上有許多教學與學習工具。詳情請見透過寫程式學習章節。
- 具備良好的開發者工具。
- Solidity 擁有龐大的開發者社群,這意味著你通常能很快找到問題的解答。
Vyper 有什麼優點?
- 對於想編寫智能合約的 Python 開發者來說,這是一個很好的入門方式。
- Vyper 的功能較少,非常適合用來快速建立想法的原型。
- Vyper 旨在易於稽核,並盡可能提高人類可讀性。
Yul 與 Yul+ 有什麼優點?
- 簡單且功能強大的低階語言。
- 允許更接近原生的 EVM,這有助於最佳化合約的燃料 (gas) 使用量。
語言比較
關於基本語法、合約生命週期、介面、運算子、資料結構、函式、控制流程等方面的比較,請查看這份 Auditless 備忘單 (opens in a new tab)。