我最近在重新学solidity,巩固一下细节,也写一个“Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
欢迎关注我的推特:@0xAA_Science
WTF技术社群discord,内有加微信群方法:链接
所有代码和教程开源在github(1024个star发课程认证,2048个star发社群NFT): github.com/AmazingAng/WTFSolidity
ABI
(Application Binary Interface,应用二进制接口)是与以太坊智能合约交互的标准。数据基于他们的类型编码;并且由于编码后不包含类型信息,解码时需要注明它们的类型。
Solidity
中,ABI编码
有4个函数:abi.encode
, abi.encodePacked
, abi.encodeWithSignature
, abi.encodeWithSelector
。而ABI解码
有1个函数:abi.decode,用于解码abi.encode
的数据。这一讲,我们将学习如何使用这些函数。
我们将用编码4个变量,他们的类型分别是uint256
, address
, string
, uint256[2]
:
uint x = 10;
address addr = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
string name = "0xAA";
uint[2] array = [5, 6];
abi.encode
将给定参数利用ABI规则编码。ABI
被设计出来跟智能合约交互,他将每个参数转填充为32字节的数据,并拼接在一起。如果你要和合约交互,你要用的就是abi.encode
。
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
时。
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)"
。当调用其他合约的时候可以使用。
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字节的函数选择器
。
abi.encodeWithSelector
与abi.encodeWithSignature
功能类似,只不过第一个参数为函数选择器
,为函数签名
Keccak哈希的前4个字节。
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.decode
abi.decode
用于解码abi.encode
生成的二进制编码,将它还原成原本的参数。
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]));
}
我们将abi.encode
的二进制编码输入给decode
,将解码出原来的参数:
在以太坊中,数据必须编码成字节码才能和智能合约交互。这一讲,我们介绍了4种abi编码
方法和1种abi解码
方法。