Search code examples
ethereumpolygonethers.js

Decode eth_call response without a given ABI


My task is to call a view function from a contract, deployed on the Polygon network, using only the hexadecimal selector of the function. I did that through an eth_call. The response I get is the following: 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000cf43616c6c207468697320636f6e747261637420776974682066756e6374696f6e207369676e6174757265202730786464633234626533272070726f766964696e672061202775696e743235362720617267756d656e74206f662076616c75653a20323237383137393730393238363831363932383837323536343431383433323338313336303133363830363431383135313932383334363635383039363531373931303039303331383138383720416c736f2073656e6420736f6d65207765693a203432303030303030303030300000000000000000000000000000000000

How do I decode this response, considering, that no ABI is available?


Solution

  • From the Solidity docs:

    The encoding is not self describing and thus requires a schema in order to decode.

    So it's impossible to decode this ABI with 100% certainty. However, given the context we are able to make an educated guess about the ABI schema.

    To illustrate, break up the response into 32 byte segments (offset labels are provided for ease of reading):

    000: 0000000000000000000000000000000000000000000000000000000000000020
    020: 00000000000000000000000000000000000000000000000000000000000000cf
    040: 43616c6c207468697320636f6e747261637420776974682066756e6374696f6e
    060: 207369676e6174757265202730786464633234626533272070726f766964696e
    080: 672061202775696e743235362720617267756d656e74206f662076616c75653a
    0a0: 2032323738313739373039323836383136393238383732353634343138343332
    0c0: 3338313336303133363830363431383135313932383334363635383039363531
    0e0: 373931303039303331383138383720416c736f2073656e6420736f6d65207765
    100: 693a203432303030303030303030300000000000000000000000000000000000
    

    Looking at the data presented this way, this ABI is almost certainly encoding a string type:

    • Slot 000 appears to be a pointer to slot 020.
    • Slot 020 appears to contain the length of the data (0xcf == 207).
    • The 207 bytes starting from slot 040 and ending midway in slot 100 are legible English when UTF-8 decoded.

    You can decode the 207 bytes in JavaScript like so:

    const str = '43616c6c207468697320636f6e747261637420776974682066756e6374696f6e207369676e6174757265202730786464633234626533272070726f766964696e672061202775696e743235362720617267756d656e74206f662076616c75653a20323237383137393730393238363831363932383837323536343431383433323338313336303133363830363431383135313932383334363635383039363531373931303039303331383138383720416c736f2073656e6420736f6d65207765693a20343230303030303030303030';
    new TextDecoder().decode(Uint8Array.from(str.match(/.{2}/g), v => parseInt(v, 16)));
    

    The result of the above code:

    "Call this contract with function signature '0xddc24be3' providing a 'uint256' argument of value: 22781797092868169288725644184323813601368064181519283466580965179100903181887 Also send some wei: 420000000000"