Introduction to Ethereum Testnet Transactions
From this section onward, we'll focus on submitting transactions using Ethereum's Sepolia testnet instead of the mainnet. Testnet development requires Gas fees, but don't worry—you can obtain test ETH for free through faucets like Sepolia Faucet.
Key Components of an ETH Transaction:
- Test ETH: Required for transaction fees
- Nonce: Transaction sequence number
- Gas Fees: Network computation costs
- Recipient Address: Destination wallet
Step-by-Step ETH Transfer Process
1. Obtain Test ETH
Before proceeding, ensure your wallet has sufficient test ETH (we'll send 0.1 ETH in this example—adjust according to your balance).
👉 Get free Sepolia test ETH here
2. Set Up Transaction Parameters
nonce, err := client.NonceAt(ctx, address, nil)Network Fee Calculation
Retrieve the current network baseFee from the latest block header:
header, err := client.HeaderByNumber(ctx, nil)
log.Printf("base fee: %s", header.BaseFee)We'll use this baseFee for our GasFeeCap. While this approach might cause delays during high network congestion (transactions could wait longer or get dropped), it's generally safe on testnets where traffic is lighter.
3. Configure Gas Settings
Get the recommended GasTipCap (priority fee):
gasTipCap, err := client.SuggestGasTipCap(ctx)4. Construct the Transaction
ETH transfers require 21,000 gas units. Here's how to send to a recipient address:
to := common.HexToAddress("0x26a1DDA0E911Ea245Fc3Fb7C5C10d18490942a60")
amount := big.NewInt(100_000_000_000_000_000) // 0.1 ether
txData := &types.DynamicFeeTx{
ChainID: chainId,
Nonce: nonce,
To: &to,
Value: amount,
Gas: 21000,
GasFeeCap: header.BaseFee,
GasTipCap: gasTipCap,
}5. Sign and Broadcast the Transaction
Sign with your private key (loaded from environment variables):
signedTx, err := types.SignNewTx(pk, types.LatestSignerForChainID(chainId), txData)Submit the signed transaction:
err = client.SendTransaction(ctx, signedTx)Complete Implementation Code
package main
import (
"context"
"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"
"log"
"math/big"
"os"
)
func main() {
apiKey := os.Getenv("INFURA_API_KEY")
url := "https://sepolia.infura.io/v3/" + apiKey
client, err := ethclient.Dial(url)
if err != nil {
log.Fatalf("could not connect to Infura with ethclient: %s", err)
}
ctx := context.Background()
chainId, err := client.ChainID(ctx)
if err != nil {
log.Fatalf("get chainID error: %s", err)
}
pk, err := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
if err != nil {
log.Fatalf("load private key error: %s", err)
}
address := crypto.PubkeyToAddress(pk.PublicKey)
log.Printf("account load success, address: %s", crypto.PubkeyToAddress(pk.PublicKey))
nonce, err := client.NonceAt(ctx, address, nil)
if err != nil {
log.Fatalf("get nonce error: %v", err)
}
log.Printf("nonce: %d", nonce)
header, err := client.HeaderByNumber(ctx, nil)
if err != nil {
log.Fatalf("get header error: %v", err)
}
log.Printf("base fee: %s", header.BaseFee)
gasTipCap, err := client.SuggestGasTipCap(ctx)
if err != nil {
log.Fatalf("get SuggestGasTipCap error: %v", err)
}
log.Printf("Suggested GasTipCap(maxPriorityFeePerGas): %s", gasTipCap)
to := common.HexToAddress("0x26a1DDA0E911Ea245Fc3Fb7C5C10d18490942a60")
amount := big.NewInt(100_000_000_000_000_000) // 0.1 ether
txData := &types.DynamicFeeTx{
ChainID: chainId,
Nonce: nonce,
To: &to,
Value: amount,
Gas: 21000,
GasFeeCap: header.BaseFee,
GasTipCap: gasTipCap,
}
signedTx, err := types.SignNewTx(pk, types.LatestSignerForChainID(chainId), txData)
if err != nil {
log.Fatalf("sign tx error: %v", err)
}
err = client.SendTransaction(ctx, signedTx)
if err != nil {
log.Fatalf("sign tx error: %v", err)
}
}👉 Explore more blockchain development tools
FAQ Section
Why use Sepolia testnet instead of mainnet?
Sepolia provides a risk-free environment for testing transactions without spending real ETH. All ETH on testnets has no monetary value.
What happens if my transaction gets stuck?
On testnets, you can simply increase the gas fees and resend with a higher nonce. The network isn't congested like mainnet, so transactions typically process quickly.
How do I check my transaction status?
Use a blockchain explorer like Etherscan's Sepolia version to track your transaction hash after broadcasting.
Can I reuse the same nonce?
No—each transaction from an address must have a unique sequential nonce. Reusing a nonce will cause conflicts.
Why is gas fixed at 21,000 for ETH transfers?
This is Ethereum's standard gas cost for simple value transfers. Smart contract interactions require more computational resources and thus higher gas limits.
How often should I fetch new gas estimates?
Gas prices fluctuate constantly. For testnet transactions, checking once is usually sufficient, but for mainnet transactions you may want to monitor more frequently during peak times.
Remember—this guide focuses on testnet transactions. When working with mainnet ETH, always double-check addresses and transaction details before sending real cryptocurrency.