Ethereum Blockchain – Signing Transactions & Understanding ABI – Part 2

ABI with Dynamic Data Types

In part 1 of this article we discussed ABI generation for JSON RPC submission. In that post we restricted ourselves to simple, static data types. With static data types, ABIs are much simpler to generate. You only need to append the parameters to the end of the ABI string. If you haven’t read part 1, then check it out here.

ABI generation with dynamic types is a little different.

What is a dynamic type? Arrays, strings, and basic bytecode items are examples of dynamic types.

address[]
bool[]
uint[]
bytes

The above are more specific examples of dynamic types. There are many more though.

When generating ABI strings which include dynamic types, we need to take into account the position in the string where the first example of that dynamic type will occur. We also need to take into account the number of elements a dynamic type contains.

The easiest way to think of how an ABI with dynamic parameters should be defined? Think in terms of dynamic offsets and static items appearing first, and then think of data portions of dynamic elements appearing last.

Easiest? I’ve probably confused you even more!

Let’s say we have this function:

runme(uint256 sales, address[] buyers, uint256 price)

And let us assume that the parameters are assigned the following values:

sales = 55

address[] = [0x10ED43C718714eb63d5aA57B78B54704E256024E, 0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F]

price = 7 

Here we have sales with integer value of 55, an address array with 2 address elements, and price with integer value 7.

Parameter 1 (sales) and parameter 3 (prices) are static. Parameter 2 (buyers) is dynamic (array of addresses).

The usual routine is the first get the 4 byte function selector. Feed the following string into a Keccak 256 converter:

runme(uint256,address[],uint256)

The first 4 bytes of the hashed string are:

d3c5daf5

Prepend 0x to create a valid hex string, and keep it handy. This will be the first segment of your ABI:

0xd3c5daf5

Note that this is the only segment which requires 0x to be prepended.

The second segment of the ABI is the hexadecimal value of the first static element. The parameter ‘sales’ with value of 55 becomes 37 when converted to hexadecimal. Remember, we need to pad this value to 32 bytes, giving us:

0000000000000000000000000000000000000000000000000000000000000037

This is the second segment of our ABI.

So far, we have created the following parts of our ABI with two elements, the function selector, and the sales static element with hex value of 37:

0xd3c5daf5
0000000000000000000000000000000000000000000000000000000000000037

Now we need to enter the third element.

Here we need to enter the offset, in bytes, of where the FIRST element of our dynamic data will appear later on in our ABI, not counting the first 4 bytes consumed by the function selector. The concept is a bit tricky to grasp at first, but once you’ve got it, then it’s easy as pie.

This third element is the distance, in bytes, that the first instance of this dynamic element occurs in the ABI. The first instance is going to appear after the second static element (price). Remember when I said to think in terms in terms of dynamic offsets and static items appearing first?

Let’s pretend that have already completed our ABI. We’ll introduce some empty elements for now to pad everything out for the purposes of calculation:

Our function selector:
0xd3c5daf5

First static element (sales)
0000000000000000000000000000000000000000000000000000000000000037

Offset in bytes, of where the FIRST element of our dynamic data will appear later on in our ABI. (null dummy data for now)
0000000000000000000000000000000000000000000000000000000000000000

Third static element (price - decimal value 7, hex value 7, padded to 32 bytes)
0000000000000000000000000000000000000000000000000000000000000007

Tying all of our elements together, we get the following (incomplete) ABI:

0xd3c5daf5
0000000000000000000000000000000000000000000000000000000000000037
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000007

Now we can start to figure out the offset for our address[] element. Not counting the first 4 bytes taken up by the function selector, we start at the first element of hex 32, and count the total number of bytes to the last element. Here, each line is 32 bytes. There are 3 lines. This gives us 96 bytes. The offset is therefore 96 bytes. Converting to 96 to hex give us 60. Padding to 32 bytes gives us:

0000000000000000000000000000000000000000000000000000000000000060

We replace the dummy null data we used for working in our ABI with this, which yields:

0xd3c5daf5
0000000000000000000000000000000000000000000000000000000000000037
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000007

Now we get to the data portion of our dynamic element. Remember how I said earlier that the data segment of dynamic elements appears last in the ABI?

First, we need to provide the number of elements in our array of addresses. Recall that the array is:

[ 0x10ED43C718714eb63d5aA57B78B54704E256024E,     
  0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F ]

The array has 2 elements. Converting decimal integer 2 to hex yields the same value – 2. Padding to 32 bytes gives us:

0000000000000000000000000000000000000000000000000000000000000002

We now append this value to our ABI string. Our ABI now is:

0xd3c5daf5
0000000000000000000000000000000000000000000000000000000000000037
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000007
0000000000000000000000000000000000000000000000000000000000000002

Finally, we need to provide the data values from the dynamic element. In this case, the data values are the two addresses in the array. Each address gets its own segment, again padded to 32 bytes.

You also need to remember to remove the 0x prefixes from the addresses.

This yields the following two segments:

00000000000000000000000010ED43C718714eb63d5aA57B78B54704E256024E
00000000000000000000000x05fF2B0DB69458A0750badebc4f9e13aDd608C7F

Putting it all together, we get the following complete set of ABI elements:

0xd3c5daf5
0000000000000000000000000000000000000000000000000000000000000037
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000007
0000000000000000000000000000000000000000000000000000000000000002
00000000000000000000000010ED43C718714eb63d5aA57B78B54704E256024E
00000000000000000000000x05fF2B0DB69458A0750badebc4f9e13aDd608C7F

To recap:

Line 1: Function selector. First 4 bytes of the Keccak256 hashed version of our function runme(uint256,address[],uint256)

Line 2: Hex version of static parameter (sales) decimal value 55 converted to hexadecimal value 37 and padded with 0s to 32 byte length.

Line 3: Hex version of offset, in bytes, where the FIRST element of our dynamic data will appear later on in our ABI, not counting the first 4 bytes consumed by the function selector.

Line 4: Hex version of static parameter (price) decimal value 7 converted to hexadecimal value 7 and padded with 0s to 32 byte length.

Line 5: Hex version of number of elements in the first dynamic element. In this case our first and only dynamic element is an address[] array with 2 elements. Padded with 0s to 32 byte length.

Lines 6 & 7: Hexadecimal addresses from our dynamic addresses array, padded with 0s to 32 bytes length.

Finally, we concatenate all of the elements into a single string, ready to pass to JSON RPC:

0xd3c5daf5000000000000000000000000000000000000000000000000000000000000003700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010ED43C718714eb63d5aA57B78B54704E256024E00000000000000000000000x05fF2B0DB69458A0750badebc4f9e13aDd608C7F

That’s it! Now that you have your hex parameters ready, we can now proceed with our JS code to make it all work!

Part 3 coming soon, where we conclude our journey into JSON RPC Eth transaction signing and submissions!

Leave a Reply