Search code examples
blockchainethereumsolidityetherscan

How to read to read Ethereum block transaction: data field containing bytes[] field


Is it possible to decode the input data of something like multicall(uint256 deadline, bytes[] data) by looking at the Ethereum block transaction data field?

A given ethereum transaction might contain a data field such as:

{
    "hash":"0x688c0f7e7ad5c59e4c3e24199e8e56753b95960324e3bb41da210eb0416e581d",
    "nonce":"0x73",
    "blockHash":"0x33fbe975a5a9c6a399c55f1bcb8130cd1d0c560722483dcd72d964020f156f7d",
    "blockNumber":"0xdc0ba9",
    "transactionIndex":"0xde",
    "from":"0xfcf9c19841cd8c6cd7114e638b02e62189a89601",
    "to":"0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
    "value":"0x0",
    "gasPrice":"0x4190ab000",
    "gas":"0x6621f",
    "data":"0x5ae401dc000000000000000000000000000000000000000000000000000000006236a988000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000e4472b43f3000000000000000000000000000000000000000000001edd40a09ca2037a8ce4000000000000000000000000000000000000000000000000008e6053de0d6216000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000fc09c7cfd9c175dd9423ca02ae1249579ab12f12000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c000000000000000000000000000000000000000000000000008e6053de0d6216000000000000000000000000fcf9c19841cd8c6cd7114e638b02e62189a8960100000000000000000000000000000000000000000000000000000000",
    "input":"0x5ae401dc000000000000000000000000000000000000000000000000000000006236a988000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000e4472b43f3000000000000000000000000000000000000000000001edd40a09ca2037a8ce4000000000000000000000000000000000000000000000000008e6053de0d6216000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000fc09c7cfd9c175dd9423ca02ae1249579ab12f12000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c000000000000000000000000000000000000000000000000008e6053de0d6216000000000000000000000000fcf9c19841cd8c6cd7114e638b02e62189a8960100000000000000000000000000000000000000000000000000000000",
    "type":"0x0",
    "v":"0x26",
    "s":"0x3397f6969aec9a7d1c168765985d9ff51d9b1bb4a84126ce561e1793bcc82af7",
    "r":"0xd692324d08b4ed25a2b7a37cc247b4f879d0e3bcb3d851fdc087f3ad40579edb"
}

According to this etherscan record, the above block is a transaction sent to Uniswap V3: Router 2. I want to decode the data field. Using the Ethereum ABI Spec, the contract's ABI can be used to find get the hash of all the functions in the smart contract. Comparing the first eight characters in the Keccak-256 hash of the function multicall(uint256,bytes[]), 5ae401dc, to the beginning of the data field above, identifies the function call.

However, at this point I cannot tell what the bytes[] array above refers to, and so cannot decode the rest of the data field. It seems like the only way to really decode this call is to comb through the uniswap source code to map the remaining bytes into something more human readable.

But somehow etherscan.io manages to show us that this transaction decodes to: enter image description here

Am I missing something, or is the only way to arrive at the list of transfers above to go through the contract data?


Solution

  • This Etherscan token transfer list comes from event logs generated by the transaction. Not directly from the data field.

    The multicall argument only specifies a list of addresses to perform internal transactions to, and their data fields.

    You can see the list of event logs in the transaction receipt. For example using web3js:

    const TRANSFER_EVENT_SIGNATURE = web3.utils.keccak256("Transfer(address,address,uint256)");
    const txHash = "0x688c0f7e7ad5c59e4c3e24199e8e56753b95960324e3bb41da210eb0416e581d";
    const txReceipt = await web3.eth.getTransactionReceipt(txHash);
    for (let log of txReceipt.logs) {
        if (log.topics[0] == TRANSFER_EVENT_SIGNATURE) {
            // This is an event log of the Transfer() event
            console.log(log);
        }
    }