Confirming Ethereum Transactions in Go: A Step-by-Step Guide

ยท

In Ethereum development, sending a transaction is just the first step. To ensure your transaction is successfully processed, you need to wait for confirmation on the blockchain. This guide will walk you through monitoring Ethereum transactions in Go, checking their status, and handling success or failure scenarios.

How Ethereum Transaction Confirmation Works

Ethereum produces a new block approximately every 15 seconds. When you send a transaction:

  1. It enters the pending state while waiting to be included in a block
  2. Once mined, it transitions to either success or failure state
  3. You can then verify the final status through transaction receipts

๐Ÿ‘‰ Learn more about Ethereum transaction lifecycle

Checking Transaction Status in Go

The Ethereum Go client (ethclient) provides essential methods for tracking transactions:

_, isPending, err := ec.TransactionByHash(ctx, txHash)

Key indicators:

After confirmation, examine the transaction receipt:

receipt, err := ec.TransactionReceipt(ctx, txHash)

The receipt contains a Status field where:

Implementing Transaction Waiting Logic

For robust applications, implement a waiting function with timeout:

func waitConfirm(ctx context.Context, ec *ethclient.Client, txHash common.Hash, timeout time.Duration) error {
    pending := true
    for pending {
        select {
        case <-ctx.Done():
            return ctx.Err()
        case <-time.After(timeout):
            return errors.New("timeout")
        case <-time.After(time.Second):
            _, isPending, err := ec.TransactionByHash(ctx, txHash)
            if err != nil {
                return err
            }
            if !isPending {
                pending = false
            }
        }
    }
    receipt, err := ec.TransactionReceipt(ctx, txHash)
    if err != nil {
        return err
    }
    if receipt.Status == 0 {
        return fmt.Errorf("transaction failed: %s", receipt.TxHash)
    }
    return nil
}

This function:

  1. Checks transaction status every second
  2. Respects context cancellation
  3. Implements a timeout (10 minutes in this example)
  4. Returns success/failure status once confirmed

Complete Transaction Sending Example

Here's a full implementation combining transaction sending and confirmation:

package main

import (
    "context"
    "crypto/ecdsa"
    "errors"
    "fmt"
    "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"
    "time"
)

func main() {
    client, err := ethclient.Dial(os.Getenv("INFURA_URL"))
    if err != nil {
        log.Fatalf("Connection error: %v", err)
    }

    privateKey, err := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
    if err != nil {
        log.Fatalf("Private key error: %v", err)
    }

    recipient := common.HexToAddress("0xReceiverAddress")
    amount := big.NewInt(100000000000000000) // 0.1 ETH
    
    txHash, err := sendTransaction(context.Background(), client, privateKey, recipient, amount)
    if err != nil {
        log.Fatalf("Transaction failed: %v", err)
    }
    
    log.Println("Transaction sent:", txHash)
    
    if err := waitConfirm(context.Background(), client, *txHash, 10*time.Minute); err != nil {
        log.Fatalf("Confirmation failed: %v", err)
    }
    
    log.Println("Transaction confirmed successfully!")
}

func sendTransaction(ctx context.Context, client *ethclient.Client, pk *ecdsa.PrivateKey, to common.Address, amount *big.Int) (*common.Hash, error) {
    // Implementation similar to example above
    // ...
}

func waitConfirm(ctx context.Context, client *ethclient.Client, txHash common.Hash, timeout time.Duration) error {
    // Implementation as shown above
    // ...
}

Best Practices for Production

  1. Set appropriate timeouts based on network conditions
  2. Implement retry logic for temporary failures
  3. Monitor gas prices to ensure timely inclusion
  4. Provide user feedback during waiting periods

๐Ÿ‘‰ Advanced Ethereum development techniques

FAQ

Q: How long does Ethereum transaction confirmation typically take?
A: On average, 15-30 seconds per block, but network congestion can cause delays.

Q: What status code indicates a failed transaction?
A: Status 0 in the transaction receipt indicates failure.

Q: Should I use TransactionByHash or TransactionReceipt?
A: Use TransactionByHash to check pending status, and TransactionReceipt for final confirmation.

Q: What's a reasonable timeout value?
A: 5-10 minutes for most cases, but adjust based on network conditions.

Q: Can transactions become stuck permanently?
A: Yes, if gas is too low. You may need to replace-by-fee in such cases.

Conclusion

Proper transaction confirmation is crucial for reliable Ethereum applications. By implementing status checks and timeouts in your Go applications, you ensure robust transaction handling and better user experience.

Remember to always: