ABI encode and decode using solidity

João Paulo Morais
Coinmonks
Published in
4 min readMar 21, 2022

--

A smart contract is basically made up of state variables and functions. Some functions are private and can only be accessed from within the contract, however many functions are public and can be accessed from outside the contract. That is, applications (and people) can send data to the contract and retrieve data from the contract.

To send data to the contract, we need to send it in a way that the contract can read it. That is, they need to be encoded. The rule on how to perform such encoding is defined by the implementation of the Ethereum Virtual Machine (EVM). Details of the specification can be found here.

In this article we will learn a little about encoding rules, and how to use solidity to encode and decode the data that must be sent as parameters of a function.

Encoding the parameters of a function using abi.encode()

Solidity has a global variable called abi that has an encode method, so we can use it to encode the parameters of any function. Let’s start with a simple example. Let’s say we have the following function

function myFunction(address _myAddress, uint _myNumber)...

We are interested in encoding only the parameters of the function, that is, an address and an integer. We can use remix to create a function that does this.

Deploying this contract, and calling the function encode(…) with the following values for address and unsigned integer: (0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 127), we get the result

0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000007f

A quick analysis of the result shows us that it has 64 bytes. This is because the encoding occurs in multiples of 32 bytes:

0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4
000000000000000000000000000000000000000000000000000000000000007f

The first 32 bytes contain the address (20 bytes) and the other 32 bytes contain the integer, 7f. Encoding is always in hexadecimal, and 7f in hex is 127.

Decoding the parameters of a function using abi.decode()

Let’s now use solidity to decode the parameters of a function. Note that it was not necessary to identify the function we are dealing with, because its signature will precede the encoded parameters. To complicate the problem a little bit, let’s use dynamic variables as parameters.

Let’s use the following contract to decode a tuple of values: string, uint and string.

contract Encode {function encode(string memory _string1, uint _uint, string memory _string2) public pure returns (bytes memory) {
return (abi.encode(_string1, _uint, _string2));
}
function decode(bytes memory data) public pure returns (string memory _str1, uint _number, string memory _str2) {
(_str1, _number, _str2) = abi.decode(data, (string, uint, string));
}
}

Deploying, then calling the function encode(…) with the following parameters (João, 3, Paulo), we have the following return:

0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000054a6fc3a36f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055061756c6f000000000000000000000000000000000000000000000000000000

You might expect, perhaps, that the return would be 96 bytes (3 x 32), because we have 3 variables. However, 2 of the 3 variables are dynamic, and the encoding of dynamic variables is not so simple. Let me break the above value into 32 byte chunks of data.

0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000005
4a6fc3a36f000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000005
5061756c6f000000000000000000000000000000000000000000000000000000

I’m going to give a quick explanation of how the encoding is done in the case of strings. The first line refers to the first variable , the second to the second variable, and the third to the third variable. Since the second variable is of type value, we can directly retrieve its value, 3, in the second line.

In the case of a variable type string, the data contained in the first line is information about the first string. In this example, the data is 60 in hexadecimal, which is 96 in decimal. But what does it mean? It means that the information about the first string is found after 96 bytes from the beginning of the data.

After 92 bytes, the chunk of 32 bytes has a number: 5. This is the number of bytes the string occupies, starting on the next line, utf-8 encoded: 4a6fc3a36f. Converting from hex to UTF-8, we retrieve the word ‘João’ (By coincidence my name).

Following the same pattern, one can retrieve the third string after a0 bytes, that is, after 160 bytes from the beginning of the data. It says that it has also 5 bytes, and its value is 5061756c6f, the UTF-8 encoding for ‘Paulo’ (also my name, what the odds!).

Now that you’ve seen how to do this manually, let’s see how to do it via solidity. Using the function decode(bytes memory data) we wrote in the contract, one can retrieve the encoded information:

Other libraries can also be used to encode and decode variables, such as web3.eth.abi. There is also currently an online site that can be used for this, abi.hashex.org/.

Thanks for reading!

Join Coinmonks Telegram Channel and Youtube Channel learn about crypto trading and investing

Also, Read

--

--

João Paulo Morais
Coinmonks

Astrophysicist, full-stack developer, blockchain enthusiast. Unraveling cosmos mysteries by day, crafting the next Latin American novel by night.