go-ethereum version: v1.12.2
Ethereum's continuous evolution brings protocol upgrades, impacting how developers interact with the blockchain. If you've consulted resources like "Ethereum Development with Go", you may notice that older code snippets for retrieving the sender (From) field in transactions no longer work. This guide demonstrates the updated approach for the latest go-ethereum client.
Prerequisites
- Basic understanding of Ethereum transactions
- Familiarity with Go programming
- Installed
go-ethereumlibrary (github.com/ethereum/go-ethereum)
Step 1: Fetching a Block
To access transactions, start by retrieving a block object:
package main
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/ethclient"
"log"
"math/big"
)
func main() {
ethereumNodeEndpoint := "https://cloudflare-eth.com"
client, err := ethclient.Dial(ethereumNodeEndpoint)
if err != nil {
log.Fatalf("Failed to connect to Ethereum node [%s]: %s", ethereumNodeEndpoint, err)
}
ctx := context.TODO()
latestBlockNumber, err := client.BlockNumber(ctx)
if err != nil {
log.Fatal("Failed to fetch latest block number: ", err)
}
fmt.Println("Latest block height:", latestBlockNumber)
block, err := client.BlockByNumber(ctx, big.NewInt(int64(latestBlockNumber)))
if err != nil {
log.Fatal("Failed to fetch block: ", err)
}
fmt.Println("Block hash:", block.Hash().Hex())
fmt.Println("Transaction count:", len(block.Transactions()))
}Output:
Latest block height: 18054718
Block hash: 0xe1122f17b2d7e9f717bdedb7062cf0a2f44b3199e702754b97f9a5832622a87a
Transaction count: 226Step 2: Extracting Transactions
Once you have the block, inspect its transactions:
tx := block.Transactions()[1]
fmt.Println("Transaction hash:", tx.Hash().Hex())
fmt.Println("Value:", tx.Value().String(), "wei")
fmt.Println("Gas limit:", tx.Gas())
fmt.Println("Gas price:", tx.GasPrice().Uint64())
fmt.Println("Recipient address:", tx.To().Hex())Output:
Transaction hash: 0xb9ac9b6a4eb857cfd410d0f3dc5604c5e52382e490acd00a14f9cf69c59391b0
Value: 10000000000000000 wei
Gas limit: 274920
Gas price: 59204457483
Recipient address: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488DStep 3: Deriving the Sender (From) Field
The From field isn't stored directly in the transaction—it’s derived from the signature. Use a signer to decode it:
signer := types.MakeSigner(params.MainnetChainConfig, block.Number(), block.Time())
from, err := signer.Sender(tx)
if err != nil {
log.Fatal("Failed to derive sender: ", err)
}
fmt.Println("Sender address:", from.Hex())Output:
Sender address: 0xCe1a934f373fb7355aFa2895d411A80e38384703Key Notes:
MakeSignerdynamically selects the appropriate signer based on block height (e.g., London, Berlin, or Cancun forks).- For private networks, use
types.LatestSignerinstead ofparams.MainnetChainConfig.
Complete Code Example
package main
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
"log"
"math/big"
)
func main() {
ethereumNodeEndpoint := "https://cloudflare-eth.com"
client, err := ethclient.Dial(ethereumNodeEndpoint)
if err != nil {
log.Fatalf("Connection error: %s", err)
}
ctx := context.TODO()
latestBlockNumber, err := client.BlockNumber(ctx)
if err != nil {
log.Fatal("Block number error: ", err)
}
block, err := client.BlockByNumber(ctx, big.NewInt(int64(latestBlockNumber)))
if err != nil {
log.Fatal("Block fetch error: ", err)
}
tx := block.Transactions()[1]
fmt.Println("Transaction details:")
fmt.Println("Hash:", tx.Hash().Hex())
fmt.Println("Value:", tx.Value().String(), "wei")
fmt.Println("Recipient:", tx.To().Hex())
signer := types.MakeSigner(params.MainnetChainConfig, block.Number(), block.Time())
from, err := signer.Sender(tx)
if err != nil {
log.Fatal("Sender derivation error:", err)
}
fmt.Println("Sender:", from.Hex())
}FAQ
Q1: Why isn’t the From field stored directly in Ethereum transactions?
A1: Storing From explicitly would bloat the blockchain. Instead, it’s computed from the signature to save space.
Q2: Can I use this method for testnets like Sepolia?
A2: Yes! Replace params.MainnetChainConfig with the testnet’s configuration (e.g., params.SepoliaChainConfig).
Q3: What if my transaction lacks a To address?
A3: A missing To field indicates a contract creation transaction.
Advanced Topics
👉 Explore Ethereum’s transaction lifecycle
👉 Deep dive into EIP-1559 fee mechanics
For further reading: