跳至主要內容
Change page

智能合約的剖析

智能合約是在以太坊上某個地址執行的程式。它們由資料和函式組成,可以在收到交易時執行。以下是構成智能合約的要素概述。

先決條件

請確保你已經先閱讀過智能合約。本文件假設你已經熟悉 JavaScript 或 Python 等程式語言。

資料

任何合約資料都必須指派一個位置:storagememory。在智能合約中修改儲存空間的成本很高,因此你需要考慮資料應該存放在哪裡。

儲存空間 (Storage)

永久性資料被稱為儲存空間,並由狀態變數表示。這些值會永久儲存在區塊鏈上。你需要宣告型別,以便合約在編譯時能追蹤它需要多少區塊鏈上的儲存空間。

// Solidity 範例
contract SimpleStorage {
    uint storedData; // 狀態變數
    // ...
}
# Vyper 範例
storedData: int128

如果你已經寫過物件導向語言的程式,你可能對大多數的型別都很熟悉。然而,如果你剛接觸以太坊開發,address 對你來說應該是個新概念。

address 型別可以容納一個以太坊地址,相當於 20 個位元組或 160 個位元。它會以 0x 開頭的十六進位表示法回傳。

其他型別包括:

  • 布林值 (boolean)
  • 整數 (integer)
  • 定點數 (fixed point numbers)
  • 固定大小的位元組陣列 (fixed-size byte arrays)
  • 動態大小的位元組陣列 (dynamically sized byte arrays)
  • 有理數與整數常值 (rational and integer literals)
  • 字串常值 (string literals)
  • 十六進位常值 (hexadecimal literals)
  • 列舉 (enums)

如需更多說明,請查看文件:

記憶體 (Memory)

僅在合約函式執行期間儲存的值稱為記憶體變數。由於這些變數不會永久儲存在區塊鏈上,因此使用它們的成本要低得多。

Solidity 文件 (opens in a new tab)中了解更多關於以太坊虛擬機 (EVM) 如何儲存資料(儲存空間、記憶體與堆疊)的資訊。

環境變數

除了你在合約中定義的變數之外,還有一些特殊的全域變數。它們主要用於提供有關區塊鏈或當前交易的資訊。

範例:

屬性狀態變數描述
block.timestampuint256當前區塊紀元時間戳記
msg.senderaddress訊息發送者(當前呼叫)

函式

簡單來說,函式可以獲取資訊或設定資訊,以回應傳入的交易。

函式呼叫有兩種類型:

  • internal – 這些不會建立 EVM 呼叫
    • 內部函式和狀態變數只能在內部存取(即從當前合約或衍生自它的合約內部)
  • external – 這些會建立 EVM 呼叫
    • 外部函式是合約介面的一部分,這意味著它們可以從其他合約和透過交易來呼叫。外部函式 f 不能在內部呼叫(即 f() 無效,但 this.f() 有效)。

它們也可以是 publicprivate

  • public 函式可以從合約內部呼叫,或透過訊息從外部呼叫
  • private 函式僅對定義它們的合約可見,在衍生合約中不可見

函式和狀態變數都可以設為公開 (public) 或私有 (private)

以下是用於更新合約上狀態變數的函式:

// Solidity 範例
function update_name(string value) public {
    dapp_name = value;
}
  • 型別為 string 的參數 value 被傳遞到函式中:update_name
  • 它被宣告為 public,這意味著任何人都可以存取它
  • 它沒有被宣告為 view,因此它可以修改合約狀態

視圖函式 (View functions)

這些函式承諾不會修改合約資料的狀態。常見的例子是「getter」函式——例如,你可能會使用它來獲取使用者的餘額。

// Solidity 範例
function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerPizzaCount[_owner];
}
dappName: public(string)

@view
@public
def readName() -> string:
  return dappName

什麼被視為修改狀態:

  1. 寫入狀態變數。
  2. 觸發事件 (opens in a new tab)
  3. 建立其他合約 (opens in a new tab)
  4. 使用 selfdestruct
  5. 透過呼叫發送以太幣。
  6. 呼叫任何未標記為 viewpure 的函式。
  7. 使用低階呼叫。
  8. 使用包含特定操作碼的行內組合語言 (inline assembly)。

建構函式

constructor 函式僅在合約首次部署時執行一次。就像許多基於類別的程式語言中的 constructor 一樣,這些函式通常會將狀態變數初始化為其指定的值。

# Vyper 範例

@external
def __init__(_beneficiary: address, _bidding_time: uint256):
    self.beneficiary = _beneficiary
    self.auctionStart = block.timestamp
    self.auctionEnd = self.auctionStart + _bidding_time

內建函式

除了你在合約中定義的變數和函式之外,還有一些特殊的內建函式。最明顯的例子是:

  • address.send() – Solidity
  • send(address) – Vyper

這些允許合約將 ETH 發送到其他帳戶。

撰寫函式

你的函式需要:

  • 參數變數和型別(如果它接受參數)
  • 宣告 internal/external
  • 宣告 pure/view/payable
  • 回傳型別(如果它回傳一個值)

一個完整的合約可能看起來像這樣。在這裡,constructor 函式為 dapp_name 變數提供了一個初始值。

事件與日誌

事件使你的智能合約能夠與你的前端或其他訂閱應用程式進行通訊。一旦交易被驗證並新增到區塊中,智能合約就可以觸發事件並記錄資訊,前端隨後可以處理和利用這些資訊。

附註解的範例

這些是一些用 Solidity 撰寫的範例。如果你想試試這些程式碼,可以在 Remix (opens in a new tab) 中與它們互動。

Hello world

代幣

獨特的數位資產

延伸閱讀

查看 Solidity 和 Vyper 的文件,以獲得更完整的智能合約概述: