Solidity中的ABI文件以及Bin文件
1、什么是ABI文件?
ABI全名:Application Binary Interface,應用二進制接口文件。智能合約的接口描述,描述了字段名稱、字段類型、方法名稱、參數名稱、參數類型、方法返回值類型等。
當合約被編譯后,對應的abi文件也就確定了。
2、部署合約步驟
- 編寫智能合約代碼(以太坊官方推薦的是Solidity語言)
- 編譯合約,將Solidity編寫的代碼編譯成EVM可識別的bytecode,同時生成abi文件。
- 部署合約,將合約部署到區塊鏈上,生成合約地址,將合約內容(也就是上一步的bytecode)作為輸入,部署合約是一個交易的過程,會生成交易hash。
- 執行合約,獲取合約地址,然后傳入參數調用合約中的方法,獲取執行結果。
從以上的步驟中可以看出,部署合約時EVM虛擬機需要的是Solidity合約的二進制代碼也就是bytecode(Bin文件)。ABI文件與合約部署到區塊鏈沒有關系,但是如果想要調用已經在區塊鏈上的合約方法就需要ABI文件。
ABI文件中是描述合約內容的json字符串。
3、怎么獲取ABI文件?
3.1、編寫一個簡單的智能合約代碼
// SPDX-License-Identifier: GPL-3.0
//合約的編譯版本聲明
pragma solidity >=0.7.0 <0.9.0;
//合約名稱Storage
contract Storage {
//定義一個uint類型長度為256位的變量number
uint256 number;
//store方法將參數num賦值給number
function store(uint256 num) public {
number = num;
}
//retrieve返回當前的number值
function retrieve() public view returns (uint256){
return number;
}
}
合約第一行表示源代碼是在gplversion3.0下授權的。在默認發布源代碼的設置中,機器可讀的許可證說明符,非常重要。
3.2、獲取ABI文件的方式
(1)使用remix編譯
可以通過Remix獲取該合約代碼的ABI文件內容以及bytecode內容。
可以看另一篇 Remix中合約編譯后的ABI以及bytecode位置
(2)使用solcjs編譯合約代碼
solcjs是用於編譯solidity文件的node.js庫和命令行工具,它不使用solc命令行編譯器,而是純粹使用JavaScript進行編譯,因此它的安裝比solc簡單得多。solc是真實的solidity編譯器,用C++編寫。
-
安裝nodejs,安裝的過程自行百度
-
使用 npm 可以便捷地安裝Solidity編譯器solcjs
npm install -g solc
加-g代表全局安裝,任意位置都可以使用solc。
- 將合約代碼保存在文件中,例如:


- 在剛才的目錄下,輸入cmd回車,進入控制台


- 使用如下命令編譯
solcjs --abi --bin Storage.sol

生成abi文件以及bin文件,其中abi文件的內容就是合約的接口描述,bin文件中內容就是EVM虛擬機可識別的合約的bytecode。
打開abi文件:

打開bin文件:

solcjs 其他命令:

-V:顯示solc的版本
--version:與-v相同
--optimize:啟用字節碼優化器
--bin:生成十六進制的合約二進制文件
--abi:生成合約的接口描述文件
--standard-json:打開標准的json格式的輸入/輸出
--output-dir,-o:合約輸出的目錄
(3)使用solc庫通過js代碼編譯
- 通過npm初始化一個項目
npm init
- 使用npm安裝solc
npm install solc
- 創建index.js
//引入solc
var solc = require("solc");
//引入fs
let fs = require("fs");
//使用node提供的fs讀取合約文件
var sourceCode = fs.readFileSync("Storage.sol").toString();
//預先定義好編譯源json對象
var jsonContractSource = JSON.stringify({
language: 'Solidity',
sources: {
'Storage.sol': { // 指明編譯的文件名
content: sourceCode, // solidity 源代碼
},
},
settings: { // 自定義編譯輸出的格式。以下選擇輸出全部結果。
outputSelection: {
'*': {
'*': [ '*' ]
}
}
},
});
//編譯得到結果,使用JSON解析
var compileCode = JSON.parse(solc.compile(jsonContractSource));
//取出abi對象
var abi = compileCode.contracts["Storage.sol"]["Storage"].abi;
//取出bytecode
var bytecode = compileCode.contracts["Storage.sol"]["Storage"].evm.bytecode.object;
//將abi以及bytecode數據輸出到文件,也可以結合web3直接通過js代碼部署
fs.writeFile('Storage.abi', JSON.stringify(abi), function(err){
if(err)
console.error(err);
console.log("contract compiled sucessfully.");
});
fs.writeFile('Storage.bin', JSON.stringify(bytecode), function(err){
if(err)
console.error(err);
console.log("contract compiled sucessfully.");
});
- 使用node執行index.js
node index.js

