51

I'm sending a transaction to an account with some data with the following command:

eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(1,"ether"),data:web3.toHex("http://localhost:8545")})

I'm getting this result, by extracting the log of the transaction (running with testRPC):

   tx hash         : 0xf654aee5ed23f9aeebd2d73c69c7b9c21a4862787966d09bcb09ed44efc1f252
   nonce           : 0
   blockHash       : 0x6ff8a0e3ac606abd2ede4331b82af52a0daa98448025051fb3b3d50f749aa49f
   blockNumber     : 1
   transactionIndex: 0
   from            : 0xf64918835bc21dff9e8210a807b3964d1be35dd0
   to              : 0x08f986b7535c2b72687d3cb193135f1c6e27c336
   value           : 1000000000000000000
   time            : 1483614904 Thu, 05 Jan 2017 11:15:04 GMT
   gasPrice        : 1
   gas             : 90000
   input           : 0x687474703a2f2f6c6f63616c686f73743a38353435

I want to decode the last line "input" and try to get "http://localhost:8545". I saw some work similar to my question here and here, but this is not working in my case. Moreover, i tried .toString('hex')) but it remains as hex.

How can I achieve this?

Thanks in advance

13 Answers 13

23

You can do web3.toAscii(transactionID.input) to return the data in readable format.

Read web3.toAscii

4
  • 7
    This works with a transaction send through commandline, but when I send transactions to my contract by using web3 contract.addBalance.sendTransaction(contractAddress, {from: senderAddress, to: contractAddress, gas:1000000, value: web3.toWei(7, "ether"),data:web3.toHex("http://localhost:8545")}) if I try to get web3.toAscii(transaction.input) it returns some random symbolic values Commented May 19, 2017 at 12:04
  • 1
    @SwapnilKumbhar check your data parameter.
    – niksmac
    Commented May 19, 2017 at 15:34
  • 1
    If you run into encoding issues using hexToAscii(), try using web3.hexToString()! Worked for me when neither toAscii or toUtf8 were behaving properly.
    – ohsully
    Commented Jun 7, 2019 at 21:10
  • 5
    depending on the version of web3 you're using, you may encounter the error web3.toAscii is not a function. To fix this issue, use web3.utils.toAscii() instead
    – adamc
    Commented May 4, 2020 at 14:26
24

If you've heard about the ethers js library, it provides a fantastic function named parseTransaction, I found it after a very long search as it wasn't mentioned in any online forums and I'm still learning a lot about this stuff.

But I found it to be the best for decoding any sort of contract and associated function call.

    const ethers = require('ethers');
    const ABI = require('./abi.json'); // Contract ABI
    const provider = ethers.getDefaultProvider();

    const inter = new ethers.utils.Interface(ABI);

    (async() => {
      const tx = await provider.getTransaction('0xa30e9e19967bd3307feeddcf99b26be0d804cdc0ade6929f3b9328a95e388b4c');
        const decodedInput = inter.parseTransaction({ data: tx.data, value: tx.value});
    
        // Decoded Transaction
        console.log({
            function_name: decodedInput.name,
            from: tx.from,
            to: decodedInput.args[0],
            erc20Value: Number(decodedInput.args[1])
          });        
    })();
22

In order to decode data you can use a library called abi-decoder

You will need to provide an ABI for the smart contract you want to decode, then just paste the input data. Very simple.

The only downfall would happen if your input data happens to be smart-contract creation "Constructor" then, in that case, you will have to change the library to accommodate with that.

3
13

UI Tools

Note for these you will need the ABI of the contract.

Node Packages

Recommendation

Use @ethersproject/abi, it's the safest approach of all. Ethers is battle-tested in thousands of Ethereum projects and there's likely no bug in its implementation of ABI encoding/ decoding.

6

We've made a nice tool for decoding. Just paste the transaction hash into input and get the result. Check it It works with Mainnet, Kovan, Ropsten and Rinkeby, but contract code must be verified in Etherscan. We use Etherscan API to get transaction and contract data, and web3 for decoding.

4
  • 4
    It is not recommended to write link only answers. If should be good if you add a little explanation about the page, like what tools/libraries you use for the decoder.
    – Ismael
    Commented Jan 29, 2018 at 18:46
  • 1
    @WTD It would be nice if it also accepted a raw transaction (signed and unsigned) as input, not only a tx hash. Commented Apr 11, 2018 at 12:18
  • We just updated our tool. Now it is easy to check the data(input and output) of all calls that execute during the transaction.
    – WTD
    Commented Oct 16, 2018 at 17:34
  • It seems that this tool is no longer working – pasting a txid and clicking on "Decode" does nothing.
    – dionyziz
    Commented Sep 5, 2021 at 8:49
6

This JavaScript/Node.js library can decode smart contract input data and contract creation input data given the JSON abi:

https://github.com/miguelmota/ethereum-input-data-decoder

5

A full definition of what you're trying to decode (the input data field) is here: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI. It's tough-going, but I was able to write some C++ that fully parses function arguments as recorded in the transactions. For your particular question, decoding the hex is correct, but for the more general case of the input data representing the function call and its parameters you have to decode the interface described in this document.

