第 2 章
以太坊基礎
本章要開始探索以太坊。我們會討論如何使用錢包、建立交易,以及執行一個基本的智能合約。
2.1. 以太幣的貨幣單位
以太坊的貨幣單位叫做以太幣(ether),也寫成 ETH,或用符號 Ξ(來自希臘字母 Xi,看起來像風格化的大寫 E)表示,比較少見的寫法是 ♦。例如:1 ether、1 ETH、Ξ1 或 ♦1。
|
提示
|
Ξ 使用 Unicode 字元 U+039E,♦ 使用 U+2666。 |
以太幣可以再細分成更小的單位,最小的單位叫做 wei。1 個以太幣等於一百京 wei(1 × 1018,也就是 1,000,000,000,000,000,000)。你可能也會聽到有人把這種貨幣叫做「以太坊」,但這是初學者常犯的錯誤。以太坊是系統,以太幣才是貨幣。
在以太坊內部,以太幣的數值一律以 wei 為單位、用無號整數表示。當你轉帳 1 ETH 時,交易裡編碼的數值是 1,000,000,000,000,000,000 wei。
以太幣的各種面額都有兩種名稱:一種是採用國際單位制(SI)的科學名稱,另一種是向許多計算機科學與密碼學偉大先驅致敬的俗稱。表 2-1 列出了各個單位、它們的俗稱與 SI 名稱。為了和數值的內部表示方式一致,表中所有面額都以 wei 表示(第一列),以太幣則是第七列的 1018 wei。
| 數值(以 wei 計) | 指數 | 俗稱 | SI 名稱 |
|---|---|---|---|
1 |
1 |
Wei |
Wei |
1,000 |
103 |
Babbage |
Kilowei 或 femtoether |
1,000,000 |
106 |
Lovelace |
Megawei 或 picoether |
1,000,000,000 |
109 |
Shannon |
Gigawei 或 nanoether |
1,000,000,000,000 |
1012 |
Szabo |
Microether 或 micro |
1,000,000,000,000,000 |
1015 |
Finney |
Milliether 或 milli |
1,000,000,000,000,000,000 |
1018 |
Ether |
Ether |
1,000,000,000,000,000,000,000 |
1021 |
Grand |
Kiloether |
1,000,000,000,000,000,000,000,000 |
1024 |
Megaether |
2.2. 選擇以太坊錢包
錢包這個詞已經有了很多種意思,不過這些定義彼此相關,在日常使用上,說穿了幾乎都是同一回事。我們會用錢包一詞,指稱幫助你管理以太坊帳戶的軟體應用程式。簡單地說,以太坊錢包就是你通往以太坊系統的入口。它保管你的金鑰,並能代替你建立、廣播交易。選擇以太坊錢包可能不太容易,因為選項很多,功能與設計各不相同。有些比較適合初學者,有些比較適合專家。以太坊平台本身仍在持續改進,「最好」的錢包往往就是那些能跟上平台升級所帶來的變化的錢包。
不過別擔心!如果你選了一個錢包,卻不喜歡它的運作方式——或是一開始很喜歡,後來想試試別的——你可以很輕鬆地換錢包。你只需要發一筆交易,把資金從舊錢包轉到新錢包,或是把私鑰匯出、再匯入新錢包即可。
請記住,錢包應用程式必須能存取你的私鑰才能運作,所以只從你信任的來源下載、使用錢包應用程式是非常重要的。幸好一般來說,錢包應用程式越熱門,可能就越值得信任。儘管如此,避免「把所有雞蛋放在同一個籃子裡」,把你的以太坊帳戶分散在幾個錢包與種子短語(seed phrase)中,仍是良好的做法。
以下是幾個適合入門的錢包;列出這些錢包並不代表我們為它們的品質或安全性背書,它們只是用來示範與測試的好起點。下列錢包都同時有瀏覽器擴充功能版與行動版:
MetaMask
MetaMask 容易使用,又能連接各種以太坊節點與測試區塊鏈,用來測試很方便。
Rabby Wallet
Rabby 的設計講求簡單易用,往往是新使用者的好選擇。它內建了許多安全功能。
Phantom
Phantom 這個錢包支援以太坊,以及其他以太坊虛擬機(EVM)網路與非 EVM 網路。
2.3. 控制權與責任
像以太坊這樣的開放區塊鏈之所以重要,是因為它們以去中心化系統的方式運作。這有很多層意義,但其中一個關鍵面向是:以太坊的每一位使用者都能夠——而且應該——自己掌控自己的私鑰,私鑰控制著資金與智能合約的存取權。我們有時把資金與智能合約存取權的組合稱為帳戶或錢包。這些詞在功能上可能變得相當複雜,我們之後會更詳細地說明。不過就基本原則而言,事情很簡單:一把私鑰就等於一個「帳戶」。有些使用者選擇透過第三方託管機構(例如線上的中心化交易所),放棄對自己私鑰的控制權。在本書中,我們會教你如何掌握並管理自己的私鑰。
有控制權,就有重大的責任。如果你弄丟了私鑰,就會失去對資金與合約的存取權。沒有人能幫你取回存取權——你的資金會永遠被鎖住。以下幾個提示可以幫助你承擔這份責任:
-
不要自己發明安全做法。請使用經過驗證、久經考驗的標準方法。
-
帳戶越重要(例如它所控制的資金價值占你總資產的比例越高),就應該採取越高的安全措施。
-
硬體錢包裝置能提供最高等級的安全性之一,但不是每個帳戶都需要這種等級。
-
絕對不要以明文形式儲存私鑰,尤其是數位形式。幸好現在大多數使用者介面都會跳出好幾次警告,才肯讓你看到原始私鑰。
-
當系統提示你把金鑰備份成一串助記詞時,請用紙筆做實體備份。不要把這件事留到「以後再說」,你會忘記的。萬一你系統上儲存的資料全部遺失,或是忘記、遺失了密碼,可以用這些備份重建私鑰。不過攻擊者也可以利用它們取得你的私鑰,所以絕對不要以數位形式儲存,並至少把一份實體備份安全地保存起來。
-
在轉出任何大筆金額之前(尤其是轉到新地址),先做一筆小額的測試交易(例如價值不到 1 美元),並等待確認款項已經收到。
-
建立新帳戶時,先只傳送一筆小額測試交易到新地址。收到這筆測試交易後,試著再從該帳戶把它轉回去。建立帳戶時可能出錯的原因有很多,如果真的出錯了,最好是在損失很小的時候發現。如果測試成功,就一切沒問題(也可以使用測試網路,以避免任何損失)。
-
公開的區塊瀏覽器是一種簡單的方法,讓你能獨立查看某筆交易是否已被網路接受;雖然這些資訊本來就公開在區塊鏈上,區塊瀏覽器卻讓取得這些資訊變得極為容易。不過這種便利會對你的隱私造成負面影響,因為你會向區塊瀏覽器透露自己的地址,而它們可能會追蹤你。
-
公開的區塊瀏覽器通常是可靠的,但並非總是如此——不要盲目信任它們。
-
不要把錢傳送到本書中出現的任何地址。這些地址的私鑰都列在書裡,任何人都可能立刻把錢拿走。
我們已經介紹了金鑰管理與安全的一些基本最佳實踐,現在就開始使用 MetaMask 吧!
2.4. 開始使用 MetaMask
打開 Google Chrome 瀏覽器,前往擴充功能頁面。搜尋「MetaMask」,然後點擊狐狸的標誌。你應該會看到類似圖 2-1 的畫面。

