ABI functions explained in SOLIDITY

Alain | Web3hackingLabs
3 min readSep 15, 2022

--

Do you know what are doing EXACTLY these 4 following functions?

abi.encode() abi.encodePacked() abi.encodeWithSignature() abi.decode()

If you want to master solidity, you absolutely need to know how these functions works.

1. What does ABI means ?

ABI stands for Contract Application Binary Interface (without the C)
This is the “main” way to interact with smart contracts on the blockchain.
It specifies a set of rule of how to call functions in a smart contract and how to get return value back.

2. Specifications

When you call a smart contract by sending a transactions on the blockchain.

The transaction is composed of several fields:

We will look in data field (know as msg.data in solidity)

It’s divided into 2 parts

  • The function selector (4 bytes long, or 8 digits), it contains the first 4 bytes of called function hash. It’s used for selecting the right function on the smart contract.
  • The rest are the function arguments encoded by ABI specifications

In solidity you can use several ABI functions to encode yourself the data.

ABI.ENCODE()

This function takes at least 1 argument and returns the encoded data (of type bytes )

All data are padded to 32 bytes.

So for example: uint8 a = 1 become.

  • 0x0000000000000000000000000000000000000000000000000000000000000001 (63 hex 0 digits + 1 hex 1 digit)

Static types like uint16,bytes32, address,bool are always encoded using this pattern

Here is some examples:

  • abi.encode(7) = 0x0000000000000000000000000000000000000000000000000000000000000007
  • abi.encode(7,7) = 0x00000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007
  • abi.encode(7,7,0x2511) = 0x000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000002511

But for dynamic types (like arrays, strings, bytes) it’s a bit different because when we decode the data, types may be ambiguous.

If you want to dive deeper, you can read the docs: https://solidity-fr.readthedocs.io/fr/latest/abi-spec.html

ABI.DECODE()

Basically abi.decode does the same as abi.encode() but in reverse.

It takes 2 arguments:

  • The data to decode. (of type bytes)
  • And a tuple representing how to decode the data.

And returns:

  • A tuple representing decoded values.

Here is a quick example:

pragma solidity ^0.8.0;contract Test {
function ex() external view returns(uint,uint,bytes memory){
bytes memory x = abi.encode(7,7,0x2511);
(uint a,uint b,bytes2 c) = abi.decode(x,(uint,uint,bytes2));
}
}

The 1st line (in the ex() function encodes the values 7, 7 and 0x2511)

The 2nd line decode() the value encoded and returns the decoded value in separate variables.

Of course it doesn’t work with encodePacked() .

ABI.ENCODEPACKED()

abi.encodePacked() does about the same thing than abi.encode() but without padding arguments, so

Examples:

function decode() external view returns(bytes memory) {
uint8 a = 1;
uint8 b = 2;
bytes2 c = 0x7777;
bytes memory x = abi.encodePacked(a,b,c);
return x;
}

Here is the value returned: 0x01027777.

  • 0x01 stands for a = 1.
  • 0x02 stands for b = 2.
  • 0x7777 stands for c = 0x7777.

Arguments aren’t padded.

ABI.ENCODEWITHSIGNATURE()

This is a slight variation of previous functions. It’s very useful for calling functions in a smart contract:

An example is worth 1000 words so let’s see:

function encode() external view returns(bytes memory) {
bytes memory x = abi.encodeWithSignature("withdraw(address)",0xaaC5322e456d45E7b6c452038836C5631C2AeBc0);
return x;
}

It returns: 0x51cff8d9000000000000000000000000aac5322e456d45e7b6c452038836c5631c2aebc0

This is exactly the same as abi.encode()

The difference is that the first arguments are hashed using keccak256 and the first 4 bytes of the result are appended to the result, the rest is processed as the same way using abi.encode().

So here:

  • 0x51cff8d9 is the signature of the function withdraw(address).
  • The rest are the encoded values using abi specifications.

This function is mainly used to call functions in another smart contract at a low level.

By specifying directly the signature and all the arguments. (instead using interfaces)

address addr;function go() external  {    (bool result, bytes memory data) = addr.call(abi.encodeWithSignature("withdraw(address)",0xaaC5322e456d45E7b6c452038836C5631C2AeBc0));}

Conclusion

I hope I leveled up your solidity skills, let’s see you in another article :)

--

--

Alain | Web3hackingLabs

Smart contract Auditor & Cybersecurity engineer, follow me on Twitter to get more value: https://rebrand.ly/twitter_medium