solidity学习过程 --- abi编码

拥有回忆 提交于 2020-03-05 21:44:59

abi编码函数

什么是abi

应用程序二进制接口,以太坊的调用合约时的接口说明
ABI是两个程序模块之间的接口,主要是用于将数据编码或解码为源代码所表示的代码。
以太坊中主要用于solidity合约的函数调用,以及反向编码读取数据的中的方法

solidityABI编码函数

  • abi.encode(…) returns (bytes):计算参数的 ABI 编码。
  • abi.encodePacked(…) returns (bytes):计算参数的紧密打包编码
  • abi. encodeWithSelector(bytes4 selector, …) returns (bytes): 计算函数选择器和参数的 ABI 编码
  • abi.encodeWithSignature(string signature, …) returns (bytes): 等价于* abi.encodeWithSelector(bytes4(keccak256(signature), …)

solidityABI编码函数 实现细节

函数选择器,官方文档定义如下:
一个函数调用数据的前 4 字节,指定了要调用的函数。这就是某个函数签名的 Keccak(SHA-3)哈希的前 4 字节(高位在左的大端序)(译注:这里的“高位在左的大端序“,指最高位字节存储在最低位地址上的一种串行化编码方式,即高位字节在左)。 这种签名被定义为基础原型的规范表达,基础原型即是函数名称加上由括号括起来的参数类型列表,参数类型间由一个逗号分隔开,且没有空格。

简单来说,函数选择器就是通过函数名来参数来标识函数,可以用于不同合约之间的函数调用

1.合约中函数调用截取调用数据的前四个字节(0x之后),就是将函数名以及参数类型进行签名处理(Keccak–Sha3),

  • 执行set(21)函数 可以得到结果:
    60fe47b1 set(uint256) -这便是获取到函数签名散列截取到的前四个字节(一个字节对应16进制2个字符)
  • 传入参数为21 对于16进制为15
  • 函数最终结果: 0x60fe47b10000000000000000000000000000000000000000000000000000000000000015

合约函数名与函数参数,每个参数最终要被补全为32个字节

solidityABI编码函数实现:

contract testABI {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function abiEncode() public view returns (bytes memory) {
        abi.encode(1);  // 计算1的ABI编码
        return abi.encodeWithSignature("set(uint256)", 1); //计算函数set(uint256) 及参数1 的ABI 编码
    }
}

remix部署合约之后,调用abiEncode()将会产生如下数据输出:

  1. abi.encode(21)

0x60fe47b10000000000000000000000000000000000000000000000000000000000000015

  1. 其中60fe47b1便是对应的set()函数的签名处理

    [
     {
     	"constant": false,
     	"inputs": [
     		{
     			"internalType": "uint256",
     			"name": "value",
     			"type": "uint256"
     		}
     	],
     	"name": "set",
     	"outputs": [],
     	"payable": false,
     	"stateMutability": "nonpayable",
     	"type": "function"
     }
    

]
``

abiDetail:

  1. constant – 如果函数改变区块链状态为true,反之false;
  2. type: 调用参数类型: string,function,callback,contsructor
  3. name: 调用参数名称
  4. payable: 是否支持ether
  5. stateMutability:(状态可变性) - pure - view - payable - nonpayable
  6. outputs: 调用输出值
  7. input:{
    • name: 参数名称
    • type: 参数类型
      }

abi.encode 与abi.encodePacked

  1. 对函数打包处理,但是处理方式不一样,对于小于32字节类型的参数,前者会将所有参数自动补全到32个字节,后者不会自动补全

  2. uint默认是uint256,验证时并不存在补全字节这个问题

contract TestDifference {
  function testUint(
    uint8 _num1,
    uint32 _num2
    )
    public view returns (bytes memory, bytes memory) {
    return (
      abi.encode(_num1),
      abi.encodePacked(_num2)
      );
  }

  function testBytes() public view returns (bytes memory, bytes memory) {
      bytes memory _bts ="Hello,world!";
    return (abi.encodePacked(_bts),abi.encode(_bts));
  }
}

关于使用abi.encodeWithSignature(string signature, …) returns (bytes)

主要应用场景: 函数调用

contract Contract {

    MyContract contract1 = new MyContract();

    function getSelector() public view returns (bytes4, bytes4) {
        return (contract1.function1.selector, contract1.getBalance.selector);
    }

    function callGetValue(uint _x) public view returns (uint) {

        bytes4 selector = contract1.getValue.selector;

        bytes memory data = abi.encodeWithSelector(selector, _x);
        (bool success, bytes memory returnedData) = address(contract1).staticcall(data);
        require(success);

        return abi.decode(returnedData, (uint256));
    }
}

contract MyContract {

    function function1() public {}

    function getBalance(address _address) public view returns (uint256){}

    function getValue (uint _value) public pure returns (uint) {
        return _value;
    }

}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!