務必確認你下載的是真正的 MetaMask 擴充功能,因為有時會有人設法讓惡意擴充功能躲過 Google 的過濾機制。真正的 MetaMask:
-
在網址列顯示的 ID 是
nkbihfbeogaeaoehlefnkodbefgpgknn -
由 https://metamask.io 提供
-
有超過 5,400 則評論
-
有超過 1,500 萬名使用者
確認你看到的是正確的擴充功能後,點擊「Add to Chrome」安裝。
2.4.1. 建立錢包
安裝好 MetaMask 後,你應該會在瀏覽器的工具列上看到一個新圖示(狐狸頭)。點擊它就可以開始。系統會要求你接受條款與細則,接著輸入一組密碼,建立你的新以太坊錢包(見圖 2-2)。

|
提示
|
這組密碼控制 MetaMask 的存取權,讓能使用你瀏覽器的人無法使用它。這組密碼只適用於你本機的裝置;如果攻擊者取得了私鑰或種子短語,就能動用你地址裡的資金。攻擊者只要有私鑰或種子短語,就不需要這組密碼。 |
設定好密碼後,MetaMask 會為你產生一個錢包,並顯示由 12 個英文單字組成的助記詞備份(見圖 2-3)。萬一 MetaMask 或你的電腦出了什麼狀況,你可以在任何相容的錢包中用這些單字恢復對資金的存取權。這種恢復方式不需要密碼,只要有這 12 個單字就夠了。

|
提示
|
把你的助記詞(12 個單字)抄在紙上,抄兩份。把這兩份紙本備份分別存放在兩個安全的地點,例如防火保險箱、上鎖的抽屜或銀行保管箱。對待這些紙本備份,要像對待與你以太坊錢包內資產等值的現金一樣。任何人只要能取得這些單字,就能存取並偷走你的錢。我們會在第 5 章更詳細地說明如何妥善保管種子短語。 |
確認已經把助記詞安全地保存好之後,你就能看到自己以太坊帳戶的詳細資訊,如圖 2-4 所示。

