decoding input of a transaction without abi?
Asked Answered
S

1

5

If I am given an input like this

0x18cbafe5000000000000000000000000000000000000000000000001885c663d0035bce200000000000000000000000000000000000000000000000000f5666f7fdaa62600000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c0be713b48822271b362e9fac00479f5134172e80000000000000000000000000000000000000000000000000000000060e93fa900000000000000000000000000000000000000000000000000000000000000020000000000000000000000009813037ee2218799597d83d4a5b6f3b6778218d9000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

If I have a function signature like this "swapExactTokensForETH(uint256,uint256,address[],address,uint256)" is it possible to decode without the ABI?

I know the types from the signature

"types": [
    "uint256",
    "uint256",
    "address[]",
    "address",
    "uint256"
],

But when decoded it looks like this:

"inputs": [
    "1885c663d0035bce2",
    "f5666f7fdaa626",
    [
        "9813037ee2218799597d83d4a5b6f3b6778218d9",
        "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
    ],
    "c0be713b48822271b362e9fac00479f5134172e8",
    "60e93fa9"
 ]

Where index 0 is the uint256, index 1 is the next uint256, index 2 is address[], index 3 is address and index 4 is the uint256

So what logic is there to know that the array in index 2 is pulled from the two addresses at the end of the input from the transaction.

I am trying not to need the ABI to decode the input like this if that is possible

I know I can split up the input from the transaction like this:

[ '000000000000000000000000000000000000000000000001885c663d0035bce2', '00000000000000000000000000000000000000000000000000f5666f7fdaa626', '00000000000000000000000000000000000000000000000000000000000000a0', '000000000000000000000000c0be713b48822271b362e9fac00479f5134172e8', '0000000000000000000000000000000000000000000000000000000060e93fa9', '0000000000000000000000000000000000000000000000000000000000000002', '0000000000000000000000009813037ee2218799597d83d4a5b6f3b6778218d9', '000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' ]

Where the last two in that array from this are the addresses that show up for index 2 in the inputs array above. But in the order of the function signature is third, how do I know that it is pulled from the input from the end?

Is this where the abi comes in useful like decoding using web3? Or is this possible to decode without the abi?

Scavenge answered 17/9, 2021 at 1:24 Comment(0)
U
6

is it possible to decode without the ABI?

Only if you have at least the function signature or definition.

If you didn't know what input types there are, you wouldn't be able to tell whether this input

00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000

is a string foo

or three consecutive unsigned integers (decimal values):

  • 32
  • 3
  • 46332796673528066027243215619882264990369332300865266851730502456685210107904

An easy way (without the actual ABI JSON) if you know the input datatypes, is to use the web3 decodeParameters function.

const data = web3.eth.abi.decodeParameters(
    ["uint256", "uint256", "address[]", "address", "uint256"],
    "000000000000000000000000000000000000000000000001885c663d0035bce200000000000000000000000000000000000000000000000000f5666f7fdaa62600000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c0be713b48822271b362e9fac00479f5134172e80000000000000000000000000000000000000000000000000000000060e93fa900000000000000000000000000000000000000000000000000000000000000020000000000000000000000009813037ee2218799597d83d4a5b6f3b6778218d9000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
);

returns

Result {
  '0': '28272584972907691234',
  '1': '69073998366549542',
  '2': [
    '0x9813037ee2218799597d83D4a5B6F3b6778218d9',
    '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
  ],
  '3': '0xc0Be713B48822271b362e9Fac00479f5134172e8',
  '4': '1625898921',
  __length__: 5
}

The values are already encoded to their respective datatypes - so for example integers are decimal (and not hex as in your question). They are returned as strings simply because JS can sometimes run into rounding errors when working with large integers.

Mind that the first 4 bytes (8 hex characters; in your case 18cbafe5) is the function selector - not the parameter value. So you don't pass it to the decodeParameters() function.


Where the last two in that array from this are the addresses that show up for index 2 in the inputs array above. But in the order of the function signature is third, how do I know that it is pulled from the input from the end?

That's how the ABI works. If you have a dynamic type, its actual slot contains the offset pointing to the actual location of the values (including the prepended length). In your case that's 160 (hexa0) bytes.

All dynamic values are ordered at the end of the payload, prepended by a length of the section.

In your case, the 2 states that there are 2 values in this array, and the rest are the actual values of the array.

You can find more info about the ordering in the docs: https://docs.soliditylang.org/en/v0.8.7/abi-spec.html#formal-specification-of-the-encoding

Uriia answered 18/9, 2021 at 11:6 Comment(3)
Hey there @Petr Hejda I'm getting this error msg when I try your suggested soln above: ` data = web3.eth.abi.decodeParameters(["address", "address"], txn.input) AttributeError: 'Eth' object has no attribute 'abi' ` Could you help pls #69731280Lycopodium
this does not seem to be available in web3.py, only in the javascript versionFlaunty
In web3.py I ended up using the code from this answer successfully: ethereum.stackexchange.com/a/59288/93343Flaunty

© 2022 - 2024 — McMap. All rights reserved.