Transferring Ether (ETH) is one of the most fundamental operations in Ethereum blockchain development. Whether you're building a decentralized application (dApp), managing digital wallets, or automating transactions, understanding how to programmatically send ETH using Go is essential. This guide walks you through the complete process of creating, signing, and broadcasting an ETH transaction using the Go programming language and the go-ethereum library.
By the end of this tutorial, you’ll understand how to securely transfer ETH from one address to another with proper nonce handling, gas configuration, and cryptographic signing — all within a Go environment.
Understanding Ethereum Transactions
Before diving into code, it’s important to grasp what an Ethereum transaction entails. Every ETH transfer requires several key components:
- Nonce: A sequential number ensuring each transaction is processed only once.
- Gas Limit: The maximum amount of computational effort (gas) you’re willing to spend.
- Gas Price: How much you’re willing to pay per unit of gas, usually in Gwei.
- Value: The amount of ETH (in Wei) being sent.
- To Address: The recipient’s Ethereum address.
- Private Key Signature: Cryptographic proof that the sender authorizes the transaction.
All these elements must be correctly configured and signed before broadcasting the transaction to the Ethereum network.
Step-by-Step: Sending ETH with Go
Let’s walk through the implementation step by step.
1. Set Up Your Ethereum Client
First, connect to an Ethereum node using Infura or a local Geth client. Here we use the Rinkeby testnet for demonstration purposes.
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}👉 Learn how to interact with blockchain networks efficiently using modern tools.
2. Load Your Private Key
Your private key controls access to your funds. In production, never hardcode it — use secure storage solutions like environment variables or hardware wallets.
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
if err != nil {
log.Fatal(err)
}3. Derive the Public Address
From the private key, derive the sender’s public Ethereum address.
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)4. Fetch the Account Nonce
The nonce prevents replay attacks and ensures transaction order.
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}5. Define Transaction Value in Wei
Ethereum uses Wei as its base unit (1 ETH = 10¹⁸ Wei).
value := big.NewInt(1000000000000000000) // 1 ETH in Wei6. Configure Gas Settings
Set gas limit and dynamically fetch current gas price for optimal fees.
gasLimit := uint64(21000) // Standard for simple transfers
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}7. Specify Recipient Address
Define who will receive the ETH.
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")8. Create the Transaction
Use types.NewTransaction to build the unsigned transaction.
var data []byte // No data needed for simple ETH transfers
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)9. Sign the Transaction
Sign using EIP-155 standard to prevent replay attacks across chains.
chainID, err := client.NetworkID(context.Background())
if err != nil {
log.Fatal(err)
}
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
log.Fatal(err)
}10. Broadcast the Transaction
Send it to the network and retrieve the transaction hash.
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())You can now track the transaction status on Etherscan using the returned hash.
Complete Working Code Example
Here's the full Go program for transferring ETH:
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
value := big.NewInt(1000000000000000000) // 1 ETH in Wei
gasLimit := uint64(21000)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
var data []byte
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
chainID, err := client.NetworkID(context.Background())
if err != nil {
log.Fatal(err)
}
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
log.Fatal(err)
}
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
}👉 Discover powerful blockchain development techniques and streamline your workflow today.
Frequently Asked Questions (FAQ)
Q: Why do I need a nonce in Ethereum transactions?
A: The nonce ensures transactions are processed in order and prevents double-spending. Each transaction from an account must have a unique, incrementing nonce starting from 0.
Q: What happens if I set too low a gas limit?
A: If the gas limit is insufficient, the transaction will fail and consume all gas paid. For simple ETH transfers, 21,000 units is standard.
Q: Can I use this method on mainnet?
A: Yes, but ensure you’re using a secure setup — never expose private keys in code. Replace the testnet URL with a mainnet endpoint like https://mainnet.infura.io.
Q: How do I check transaction status after sending?
A: Use the transaction hash with client.TransactionReceipt() or look it up on Etherscan.
Q: Is it safe to hardcode gas prices?
A: Not recommended. Market conditions change rapidly. Always prefer SuggestGasPrice() unless you have specific requirements.
Q: Can I send tokens (ERC-20) the same way?
A: No — token transfers require interacting with smart contracts via encoded function calls in the data field.
Core Keywords for SEO
- ETH transfer
- Go Ethereum development
- Send ETH with Go
- Ethereum transaction signing
- Go-ethereum library
- Programmatic ETH transfer
- Blockchain development with Go
- Secure Ethereum transactions
This guide equips developers with practical knowledge for building robust Ethereum applications using Go. With attention to security, proper tooling, and understanding of core concepts like nonce and gas management, you can confidently implement automated and scalable blockchain solutions.