|
備註
|
不要把任何資產傳送到本書中出現的地址。書中的種子短語是為了教學目的而公開的,傳送到這些地址的資產很可能都會遺失。 |
你的帳戶頁面會顯示帳戶名稱(預設是「Account 1」)、一個以太坊地址(範例中是 0xaa529…f17f),以及一個彩色圖示,幫助你在視覺上分辨這個帳戶與其他帳戶。在帳戶頁面的上方,你可以看到目前使用的是哪一個以太坊網路(範例中是「Main Network」)。
恭喜!你已經設定好第一個以太坊錢包了。
2.4.2. 切換網路
從 MetaMask 的帳戶頁面可以看到,你能在多個以太坊網路之間選擇。MetaMask 預設會嘗試連接主網。其他選擇包括公開的測試網路、任何你指定的以太坊節點,或是在你自己電腦上(localhost)執行私有區塊鏈的節點:
Main Ethereum Network
公開的以太坊主區塊鏈。真正的 ETH、真正的價值,也會有真正的後果。
Sepolia Test Network
Sepolia 由以太坊核心開發者在 2021 年 10 月推出,最初是權威證明(proof of authority)網路,之後已改用權益證明(PoS)共識,與以太坊主網的環境一致。
Holesky Test Network
Holesky 測試網路是以太坊用於質押、基礎設施與協議開發的進階測試場。
Localhost 8545
連接到與瀏覽器在同一台電腦上執行的節點。這個節點可以屬於任何公開區塊鏈(主網或測試網路),也可以屬於私有測試網路。
Custom RPC
讓你把 MetaMask 連接到任何具備與 Geth 相容的遠端程序呼叫(RPC)介面的節點。這個節點可以屬於任何公開或私有區塊鏈。
|
備註
|
你的 MetaMask 錢包在它連接的所有網路上,都使用同一把私鑰與同一個以太坊地址。不過,你的以太坊地址在每個以太坊網路上的餘額都不一樣。舉例來說,如果你用自己的金鑰在 Sepolia 測試網路上傳送以太幣,你在其他網路上的餘額並不會受到影響。 |
2.4.3. 取得一些測試用以太幣
你的第一項任務,是讓錢包裡有資金。你不會在主網上做這件事,因為真正的以太幣要花錢買,而且處理起來需要多一點經驗。現在,你要先在錢包裡存入一些測試網路的以太幣。
點擊左上角的以太坊圖示,把 MetaMask 切換到 Sepolia Test Network:打開「Show test networks」選項,再點擊 Sepolia,如圖 2-5 所示。

點擊 Buy,然後前往這份清單中的任何一個水龍頭。決定要用哪個水龍頭之後——它們其實都差不多——你就可以在測試網路上申請以太幣,如圖 2-6 所示。

申請測試網路以太幣的交易 ID 看起來像這樣:
0x471273d9417e98e7f1adaae61e53a353b2d2313de2e71fc4b6184bf5a63fa0ae
幾秒鐘後,Sepolia 網路就會處理這筆新交易,你的 MetaMask 錢包會顯示 0.05 ETH 的餘額(實際金額取決於水龍頭願意發送多少以太幣)。現在,在瀏覽器擴充功能中點開第一筆交易,再點擊「View on block explorer」,如圖 2-7 所示。這會帶你前往區塊瀏覽器,也就是一種讓你以視覺化方式瀏覽、探索區塊、地址與交易的網站。

MetaMask 使用的是 Etherscan 區塊瀏覽器,這是比較熱門的以太坊區塊瀏覽器之一。圖 2-8 顯示了包含 Sepolia 測試水龍頭付款的那筆交易。

這筆交易已經記錄在 Sepolia 區塊鏈上,任何人在任何時候只要搜尋交易 ID 就能查看。試著把這個交易雜湊輸入 sepolia.etherscan.io 網站,親自看看:
0x471273d9417e98e7f1adaae61e53a353b2d2313de2e71fc4b6184bf5a63fa0ae
2.4.4. 從 MetaMask 傳送以太幣
從 Sepolia 測試水龍頭收到第一筆測試用以太幣後,你可以試著把一些以太幣傳回水龍頭或任何其他地址,練習傳送以太幣。在這個例子中,我們要試著把一些測試網路以太幣傳送給 Vitalik Buterin,如圖 2-9 所示。