2
  • 5
    Your C++ doesn't happen to be open source does it?
    – Joël
    Commented Jan 23, 2017 at 8:18
  • 2
    Wow. It took me a year to answer, but "yes", it is open source (it wasn't in January). More information is here: quickblocks.io. Commented Dec 21, 2017 at 0:54
1

In python, this is done using decode_function_input Contract method:

contract.decode_function_input(transaction.input)

see:

1
  • I don't see a contract in the original question, just a transaction. Aren't these two different things? Commented Feb 7, 2020 at 0:11
1

Consider using truffle and abi-decoder:

# compile contracts to generate ABIs
truffle compile

# let's do it from truffle console
truffle console

const abiDecoder = require('abi-decoder');

# take ABIs of required contracts
const { abi: abi1 } = require('./build/contracts/sc1.json');
const { abi: abi2 } = require('./build/contracts/sc2.json');
..

# register ABIs
abiDecoder.addABI(abi1);
abiDecoder.addABI(abi2);
..

# take 'TX data' of transaction & decode it (take for example the first transaction in  100th block)

let input = (await web3.eth.getTransactionFromBlock(100, 0)).input

abiDecoder.decodeMethod(input);

/* result
{
  name: 'request',
  params: [
    { name: '_control', value: 'control', type: 'string' },
    { name: '_tId', value: 'tid', type: 'string' },
    { name: '_number', value: 'inumber', type: 'string' }
  ]
}
*/
1

I am using "web3": "^1.6.1",

const data = await web3.eth.getTransaction(txHash);
return web3.eth.abi.decodeParameters(
      // ERC20 transfer method args
      [
        { internalType: 'address', name: 'recipient', type: 'address' },
        { internalType: 'uint256', name: 'amount', type: 'uint256' },
      ],
      `0x${txData.input.substring(10)}`
    );

The first 4 bytes of input is method's signature

output:

 {
    "0": "0x45d8253c7980d5718C5Fa3626d446886Fd857CfE",
    "1": "160750000000000000000",
    "__length__": 2,
    "recipient": "0x45d8253c7980d5718C5Fa3626d446886Fd857CfE",
    "amount": "160750000000000000000"
  }

Docs: https://web3js.readthedocs.io/en/v1.2.11/web3-eth-abi.html#decodeparameters

0

Using tenderly is a good option. If you add a verified contract (or have the abi/sources to add to tenderly directly), you can supply raw transaction data and run a simulation with it, and it will show you what the input decodes as.

0

This is slightly off topic, but for those who are looking to decode topic data like I was, here is an example of how to do it for if your topic is an address:

web3.eth.abi.decodeParameter("address", event.topics[1])

Where topics[1] was an address.

0

Great thanks @Achal Singh for your answer - https://ethereum.stackexchange.com/a/82318/93117.

Here is a variation:

  • for Hardhat dev environments
  • generalised console.log outputs (rather than looking for specific fields),
  • made it for Ethers 6
  • print out the function that was called (if any and if none ... well then I've left it as a reader exercise to fix).
  • Loops over list of ABIs you have created in your artifacts folder (you need to create list ... reader exercise to automate this!)

I use a separate calling script parseTransactionCall.sh so i can pass a transaction as an arg.

NOTE my use of a separate tsconfig.hardhat.json so i can have contracts and front-end typescript in same project.

parseTransactionCall.sh:

#!/usr/bin/env sh

npx hardhat --tsconfig tsconfig.hardhat.json vars set TX $1
npx hardhat --tsconfig tsconfig.hardhat.json run scripts/parseTransaction.ts

parseTransaction.ts:

/*
 * Decode information from transaction.  Usage:
 * - CALL THIS USING parseTransactionCall.sh as it allows passing in TX as argument
 * > parseTransactionCall.sh <TX>
 * - Add any ABIs to the list at top of script.
 */
import * as JSONbig from "json-bigint";

import {vars} from "hardhat/config";

const { ethers } = require('hardhat');
const abiEvent = require("../src/artifacts/contracts/FunEvent.sol/FunEvent.json");
const abiFun = require("../src/artifacts/contracts/FunTShirts.sol/FunTShirts.json");
const abiNFT = require("../src/artifacts/contracts/FunNFT.sol/FunNFT.json");

type ABIENTRY = {
    name: string,
    abi: any
}
const abis: ABIENTRY[] = [
    {name: "Event", abi: abiEvent},
    {name: "Fun", abi: abiFun},
    {name: "NFT", abi: abiNFT},
]
const provider = ethers.provider;

const transaction = vars.get("TX") || "0x9c17159774507a669ce1f8a9453998db98bc366555baa62e5b89909c6c8bb582";

console.log(`transaction: ${transaction}`);

(async() => {
    const tx = await provider.getTransaction(transaction);
    console.log(`transaction tx: ${JSONbig.stringify(tx, null, 2)}`)
    if (tx == null) {
        return;
    }
    for (const abiEntry of abis) {
        console.log(`TRY ABI: ${abiEntry.name}`)
        const interfaceX:any = new ethers.Interface(abiEntry.abi.abi);
        const decodedInput = interfaceX.parseTransaction(tx);

        if (decodedInput == null) {
            console.log(`decodedInput is null so try next`)
            continue;
        }

        console.log(`  decodedInput: ${JSONbig.stringify(decodedInput, null, 2)}`);

        const abiFn = abiEntry.abi.abi.filter(a => a.name == decodedInput.name);

        // Decoded Transaction
        console.log(`Decoded Transaction`, {
            transaction: transaction,
            function_name: decodedInput.name,
            fragment: decodedInput.fragment,
            from: tx.from,
            to: tx.to,
            args: decodedInput.args,
            erc20Value: Number(decodedInput.value)
        });
        console.log(`Function from ABI: ${JSONbig.stringify(abiFn, null, 2)}`);
    }
})();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.