Ethereum and EVM Traces Explained

Ethereum and EVM Traces Explained

Use Infura's Trace API to help debug and analyze your dapps

·

5 min read

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

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.

  • 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) 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 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. 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.

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.

Request:

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:

{
  "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.

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.