solidity-ABI编码函数

  1. solidity-ABI编码函数
  2. 测试合约代码
  3. abi.encode
  4. abi.encodePacked
  5. abi.encodeWithSignature
  6. abi.encodeWithSelector
  7. abi.decode

solidity-ABI编码函数

  • abi.encode(…) returns (bytes):计算参数的 ABI 编码。自动补全32位
  • abi.encodePacked(…) returns (bytes):计算参数的紧密打包编码

计算函数选择器和参数的 ABI 编码

  • abi.encodeWithSignature(string signature, …) returns (bytes)
  • abi.encodeWithSelector(bytes4(keccak256(signature), …)

测试合约代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract ABI_Test {
    uint256 x = 10;
    address addr = 0x7bf58A323CEA52c66E098B273D9FFA27e2bCd6C2;
    string str = "This is abi test";
    uint256[3] arr = [1, 2, 3];

    function test_encode() public view returns (bytes memory) {
        return abi.encode(x, addr, str, arr);
    }

    // 0x
    // 000000000000000000000000000000000000000000000000000000000000000a
    // 0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c2
    // 00000000000000000000000000000000000000000000000000000000000000c0
    // 0000000000000000000000000000000000000000000000000000000000000001
    // 0000000000000000000000000000000000000000000000000000000000000002
    // 0000000000000000000000000000000000000000000000000000000000000003
    // 0000000000000000000000000000000000000000000000000000000000000010
    // 5468697320697320616269207465737400000000000000000000000000000000

    function test_encodePacked() public view returns (bytes memory) {
        return abi.encodePacked(x, addr, str, arr);
    }

    // 0x
    // 000000000000000000000000000000000000000000000000000000000000000a
    // 7bf58a323cea52c66e098b273d9ffa27e2bcd6c2
    // 54686973206973206162692074657374
    // 0000000000000000000000000000000000000000000000000000000000000001
    // 0000000000000000000000000000000000000000000000000000000000000002
    // 0000000000000000000000000000000000000000000000000000000000000003

    function test_encodeWithSignature() public view returns (bytes memory) {
        return abi.encodeWithSignature("core(uint256,address,string,uint256[3])", x, addr, str, arr);
    }

    // 0x
    // 9545b557
    // 000000000000000000000000000000000000000000000000000000000000000a
    // 0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c2
    // 00000000000000000000000000000000000000000000000000000000000000c0
    // 0000000000000000000000000000000000000000000000000000000000000001
    // 0000000000000000000000000000000000000000000000000000000000000002
    // 0000000000000000000000000000000000000000000000000000000000000003
    // 0000000000000000000000000000000000000000000000000000000000000010
    // 5468697320697320616269207465737400000000000000000000000000000000

    function test_encodeWithSelector() public view returns (bytes memory) {
        return abi.encodeWithSelector(bytes4(keccak256("core(uint256,address,string,uint256[3])")), x, addr, str, arr);
    }
    // 0x
    // 9545b557
    // 000000000000000000000000000000000000000000000000000000000000000a
    // 0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c2
    // 00000000000000000000000000000000000000000000000000000000000000c0
    // 0000000000000000000000000000000000000000000000000000000000000001
    // 0000000000000000000000000000000000000000000000000000000000000002
    // 0000000000000000000000000000000000000000000000000000000000000003
    // 0000000000000000000000000000000000000000000000000000000000000010
    // 5468697320697320616269207465737400000000000000000000000000000000
    
    function test_decode() public view returns (uint256 _x, address _addr, string memory _str, uint256[3] memory _arr) {
        bytes memory result = test_encode();
        return abi.decode(result, (uint256, address, string, uint256[3]));
    }

    // uint256: _x 10
    // address: _addr 0x7bf58A323CEA52c66E098B273D9FFA27e2bCd6C2
    // string: _str This is abi test
    // uint256[3]: _arr 1,2,3
}

abi.encode

将给定参数利用ABI 规则编码。ABI 被设计出来跟智能合约交互,他将每个参数转填充为 32 位的数据,并拼接在一起。如果你要和合约交互,你要用的就是 abi.encode

编码的结果为:

## encode测试结果 
0x000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c200000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000105468697320697320616269207465737400000000000000000000000000000000

由于 abi.encode 将每个数据都填充为 32 位,中间有很多 0。将其分割开,则有:

0x
000000000000000000000000000000000000000000000000000000000000000a
0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c2
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000010
5468697320697320616269207465737400000000000000000000000000000000
  • 第1个32位存储了x,0xa16进制转为10进制就是10,它就是uint256 x = 10;
  • 第2个32位存储了addr,即address addr = 0x7bf58a323cea52c66e098b273d9ffa27e2bcd6c2;
  • 第3个32位存储了动态类型string的存储位置,0xc0即192位,即说明string类型存储在了192位的位置,以0位开始计数,往后推192位,即6个32,则第7个32位开始存储string类型的信息;
  • 第4个32位存储了arr的第一个值arr[0];
  • 第5个32位存储了arr的第一个值arr[1];
  • 第6个32位存储了arr的第一个值arr[1];
  • 第7个32位存储了str的长度,值为0x10,即10进制的16,是我们这里This is abi test的长度;
  • 第8个32位即是This is abi test的内容本身。

abi.encodePacked

将给定参数根据其所需最低空间编码。它类似 abi.encode,但是会把其中填充的很多 0 省略。比如,只用 1 位来编码 uint 类型。当你想省空间,并且不与合约交互的时候,可以使用 abi.encodePacked,例如算一些数据的 hash 时。

编码的结果为:

## encodePacked测试结果 
0x000000000000000000000000000000000000000000000000000000000000000a7bf58a323cea52c66e098b273d9ffa27e2bcd6c254686973206973206162692074657374000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

将其分割开,则有:

0x
000000000000000000000000000000000000000000000000000000000000000a
7bf58a323cea52c66e098b273d9ffa27e2bcd6c2
54686973206973206162692074657374
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003

可以看到这里没有要与EVM底层执行的格式适配,就仅仅是实际存储内容的拼接加密,所以没有多余的要凑齐256位字长的0值。

abi.encodeWithSignature

与 abi.encode 功能类似,只不过第一个参数为函数签名,比如”foo(uint256,address)”。当调用其他合约的时候可以使用。

编码的结果为:

## encodeWithSignature测试结果 
0x9545b557000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c200000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000105468697320697320616269207465737400000000000000000000000000000000

等同于在 abi.encode 编码结果前加上了 8 位的函数选择器。
将其分割开,则有:

0x
9545b557
000000000000000000000000000000000000000000000000000000000000000a
0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c2
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000010
5468697320697320616269207465737400000000000000000000000000000000

这里的第一行的9545b557是函数签名对core(uint256,address,string,uint256[3])进行keccak256运算后取前8位的结果,这样的结果作为一种函数选择器,作为函数的唯一标识。剩下的就跟abi.encode结果一样了,所以说,abi.encode是用于合约交互的,因为合约交互就涉及到函数的调用,函数的调用就需要abi.encode这种对数据的编码格式。

abi.encodeWithSelector

与 abi.encodeWithSignature 功能类似,只不过第一个参数为函数选择器,为函数签名 Keccak 哈希的前8位。

编码的结果为:

## encodeWithSelector测试结果 
0x9545b557000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c200000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000105468697320697320616269207465737400000000000000000000000000000000

将其分割开,则有:

0x
9545b557
000000000000000000000000000000000000000000000000000000000000000a
0000000000000000000000007bf58a323cea52c66e098b273d9ffa27e2bcd6c2
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000010
5468697320697320616269207465737400000000000000000000000000000000

这个从结果上是跟encodeWithSignature一样的,就是用法上存在差别:

# 用法差异
abi.encodeWithSignature("core(uint256,address,string,uint256[3])", x, addr, str, arr);
abi.encodeWithSelector(bytes4(keccak256("core(uint256,address,string,uint256[3])")), x, addr, str, arr);

abi.decode

abi.decode 用于解码 abi.encode 生成的二进制编码,将它还原成原本的参数。
2c18cb77-399b-438a-87a8-c77a0cfd1ab8-image.png


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com