How to Transfer ETH Using Go in Ethereum Development

·

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:

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 Wei

6. 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

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.