即可得到abi文件以及bytecode。

4、ABI文件內容詳解
(1)ABI文件中各參數
- name: 函數名稱
- type:方法類型,包括function, constructor, fallback(缺省方法)可以缺省,默認為function
- constant:布爾值,如果為true指明方法不會修改合約字段的狀態變量
- payable:布爾值,標明方法是否可以接收ether
- stateMutability:狀態類型,包括pure (不讀取區塊鏈狀態),view (和constant類型,只能查看,不會修改合約字段),nonpayable(和payable含義一樣),payable(和payable含義一樣)。其實保留payable和constant是為了向后兼容
- inputs:數組,描述參數的名稱和類型
- name:參數名稱
- type:參數類型
- outputs:和inputs一樣,如果沒有返回值,缺省是一個空數組
以上述合約代碼為例,得到的ABI文件如下:
[
{
"inputs": [],
"name": "retrieve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "num",
"type": "uint256"
}
],
"name": "store",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
json的第一部分,描述的是retrieve方法。
{
"inputs": [],#輸入的參數為空
"name": "retrieve", #方法名為retrieve
"outputs": [ #輸出的參數
#1個參數
{
"internalType": "uint256",#內部參數類型uint256
"name": "", #參數名,匿名,所以為空
"type": "uint256" #參數類型uint256
}
],
"stateMutability": "view", #狀態類型view,表示該方法只查看合約內部狀態,不會修改合約內部狀態
"type": "function" #方法類型
},
json的第二部分,描述合約內的store方法
{
"inputs": [ #輸入參數
{
"internalType": "uint256", #參數內部類型 uint256
"name": "num", #參數名num
"type": "uint256" #參數類型,uint256
}
],
"name": "store", #方法名store
"outputs": [], #輸出參數為空
"stateMutability": "nonpayable", #方法狀態類型,不支持接受ether
"type": "function" #方法類型
}
5、ABI文件的用處
(1)通過ABI文件內容創建區塊鏈上已部署合約的實例,方便調用合約方法。
let Web3 = require("web3");
let web3 = new Web3("http://localhost:8545");
//將合約的代碼的abi文件內容復制粘貼給這個變量
let abi = JSON.parse('[{"inputs":[],"name":"retrieve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"store","outputs":[],"stateMutability":"nonpayable","type":"function"}]');
//將已經部署的合約地址粘貼,如果沒有,需要先部署合約
let contractAddr = "0xA5330ede5f68050Fa83dcb417E7CbA8F1DF875Ed";
//根據abi和合約地址創建合約實例
let contract = new web3.eth.Contract(abi,contractAddr);
//通過合約實例contract調用方法,參數from代表通過哪個賬戶向合約發起交易,修改合約的內容,from使用自己的賬戶
var num = 23;
contract.methods.store(num).send({from:"",gas: '5000000'}).then((res,err)=>{
if(err){
console.log("Error:"+JSON.stringify(err));
}else{
console.log(JSON.stringify(res));
}
});
//調用retrieve方法,查詢合約
contract.methods.retrieve().call().then((res,err)=>{
if(err){
console.log("Error:"+JSON.stringify(err));
}else{
console.log(JSON.stringify(res));
}
});
