跳至主要内容
Change page

智慧型合約語言

最後編輯: @K0ue1(opens in a new tab), 2024年1月18日

以太坊一大好處是,對開發者而言,編寫智慧型合約的語言相對簡單。 如你熟悉 Python 或任何大括號語言(opens in a new tab),會發現其實他們的語法非常相似。

兩種最熱門、最受管理的語言為:

  • Solidity
  • Vyper

經驗更豐富的開發者可能也會想使用 Yul,這是以太坊虛擬機的中階語言,或是使用 Yul 的延伸語言 Yul+。

若你有興趣,且想協助測試還處於大力開發階段的新語言,可以實驗仍在發展初期的新興智慧型合約語言 Fe。

基本資訊

如果已經有編程語言的知識,特別是 JavaScript 或 Python,可以幫助你瞭解智慧型合約語言的差異。 同時,我們建議你在深入理解語言差異之前,先理解智慧型合約的概念。 智慧型合約簡介

Solidity

  • 實作智慧型合約的物件導向高階語言。
  • 深受 C++ 語言影響的大括號語言。
  • 靜態類型(編譯時已知變數類型)。
  • 支援:
    • 繼承(你可以延展其他合約)。
    • 資料庫(你可以建立能從不同的合約調用的可重複使用代碼,如同其他物件導向程式語言中的靜態類別靜態函數)。
    • 複雜的使用者定義類型。

合約範例

1// SPDX-License-Identifier: GPL-3.0
2pragma solidity >= 0.7.0;
3
4contract Coin {
5 // 關鍵字 "public" 使變量可以被其它合約訪問
6 address public minter;
7 mapping (address => uint) public balances;
8
9 // 事件Events允許客戶讀取你聲明的特定合約變更。
10 event Sent(address from, address to, uint amount);
11
12 // Constructor構造代碼僅在合約創建時執行一次。
13 constructor() {
14 minter = msg.sender;
15 }
16
17 // 發送一定數量新創建的代幣到某個地址。
18 // 只有合約創建者可以調用。
19 function mint(address receiver, uint amount) public {
20 require(msg.sender == minter);
21 require(amount < 1e60);
22 balances[receiver] += amount;
23 }
24
25 // 發送一定量已經存在的代幣
26 // 從調用者到任意地址
27 function send(address receiver, uint amount) public {
28 require(amount <= balances[msg.sender], "Insufficient balance.");
29 balances[msg.sender] -= amount;
30 balances[receiver] += amount;
31 emit Sent(msg.sender, receiver, amount);
32 }
33}
顯示全部
複製

這個範例應該能讓你瞭解 Solidity 的合約語法。 關於函數和變數的詳細描述,請參閱文件(opens in a new tab)

Vyper

  • Python 程式語言
  • 強輸入類型
  • 精巧易懂的編譯器代碼
  • 有效率的產生位元組碼
  • 為了提升合約安全性並更容易審核,特意提供比 Solidity 更少功能。 Vyper 不支援:
    • 修飾符
    • 繼承
    • 內嵌組譯碼
    • 函數重載
    • 運算子重載
    • 遞迴調用
    • 無限長度迴圈
    • 二進制定點

如需更多資訊,請參閱 Vyper 原理(opens in a new tab)

範例

1# Open Auction
2
3# Auction params
4# Beneficiary receives money from the highest bidder
5beneficiary: public(address)
6auctionStart: public(uint256)
7auctionEnd: public(uint256)
8
9# Current state of auction
10highestBidder: public(address)
11highestBid: public(uint256)
12
13# Set to true at the end, disallows any change
14ended: public(bool)
15
16# Keep track of refunded bids so we can follow the withdraw pattern
17pendingReturns: public(HashMap[address, uint256])
18
19# Create a simple auction with `_bidding_time`
20# seconds bidding time on behalf of the
21# beneficiary address `_beneficiary`.
22@external
23def __init__(_beneficiary: address, _bidding_time: uint256):
24 self.beneficiary = _beneficiary
25 self.auctionStart = block.timestamp
26 self.auctionEnd = self.auctionStart + _bidding_time
27
28# Bid on the auction with the value sent
29# together with this transaction.
30# The value will only be refunded if the
31# auction is not won.
32@external
33@payable
34def bid():
35 # Check if bidding period is over.
36 assert block.timestamp < self.auctionEnd
37 # Check if bid is high enough
38 assert msg.value > self.highestBid
39 # Track the refund for the previous high bidder
40 self.pendingReturns[self.highestBidder] += self.highestBid
41 # Track new high bid
42 self.highestBidder = msg.sender
43 self.highestBid = msg.value
44
45# Withdraw a previously refunded bid. The withdraw pattern is
46# used here to avoid a security issue. If refunds were directly
47# sent as part of bid(), a malicious bidding contract could block
48# those refunds and thus block new higher bids from coming in.
49@external
50def withdraw():
51 pending_amount: uint256 = self.pendingReturns[msg.sender]
52 self.pendingReturns[msg.sender] = 0
53 send(msg.sender, pending_amount)
54
55# End the auction and send the highest bid
56# to the beneficiary.
57@external
58def endAuction():
59 # It is a good guideline to structure functions that interact
60 # with other contracts (i.e. they call functions or send ether)
61 # into three phases:
62 # 1. checking conditions
63 # 2. performing actions (potentially changing conditions)
64 # 3. interacting with other contracts
65 # If these phases are mixed up, the other contract could call
66 # back into the current contract and modify the state or cause
67 # effects (ether payout) to be performed multiple times.
68 # If functions called internally include interaction with external
69 # contracts, they also have to be considered interaction with
70 # external contracts.
71
72 # 1. Conditions
73 # Check if auction endtime has been reached
74 assert block.timestamp >= self.auctionEnd
75 # Check if this function has already been called
76 assert not self.ended
77
78 # 2. Effects
79 self.ended = True
80
81 # 3. Interaction
82 send(self.beneficiary, self.highestBid)
顯示全部
複製

