Understanding how to read event logs is a crucial skill for developers building decentralized applications (dApps) on the Ethereum blockchain. Events provide a lightweight and efficient way for smart contracts to communicate state changes to off-chain systems. In this guide, we'll walk through how to fetch and decode Ethereum event logs using Go, leveraging the powerful go-ethereum
library.
Whether you're building a blockchain explorer, monitoring contract activity, or syncing on-chain data to a backend service, mastering event log handling unlocks real-time insights into smart contract behavior.
Setting Up the Filter Query
To retrieve event logs from the Ethereum blockchain, we begin by defining a filter query. This query specifies the criteria for which logs we want to retrieve. Using the ethereum.FilterQuery
struct from the go-ethereum
package, we can set parameters such as the block range and target contract address.
Here’s an example of initializing a filter query to fetch logs from a specific block:
query := ethereum.FilterQuery{
FromBlock: big.NewInt(2394201),
ToBlock: big.NewInt(2394201),
Addresses: []common.Address{
contractAddress,
},
}
This configuration targets logs generated during block 2394201
and restricts results to those emitted by a specific smart contract. The Addresses
field ensures we only receive logs from our contract of interest, reducing noise and improving performance.
👉 Discover how real-time blockchain data can power your next dApp
Fetching Logs Using FilterLogs
Once the query is defined, we use the FilterLogs
method of an Ethereum client instance to execute it. This method runs against a connected node—typically via WebSocket or HTTP—and returns all matching logs.
logs, err := client.FilterLogs(context.Background(), query)
if err != nil {
log.Fatal(err)
}
The returned logs
slice contains raw log entries, each including metadata like block hash, transaction hash, and encoded event data. However, these logs are ABI-encoded, meaning they aren’t immediately human-readable. To extract meaningful information, we must decode them using the contract's Application Binary Interface (ABI).
Decoding Event Data with ABI
Smart contract events are encoded according to their ABI specification. To interpret the data correctly in Go, we need to parse the contract’s ABI and use it to unpack the log contents.
First, import the compiled contract’s ABI:
contractAbi, err := abi.JSON(strings.NewReader(string(store.StoreABI)))
if err != nil {
log.Fatal(err)
}
With the parsed ABI, we can now decode each log entry. Suppose our contract emits an ItemSet
event with two bytes32
parameters: key
and value
. We define a matching Go struct and pass it to the Unpack
method:
for _, vLog := range logs {
event := struct {
Key [32]byte
Value [32]byte
}{}
err := contractAbi.Unpack(&event, "ItemSet", vLog.Data)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(event.Key[:])) // Output: foo
fmt.Println(string(event.Value[:])) // Output: bar
}
This process transforms opaque byte arrays into usable Go values, enabling further processing or storage.
Extracting Log Metadata
Beyond event data, each log entry includes valuable contextual information:
fmt.Println(vLog.BlockHash.Hex()) // e.g., 0x3404b8c...
fmt.Println(vLog.BlockNumber) // e.g., 2394201
fmt.Println(vLog.TxHash.Hex()) // e.g., 0x280201e...
These fields help correlate events with specific transactions and blocks, essential for auditing, debugging, or building time-series analytics dashboards.
Understanding Event Topics
In Solidity, when event parameters are marked as indexed
, they are stored as topics rather than in the log’s data field. Topics allow for efficient filtering at the node level.
Each log can have up to four topics:
- Topic[0]: The Keccak-256 hash of the event signature (e.g.,
ItemSet(bytes32,bytes32)
). - Topics[1–3]: Up to three indexed parameters.
Here’s how to extract and verify topics:
var topics [4]string
for i := range vLog.Topics {
topics[i] = vLog.Topics[i].Hex()
}
fmt.Println(topics[0]) // Event signature hash
You can manually compute the signature hash for validation:
eventSignature := []byte("ItemSet(bytes32,bytes32)")
hash := crypto.Keccak256Hash(eventSignature)
fmt.Println(hash.Hex()) // Should match topics[0]
This mechanism enables powerful filtering—such as listening only to events where a specific wallet address is involved—without downloading unnecessary data.
Core Keywords for SEO Optimization
This guide integrates the following core keywords naturally throughout the content:
- Ethereum event logs
- Read smart contract events
- Go Ethereum development
- Decode event data
- FilterLogs in Go
- ABI decoding in Go
- Blockchain event monitoring
- Ethereum smart contract logging
These terms align with common developer search queries and improve visibility in technical searches related to Ethereum and Go-based blockchain tooling.
Frequently Asked Questions
How do I connect to an Ethereum node in Go?
Use ethclient.Dial()
with an Infura or Alchemy WebSocket/HTTP endpoint. Example: client, err := ethclient.Dial("wss://rinkeby.infura.io/ws")
Can I filter logs by indexed event parameters?
Yes. Include topic filters in your FilterQuery
. For example, to filter by a specific wallet address as an indexed parameter, set the corresponding topic value in the query.
What’s the difference between log data and topics?
Data contains non-indexed event parameters in ABI-encoded form. Topics store the event signature and up to three indexed parameters, enabling efficient node-side filtering.
Why do I need the contract ABI to read logs?
The ABI defines how event data is structured and encoded. Without it, you cannot correctly unpack parameters into meaningful values in Go.
Can I read logs from multiple contracts at once?
Yes. Add multiple addresses to the Addresses
field in your filter query to monitor several contracts simultaneously.
Is there a limit to how many logs I can fetch at once?
Nodes may impose limits on result size. For large ranges, consider paginating your queries by splitting the block range into smaller intervals.
👉 Access advanced tools for Ethereum developers
Complete Working Example
Below is a full implementation that connects to the Rinkeby testnet, queries logs from a deployed contract, decodes them, and prints key details:
package main
import (
"context"
"fmt"
"log"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
store "./contracts"
)
func main() {
client, err := ethclient.Dial("wss://rinkeby.infura.io/ws")
if err != nil {
log.Fatal(err)
}
contractAddress := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
query := ethereum.FilterQuery{
FromBlock: big.NewInt(2394201),
ToBlock: big.NewInt(2394201),
Addresses: []common.Address{contractAddress},
}
logs, err := client.FilterLogs(context.Background(), query)
if err != nil {
log.Fatal(err)
}
contractAbi, err := abi.JSON(strings.NewReader(string(store.StoreABI)))
if err != nil {
log.Fatal(err)
}
for _, vLog := range logs {
fmt.Println("Block Hash:", vLog.BlockHash.Hex())
fmt.Println("Block Number:", vLog.BlockNumber)
fmt.Println("Transaction Hash:", vLog.TxHash.Hex())
event := struct {
Key [32]byte
Value [32]byte
}{}
err := contractAbi.Unpack(&event, "ItemSet", vLog.Data)
if err != nil {
log.Fatal(err)
}
fmt.Println("Key:", string(event.Key[:]))
fmt.Println("Value:", string(event.Value[:]))
var topics [4]string
for i := range vLog.Topics {
topics[i] = vLog.Topics[i].Hex()
}
fmt.Println("Event Signature Hash:", topics[0])
}
}
👉 Start building with real-time blockchain data today
By mastering event log retrieval and decoding in Go, developers gain a powerful tool for creating responsive, data-driven blockchain applications. Whether you're tracking token transfers, monitoring governance votes, or syncing NFT mints, this pattern forms the backbone of modern dApp infrastructure.