Yul storage 如何運作?
Yul 是一種中介程式語言,可以用來在智能合約中撰寫組合語言的一種形式。理解 Yul 可以提升你的智能合約,並幫助你節省使用者的 gas 成本。要開始用 Yul 撰寫智能合約,理解 storage 的運作方式非常重要。
Yul storage 是如何運作的?
在深入了解 Yul 之前,我們需要先充分理解智能合約中 storage 的運作方式。Storage 由一系列的 slot 組成。一個智能合約有 2²⁵⁶ 個 slot。
在宣告變數時,我們從 slot 0 開始,依序遞增。每個 slot 長度為 256 位元(32 bytes),這也是 uint256 和 bytes32 名稱的由來。所有變數都會被轉換為十六進位。
如果使用的是像 uint128 這樣的變數,我們不會用整個 slot 來儲存這個變數。相反地,它會在左側被填補 0。讓我們透過一個範例來加深理解。
var1:由於 uint256 變數等於 32 bytes,var1 會佔用整個 slot 0。
以下是 slot 0 中所儲存的內容:
0x0000000000000000000000000000000000000000000000000000000000000100
var2:地址稍微複雜一些。由於地址只佔用 20 bytes 的儲存空間,地址會在左側被填補 0。
以下是 slot 1 中所儲存的內容:
0x0000000000000000000000009acc1d6aa9b846083e8a497a661853aae07f0f00.
var3:這個看起來很簡單,slot 2 完全被這個 bytes32 變數佔用。
var4 和 var5:還記得我提到 uint128 會被填補 0 嗎?如果我們安排變數順序,讓它們的儲存總和小於 32 bytes,我們就可以把它們一起放進同一個 slot!這稱為 packing variables(打包變數),可以幫你節省 gas。
讓我們看看 slot 3 中儲存了什麼:
0x0000000000000000000000000000000200000000000000000000000000000001.
注意 0x000000000000000000000000000002 和 0x000000000000000000000000000001 恰好完美地放進同一個 slot 中。這是因為它們各自佔用 16 bytes(一個 slot 的一半)。
我們再來看另一個範例!
x = 3。這說得通,因為我們知道 var5 被打包進 slot 3。
y = 16。這也應該說得通,因為我們知道 var4 佔用 slot 3 的一半。由於變數是從右到左打包的,所以 var5 的起始索引是 byte 16。
z = 1。sstore() 將 slot 0 的值設為 1。接著,我們用 sload() 將 slot 0 的值賦予給 z。
在繼續之前,你應該把這個函式加進你的 remix 檔案中。它可以幫助你查看每個 storage slot 中儲存的內容。
現在讓我們來看一些更複雜的資料結構!
在處理靜態陣列時,EVM 知道要為我們的資料分配多少個 slot。以這個陣列為例,我們每個 slot 打包了 2 個元素。
所以如果你呼叫 getValInHex\(4\),它會回傳 0x0000000000000000000000000000000100000000000000000000000000000000。
如我們所預期的,從右到左讀取,我們會看到 value 0 和 value 1。
Slot 5 包含 0x0000000000000000000000000000000300000000000000000000000000000002。
接下來我們要看動態陣列。
試著呼叫 getValInHex\(6\)。你會看到它回傳 0x00。因為 EVM 不知道需要分配多少個 storage slot,所以我們無法把陣列儲存在這裡。
相反地,目前 storage slot(slot 6)的 keccak256 雜湊值會被用作陣列的起始索引。從這裡開始,我們只需要加上想要的元素索引,就可以取得該值。
以下是示範如何找到動態陣列中某個元素的程式碼範例:
我們先取得陣列的 slot,接著執行 add() 運算並搭配 sload() 來取得我們想要的陣列元素值。
你可能會問,什麼可以防止我們與其他變數的 slot 發生碰撞?
這種情況完全有可能發生,但由於 2²⁵⁶ 是一個非常大的數字,發生的機率極低。
Mappings 的行為與動態陣列類似,差別在於我們會把 slot 與 key 一起雜湊。
在這個示範中,我設定了 mapping 的值 var8[1] = 2。現在讓我們看一個範例,說明如何取得 mapping 中某個 key 的值。
如你所見,這段程式碼看起來與我們在動態陣列中找元素時非常相似。主要差別在於我們把 key 和 slot 一起雜湊。
本節關於 storage 的最後一部分是學習巢狀 mapping。在繼續閱讀之前,我建議你根據目前所學,自己試著寫出如何讀取巢狀 map 值的實作方式。
在這個範例中,我設定了 mapping 的值 var9[0][1] = 2。
以下是程式碼,我們來深入了解!
以下是運作的過程:
- 我們先取得第一個 key(0)的雜湊值。
- 接著把這個雜湊值與第二個 key(1)一起再做一次雜湊。
- 最後,從 storage 讀取該 slot 以取得我們的值。
恭喜,你已經完成了 Yul storage 這個章節!
延伸閱讀 Yul Memory、Yul Contract Calls 以及 Yul Packed Storage Variables。
相關總覽

打造區塊鏈魔法
Alchemy 結合最強大的 Web3 開發者產品與工具,並提供資源、社群與卓越的支援。


