When working with Ethereum, a common task is to retrieve the balance of a specific account. This balance refers to the amount of native Ether (ETH) held, not ERC-20 tokens or other assets. This guide will walk you through how to accomplish this programmatically using the Go programming language and the go-ethereum library.
Prerequisites
Before you begin, ensure you have the following:
- A basic understanding of the Go programming language.
- Go installed on your system.
- An Infura project ID (or another Ethereum node provider) to connect to the Ethereum network.
- The go-ethereum library added to your project. You can install it using:
go get github.com/ethereum/go-ethereum
Understanding Account Balances on Ethereum
An Ethereum account balance is stored on the blockchain as a value in Wei, which is the smallest denomination of Ether. One Ether is equal to 1e18 Wei (1,000,000,000,000,000,000). Querying a balance does not require a private key; you only need the public address of the account, which can be either an Externally Owned Account (EOA) or a smart contract address.
Step-by-Step Guide to Fetching a Balance
1. Import Required Packages
Your Go file will need to import the necessary packages to handle connections, big integers, and context.
import (
"context"
"log"
"math/big"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)2. Connect to an Ethereum Client
Establish a connection to an Ethereum node. This example uses Infura, but any JSON-RPC endpoint will work.
apiKey := os.Getenv("INFURA_API_KEY")
url := "https://mainnet.infura.io/v3/" + apiKey
client, err := ethclient.Dial(url)
if err != nil {
log.Fatalf("Could not connect to Infura: %v", err)
}
defer client.Close()3. Define the Account and Block Number
Specify the Ethereum address you want to query. You can also define an optional block number to check the balance at a specific point in history. Use nil to get the latest balance.
ctx := context.Background()
// Example address: vitalik.eth
addr := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
// For latest balance, set bn to nil
bn := big.NewInt(18382246) // Specific block number4. Call the BalanceAt Function
Use the BalanceAt method from the client to retrieve the balance. The returned value is a *big.Int representing the balance in Wei.
balanceWei, err := client.BalanceAt(ctx, addr, bn)
if err != nil {
log.Fatalf("Failed to get balance: %v", err)
}
log.Printf("Balance in Wei: %s", balanceWei.String())Handling Large Numbers and Precision
The balance returned from the blockchain is a very large integer. Converting it to Ether is a common requirement but introduces the risk of precision loss. It is strongly recommended to store and perform calculations with the Wei value whenever possible to maintain consistency with the blockchain state.
Converting Wei to Ether
If you must convert to Ether, use a library that can handle high precision.
Using math/big (Standard Library):
This method is convenient but may result in a loss of precision due to the nature of floating-point arithmetic.
balanceFloat := new(big.Float).SetInt(balanceWei)
etherValue := new(big.Float).Quo(balanceFloat, big.NewFloat(1e18))
log.Printf("Balance in Ether (big.Float): %s", etherValue.String())For the most accurate calculations and financial applications, consider using a dedicated decimal library. ๐ Explore more strategies for precise financial computations
Using a Decimal Library (e.g., shopspring/decimal):
First, install the library: go get github.com/shopspring/decimal
import "github.com/shopspring/decimal"
// ...
balanceDecimal := decimal.RequireFromString(balanceWei.String())
etherValueDecimal := balanceDecimal.Div(decimal.NewFromInt(1e18))
log.Printf("Balance in Ether (decimal.Decimal): %s", etherValueDecimal.String())Complete Example Code
Here is the full code combining all the steps, including both conversion methods for comparison.
package main
import (
"context"
"log"
"math/big"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/shopspring/decimal"
)
func main() {
apiKey := os.Getenv("INFURA_API_KEY")
if apiKey == "" {
log.Fatal("INFURA_API_KEY environment variable not set")
}
url := "https://mainnet.infura.io/v3/" + apiKey
client, err := ethclient.Dial(url)
if err != nil {
log.Fatalf("Could not connect to Infura: %v", err)
}
defer client.Close()
ctx := context.Background()
addr := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") // vitalik.eth
bn := big.NewInt(18382246) // Use nil for the latest block
balanceWei, err := client.BalanceAt(ctx, addr, bn)
if err != nil {
log.Fatalf("Failed to get balance: %v", err)
}
log.Printf("Balance in Wei: %s", balanceWei.String())
// Conversion using big.Float
balanceFloat := new(big.Float).SetInt(balanceWei)
etherValueFloat := new(big.Float).Quo(balanceFloat, big.NewFloat(1e18))
log.Printf("Balance in Ether (big.Float): %s", etherValueFloat.String())
// Conversion using shopspring/decimal
balanceDecimal := decimal.RequireFromString(balanceWei.String())
etherValueDecimal := balanceDecimal.Div(decimal.NewFromInt(1e18))
log.Printf("Balance in Ether (decimal.Decimal): %s", etherValueDecimal.String())
}Frequently Asked Questions
Do I need a private key to check an account balance?
No, you do not. Account balances are public information on the Ethereum blockchain. You only need the public address of the account to query its balance.
What is the difference between Wei and Ether?
Wei is the base unit of Ether, much like cents are to a dollar. One Ether is equal to 1,000,000,000,000,000,000 Wei (10^18). All values are stored as Wei on the blockchain.
Why is precision loss a concern when converting balances?
Ethereum account balances can be extremely large integers. Converting them to floating-point numbers (like float64 or big.Float) can truncate or round the value, leading to an inaccurate representation of the true balance. For any critical financial operation, it's best to perform calculations in Wei.
Can I get the balance of a smart contract?
Yes, you can. Smart contracts have addresses and can hold Ether. The process for checking their balance is identical to checking the balance of a user-owned account (EOA).
What does the blockNumber parameter do?
This optional parameter allows you to query the account's balance as it was at a specific block height in the past. If you pass nil, the client will return the balance from the most recent block.
My connection is failing. What should I check?
First, verify your API key or node endpoint URL is correct. Ensure your internet connection is stable and that any necessary environment variables are properly set. ๐ Get advanced methods for debugging connection issues