May 3, 2023 Ali Ashar

Guide to Decode and Analyze Ethereum Transactions

Decoding an Ethereum transaction can reveal valuable information about the transaction, including the sender, recipient, amount, and any data attached to the transaction.

If you want to do anything interesting in Ethereum, you will have to interact with smart contracts. Whether you want to send ERC20 tokens like LINK or DAI, trade non fungible tokens like digital art, or earn interest on your crypto and interact with other DeFi products, a smart contract is always involved.

However, smart contracts are becoming increasingly complex. From proxy contracts to allow for upgradeability, to multi-send contracts that allow for the batching of transactions, what we are seeing is a rapid evolution of features that allow for the movement of one’s assets.

You’ve also got DeFi transactions, they are often not generated by you or your code so you need to verify it’s doing what you think it is. Additionally, understanding a method call is useful for:

  • Seeing and knowing that the contract methods calls are what you expect
  • Seeing the parameter types and values that allow you to understand how the contracts work
  • Analysing a contract to produce stats on method calls
  • Tracking interactions with key addresses
  • Writing your own rules to decide on which transactions to sign (more on that later)
  • Having fun, if you like that sort of thing

Let’s dig in.

We’re going to start with a Gnosis Safe contract and a transaction a user would send that the Gnosis safe Dapp would generate.

Gnosis Safe is a popular wallet contract implemented by Gnosis. A multi-user organisation that wants to use Gnosis Safe will first have to define a list of accounts and a required threshold of signatures needed to send a transaction. Users will submit the transaction to the Safe, which will authorise the execution of the transaction only when the threshold required is reached. This way, the users have a tighter control of their funds.

The Gnosis safe has lots of features including offline signing (which saves on gas fees) but for now we’re going to take a look at the basic method for executing a transaction.

‏‏‎ ‎

First port of call: Start with Etherscan

Let’s start with a transaction as seen by Etherscan and see if we can get the output they do.

Decoding an Ethereum Transaction

The transaction above is from a user that has interacted with the Gnosis Safe smart contract on the Ethereum mainnet and we can see the details directly on Etherscan.

Let’s start by looking at the “data” field. This is the field that Ethereum uses to decide what method to call on the “to” (or “Interacted with” in Etherscan) contract.

Here’s the full data payload below. As you can see, a lot of hex values. This looks difficult to understand but with a little skill and knowledge we can break this up into the important parts.

Decoding an Ethereum Transaction


Start at the beginning

All “data” fields on an Ethereum transaction contain the name of the method that is to be called. This is the first thing we should look at. Etherscan shows this for us in the “Input Data” section:

In this section we can see what Etherscan has given us:

Function: execTransaction(address to, uint256 value, bytes data, uint8 operation, uint256 safeTxGas, uint256 dataGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes signatures)


So, how did they get this?

Well, the first thing we need is to use the first 4 bytes (first 8 hex characters) of the input data, which is: 6a761202

This hex value is derived from taking the method name and its argument types, removing any whitespace, taking the `keccak hash of the result, and then taking the first 4 bytes of it and displaying it in hex. With me so far?

Note: The parameter names are not included in the hash, only the types. This means that different contracts can have the same methodId’s but call their parameters differently and potentially have different logic. Thus it is sensible to keep track of the contract which the method belongs to. There is a handy website that tracks these. Check out and enter the hash 6a761202. At time of writing there is only 1 registered method.

We have the source of the Gnosis contract so we know what the method name and the parameters are. We grab them from here.

In JavaScript, the following will output the keccak hash of our method plus parameters:

// import a keccak decoder or write your own
import { keccak } from "../decoder/keccak";

const method = `execTransaction(address to, uint256 value, bytes data, uint8 operation, uint256 safeTxGas, uint256 dataGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes signatures)`

// regex pattern to remove the word before a comma or closing bracket
export const removeArgsFromMethod = (method: string) => {
return method.replace(/\s\w+(,|\))/g, (_, commaOrBracket) => commaOrBracket);

// remove the argument names and remove any spaces
const preparedMethod = removeArgsFromMethod(method).replace(/s/g, "")

// keccak hash of the method
const keccakHashOfMethod = keccak(Buffer.from(preparedMethod))

// first 4 bytes
const methodId = keccakHashOfMethod.slice(0,4).toString("hex") // 6a761202

Note: If you don’t have the source code of the smart contract then you won’t be able to generate the hash to check and it will be more difficult calculate the exact parameter types.

The output from that Javascript is 6a761202 which matches the first 4 bytes (8 hex characters) of the data payload. Great. We’ve identified that the method being called is in fact “execTransaction” with the 10 parameters.

Now, if you go back to Etherscan and click on the `Decode Input Data button you can see how Etherscan has taken the data payload and broken it up into the 10 parameters including the types. Again, how have they done this?

