Sending Ether (ETH) is one of the most fundamental operations on the Ethereum blockchain. Whether you're building a decentralized application or simply transferring funds, understanding how to construct, sign, and broadcast a transaction is essential. This guide will walk you through the entire process programmatically using the Go programming language (Golang) and the go-ethereum library.
Prerequisites for ETH Transfers
Before you begin, ensure you have a basic understanding of Ethereum transactions. A standard ETH transfer transaction includes several key components:
- The amount of ETH to send.
- A gas limit, which defines the maximum computational effort allowed.
- A gas price, which determines the fee you pay per unit of gas.
- A nonce, which is a unique sequence number for the sending account.
- The recipient's address.
- An optional data field (not used for simple ETH transfers).
Crucially, every transaction must be cryptographically signed with the sender's private key before it is valid and can be sent to the network.
Step-by-Step: Building a Transaction in Go
Assuming you already have a connected ethclient, the following steps outline the process of creating and sending a transaction.
1. Load Your Private Key
The private key is required to sign the transaction and prove ownership of the sending account. Always handle private keys with extreme care and never expose them in production code.
privateKey, err := crypto.HexToECDSA("your_private_key_hex_string_here")
if err != nil {
log.Fatal(err)
}2. Derive the Public Address
From the private key, you can derive the corresponding public key and then the Ethereum public address. This address is used to identify your account on the network.
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("Error casting public key to ECDSA type")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)3. Obtain the Account Nonce
The nonce is a critical counter that prevents replay attacks. It must be unique for each transaction from your address. You can retrieve the next available nonce for your account using the client.
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}4. Set the Transfer Value
Ethereum transactions are denominated in wei, the smallest unit of ETH, where 1 ETH equals 1,000,000,000,000,000,000 wei (10^18). Use the big.Int type to handle these large numbers.
value := big.NewInt(1000000000000000000) // This represents 1 ETH5. Configure Gas Settings
Every transaction consumes gas. A standard ETH transfer requires a gas limit of 21,000 units. The gas price, which you pay per unit of gas, can be set manually or fetched as a suggestion from the network.
gasLimit := uint64(21000) // Standard gas limit for ETH transfers
gasPrice, err := client.SuggestGasPrice(context.Background()) // Fetch current suggested price
if err != nil {
log.Fatal(err)
}6. Define the Recipient Address
Specify the destination address for the ETH transfer.
toAddress := common.HexToAddress("0xRecipientAddressHexHere")7. Create the Unsigned Transaction
With all components prepared, you can now create the transaction object. For a simple transfer, the data field is left empty.
txData := []byte{} // Empty data for simple ETH send
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, txData)8. Sign the Transaction
To authorize the transaction, you must sign it with your private key. This requires knowing the network's chain ID.
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)
}9. Broadcast the Transaction
The final step is to send the signed transaction to the Ethereum network for processing. 👉 Explore more strategies for transaction management
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Transaction successfully sent with hash: %s", signedTx.Hash().Hex())You can then use the returned transaction hash to track its status on a block explorer like Etherscan.
Complete Go Code Example
Here is the consolidated code for sending an ETH transaction using Go.
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() {
// 1. Connect to an Ethereum node
client, err := ethclient.Dial("https://rinkeby.infura.io")
if err != nil {
log.Fatal(err)
}
// 2. Load private key
privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
if err != nil {
log.Fatal(err)
}
// 3. Derive public 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. Get the account nonce
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
// 5. Set value (1 ETH in wei)
value := big.NewInt(1000000000000000000)
// 6. Set gas parameters
gasLimit := uint64(21000)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
// 7. Set recipient address
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
// 8. Create transaction
var data []byte
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
// 9. Sign the transaction
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. Send the transaction
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
// 11. Print transaction hash
fmt.Printf("Transaction sent: %s", signedTx.Hash().Hex())
}Frequently Asked Questions
What is a nonce in Ethereum?
A nonce is a number that increments with each transaction sent from a specific Ethereum address. It ensures each transaction is unique and prevents double-spending or replay attacks. The first transaction from an account has a nonce of 0.
Why is 21,000 gas the standard limit for ETH transfers?
A simple transfer of ETH (without any smart contract interaction) requires a fixed amount of computational work for the network to process. This work has been standardized to cost 21,000 gas units, covering the cost of updating account balances and logging the transaction.
What happens if I set the gas price too low?
If your gas price is set significantly lower than the current network average, miners are less likely to prioritize your transaction. This can result in your transaction taking a very long time to confirm or getting stuck. You can resubmit the transaction with a higher gas price and the same nonce to replace it.
Can I send a transaction without using SuggestGasPrice?
Yes, you can manually set a gas price using big.NewInt and a value in wei. However, using the suggested gas price helps ensure your transaction is processed in a timely manner based on current network congestion.
Is the data field required for ETH transfers?
No, the data field is optional and is typically left empty for simple transfers of value. It is primarily used when interacting with smart contracts to call functions and pass parameters.
How can I track my transaction after sending it?
After broadcasting the transaction, you will receive a transaction hash. You can use this hash to look up the transaction's status, block confirmation, and other details on a blockchain explorer like Etherscan or Blockscout. 👉 View real-time tools for tracking blockchain activity