# ABIEncode **Published by:** [startha](https://paragraph.com/@starha/) **Published on:** 2023-10-20 **URL:** https://paragraph.com/@starha/abiencode ## Content ABI(Application Binary Interface,应用二进制接口)是与以太坊智能合约交互的标准。数据基于他们的类型编码;并且由于编码后不包含类型信息,解码时需要注明它们的类型。 Solidity中ABI编码有4个函数:abi.encode,abi.encodePacked,abi.encodeWithSignature,abi.encodeWithSelector。而ABI编码有一个函数:abi.decode,用于解码abi.encode的数据。 ABI编码 我们将编码4个变量,他们的类型分别是uint256(别名 uint), 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); } 编码结果为0x000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043078414100000000000000000000000000000000000000000000000000000000,由于abi.encode将每个数据都填充为32字节,中间有很多0。 abi.encodePacked 将给定参数根据所需最低空间编码。它类似abi.encode,但是会把其中填充的很多0省略。比如,只有一个字节来编码uint8类型。当你想省空间,并且不与合约交互的时候,可以使用abi.encodePacked,例如算一些数据的hash时。 function encodePacked() public view returns(bytes memory result) { result = abi.encodePacked(x, addr, name, array); } 编码结果为0x000000000000000000000000000000000000000000000000000000000000000a5b38da6a701c568545dcfcb03fcb875f56beddc43078414100000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006,由于abi.encodePacked对编码进行了压缩,长度比abi.encode短很多。 abi.encodeWithSignature 与abi.encode功能类似,只不过第一个参数为函数名,比如"foo(uint256,address,string,uint256[2])"。当调用其他合约的时候可以使用。 function encodeWithSignature() public view returns(bytes memory result) { result = abi.encodeWithSignature("foo(uint256,address,string,uint256[2])", x, addr, name, array); } 编码的结果为0xe87082f1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043078414100000000000000000000000000000000000000000000000000000000,等同于在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); } 编码结果为0xe87082f1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043078414100000000000000000000000000000000000000000000000000000000,与abi.encodeWithSignature结果一样。 ABI解码 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,将解码出原来的参数: 在remix验证结果 部署ABIEncode合约,验证各种编码 ABI的试用场景 在合约开发中,ABI长配合call来实现对合约的底层调用 bytes4 selector = contract.getValue.selector; bytes memory data = abi.encodeWithSelector(selector, _x); (bool success, bytes memory returnedData) = address(contract).staticcall(data); require(success); return abi.decode(returnedData, (uint256)); ethers.js中常用ABI实现合约的导入和函数调用。 const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer); /* * Call the getAllWaves method from your Smart Contract */ const waves = await wavePortalContract.getAllWaves(); 对不开源合约进行反编译后,某些函数无法查到函数签名,可通过ABI进行调用。 0x533ba33a() 是一个反编译后显示的函数,只有函数编码后的结果,并且无法查到函数签名 这种情况无法通过构造interface接口或contract来进行调用 这种情况下,就可以通过ABI函数选择器来调用 bytes memory data = abi.encodeWithSelector(bytes4(0x533ba33a)); (bool success, bytes memory returnedData) = address(contract).staticcall(data); require(success); return abi.decode(returnedData, (uint256)); ## Publication Information - [startha](https://paragraph.com/@starha/): Publication homepage - [All Posts](https://paragraph.com/@starha/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@starha): Subscribe to updates