Learning how to navigate a block explorer like Etherscan and understand the data in these three key structures is fundamental to all Ethereum data analysis. This guide will cover all the essential parts and how to find the tables you need to query in SQL.
What's Inside a Transaction?
If you've ever made a transaction on Ethereum (or any smart contract-enabled blockchain), you've likely looked it up on a block explorer like etherscan.io and seen a pile of information. If you try to view the logs or traces (internal transactions), you might encounter these confusing pages.
Understanding how to read a transaction's details on a block explorer will be the foundation of all your Ethereum data analysis and knowledge. Let's cover all the parts and how to use them in SQL.
This guide focuses on a high-level understanding of these concepts. To manually decode this data, you would need to be familiar with how data is encoded (transactions/traces/logs all use the same method) and how to use Dune's byte array/hex functions to convert between different types.
By the end of this guide, you will be able to understand and navigate the data tables for any contract using this transaction table lookup query:
You should also learn to use my EVM Quickstart Dashboard to begin any contract analysis and how to analyze any Web3 protocol or product in five minutes after grasping the concepts here.
Transactions
A transaction is just the tip of the data iceberg. All traces and logs are called after the initial input data kicks off the top-level function. Let's first label all the fields you see on a block explorer's transaction page.
These are the same fields you see when querying ethereum.transactions on Dune. The key item to learn to identify is whether the "to" address is a contract. Usually, contracts are clearly marked. If it is a contract, there should be "input data" containing a function call.
In all these concepts, the first one to learn well is EOA vs. contract address. Contracts are deployed by EOAs and can be called in the "to" field of a transaction. If you click on an address, the explorer will show in the top left whether it is a contract or an account. On Dune, you can join the ethereum.creation_traces table to check if it's a contract. Note that only an EOA can be the "from" signer of a transaction.
It's important to understand the difference between data coming directly from the chain and data displayed by the browser/frontend. Everything on the blockchain is represented in hexadecimal (sometimes called binary or bytes), so a 1inch swap call will have an input data string like this.
The first 4 bytes (8 characters) are the "function signature," which is the Keccak hash of the function name and input types. Etherscan provides a nice "Decode" button for some contracts, allowing you to get this human-readable form.
As you can see, many variables are packed into that long hex string. Their encoding follows the smart contract's Application Binary Interface (ABI) specification.
The ABI is like the API documentation for a smart contract (like an OpenAPI spec); you can read more technical details here. Most developers will verify that their ABI matches the contract and upload the ABI for others to reference for decoding. Many contracts might be MEV/trading-related, where developers want to keep things closed-source and private—so we can't get any decoding from them.
On Dune, we have decoded tables based on submitted contract ABIs to the contracts table (i.e., ethereum.contracts). Functions and events are converted to byte signatures (ethereum.signatures) and then matched with traces and logs to provide decoded tables, such as uniswap_v2_ethereum.Pair_evt_Swap, which stores all swaps for all pair contracts created by the Uniswap v2 exchange factory. You can filter swaps for a specific pair by looking at the event's contract_address table.
On Dune, you will want to query this table for this function call: oneinch_ethereum.AggregationRouterV6_call_swap. You'll see this table name at the top of the table lookup in the query result from the beginning of this guide. We will use the same 1inch aggregator swap transaction in the upcoming sections on traces and logs. This is a great example because the router will swap tokens between multiple DEX contracts, so we'll get a variety of traces and logs to investigate.
Logs
Next, let's talk about event logs. Logs can be emitted at any point during a function call. Developers often emit logs at the end of a function, after all transfers/logic are completed without errors. Let's look at the Uniswap V3 swap event emitted in the previous transaction.
You'll see there are topic0, topic1, topic2, and data fields. topic0 is similar to a function signature, but it's 32 bytes instead of just 4 bytes (still hashed the same way). Events can have "indexed" fields for easier data filtering, and these can appear in topic1, topic2, or topic3. All other fields are encoded in the "data" object. Again, they follow the same encoding rules as transactions and traces. The "28" is the index of the event within the entire block. This can sometimes be useful when you want to find the first swap or transfer in a transaction.
To find the logic where this event was emitted, I need to dive into the Solidity code. I would click the linked address of the event, go to the contract tab, and search for emit Swap because I know all events are called with "emit" before they appear in the code.
This is the Uniswap V3 Pool contract, a factory contract created for each pair.
I can see this is emitted at line 786 of the contract as part of the "swap" function.
Being able to navigate to functions and events across contracts is a key skill you'll need to master to accurately understand the data you're querying. You don't need to be an expert in Solidity to navigate these files—just know how to understand the contract interface and when a function/event is called ("function" and "emit" are your keywords).
For a deep dive into sleuthing function and event code, check out this analysis of Sudoswap contracts and data.
Using the previous table lookup query, I can see that the table I should query is uniswap_v3_ethereum.Pair_evt_Swap, and it was emitted after the swap() function was called.
Traces (ethereum.traces)
Traces can quickly become very difficult to navigate, as nested calls between different contracts can get complex. Let's first understand the types of traces:
- CREATE: This trace is emitted when a new contract is deployed. You can deploy a contract directly at the top of a transaction, meaning there is no "to" address in the transaction data. You can also deploy a contract within a function call, hence the existence of contract factories. Check the
ethereum.creation_tracestable for a simpler view. - DELEGATECALL: When looking at transactions, this goes on your "ignore" list. Think of it as forwarding a request from one server to the next without changing any logic. This relates to proxies and storage; you can see more details here.
- CALL: This is the most common and generic trace. A call can be just an ETH value transfer with no contract involvement. It can also be any function call to any contract.
- STATICCALL: This is a function call that does not modify any state, used only for computation. Oracle price feeds, AMM price calculations, liquidation ratio checks, balance checks, etc., all happen in staticcalls. In Solidity, these are often called "view" or "pure" function types.
You also need to understand the trace_address column/index. This is the pattern you often see like [0,1,1,1,1]. Think of it like bullet points, where the number of digits in the array indicates the depth and order of the function call.
A (null) -- The first input of the transaction has a trace_address of []
CALLs B (0)
CALLs C (0,0)
CALLs D (1)
CALLs E (1,0)
CALLs F (1,0,0)
CALLs G (1,1)
CALLs H (2)From our previous internal transaction (traces) screenshot, you can see that Etherscan isn't the most friendly place to view traces. I prefer using Phalcon BlockSec, which expands the transaction like this.
Link to the explorer
This might look overwhelming, but it's actually a super simple way to explore all the functions, events, and parameters in the transaction flow. Once you can understand this, you can safely say you understand all the data in a transaction. Note that my table lookup query is almost an exact replica of this layout, and I was heavily inspired by them!
Note that on Dune, we automatically decode transaction calls and traces of the same function name into the same table. You might wonder if it's easy to connect events and traces/transactions in the nice order displayed in Phalcon. On Dune, you can connect the data by transaction hash, which you can't do on the explorer.
Diving Deeper into the Crypto Dark Forest
If you understand the concepts I've laid out in this guide, you're ready to dig deeper and write more complex queries. Using a variety of different tools to navigate data within transactions will be one of the key skills that sets you apart in this field.
👉 Explore advanced data analysis tools
Below is a guide involving various different browsers and tool tables. The data tool stack is also constantly evolving, and you should understand the purpose of using each tool.
Guide link
The more data tools you learn to use, the more flexibly you can build and communicate in this vast ecosystem! As always, if you have feedback or questions, my Twitter DMs are open.
Frequently Asked Questions
What is the difference between a transaction and a trace?
A transaction is the top-level operation submitted to the blockchain, containing basic information like sender, receiver, value, and input data. Traces represent the internal actions that occur during the execution of that transaction, such as contract calls or value transfers between contracts. Think of the transaction as the main story and traces as the detailed behind-the-scenes actions.
How do I find which table to query for a specific event on Dune?
First, identify the contract address that emitted the event. Then, use Dune's table search feature to look for the contract name or the event name. Most decoded event tables follow the naming pattern projectname_blockchain.ContractName_evt_EventName. You can also use the table lookup query method described in this article to find the correct table for any transaction.
Why are some events not decoded on block explorers?
Events remain undecoded when the contract's ABI is not publicly available or hasn't been submitted to the block explorer. Without the ABI, the explorer cannot interpret the hexadecimal data into human-readable parameters. Some developers choose to keep their contracts private, especially for MEV/trading-related strategies.
What's the best way to understand complex transaction flows?
Start with a block explorer like Etherscan for basic transaction details, then use advanced tools like Phalcon BlockSec to visualize the complete call flow with all internal transactions. Finally, use Dune Analytics to query and analyze the decoded data across multiple similar transactions to identify patterns and understand the broader context.
How can I learn to read smart contract code without being a developer?
Focus on understanding function signatures, event emissions, and common patterns rather than trying to comprehend the entire codebase. Look for "function" and "emit" keywords, follow parameter flows, and use block explorers that often display verified source code with comments. Start with simpler contracts before moving to more complex ones.
Are there any shortcuts to decoding transaction data?
Many blockchain analysis platforms like Dune have pre-decoded tables for popular contracts, which automatically transform hexadecimal data into readable information. You can also use online tools that decode function calls and events when you provide the ABI. For custom analysis, learning basic SQL queries against decoded tables is the most efficient approach.