Ethereum and EVM Traces Explained
Tags: blockchain, web3, ethereum, tutorial
Published: 2023-06-08 21:43:39

As developers, it’s important we understand what actually happens under the hood during an Ethereum transaction. Trying to debug, improve, or secure code often has limited visibility into exactly what happens when a smart contract executes. That’s where **Ethereum Virtual Machine (EVM) traces** come into play. In this article we’ll look at the type of data traces give you, _how_ you can execute a trace, and options for tracing — manual, running archive nodes, and using the Infura Trace API.

Traces make an EVM developer’s life a whole lot easier. Let’s start with how they work.

## Understanding Ethereum Transactions

Ethereum transactions involve a lot of information. There’s basic info such as who initiates them, how much gas is used, and when they are called. Plus a set of more complex (and often more important) information such as what is transferred and what smart contract interactions took place. It can be a tough world out there for web3 devs trying to figure it all out!

Yes, you can use the transaction receipts to understand the basic info — status code, etc. — but there is still **a lack of specific, detailed information** that prevents you from totally understanding what happened and what steps you should take next to fix the issues when they inevitably arrive.

![On-chain and off-chain detail differences](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cvkzhnsdpx6lih25mggj.png)

**EVM traces** are the critical piece that gives you missing information — reasons for failure, internal calls, interactions with other contracts, transfers of assets, etc.

An EVM trace can give you a step-by-step analysis of a transaction, including those details not on-chain. Traces allow you to quickly and clearly see the actions taken by the Ethereum Virtual Machine during a specific transaction. This makes it easier to debug, analyze, and understand dapps/smart contracts and exactly what is going on behind the scenes. You can use the traces to see all those context-rich details you may need as a developer.

With trace information, you can:

* See the details of what’s happening in a transaction
* Analyze and optimize performance
* Dig into possible security violations and attack vectors
* Audit and monitor by tracking the flow of tokens/assets
* Get historical data for research or analytics tools

In short, you need traces when you need a _granular understanding of what’s happening on Ethereum_ — situations like **analysis, debugging Solidity, and improving security**.

So how do you execute a trace? A few common ways to see EVM traces include:

* You can see a trace API in action and access some of the results at [Etherscan.io](http://Etherscan.io).
* You can try to replay the transaction using a full Ethereum node. (Warning: this is complicated, slow, and requires a full node.)
* You can run a full archive node with traces enabled. (Warning: this is expensive, slow, and requires a full archive node.)
* You can use a third-party API (such as the [Infura Trace API](https://www.infura.io/platform/trace-api)) from your code.

Unfortunately, as you can see, a trace can be time and resource intensive. The Inura Trace API is a new option (just released to beta on June 6, 2023) — so let’s look at it in more detail.

## The Infura Trace API

The Infura Trace API is a new set of APIs that gives you a quick and programmatic way to get trace information.

From their website:

> “The [Trace API](https://www.infura.io/platform/trace-api?ref=infura.ghost.io) is an essential tool for Ethereum developers that provides the ability to gain deeper insights into the execution of smart contracts and transactions on Ethereum.
> 
> Developers can retrieve details from transaction data not logged on the blockchain, enabling them to debug, analyze, and perform other tasks and smart contract functions that require a granular understanding of transaction execution on Ethereum.”

Using it is easy enough — you just need (and probably already have) an Infura account. If you don’t, [sign up here](https://www.infura.io/). Note that the Trace API is only available for paid plans (so you’ll need to create a paid account if you currently have a free one), and it is only available on Ethereum and the testnets, Sepolia and Goerli.

## Trace API Options

Let’s look at the options. There are five types of calls you can make to the Trace API:

* **Trace Transaction** — Helps you understand what happened during a transaction (gas used, outputs, why it failed, etc). It’s great for optimizing performance. You provide the transaction hash and get back all the trace details.
* **Trace Block** — Traces all transactions in a given block. Helps you to analyze overall blockchain behavior.
* **Trace Call** — Gives you info on a specific call based on a transaction. You provide the to and from addresses and the block. Trace Call is a lot like Trace Transaction — except Trace Call also returns any state diff as well.
* **Trace Call Many** — Just like the above, but traces multiple calls at once.
* **Trace Filter** — You can provide a filter for specific parameters for what traces are returned, such as block #, to address, from address, etc). It can trace from block to block, or from address to address. Allows you to track wallets, contract activity, etc.

For more details, [check out the Infura documentation](https://docs.infura.io/infura/networks/ethereum/how-to/trace-transactions).

## Trace API Example

Let’s look at a quick example so you can see what it looks like in action. We’ll use the `trace_transaction` call to trace [this transaction](https://etherscan.io/tx/0xeb852958eeb1d026b10c6ad3c995520364b83929e1e0908801194f5b680d54ea).

**Request:**

```bash
curl --location 'https://mainnet.infura.io/v3/<INFURA API KEY> \
     --header 'Content-Type: text/plain' \
     --data '{ "id": 1, "method": "trace_transaction", "jsonrpc": "2.0", "params": ["0xeb852958eeb1d026b10c6ad3c995520364b83929e1e0908801194f5b680d54ea"] }'
```

**Response:**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "action": {
        "callType": "call",
        "from": "0xeecc9674d3600e3feb1b01b9ff7c72ac2e1c560f",
        "gas": "0x1a0c8",
        "input": "0xe2bbb158000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037b1d9c38a24b27c",
        "to": "0xfb063b1ae6471e6795d6ad1fc7f47c1cab1f3422",
        "value": "0x0"
      },
      "blockHash": "0x89b3ee89b30133dfd67888bf0baeea58b8a52b1beb983efcc7aa771a3fd489af",
      "blockNumber": 17370663,
      "error": "out of gas",
      "result": null,
      "subtraces": 4,
      "traceAddress": [],
      "transactionHash": "0xeb852958eeb1d026b10c6ad3c995520364b83929e1e0908801194f5b680d54ea",
      "transactionPosition": 110,
      "type": "call"
    },
. . .
}
```

This is a simple example — but you can see in the above that we’ve passed in a transaction hash and gotten back a trace for that transaction, including the reason it failed: **out of gas**. More complicated examples can return child traces, filtered results, and more.

Check out the docs and other tutorials [here](https://docs.infura.io/networks/ethereum/how-to/trace-transactions).

## Start using Trace APIs Today

EVM traces are a critical piece of your life as an Ethereum/EVM developer. They give you the details you need to debug, improve, and secure your code. Hopefully this article has explained _why_ you need traces, _how_ traces work, and a few ways you can execute a trace to get that info you need.