此範例應該能讓你瞭解 Solidity 的合約語法。 關於函數和變數的詳細描述,請參閱文件(opens in a new tab)

Yul 和 Yul+

如果你是以太坊新手並且尚未使用過智慧型合約語言編碼,建議你從 Solidity 或 Vyper 開始。 只有在你熟悉智慧型合約安全性最佳案例和使用以太坊虛擬機的具體細節後,才可投入 Yul 或 Yul+。

Yul

  • 以太坊的中階語言。
  • 支援以太坊虛擬機eWASM(opens in a new tab),一種以太坊風格的 WebAssembly,目的在於成為兩個平台均可使用的通用工具。
  • 高級最佳化階段的優良目標,能使以太坊虛擬機和 eWASM 平台均等受益。

Yul+

  • Yul 的低階高效延伸語言。
  • 最初設計用於樂觀卷軸合約。
  • Yul+ 可以被視為 Yul 的實驗性升級建議,為其添加新功能。

合約範例

以下簡單範例採用冪函數。 它可以使用 solc --strict-assembly --bin input.yul 編譯。 這個範例應該 儲存在 input.yul 檔案中。

1{
2 function power(base, exponent) -> result
3 {
4 switch exponent
5 case 0 { result := 1 }
6 case 1 { result := base }
7 default
8 {
9 result := power(mul(base, base), div(exponent, 2))
10 if mod(exponent, 2) { result := mul(base, result) }
11 }
12 }
13 let res := power(calldataload(0), calldataload(32))
14 mstore(0, res)
15 return(0, 32)
16}
顯示全部

如果你已經熟悉智慧型合約,可以在此處(opens in a new tab)找到 Yul 言語的完整 ERC20 實作。

Fe

  • 用於以太坊虛擬機 (EVM) 的靜態類型語言。
  • 受 Python 和 Rust 所啟發。
  • 目標是讓以太坊生態系統的新手開發者,都能輕鬆學習這門語言。
  • Fe 還處於早期開發階段,其 Alpha 版本於 2021 年 1 月推出。

合約範例

以下為採用 Fe 的簡單合約。

1type BookMsg = bytes[100]
2
3contract GuestBook:
4 pub guest_book: map<address, BookMsg>
5
6 event Signed:
7 book_msg: BookMsg
8
9 pub def sign(book_msg: BookMsg):
10 self.guest_book[msg.sender] = book_msg
11
12 emit Signed(book_msg=book_msg)
13
14 pub def get_msg(addr: address) -> BookMsg:
15 return self.guest_book[addr].to_mem()
16
顯示全部

如何選擇

與任何其他編程語言一樣,重點在於根據合適的工作與個人偏好選擇正確工具。

如果你還沒有嘗試過任何一種語言,請考慮以下幾點:

Solidity 的優點是什麼?

  • 如果你是初學者,有不少使用教學和學習工具。 在透過編碼學習部分瞭解更多相關資訊。
  • 提供優良的開發者工具。
  • Solidity 擁有龐大的開發者社群,這表示你很可能會很快找到問題的答案。

Vyper 的優點是什麼?

  • 適合想要編寫智慧型合約的 Python 開發者入門。
  • Vyper 的功能較少,因此非常適合快速製作創意原型。
  • Vyper 的目的是容易審查並盡可能提高人類可讀性。

Yul 和 Yul+ 的優點是什麼?

  • 簡單而實用的低階語言。
  • 允許更接近原始以太坊虛擬機,有助於最佳化合約的燃料使用量。

語言比較

如需瞭解基本語法比較、合約生命週期、介面、運算子、數據結構、功能、控制流程等資訊,請參閱由 Auditless 編寫的懶人包(opens in a new tab)

衍生閱讀

這篇文章對你有幫助嗎?