JSON-RPC Transactions – ABI-Derived JSON in `data` Parameter of Raw Transaction

abijson-rpcraw-transactionrpc

This is what I have so far, please correct me if I'm wrong:

The data parameter of a transaction is where we tell a deployed smart contract how we want to interact with it.
It contains a hexed JSON containing all the input for all the functions of the contract we want to interact with.
This JSON is coded using the contract's ABI, which is kind of a manual on what formats the contract can take and will return.
To fit in the data parameter, the JSON we want to send must be turned into hex.

Assuming all this is right, I'm looking for examples of non-hexed data JSONs to get a grasp on what I'm suposed to write using the ABI.

Best Answer

The data is not a hexadecimal representation of JSON data. Solidity has a custom format, described in the Contract ABI Specification. Essentially there are two different types of types, static and dynamic. Static types are types that always fit in 32 bytes (= 256 bits) of data, like uint256, address, etc. Dynamic types are types that don't fint in this 32 bytes of data, like bytes, string and arrays (including things like uint256[]).

Static types are encoded in the same order as your Solidity function. So let's say we have a function like this:

function foo(uint256 bar, address baz) external

The transaction data field starts with the function selector, specifying which function to call. In this case that's a68b44f8 (the first four bytes of the Keccak hash of foo(uint256,address). After that we put the values of bar in baz in that order, where each value is 32 bytes long. If we want to encode 12345 for bar and 0x4bbeeb066ed09b7aed07bf39eee0460dfa261520 for baz, the data becomes:

0xa68b44f8 (function selector)
0000000000000000000000000000000000000000000000000000000000003039 (bar)
0000000000000000000000004bbeeb066ed09b7aed07bf39eee0460dfa261520 (baz)

In the case of dynamic types, we encode a pointer to dynamic value instead. The value is appended to the end of the data, starting with the length of the value (32 bytes). So if we have a function like this:

function foo(uint256[] bar, address baz) external

And we want to encode [12345, 67890] for bar and the same address for baz, the data becomes:

0xd22ec959
0000000000000000000000000000000000000000000000000000000000000040 (pointer to bar)
0000000000000000000000004bbeeb066ed09b7aed07bf39eee0460dfa261520 (baz)
0000000000000000000000000000000000000000000000000000000000000002 (length of bar)
0000000000000000000000000000000000000000000000000000000000003039 (bar[0])
0000000000000000000000000000000000000000000000000000000000010932 (bar[1])

The Contract ABI Specification describes how different types in Solidity should be encoded. I wrote a more detailed explanation of transaction data which you can find here.

Related Topic