15

I am new to solidity. I have a doubt. How and when to use these different abi encoding techniques that are there in solidity and what's the difference between them. For example, should we abi.encode or abi.encodePacked the input for keccak256 hashing function.

2 Answers 2

19

This depends what you require and what you want to ensure.

abi.encode will apply ABI encoding rules. Therefore all elementary types are padded to 32 bytes and dynamic arrays include their length. Therefore it is possible to also decode this data again (with abi.decode) when the type are known.

abi.encodePacked will only use the minimal required memory to encode the data. E.g. an address will only use 20 bytes and for dynamic arrays only the elements will be stored without length. For more info see the Solidity docs for packed mode.

For the input of the keccak method it is important that you can ensure that the resulting bytes of the encoding are unique. So if you always encode the same types and arrays always have the same length then there is no problem. But if you switch the parameters that you encode or encode multiple dynamic arrays you might have conflicts.

For example:

abi.encodePacked(address(0x0000000000000000000000000000000000000001), uint(0)) encodes to the same as abi.encodePacked(uint(0x0000000000000000000000000000000000000001000000000000000000000000), address(0))

and

abi.encodePacked(uint[](1,2), uint[](3)) encodes to the same as abi.encodePacked(uint[](1), uint[](2,3))

Therefore these examples will also generate the same hashes even so they are different inputs.

On the other hand you require less memory and therefore in most cases abi.encodePacked uses less gas than abi.encode.


EDIT: abi.encodePacked should stop to be used since there are conversions around to deprecate it in future versions of Solidity, see: https://github.com/ethereum/solidity/issues/11593

0

Let's declare a few variables:

➜ uint16 a = 2344
➜ uint16 b = 359
➜ address _addr = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

2344 = 0x928 and 359 = 0x167

When using abi.encode:

➜ abi.encode(a, b, _addr)
Type: dynamic bytes
├ Hex (Memory):
├─ Length ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000060
├─ Contents ([0x20:..]): 0x000000000000000000000000000000000000000000000000000000000000092800000000000000000000000000000000000000000000000000000000000001670000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4
├ Hex (Tuple Encoded):
├─ Pointer ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000020
├─ Length ([0x20:0x40]): 0x0000000000000000000000000000000000000000000000000000000000000060
└─ Contents ([0x40:..]): 0x000000000000000000000000000000000000000000000000000000000000092800000000000000000000000000000000000000000000000000000000000001670000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4
  • Splitting the single line output:

0x0000000000000000000000000000000000000000000000000000000000000928 0x0000000000000000000000000000000000000000000000000000000000000167 0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4

Here we can see that it follows the abi encoding specifications.

Now what if I have a scenario where I want to concatenate them or in the sense just pack all of them into one, why waste the extra space ?

When using abi.encodepacked()

➜ abi.encodePacked(a, b, _addr)
Type: dynamic bytes
├ Hex (Memory):
├─ Length ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000018
├─ Contents ([0x20:..]): 0x092801675b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000
├ Hex (Tuple Encoded):
├─ Pointer ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000020
├─ Length ([0x20:0x40]): 0x0000000000000000000000000000000000000000000000000000000000000018
└─ Contents ([0x40:..]): 0x092801675b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000

Output: 0x092801675b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000

Here we can see that all the variables has been packed together, both the uints and the address.


  • Hashing using abi.encodePacked() is not recommended because hash collisions can occur since the values are concatenated, Here's an example.

Using "AAA","BBB"

➜ abi.encodePacked("AAA","BBB")
Type: dynamic bytes
├ Hex (Memory):
├─ Length ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000006
├─ Contents ([0x20:..]): 0x4141414242420000000000000000000000000000000000000000000000000000
├ Hex (Tuple Encoded):
├─ Pointer ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000020
├─ Length ([0x20:0x40]): 0x0000000000000000000000000000000000000000000000000000000000000006
└─ Contents ([0x40:..]): 0x4141414242420000000000000000000000000000000000000000000000000000

Output: 0x4141414242420000000000000000000000000000000000000000000000000000

Now let's encode it with "AA", "ABBB"

➜ abi.encodePacked("AA","ABBB")
Type: dynamic bytes
├ Hex (Memory):
├─ Length ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000006
├─ Contents ([0x20:..]): 0x4141414242420000000000000000000000000000000000000000000000000000
├ Hex (Tuple Encoded):
├─ Pointer ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000020
├─ Length ([0x20:0x40]): 0x0000000000000000000000000000000000000000000000000000000000000006
└─ Contents ([0x40:..]): 0x4141414242420000000000000000000000000000000000000000000000000000

Output: 0x4141414242420000000000000000000000000000000000000000000000000000

It also produces the same output because it's being concatenated and packed.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.