Solidity中的ABI文件以及Bin文件详解

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。

  • 将合约代码保存在文件中,例如:

image-20210515194540240

image-20210515194612125

  • 在刚才的目录下,输入cmd回车,进入控制台

image-20210515194753846

image-20210515194851251

  • 使用如下命令编译
solcjs --abi --bin Storage.sol

image-20210515195224074

生成abi文件以及bin文件,其中abi文件的内容就是合约的接口描述,bin文件中内容就是EVM虚拟机可识别的合约的bytecode。

打开abi文件:

image-20210515195544892

打开bin文件:

image-20210515195618022

solcjs 其他命令:

image-20210515200524815

-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

image-20210515204311484

即可得到abi文件以及bytecode。

image-20210515204157345

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));
	}
});
posted @ 2021-05-15 22:03  LikFre  阅读(5694)  评论(0编辑  收藏  举报