---
title: "Yul storage 如何運作？"
description: "了解 Yul 智慧合約程式語言中 storage 的運作方式"
---

# Yul storage 如何運作？

[Yul](http://www.alchemy.com/overviews/what-is-yul) 是一種中介程式語言，可以用來在[智能合約](https://www.alchemy.com/overviews/solidity-smart-contract)中撰寫組合語言的一種形式。理解 Yul 可以提升你的智能合約，並幫助你節省使用者的 gas 成本。要開始用 Yul 撰寫智能合約，理解 storage 的運作方式非常重要。

## **Yul storage 是如何運作的？**

在深入了解 Yul 之前，我們需要先充分理解[智能合約中 storage 的運作方式](https://www.alchemy.com/docs/smart-contract-storage-layout)。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 的一半）。

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Instruction", dataType: "object" },
      { key: "2", width: 200, title: "Explanation", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>sload(p)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Loads the variable in slot p from storage.</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>sstore(p,v)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Assigns storage slot p value v.</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>v.slot</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Returns the storage slot of variable v.</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>v.offset</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Returns the index in bytes of where variable v begins in a storage slot. Variables are packed from right to left.</p>", tooltip: "", icon: "" },
        id: 3,
      },
    ],
  }}
/>

我們再來看另一個範例！

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。

以下是程式碼，我們來深入了解！

**以下是運作的過程：**

1. 我們先取得第一個 key（0）的雜湊值。
1. 接著把這個雜湊值與第二個 key（1）一起再做一次雜湊。
1. 最後，從 storage 讀取該 slot 以取得我們的值。

恭喜，你已經完成了 Yul storage 這個章節！

延伸閱讀 [Yul Memory](http://www.alchemy.com/overviews/yul-memory)、[Yul Contract Calls](http://www.alchemy.com/overviews/yul-contract-calls) 以及 [Yul Packed Storage Variables](http://www.alchemy.com/overviews/yul-packed-storage-variables)。
