ABI functions explained in SOLIDITY
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 :)