# ABIEncode

By [startha](https://paragraph.com/@starha) · 2023-10-20

---

`ABI`(Application Binary Interface,应用二进制接口)是与以太坊智能合约交互的标准。数据基于他们的类型编码；并且由于编码后不包含类型信息，解码时需要注明它们的类型。

Solidity中`ABI编码`有4个函数：`abi.encode`，`abi.encodePacked`，`abi.encodeWithSignature`，`abi.encodeWithSelector`。而`ABI编码`有一个函数：`abi.decode`，用于解码`abi.encode`的数据。

[ABI编码](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abi%E7%BC%96%E7%A0%81)
-------------------------------------------------------------------------------------------------------------

我们将编码4个变量，他们的类型分别是`uint256`（别名 uint）, `address`, `string`, `uint256[2]`：

        uint x = 10;
        address addr = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
        string name = "0xAA";
        uint[2] array = [5, 6]; 
    

### [abi.encode](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abiencode)

将给定参数利用[ABI规则](https://learnblockchain.cn/docs/solidity/abi-spec.html)编码。`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](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abiencodepacked)

将给定参数根据所需最低空间编码。它类似`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](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abiencodewithsignature)

与`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](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abiencodewithselector)

与`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解码](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abi%E8%A7%A3%E7%A0%81)
-------------------------------------------------------------------------------------------------------------

### [abi.decode](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abidecode)

`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`，将解码出原来的参数：

![](https://storage.googleapis.com/papyrus_images/b18268edc332d8c918365786a53283d063c1e7c6adbc473ff4f1d44df59da045.png)

### [在remix验证结果](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#%E5%9C%A8remix%E9%AA%8C%E8%AF%81%E7%BB%93%E6%9E%9C)

部署ABIEncode合约，验证各种编码

![](https://storage.googleapis.com/papyrus_images/90952836b3373a416c6301144b42b10d90a1b61f03942f59e3807b0874f6c490.png)

[ABI的试用场景](https://github.com/starthapro/starha_solidity/blob/main/26_ABIEncode/readme.md#abi%E7%9A%84%E8%AF%95%E7%94%A8%E5%9C%BA%E6%99%AF)
-------------------------------------------------------------------------------------------------------------------------------------------

1.  在合约开发中，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));
    

1.  ethers.js中常用ABI实现合约的导入和函数调用。
    

        const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
        /*
            * Call the getAllWaves method from your Smart Contract
            */
        const waves = await wavePortalContract.getAllWaves();
    

1.  对不开源合约进行反编译后，某些函数无法查到函数签名，可通过ABI进行调用。
    

*   0x533ba33a() 是一个反编译后显示的函数，只有函数编码后的结果，并且无法查到函数签名
    

![](https://storage.googleapis.com/papyrus_images/a40cc6e9d9c273907df1b7a1c9b31803c41cd9acccadf4ab01fe93c9f0800337.png)

![](https://storage.googleapis.com/papyrus_images/15516f39fdbb65885c18485eb819b183a51b33eb4cf98bf05b072c66263537b8.png)

*   这种情况无法通过构造interface接口或contract来进行调用
    

![](https://storage.googleapis.com/papyrus_images/66d3764b820ed2dbeec5f5dc142f95b72f8823fbbaf1490d8f3b494b8bfb79d0.png)

这种情况下，就可以通过ABI函数选择器来调用

        bytes memory data = abi.encodeWithSelector(bytes4(0x533ba33a));
    
        (bool success, bytes memory returnedData) = address(contract).staticcall(data);
        require(success);
    
        return abi.decode(returnedData, (uint256));

---

*Originally published on [startha](https://paragraph.com/@starha/abiencode)*