Screenshot 2020-11-28 at 13.59.59

‏‏‎ ‎

Get a handle on parameters

Our next job is to break up all the parameters that have been passed to the execTransaction and see what they are.

Before we do that a quick note on bytes to hex conversion. Very simply 1 byte of data = 2 characters of Hex. So, whenever you see a string of hex characters, you can divide the number of characters by 2 to get the size in bytes. Any “0x” at the beginning is ignored in the actual calculation.

Now, on to decoding the method parameters.

Let’s have a look at the data again but this time we split it into 32 byte (64 Hex characters) strings. Why? Because ethereum uses 32 bytes blocks and nearly all primitive types are 32 bytes. There are a couple of exceptions though, such as the “bytes” type which we’ll see later.

So, once we strip off the methodId (0x6a761202) and then break the remaining data into 32 bytes (or 64 characters) chunks we can start to see something more interesting.


Decoding an Ethereum Transaction

These are the values of the input parameters of the execTransaction function. It’s not quite straightforward so let’s work through each 32 byte value in turn. Column 3 of the table above shows where each of the values maps to the parameters of the execTransaction function.

For example, the first parameter (the `”to” parameter):


is of type address.

Ethereum addresses are 20 byte values so it has been zero-padded to fit into a 32 byte value. To get the actual address, we just need to extract the last 20 bytes and prefix 0x.

In this case it becomes:


(Hmm…this is the Tether contract)

In fact, removing the zero padding is how to decode most of the simple parameters like uint8, unit256, etc…

The next parameter is the “value”. This, we assume, is how much value needs to be transferred from the Gnosis safe.  Its value is zero.  (This is the value of Ether remember)

The next parameter is interesting, it’s of types bytesbytes is a variable-length so how long is it?  The value is:


This value represents the offset in hex of bytes from the `methodId` of where the actual data begins. Think of it as a pointer, redirecting you to where you should look for this information. So we need to convert 0x140 bytes into hex characters.

0x140 (bytes in hex) = 320 bytes in decimal

320 bytes in decimal x 2 = 640 characters in hex

so the data begins 320 bytes from the start, and thus it is 640 hex characters along, or 10 rows along. In JavaScript, it will look as follows:

const numberOfBytesInHex = 140

const numberOfHexCharacters = parseInt(numberOfBytesInHex, 16) * 2; // = 640

Going to the 10th row the first 64 characters represent the length of the data (this requires the same conversion done above).


is decoded into a length of 136 hex characters.

So finally on the 11th row the data begins and ends after 136 characters. Which means our data field is the following:


So in summary, for a bytes field, the first 32 bytes is a pointer to where the data starts, once there, the next 32 bytes tells you how long the data is. Then you read that many bytes. If the data doesn’t end in a 32 byte block, it will be padded so whatever is next, will start in the next 32 byte block.

Looking at the value again:


It looks a little familiar… It has a 4 byte methodId and then 2 x 32 byte chunks (2 lots of 64 hex characters). It’s another method call!

In fact, we can recognise immediately that this transaction is in itself an ERC20 transfer transaction as it has the famous a9059cbb method signature.

(Tip: go to and type in transfer(address,uint256) and you will see the first 8 characters are in fact a9059cbb`)

So, we can see that this Gnosis transaction is to call another contract of address 0xdAC17F958D2ee523a2206206994597C13D831ec7 with the “data” of:


Which is an ERC20 “transfer” call. We could then use what we learnt above to recursively calculate the details of this data call by extracting the methodId bytes and breaking up the data and looking at the parameters.

Take a look and see if you can work out the recipient and value of tokens sent to the Tether contract.

, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Stay in touch

Join the community