糟糕!你大概注意到了,這筆交易無法完成——MetaMask 說你的餘額不足。乍看之下這可能讓人困惑:你有 0.05 ETH,想傳送 0.05 ETH,MetaMask 為什麼說你的資金不足?
答案在於 gas 的成本。每一筆以太坊交易都需要支付一筆手續費,由網路收取,作為驗證這筆交易的代價。以太坊的手續費是以一種叫做 gas 的虛擬貨幣計價的。你用以太幣支付 gas,這筆費用是交易的一部分。
|
備註
|
測試網路上也需要支付手續費。如果沒有手續費,測試網路的行為就會和主網不同,也就不適合當作測試平台。手續費也能保護測試網路,使其免受阻斷服務(DoS)攻擊與寫得很糟的合約(例如無窮迴圈)影響,就像手續費保護主網一樣。 |
當你送出交易時,MetaMask 會計算近期成功交易的平均 gas 價格,例如 3 gwei;gwei 代表 gigawei。我們在「以太幣的貨幣單位」一節討論過,wei 是以太幣這種貨幣最小的細分單位。gas 上限則設定為傳送一筆基本交易的成本:21,000 單位的 gas,這是傳送一筆交易所能使用的最少 gas 量。因此,你最多會花費的 ETH 是 3 × 21,000 gwei = 63,000 gwei = 0.000063 ETH。(請注意,平均 gas 價格可能會波動。我們會在後面的章節看到,必要時如何提高或降低 gas 上限,確保你的交易能優先處理。)
這一切的意思是,進行一筆 0.05 ETH 的交易,實際要花費 0.050063 ETH。點擊 Reject 取消這筆交易。我們再試一次,這次傳送 0.01 ETH。
2.4.5. 探索某個地址的交易紀錄
到目前為止,你已經成了用 MetaMask 收發測試用以太幣的專家。你的錢包已經收過、也付過款項。你可以用 sepolia.etherscan.io 區塊瀏覽器查看所有這些交易。你可以複製錢包地址,貼到區塊瀏覽器的搜尋框,也可以讓 MetaMask 替你開啟該頁面。在 MetaMask 中,你的帳戶圖示旁邊有一個顯示三個點的按鈕。點擊它,就會出現與帳戶相關的選項選單(見圖 2-10)。

|
提示
|
MetaMask 的預設設定並不怎麼重視隱私。建議你仔細研究 Settings → Security and Privacy 裡的設定。熟悉 MetaMask 的運作方式之後,也建議把以太坊主網的網路設定從預設值改成使用另一個 RPC,選擇隱私設定符合你需求的 RPC。最保護隱私的做法,是擁有自己的節點與可供連接的 RPC;我們會在第 3 章說明怎麼做。 |
選擇「View account on Etherscan」,在區塊瀏覽器中開啟顯示你帳戶交易紀錄的網頁,如圖 2-11 所示。

