
開發(fā) DApp 時要調(diào)用在區(qū)塊鏈上的 Ethereum 智能合約,就需要智能合約的 ABI。本文希望更多了解 ABI,如為什么需要 ABI?如何解讀 Ethereum 的智能合約 ABI?以及如何取得智能的 ABI?
ABI(Application Binary Interface)
如果理解 API 就很容易了解 ABI。簡單來說,API 是程序與程序間互動的接口。這個接口包含程序提供外界存取所需的 functions、variables 等。ABI 也是程序間互動的接口,但程序是被編譯后的 binary code。所以同樣的接口,但傳遞的是 binary 格式的信息。所以 ABI 就要描述如何 decode/encode 程序間傳遞的 binary 信息。下圖以 Linux 為例,描述 Linux 中 API、ABI 和程序的關系。

編譯和部署智能合約
在 Ethereum 智能合約可以被大家使用前,必須先被部署到區(qū)塊鏈上。
從智能合約的代碼到使用智能合約,大概包含幾個步驟:
- 編寫智能合約的代碼(一般是用 Solidity 寫)
- 編譯智能合約的代碼變成可在 EVM 上執(zhí)行的 bytecode(binary code)。同時可以通過編譯取得智能合約的 ABI
- 部署智能合約,實際上是把 bytecode 存儲在鏈上(通過一個transaction),并取得一個專屬于這個合約的地址
- 如果要寫個程序調(diào)用這個智能合約,就要把信息發(fā)送到這個合約的地址(一樣的也是通過一個 transaction)。Ethereum 節(jié)點會根據(jù)輸入的信息,選擇要執(zhí)行合約中的哪一個 function 和要輸入的參數(shù)
而要如何知道這這個智能合約提供哪些 function 以及應該要傳入什么樣的參數(shù)呢?這些信息就是記錄在智能合約的 ABI!
Ethereum 智能合約 ABI
Ethereum 智能合約 ABI 用一個 array 表示,其中會包含數(shù)個用 JSON 格式表示的 Function 或 Event。根據(jù)最新的 Solidity 文件:
Function
共有 7 個參數(shù):
name:a string,function 名稱type:a string,"function", "constructor", or "fallback"-
inputs:an array,function 輸入的參數(shù),包含:name:a string,參數(shù)名type:a string,參數(shù)的 data type(e.g. uint256)components:an array,如果輸入的參數(shù)是 tuple(struct) type 才會有這個參數(shù)。描述 struct 中包含的參數(shù)類型
outputs:an array,function 的返回值,和inputs使用相同表示方式。如果沒有返回值可忽略,值為[]payable:true,function 是否可收 Ether,預設為falseconstant:true,function 是否會改寫區(qū)塊鏈狀態(tài),反之為falsestateMutability:a string,其值可能為以下其中之一:"pure"(不會讀寫區(qū)塊鏈狀態(tài))、"view"(只讀不寫區(qū)塊鏈狀態(tài))、"payable" and "nonpayable"(會改區(qū)塊鏈狀態(tài),且如可收 Ether 為 "payable",反之為 "nonpayable")
仔細看會發(fā)現(xiàn) payable 和 constant 這兩個參數(shù)所描述的內(nèi)容,似乎已包含在 stateMutability 中。
事實也確實是這樣的,在 Solidity v0.4.16 中把 constant 這個修飾function 的 key words 分成: view(neither reads from nor writes to the state)和 pure(does not modify the state),并從 v0.4.17 開始 Type Checker 會強制檢查。constant 改為只用來修飾不能被修改的 variable。并在 ABI 中加入 stateMutability 這個參數(shù)統(tǒng)一表示,payable 和 constant 目前保留是為了向后兼容。這個改動詳細的內(nèi)容和討論可參考:
https://github.com/ethereum/solidity/issues/992
Event
共有 4 個參數(shù):
name: a string,event 的名稱type: a string,always "event"-
inputs: an array,輸入?yún)?shù),包含:name: a string,參數(shù)名稱type: a string,參數(shù)的 data type(e.g. uint256)components: an array,如果輸入?yún)?shù)是 tuple(struct) type 才會有這個參數(shù)。描述 struct 中包含的信息類型indexed:true,如果這個參數(shù)被定義為 indexed ,反之為false
anonymous:true,如果 event 被定義為 anonymous
更新智能合約狀態(tài)需要發(fā)送 transaction,transaction 需要等待驗證,所以更新合約狀態(tài)是非同步的,無法馬上取得返回值。使用 Event 可以在狀態(tài)更新成功后,將相關信息記錄到 Log,并讓監(jiān)聽這個 Event 的 DApp 或任何應用這個接口的程序收到通知。每筆 transaction 都有對應的 Log。
所以簡單來說,Event 可用來:1. 取得 function 更新合約狀態(tài)的返回值 2. 也可作為合約另外的存儲空間。
Event 的參數(shù)分為:有 indexed,和其他沒有 indexed 的。有 indexed 的參數(shù)可以使用 filter,例如同一個 Event,我可以選擇只監(jiān)聽從特定 address 發(fā)出來的交易。每筆 Log 的信息同樣分為兩個部分:Topics(長度最多為 4 的 array) 和 Data。有 indexed 的參數(shù)會存儲存在 Log 的 Topics,其他的存在 Data。如果定義為 anonymous,就不會產(chǎn)生以下示例中的 Topics[0],其值為 Event signature 的 hash,作為這個 Event 的 ID。

event Set(address indexed _from, uint value)
用一個簡單的智能合約舉個例子
這個智能合約包含:
-
data:一個可修改的 state variable,會自動產(chǎn)生一個只能讀取的data()function -
set():一個修改data值的 function -
Set():一個在每次修寫data時記錄 Log 的 event
智能合約 Source Code:
pragma solidity ^0.4.20;
contract SimpleStorage {
uint public data;
event Set(address indexed _from, uint value);
function set(uint x) public {
data = x;
Set(msg.sender, x);
}
}
智能合約 ABI:
[{
"constant": true,
"inputs": [],
"name": "data",
"outputs": [{"name": "","type": "uint256"}],
"payable": false,
"stateMutabㄒility": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [{"indexed": true,"name": "_from","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],
"name": "Set",
"type": "event"
},
{
"constant": false,
"inputs": [{"name": "x","type": "uint256"}],
"name": "set",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}]
取得 Ethereum 智能合約 ABI
Solidity Compiler
可以用 Solidity Compiler 取得合約 ABI,我使用 JavaScript 版本的 Compiler 為例。
安裝:
npm install solc -g
取得合約 ABI:
solcjs simpleStorage.sol --abi
會生成一個 simpleStorage_sol_SimpleStorage.abi 文件,里面就是合約ABI 內(nèi)容。
也可以取得合約的 binary code:
solcjs your_contract.sol --bin
Remix
同樣的使用 Solidity Compiler,也可以用 Remix。在合約的 Details 可以看到完整的 ABI。可以在 Settings 中指定 Compiler 版本。

Etherscan
許多知名合約會把合約 source code 放上 Etherscan 做驗證,可以同時看到h 合約ABI。

另外 Etherscan 提供 API,可用來取得經(jīng)過驗證的合約 ABI。