---
title: "什麼是 Solidity 二進位檔？"
description: "什麼是 Solidity 二進位檔，以及如何運用它們來優化你的智能合約程式碼"
---

# 什麼是 Solidity 二進位檔？

透過智慧合約發布到 Ethereum 區塊鏈上的原始資料是位元組碼（bytecode），也就是一長串十六進位字元。雖然開發者是用人類可讀的 [Solidity](https://www.alchemy.com/overviews/solidity) 程式碼來撰寫與閱讀智慧合約，但發布到區塊鏈上的並不是這種文字。

同樣地，每一次智慧合約的「呼叫」——也就是對智慧合約公開的外部可見函式所發出的請求——形式上都是原始位元組碼，或稱「binaries」。

以下是一個上傳至 [Ethereum mainnet](https://www.alchemy.com/rpc/ethereum) 的智慧合約，具有以下（Solidity 編碼的）結構：

假設某位使用者想要呼叫函式 baz，並帶入參數 69 和 true。

以下是這個請求以位元組碼傳輸時實際的樣子：

`0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450`...

很難讀懂，對吧？

在這篇文章中，我們會討論為什麼 [Ethereum Virtual Machine](https://www.alchemy.com/overviews/what-is-the-ethereum-virtual-machine-evm) 會把所有東西都編碼成位元組碼、了解什麼是 ABI 以及如何使用它，並學習一些基本工具，把位元組碼反編譯回人類可讀的 [Solidity](https://www.alchemy.com/dapps/solidity)。

**備註：**本文中的範例取自官方的 [Solidity ABI 文件](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html)。

## **為什麼 Solidity 要用二進位來編碼智慧合約？**

由於在 Ethereum 區塊鏈上儲存資料的成本極高，而且上傳的每一位元組資料都需要複製到區塊鏈上所有的全節點，因此讀寫原始位元組碼會比上傳 Solidity 程式碼更有成本效益。

解析並儲存人類可讀的程式碼可能會多消耗一個數量級的資料量，而智慧合約在 mainnet 上部署的成本本身就已經可能高達數千美元，這會是一個問題。

## **什麼是 Solidity ABI？為什麼你需要它才能讀懂智慧合約？**

智慧合約發布時，會在發布到 Ethereum 之前自動被轉譯成位元組碼。但問題是——一旦發布到網路上，使用者要怎麼知道該如何與這個智慧合約互動？光看一長串位元組碼，幾乎不可能理解有哪些函式可以呼叫。

答案就是 [Application Binary Interface，簡稱 ABI](https://www.alchemy.com/overviews/what-is-an-abi-of-a-smart-contract-examples-and-usage)。

ABI 是一份人類可讀、公開的方法清單，描述了可以對某個特定智慧合約發出的呼叫，以及每個呼叫會回傳什麼。

有了 ABI，智慧合約的使用者就不需要閱讀位元組碼，可以把呼叫轉換成位元組碼來與智慧合約互動。

ABI 和傳統 Web2 架構中的 API（Application Programming Interface）非常相似。不過主要的差異在於：**Solidity ABI 讓使用者可以存取以二進位編碼的智慧合約中的方法**，而 API 則是讓使用者可以存取線上伺服器端點提供的方法。

由於 ABI 是設計給人閱讀與使用的，智慧合約開發者不會把智慧合約的 ABI 發布到區塊鏈上，因為那樣做成本會非常高。

取而代之，你可以透過以下方式取得 ABI：

1. 從智慧合約開發者那裡取得公開的合約原始碼，並用它來產生 ABI。
1. 如果智慧合約已在 Etherscan 上驗證過，可以從 Etherscan 的合約資訊中取得。
1. 從智慧合約位元組碼反向工程出 ABI（不建議這麼做）。

ABI 通常是以 JSON 格式編碼發布，內容是 Solidity 智慧合約中公開函式宣告的編碼。

以下面這個智慧合約的函式定義為例：

對應的 JSON 編碼看起來會像這樣：

## **如何解讀 Solidity 呼叫資料的二進位內容**

雖然你不會想手動解析 Solidity 二進位資料來反推函式呼叫——因為這個過程複雜、不直觀，而且很容易出錯——但大致了解 Solidity 的二進位資料是怎麼組成的還是很有幫助，這樣你就能快速瀏覽呼叫資料，或是快速核對數值。

我們會在下一節連結幾個工具，這些工具應該能幫你處理大部分這類轉譯工作。

以上面的範例為例。

假設某位使用者想要呼叫智慧合約中的函式 **baz**，並帶入參數 69 和 true。以下是這個請求以位元組碼呈現的樣子，總長度為 68 位元組：

`0xcdcd77c0000000000000000000000000000000000000000000000000000000000000004500`...

### 1. 使用呼叫資料的前 4 個位元組來識別方法 ID。

在這個例子中，0xcdcd77c 用來識別方法 **baz**，這是取簽章 baz\(uint32,bool\) 的 ASCII 形式的 Keccak 雜湊值的前 4 個位元組而得。

### 2. 使用接下來的 32 個位元組來識別第一個參數

`0x00000000000000000000000000000000000000000000000000000000000000045` 識別出第一個參數 **69**，這是一個 uint32 值、補齊到 32 位元組。所謂補齊，就是加上一些 0，以確保整串字串（在這個例子中）長度固定為 32 位元組，不論實際數字有多大。

`0x00000000000000000000000000000000000000000000000000000000000000045 `

### 3. 使用**最後 32 個位元組來識別第二個參數**

第二個參數是 **true**，這是一個補齊到 32 位元組的 bool 值：

`0x0000000000000000000000000000000000000000000000000000000000000001`

若參數包含動態型別（dynamic types），編碼方式會稍有不同，因為不像 address、bool 或 uint32 這類靜態型別是就地編碼，[動態型別會被編碼在另外分配的位置](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#use-of-dynamic-types)。

## **如何解讀 Solidity 的事件資料二進位內容？**

事件（event）是智慧合約在執行方法呼叫時發布的一種紀錄（log），事件會以二進位資料的形式發布。

事件可以帶入參數，用來指定事件會輸出什麼內容。這些參數可以被設為索引（indexed），意思是之後可以用這個索引參數作為篩選條件來搜尋該事件。這些索引參數在 Solidity 的術語中也稱為 topics！

大致上，一個 Solidity 事件會遵循以下結構：

- address：合約的位址
- topics\[n\]：0 到 4 個 topic，也就是索引參數
- 任意長度的二進位資料，可以依照 ABI 來解析。

## **我應該用什麼工具來反編譯 Solidity 二進位資料？**

市面上有多種 EVM 反編譯器可以幫你把 Solidity 二進位資料還原成較易讀的版本，包括 [EtherVM Decompiler](https://ethervm.io/decompile) 和 [Panoramix decompiler](https://github.com/palkeo/panoramix)。

這些 EVM 反編譯器並不會完美還原出原始的原始碼（為了縮小二進位檔案大小，名稱或其他重要資訊可能會被移除），但它們應該能讓你大致理解該合約允許哪些 ABI 請求。
