0%

solidity | ABI 进阶

在此之前你可以先看。

ABI 编码函数

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

通过 ABI 编码函数可以在不用调用函数的情况下,获得 ABI 编码值,下面通过一段代码来看看这些方式的使用:

我们将用编码 4 个变量,他们的类型分别是 uint256, address, string, uint256[2]

1
2
3
4
uint x = 10;
address addr = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
string name = "0xAA";
uint[2] array = [5, 6];
  • abi.encode

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

1
2
3
function encode() public view returns(bytes memory result) {
result = abi.encode(x, addr, name, array);
}

编码的结果为 0x000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007a58c0be72be218b41c608b7fe7c5bb630736c7100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043078414100000000000000000000000000000000000000000000000000000000 ,由于 abi.encode 将每个数据都填充为 32 字节,中间有很多0

  • abi.encodePacked

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

1
2
3
function encodePacked() public view returns(bytes memory result) {
result = abi.encodePacked(x, addr, name, array);
}

编码的结果为 0x000000000000000000000000000000000000000000000000000000000000000a7a58c0be72be218b41c608b7fe7c5bb630736c713078414100000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006 ,由于 abi.encodePacked 对编码进行了压缩,长度比 abi.encode 短很多。

  • abi.encodeWithSignature

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

1
2
3
function encodeWithSignature() public view returns(bytes memory result) {
result = abi.encodeWithSignature("foo(uint256,address,string,uint256[2])", x, addr, name, array);
}

编码的结果为 0xe87082f1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007a58c0be72be218b41c608b7fe7c5bb630736c7100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043078414100000000000000000000000000000000000000000000000000000000,等同于在 abi.encode 编码结果前加上了 4 字节的函数选择器说明。 说明: 函数选择器就是通过函数名和参数进行签名处理(Keccak–Sha3)来标识函数,可以用于不同合约之间的函数调用

  • abi.encodeWithSelector

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

1
2
3
function encodeWithSelector() public view returns(bytes memory result) {
result = abi.encodeWithSelector(bytes4(keccak256("foo(uint256,address,string,uint256[2])")), x, addr, name, array);
}

编码的结果为 0xe87082f1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007a58c0be72be218b41c608b7fe7c5bb630736c7100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043078414100000000000000000000000000000000000000000000000000000000 ,与 abi.encodeWithSignature 结果一样。

ABI解码

  • abi.decode

abi.decode 用于解码 abi.encode 生成的二进制编码,将它还原成原本的参数。

1
2
3
function decode(bytes memory data) public pure returns(uint dx, address daddr, string memory dname, uint[2] memory darray) {
(dx, daddr, dname, darray) = abi.decode(data, (uint, address, string, uint[2]));
}
请我喝杯咖啡吧~