在這裡,你可以看到自己以太坊地址的完整交易紀錄。它列出了 Sepolia 區塊鏈上記錄的、以你的地址為傳送方或接收方的所有交易。點開其中幾筆交易,看看更多細節。
|
警告
|
要小心,有一種已知的攻擊叫做 地址投毒(address poisoning),能讓區塊瀏覽器顯示帶有偽造地址的交易。區塊瀏覽器適合拿來快速檢查,但上面顯示的資訊不一定正確。 |
你可以探索任何地址的交易紀錄。看看 Sepolia 測試水龍頭地址的交易紀錄吧(提示:它就是你的地址最早收到的那筆款項中所列的「傳送方」地址)。你可以看到水龍頭發送給你和其他地址的所有測試用以太幣。你看到的每一筆交易,都能帶你找到更多地址與更多交易。沒過多久,你就會迷失在這座彼此相連的資料迷宮裡。公開區塊鏈蘊藏著極為豐富的資訊,而這些資訊全都可以用程式來探索,我們會在之後的範例中看到。
2.5. 認識世界電腦
你現在已經建立了錢包,也收發過以太幣。到目前為止,我們都把以太坊當成一種加密貨幣。但以太坊遠遠不只如此。事實上,加密貨幣的功能是附屬於以太坊作為去中心化世界電腦的功能之下的。以太幣的用途是支付執行智能合約的費用;智能合約是在一台叫做 EVM 的模擬電腦上執行的電腦程式。
EVM 是一個全域單例(global singleton),意思是它的運作就像一台全球唯一、無所不在的電腦。以太坊網路上的每個節點都會在本機執行一份 EVM,用來驗證合約的執行;而以太坊區塊鏈則記錄這台世界電腦在處理交易與智能合約時不斷變化的狀態。我們會在第 14 章更詳細地討論這部分。
2.6. 外部擁有帳戶與合約
你在 MetaMask 錢包裡建立的這種帳戶,叫做外部擁有帳戶(externally owned account,EOA)。外部擁有帳戶是擁有私鑰的帳戶;擁有私鑰,就代表能控制資金或合約的存取權。
你大概猜到還有另一種帳戶。另一種帳戶就是合約帳戶。合約帳戶擁有智能合約程式碼,這是單純的外部擁有帳戶所沒有的。此外,合約帳戶沒有私鑰,而是由其智能合約程式碼的邏輯擁有(並控制);這段程式碼是在建立合約帳戶時記錄在以太坊區塊鏈上、由 EVM 執行的軟體程式。
合約和外部擁有帳戶一樣有地址,也和外部擁有帳戶一樣能收發以太幣。不過,當交易的目的地是合約地址時,這筆交易會讓該合約在 EVM 中執行,並以這筆交易——以及交易中的資料——作為輸入。除了以太幣之外,交易還可以包含資料,指出要執行合約中的哪一個函式,以及要傳給該函式哪些參數。交易就是透過這種方式呼叫合約內的函式。
請注意,由於合約帳戶沒有私鑰,它無法發起交易。只有外部擁有帳戶能發起交易,但合約可以透過呼叫其他合約來回應交易,建構出複雜的執行路徑。
接下來幾節,我們會撰寫第一個合約。接著你會學到如何用 MetaMask 錢包與 Sepolia 測試網路上的測試用以太幣,建立這個合約、為它注入資金,並使用它。
2.7. 一個簡單的合約:測試用以太幣水龍頭
以太坊有幾種不同的高階語言,都可以用來撰寫合約並產生 EVM 位元組碼。你可以在第 7 章讀到其中最主要、最有趣的幾種。在智能合約程式設計上,有一種高階語言遙遙領先、成為主流選擇:Solidity。Solidity 由 Gavin Wood 創造,已經成為以太坊(以及以太坊以外)最廣泛使用的語言。我們會用 Solidity 撰寫第一個合約。
在第一個範例(範例 2-1)中,我們要寫一個控制水龍頭的合約。你已經用過水龍頭,在 Sepolia 測試網路上取得測試用以太幣。水龍頭是相對簡單的東西:任何地址來要,它就發以太幣給對方,而且可以再補充資金。
pragma solidity 0.8.26;
// SPDX-License-Identifier: GPL-3.0
// Our first contract is a faucet!
contract Faucet {
// Give out ether to anyone who asks
function withdraw(uint256 _withdrawAmount, address payable _to) public {
// Limit withdrawal amount
require(_withdrawAmount <= 1000000000000);
// Send the amount to the address that requested it
_to.transfer(_withdrawAmount);
}
// Function to receive Ether. msg.data must be empty
receive() external payable {}
// Fallback function is called when msg.data is not empty
fallback() external payable {}
}
這是一個非常簡單的合約,簡單到幾乎不能再簡單了。它同時也是一個有缺陷的合約,示範了不少不良做法與安全漏洞。我們會在後面的小節檢視它的許多缺陷,從中學習。不過現在,先逐行看看這個合約做了什麼、如何運作。你很快就會發現,Solidity 的許多元素都和現有的程式語言(例如 JavaScript、Java 或 C++)很相似。
第一行是 pragma 陳述式:
pragma solidity 0.8.26;
把這一行放在 Solidity 原始碼檔案的開頭,就能確保程式碼是用 0.8.26 版的 Solidity 編譯器編譯的,藉此確保相容性,並避免使用不同版本編譯器可能引發的問題。
接下來是一行註解,指出這個智能合約採用 GPL-3.0 授權:
// SPDX-License-Identifier: GPL-3.0
基於法律與合規上的理由,這一點很重要,因為它讓使用者與開發者知道自己在使用、散布這份程式碼時有哪些權利與義務。
註解是給人看的,不會包含在可執行的 EVM 位元組碼中。我們通常把註解放在要解釋的程式碼的前一行,有時也放在同一行。註解以兩個斜線 // 開頭。從第一個斜線到該行結尾的所有內容,都會被當成空白行一樣忽略。
這也是一行註解:
// Our first contract is a faucet!
下一行是我們的合約真正開始的地方:
contract Faucet {
這一行宣告了一個 contract 物件,類似其他物件導向語言中的 class 宣告。合約的定義包含大括號({})之間的所有行,大括號界定了一個作用域,就和許多其他程式語言使用大括號的方式差不多。
接下來,我們宣告 Faucet 合約的第一個函式:
function withdraw(uint256 _withdrawAmount, address payable _to) public {
這個函式名為 withdraw,它接受一個名為 _withdrawAmount 的無號整數(uint256),以及一個名為 _to 的 address payable。它被宣告為公開函式,也就是說其他合約可以呼叫它。接在後面、大括號之間的就是函式的定義。withdraw 函式的第一部分為提領金額設定了上限:
require(_withdrawAmount <= 1000000000000);
它使用 Solidity 的內建函式 require 檢查一個前置條件:_withdrawAmount 必須小於或等於 1,000,000,000,000 wei(wei 是以太幣的基本單位,見表 2-1),也就是 0.000001 ETH。如果呼叫 withdraw 函式時傳入的 withdraw_amount 大於這個金額,這裡的 require 函式就會讓合約停止執行,並以例外宣告失敗[2]。請注意,在 Solidity 中,陳述式必須以分號結尾。
這部分是我們水龍頭的主要邏輯。它藉由限制提領金額,控制資金從合約流出。這是非常簡單的控制,但能讓你一窺可程式化區塊鏈的威力:由去中心化的軟體控制金錢。
這裡就是這個合約的第一個設計缺陷。它不是安全上的缺陷,但最好一律在 require 陳述式中加上錯誤訊息。如此一來,當使用者的交易因為 require 陳述式而失敗時,原因就會很清楚。
修正後的 require 陳述式如下:
require(_withdrawAmount <= 1000000000000, "The requested amount is too much, try a smaller amount!");
接下來是實際的提領:
_to.transfer(_withdrawAmount);
這裡發生了幾件有趣的事。transfer 是一個內建函式,會把以太幣從目前的合約轉到另一個指定的地址——在這個例子中就是 _to 地址。transfer 函式只接受一個引數:金額。我們傳入的是 _withdrawAmount 的值,也就是前幾行宣告的 withdraw 函式的參數。
之所以能這樣做,是因為 _to 地址被定義成 payable。內建函式 transfer 與 send 只能在 payable 地址上呼叫。這裡是程式碼的第二個缺陷:如果是外部擁有帳戶呼叫 withdraw 函式,使用 transfer 完全沒有問題;但如果是另一個合約觸發這個函式,就會出問題。在那種情況下,交易可能會失敗,因為 transfer 函式最多只能使用 2,300 gas,而多次合約呼叫很可能會超過這個上限。要修正這個問題,可以改用內建的 call 函式,取代 transfer 與 send。不過,這個內建函式必須妥善處理,才不會造成安全缺陷。目前,我們先保留內建的 transfer 函式不動。
緊接著的下一行是右大括號,表示 withdraw 函式的定義到此結束。
接下來,我們再宣告兩個函式:
receive() external payable {}
fallback() external payable {}
這兩個函式就是 fallback 函式與 receive 函式。如果觸發合約的交易沒有指定合約中宣告的任何函式、根本沒有指定函式,或是不含資料,就會呼叫這兩個函式。合約可以擁有這兩個函式,而接收以太幣的通常就是它們。當 msg.data 為空時,會觸發 receive 函式;當 msg.data 不為空時,則會觸發 fallback 函式。
在 fallback 函式正下方的是最後一個右大括號,它結束了 Faucet 合約的定義。就這樣!
2.8. 編譯 Faucet 合約
我們已經有了第一個範例合約,現在需要用 Solidity 編譯器把 Solidity 程式碼轉換成 EVM 位元組碼,讓區塊鏈上的 EVM 能夠執行它。
Solidity 編譯器有獨立的可執行檔版本,也包含在各種框架中,或內建於 IDE。為了簡單起見,我們會使用比較熱門的 IDE 之一:Remix。
用你的 Chrome 瀏覽器(裝有你稍早安裝的 MetaMask 錢包)前往 Remix IDE。
第一次載入 Remix 時,它會開啟一個名為 Storage.sol 的範例合約。我們不需要它,所以點擊分頁角落的 x 把它關掉,如圖 2-12 所示。

現在,如圖 2-13 所示建立一個新檔案,命名為 Faucet.sol。

開啟新分頁後,把範例 Faucet.sol 的程式碼複製貼上,如圖 2-14 所示。

把 Faucet.sol 合約載入 Remix IDE 之後,前往 Remix 的編譯區,點擊 Compile Faucet.sol。如果一切順利,你會看到一個綠色方框(見圖 2-15)。

如果出了問題,最可能的原因是 Remix IDE 使用的 Solidity 編譯器版本不是 0.8.26。在這種情況下,我們的 pragma 指示詞會讓 Faucet.sol 無法編譯。要更改編譯器版本,請前往 Compiler 分頁,把版本設成 0.8.26,然後再試一次。
Solidity 編譯器現在已經把我們的 Faucet.sol 編譯成 EVM 位元組碼。如果你好奇,位元組碼長這樣:
6080604052348015600e575f80fd5b506101af8061001c5f395ff3fe608060405260043610610020575f3560 e01c8062f714ce1461002957610027565b3661002757005b005b348015610034575f80fd5b5061004f600480 360381019061004a919061013b565b610051565b005b64e8d4a51000821115610062575f80fd5b8073ffffff ffffffffffffffffffffffffffffffffff166108fc8390811502906040515f60405180830381858888f19350 5050501580156100a5573d5f803e3d5ffd5b505050565b5f80fd5b5f819050919050565b6100c0816100ae56 5b81146100ca575f80fd5b50565b5f813590506100db816100b7565b92915050565b5f73ffffffffffffffff ffffffffffffffffffffffff82169050919050565b5f61010a826100e1565b9050919050565b61011a816101 00565b8114610124575f80fd5b50565b5f8135905061013581610111565b92915050565b5f80604083850312 15610151576101506100aa565b5b5f61015e858286016100cd565b925050602061016f85828601610127565b 915050925092905056fea26469706673582212207de2f4d88c747c9332dceef5dcd739f3380ec8a8c2167a29 2ba64ee24fa32a8a64736f6c634300081a0033
能用 Solidity 這樣的高階語言,而不必直接用 EVM 位元組碼寫程式,你是不是很慶幸?我們也是!
2.9. 在區塊鏈上建立合約
我們已經有了合約,也把它編譯成位元組碼了。現在,我們需要在以太坊區塊鏈上「註冊」這個合約。我們會用 Sepolia 測試網路來測試合約,所以要把它提交到這條區塊鏈上。
在區塊鏈上註冊合約,需要建立一筆特殊的交易,這筆交易的 to 欄位留空(null)。這個空的目的地會告訴以太坊區塊鏈:你想部署一個新合約,而不是傳送交易到某個既有的地址。交易的 data 欄位則包含編譯好的合約位元組碼。幸好 Remix IDE 會替你處理這一切,並把交易送到 MetaMask。
首先,切換到 Run 分頁,在 Environment 下拉選單中選擇 Injected Web3。這會把 Remix IDE 連接到 MetaMask 錢包,並透過 MetaMask 連上 Sepolia 測試網路。完成之後,你可以在 Environment 下方看到 Sepolia。此外,Account 選擇框會顯示你錢包的地址(見圖 2-16)。

在你剛確認的 Run 設定正下方,就是準備好要建立的 Faucet 合約。點擊圖 2-16 中的 Deploy 按鈕。
Remix 會建構這筆特殊的「建立」交易,MetaMask 則會請你核准,如圖 2-17 所示。你會注意到,這筆建立合約的交易裡沒有以太幣,但有一些位元組的資料(也就是編譯好的合約),而且會消耗一些以 gwei 計的 gas。點擊 Submit 核准這筆交易。

現在你得等一下。合約在 Sepolia 上完成處理大約需要 15 到 30 秒。Remix 看起來好像沒在做什麼,但請耐心等候。
合約建立之後,會出現在 Run 分頁的底部(見圖 2-18)。

請注意,Faucet 合約現在有了自己的地址:Remix 顯示為「Faucet at 0x4E7…6EA46」(不過你的地址——那串隨機的字母與數字——會不一樣)。
2.10. 與合約互動
我們先回顧一下目前學到的東西。以太坊合約是控制金錢的程式,在一台叫做 EVM 的虛擬機裡執行。合約是由一筆特殊交易建立的,這筆交易會提交合約的位元組碼,記錄到區塊鏈上。合約在區塊鏈上建立之後,就和錢包一樣擁有一個以太坊地址。每當有人傳送交易到合約地址,就會讓合約在 EVM 中執行,並以該交易作為輸入。傳送到合約地址的交易可以包含以太幣、資料,或兩者都有。如果包含以太幣,這些以太幣會「存入」合約的餘額。如果包含資料,資料可以指定合約中某個具名的函式並呼叫它,同時把引數傳給該函式。
2.10.1. 在區塊瀏覽器中查看合約地址
現在區塊鏈上已經記錄了我們的合約,而且我們看得到它有一個以太坊地址。我們到 sepolia.etherscan.io 區塊瀏覽器看看,合約長什麼樣子。在 Remix IDE 中,點擊合約名稱旁邊的圖示,複製合約地址(見圖 2-19)。

讓 Remix 保持開啟,我們稍後還會回來。現在,用瀏覽器前往 sepolia.etherscan.io,把地址貼到搜尋框中。你應該會看到這個合約的以太坊地址紀錄,如圖 2-20 所示。

2.10.2. 為合約注入資金
目前,這個合約的紀錄中只有一筆交易:建立合約的交易。如你所見,這個合約也沒有任何以太幣(餘額為零)。這是因為我們在建立交易中沒有傳送任何以太幣給合約,雖然其實可以這麼做。
我們的水龍頭需要資金!我們的第一個任務,是用 MetaMask 傳送以太幣給這個合約。合約地址應該還在你的剪貼簿裡(如果沒有,就從 Remix 再複製一次)。打開 MetaMask,傳送 0.01 ETH 給它,做法和傳送到其他任何以太坊地址完全一樣(見圖 2-21)。

大約一分鐘後,如果你重新載入 Etherscan 區塊瀏覽器,就會看到另一筆傳送到合約地址的交易,餘額也更新為 0.01 ETH。
還記得 Faucet.sol 程式碼裡的 receive 函式嗎?它長這樣:
receive() external payable {}
當你傳送交易到合約地址、又沒有附上指定要呼叫哪個函式的資料時,就會呼叫這個 receive 函式。你的交易讓合約在 EVM 中執行,更新了它的餘額。你已經為水龍頭注入資金了!
2.10.3. 從合約提領
接下來,我們從水龍頭提領一些資金。要提領,就必須建構一筆交易,呼叫 withdraw 函式,並傳入 _withdrawAmount 與 _to 兩個引數。為了讓事情暫時保持簡單,我們會讓 Remix 替我們建構這筆交易,再由 MetaMask 呈現給我們核准。
回到 Remix 分頁,查看 Run 分頁上的合約。你應該會看到一個標示為「withdraw」的紅色方框,旁邊有一個標示為「uint256 _withdrawAmount, address _to」的輸入欄位(見圖 2-22)。

這是 Remix 為合約提供的介面。它讓我們能建構交易,呼叫合約中定義的函式。我們會輸入 _withdrawAmount 與 _to 地址,然後點擊 withdraw 按鈕產生交易。
首先,來決定 _withdrawAmount。我們想試著提領 0.000001 ETH,這是合約允許的最高金額。記住,以太坊內部所有的貨幣數值都以 wei 計價,而我們的 withdraw 函式也預期 _withdrawAmount 是以 wei 計價的。我們要的金額是 0.000001 ETH,也就是 1,000,000,000,000 wei(1 後面接 12 個零)。
至於 _to 地址,我們直接使用 MetaMask 中的 Account 1。
|
提示
|
由於 JavaScript 的限制,Remix 無法處理像 1017 這麼大的數字[3]。因此,我們用引號把它括起來,讓 Remix 把它當成字串接收,再以 |
在 _withdrawAmount 方框中輸入 "1000000000000"(包括引號),從 MetaMask 複製貼上你 Account 1 的地址,然後點擊 transact 按鈕。(你看到的按鈕也可能叫 withdraw。圖 2-23 顯示的是函式的展開檢視;如果你的檢視沒有展開,按鈕就會叫「withdraw」。)

MetaMask 會跳出一個交易視窗,請你核准。點擊 Submit,把你的提領呼叫送到合約(見圖 2-24)。

等一分鐘左右,再重新載入 Etherscan 區塊瀏覽器,就能在 Faucet 合約的地址紀錄中看到這筆交易(見圖 2-25)。

現在我們看到一筆新交易,目的地是合約地址,金額為 0 ETH。合約餘額變了,現在是 0.009999 ETH,因為它依照要求傳送了 0.000001 ETH 給我們。
可是在合約的地址紀錄中,我們沒有看到「OUT」交易。轉出的那筆提領在哪裡?合約的地址紀錄頁面上出現了一個新分頁,名為內部交易。由於這筆 0.000001 ETH 的轉帳源自合約程式碼,它是一筆內部交易(internal transaction),也稱為訊息。點擊那個分頁就能看到它(見圖 2-26)。
這筆「內部交易」是由合約的這一行程式碼送出的(出自 Faucet.sol 的 withdraw 函式):
_to.transfer(_withdrawAmount);

回顧一下:你從 MetaMask 錢包送出了一筆交易,其中包含資料指令,要以 0.000001 ETH 的 _withdrawAmount 引數和一個地址呼叫 withdraw 函式。這筆交易讓合約在 EVM 中執行。EVM 執行 Faucet 合約的 withdraw 函式時,先呼叫 require 函式,驗證申請的金額小於或等於允許提領的上限 0.000001 ETH。接著,它呼叫 transfer 函式,把以太幣傳送給你。執行 transfer 函式產生了一筆內部交易,從合約的餘額中把 0.000001 ETH 存入你的錢包地址。這就是 Etherscan 的內部交易分頁上顯示的那一筆。
2.11. 結語
在本章中,你用 MetaMask 設定了錢包,並用 Sepolia 測試網路上的水龍頭為錢包注入資金。你在錢包的以太坊地址收到了以太幣,接著又把以太幣傳送到另一個地址。
接下來,你用 Solidity 寫了一個 Faucet 合約。你用 Remix IDE 把合約編譯成 EVM 位元組碼,再用 Remix 組成一筆交易,在 Sepolia 區塊鏈上建立了 Faucet 合約。建立之後,Faucet 合約有了一個以太坊地址,你也傳送了一些以太幣給它。最後,你建構了一筆呼叫 withdraw 函式的交易,成功申請到 0.000001 ETH。合約檢查了這項申請,並透過一筆內部交易傳送 0.000001 ETH 給你。
這看起來也許沒什麼,但你剛剛已經成功地和一個在去中心化世界電腦上控制金錢的軟體互動了。
我們會在第 7 章撰寫更多 Solidity 智能合約程式,並在第 9 章學習最佳實踐與安全考量。