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 生成的二进制编码,将它还原成原本的参数。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com