A comprehensive Go library for interacting with Ethereum blockchain through JSON-RPC APIs, designed with web3.js-like interface for familiar usage patterns.
- Complete Ethereum JSON-RPC Implementation: All major eth_* methods supported
- Pending Transaction Support: Monitor mempool, get pending transactions by account
- Context-Aware Operations: Proper context handling for timeouts and cancellation
- Type-Safe Structures: Strongly-typed transaction, block, and receipt objects
- Web3.js-Like API: Familiar method names and usage patterns for JavaScript developers
- Built-in Utilities: Wei/Ether conversion, address validation, hex operations
- Robust Error Handling: Detailed RPC error information with proper Go error wrapping
- Production Ready: Thread-safe client with connection pooling
go get github.com/donghquinn/go-web3package main
import (
"context"
"fmt"
"log"
"time"
"github.com/donghquinn/go-web3"
)
func main() {
// Create a new client with your Ethereum RPC endpoint
client := web3.NewClient("https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY")
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Get latest block number
blockNumber, err := client.Eth().GetBlockNumber(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Latest block: %d\n", blockNumber)
// Check account balance
address := "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
balance, err := client.Eth().GetBalance(ctx, address, "latest")
if err != nil {
log.Fatal(err)
}
// Convert Wei to Ether for display
balanceEth, _ := web3.FromWei(balance, "ether")
fmt.Printf("Balance: %s ETH\n", balanceEth)
}client := web3.NewClient("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")import (
"net/http"
"time"
)
httpClient := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
},
}
client := web3.NewClient("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
// Note: Custom HTTP client configuration would require extending the libraryAlways use context for proper timeout and cancellation handling:
// Context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Context with deadline
deadline := time.Now().Add(1 * time.Minute)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// Cancellable context
ctx, cancel := context.WithCancel(context.Background())
// Call cancel() when neededclient := web3.NewClient("YOUR_ETHEREUM_RPC_URL")
// Popular RPC endpoints:
// Mainnet: "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"
// Goerli: "https://goerli.infura.io/v3/YOUR_PROJECT_ID"
// Alchemy: "https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY"
// Local: "http://localhost:8545"// Get balance in Wei
balance, err := client.Eth().GetBalance(ctx, address, "latest")
if err != nil {
log.Fatal(err)
}
// Convert to Ether for display
balanceEth, _ := web3.FromWei(balance, "ether")
fmt.Printf("Balance: %s ETH\n", balanceEth)
// Block parameters: "latest", "earliest", "pending", or hex block number
balance, err := client.Eth().GetBalance(ctx, address, "0x1b4") // specific block// Get nonce for transaction
nonce, err := client.Eth().GetTransactionCount(ctx, address, "latest")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Next nonce: %d\n", nonce)
// For pending transactions (useful for rapid transaction sending)
pendingNonce, err := client.Eth().GetTransactionCount(ctx, address, "pending")blockNumber, err := client.Eth().GetBlockNumber(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Latest block: %d\n", blockNumber)// Get block without full transaction details
block, err := client.Eth().GetBlockByNumber(ctx, "latest", false)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Block Hash: %s\n", block.Hash)
fmt.Printf("Miner: %s\n", block.Miner)
fmt.Printf("Gas Used: %s\n", block.GasUsed)
fmt.Printf("Timestamp: %s\n", block.Timestamp)
fmt.Printf("Transaction count: %d\n", len(block.Transactions))
// Get block with full transaction details
blockWithTxs, err := client.Eth().GetBlockByNumber(ctx, "latest", true)blockHash := "0x1234567890abcdef..."
block, err := client.Eth().GetBlockByHash(ctx, blockHash, false)
if err != nil {
log.Fatal(err)
}txHash := "0xabcdef1234567890..."
tx, err := client.Eth().GetTransactionByHash(ctx, txHash)
if err != nil {
log.Fatal(err)
}
fmt.Printf("From: %s\n", tx.From)
fmt.Printf("To: %s\n", tx.To)
fmt.Printf("Value: %s\n", tx.Value)
fmt.Printf("Gas: %s\n", tx.Gas)
fmt.Printf("Gas Price: %s\n", tx.GasPrice)txHash := "0xabcdef1234567890..."
receipt, err := client.Eth().GetTransactionReceipt(ctx, txHash)
if err != nil {
log.Fatal(err)
}
// Check if transaction was successful
if receipt.Status == "0x1" {
fmt.Println("Transaction successful")
} else {
fmt.Println("Transaction failed")
}
fmt.Printf("Gas Used: %s\n", receipt.GasUsed)
fmt.Printf("Block Number: %s\n", receipt.BlockNumber)// Send a pre-signed transaction
signedTxHex := "0xf86c808504a817c800825208940xd8da6bf26964af9d7eed9e03e53415d37aa96045880de0b6b3a764000080820a95a0..."
txHash, err := client.Eth().SendRawTransaction(ctx, signedTxHex)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Transaction sent: %s\n", txHash)// Get all pending transactions from mempool
pendingTxs, err := client.Eth().GetPendingTransactions(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d pending transactions\n", len(pendingTxs))
for _, tx := range pendingTxs {
fmt.Printf("Hash: %s, From: %s, To: %s\n", tx.Hash, tx.From, tx.To)
// Convert gas price to Gwei for readability
if tx.GasPrice != "" {
gasPriceWei, _ := web3.FromHex(tx.GasPrice)
gasPriceGwei, _ := web3.WeiToGwei(gasPriceWei)
fmt.Printf("Gas Price: %s Gwei\n", gasPriceGwei)
}
}// Get total number of pending transactions
count, err := client.Eth().GetPendingTransactionCount(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Pending transactions in mempool: %d\n", count)// Get pending transactions for a specific account
address := "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
accountTxs, err := client.Eth().GetAccountPendingTransactions(ctx, address)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d pending transactions for %s\n", len(accountTxs), address)
for _, tx := range accountTxs {
fmt.Printf("Hash: %s, Value: %s\n", tx.Hash, tx.Value)
}// Check if a specific transaction hash is in the pending pool
txHash := "0xabcdef1234567890..."
isPending, err := client.Eth().IsPendingTransaction(ctx, txHash)
if err != nil {
log.Fatal(err)
}
if isPending {
fmt.Printf("Transaction %s is still pending\n", txHash)
} else {
fmt.Printf("Transaction %s is not in pending pool\n", txHash)
}gasPrice, err := client.Eth().GetGasPrice(ctx)
if err != nil {
log.Fatal(err)
}
// Convert to Gwei for display
gasPriceGwei, _ := web3.FromWei(gasPrice, "gwei")
fmt.Printf("Current gas price: %s Gwei\n", gasPriceGwei)// Create transaction object
txObj := map[string]interface{}{
"from": "0x742d35Cc6084C0532C9d2b908B8C0c9ff3e3ba0A",
"to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"value": "0xde0b6b3a7640000", // 1 ETH in wei
}
gasEstimate, err := client.Eth().EstimateGas(ctx, txObj)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Estimated gas: %d\n", gasEstimate)// Call a contract method (read-only)
callObj := map[string]interface{}{
"to": "0xA0b86a33E6417c48cd7a94Ca95e70aD2c51e74f7", // contract address
"data": "0x70a08231000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045", // balanceOf call
}
result, err := client.Eth().Call(ctx, callObj, "latest")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Contract call result: %s\n", result)// Create a new random wallet
wallet, err := web3.CreateWallet(client)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Address: %s\n", wallet.GetAddress())
fmt.Printf("Private Key: %s\n", wallet.GetPrivateKey()) // Keep this secure!// Load wallet from private key
privateKey := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
wallet, err := web3.NewWallet(privateKey, client)
if err != nil {
log.Fatal(err)
}
// Check wallet balance
balance, err := wallet.GetBalance(ctx)
if err != nil {
log.Fatal(err)
}
balanceEth, _ := web3.FromWei(balance, "ether")
fmt.Printf("Balance: %s ETH\n", balanceEth)// Create transaction parameters
txParams := web3.NewTransactionParams().
SetTo("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").
SetValueInEther("0.1"). // Send 0.1 ETH
SetGas(21000). // Standard transfer gas limit
SetGasPriceInGwei("20"). // 20 Gwei gas price
SetNonce(42). // Transaction nonce
SetChainID(big.NewInt(1)) // Mainnet chain ID
// Sign the transaction
privateKey, err := web3.PrivateKeyFromHex("0x...")
if err != nil {
log.Fatal(err)
}
signedTx, err := web3.SignTransaction(txParams, privateKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Transaction Hash: %s\n", signedTx.Hash)
fmt.Printf("Raw Transaction: %s\n", signedTx.Raw)// Create EIP-1559 transaction parameters
eip1559Params := web3.NewEIP1559TransactionParams()
eip1559Params.To = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
eip1559Params.Value, _ = web3.ToWei("0.05", "ether")
eip1559Params.Gas = 21000
eip1559Params.MaxFeePerGas, _ = web3.ToWei("30", "gwei") // Maximum fee per gas
eip1559Params.MaxPriorityFeePerGas, _ = web3.ToWei("2", "gwei") // Tip to miners
eip1559Params.Nonce = 43
eip1559Params.ChainID = big.NewInt(1)
// Sign EIP-1559 transaction
signedTx, err := web3.SignEIP1559Transaction(eip1559Params, privateKey)
if err != nil {
log.Fatal(err)
}// Send ETH using wallet (handles nonce, gas estimation automatically)
result, err := wallet.SendEther(ctx, "0xRECIPIENT_ADDRESS", "1.5")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Transaction sent: %s\n", result.TransactionHash)
fmt.Printf("From: %s\n", result.From)
fmt.Printf("To: %s\n", result.To)// Send with custom gas settings
transferOpts := &web3.TransferOptions{
To: "0xRECIPIENT_ADDRESS",
Value: web3.ToWei("2.0", "ether"),
GasLimit: 25000, // Custom gas limit
GasPrice: web3.ToWei("25", "gwei"), // Custom gas price
Data: []byte("Hello Ethereum!"), // Optional data
}
result, err := wallet.SendTransaction(ctx, transferOpts)
if err != nil {
log.Fatal(err)
}maxFee, _ := web3.ToWei("50", "gwei")
priorityFee, _ := web3.ToWei("3", "gwei")
result, err := wallet.SendEIP1559Transaction(ctx, transferOpts, maxFee, priorityFee)
if err != nil {
log.Fatal(err)
}// Call a contract method (no transaction, no gas cost)
contractAddress := "0xA0b86a33E6417c48cd7a94Ca95e70aD2c51e74f7"
// Example: balanceOf(address) call
balanceOfData, err := web3.EncodeABI("balanceOf(address)", wallet.GetAddress())
if err != nil {
log.Fatal(err)
}
result, err := wallet.CallContract(ctx, contractAddress, balanceOfData)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Token balance (hex): %s\n", result)// Example: ERC-20 transfer
recipientAddress := "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
transferAmount := web3.ToWei("100", "ether") // 100 tokens (assuming 18 decimals)
// Encode transfer(address,uint256) function call
transferData, err := web3.EncodeABI("transfer(address,uint256)", recipientAddress, transferAmount)
if err != nil {
log.Fatal(err)
}
// Send contract transaction
result, err := wallet.SendContractTransaction(ctx, contractAddress, transferData, big.NewInt(0))
if err != nil {
log.Fatal(err)
}
fmt.Printf("Token transfer transaction: %s\n", result.TransactionHash)// Deploy a smart contract
contractBytecode := []byte{0x60, 0x80, 0x60, 0x40, /* ... contract bytecode ... */}
constructorArgs := []byte{/* encoded constructor parameters */}
result, err := wallet.DeployContract(ctx, contractBytecode, constructorArgs, 500000, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Contract deployment transaction: %s\n", result.TransactionHash)
// Wait for deployment confirmation
receipt, err := wallet.WaitForTransaction(ctx, result.TransactionHash)
if err != nil {
log.Fatal(err)
}
if receipt.ContractAddress != "" {
fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress)
}// Wait for transaction to be mined
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
receipt, err := wallet.WaitForTransaction(ctx, txHash)
if err != nil {
log.Fatal(err)
}
// Check transaction status
if receipt.Status == "0x1" {
fmt.Println("✅ Transaction successful!")
fmt.Printf("Gas used: %s\n", receipt.GasUsed)
fmt.Printf("Block number: %s\n", receipt.BlockNumber)
} else {
fmt.Println("❌ Transaction failed!")
}// Send multiple transactions concurrently
addresses := []string{
"0xRecipient1...",
"0xRecipient2...",
"0xRecipient3...",
}
type TxResult struct {
Address string
TxHash string
Error error
}
results := make(chan TxResult, len(addresses))
for _, addr := range addresses {
go func(recipient string) {
result, err := wallet.SendEther(ctx, recipient, "0.01")
if err != nil {
results <- TxResult{recipient, "", err}
return
}
results <- TxResult{recipient, result.TransactionHash, nil}
}(addr)
}
// Collect results
for i := 0; i < len(addresses); i++ {
result := <-results
if result.Error != nil {
fmt.Printf("❌ Failed to send to %s: %v\n", result.Address, result.Error)
} else {
fmt.Printf("✅ Sent to %s: %s\n", result.Address, result.TxHash)
}
}// Get optimal gas price with buffer
currentGas, err := client.Eth().GetGasPrice(ctx)
if err != nil {
log.Fatal(err)
}
// Add 10% buffer for faster confirmation
buffer := new(big.Int).Div(currentGas, big.NewInt(10))
optimalGas := new(big.Int).Add(currentGas, buffer)
fmt.Printf("Optimal gas price: %s Gwei\n",
web3.FromWei(optimalGas, "gwei"))
// Use in transaction
txParams := web3.NewTransactionParams().
SetTo("0x...").
SetValueInEther("1.0").
SetGasPrice(optimalGas)// Recover the signer address from a raw transaction
rawTxHex := "0xf86c808504a817c800825208940x..."
signerAddress, err := web3.RecoverSigner(rawTxHex)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Transaction was signed by: %s\n", signerAddress)The library eliminates hardcoded strings with comprehensive typed constants for better type safety and IDE support.
Use typed block parameters instead of strings:
// Instead of hardcoded strings
balance, err := client.Eth().GetBalance(ctx, address, "latest")
// Use typed constants
balance, err := client.Eth().GetBalance(ctx, address, web3.BlockLatest)
balance, err := client.Eth().GetBalance(ctx, address, web3.BlockPending)
balance, err := client.Eth().GetBalance(ctx, address, web3.BlockEarliest)
// For specific block numbers
blockParam := web3.BlockNumber(18500000)
balance, err := client.Eth().GetBalance(ctx, address, blockParam)Type-safe unit conversions:
// Instead of string units
weiValue, _ := web3.ToWei("1", "ether")
gweiValue, _ := web3.ToWei("20", "gwei")
// Use typed units
weiValue, _ := web3.ToWei("1", web3.Ether)
gweiValue, _ := web3.ToWei("20", web3.Gwei)
// Helper functions
ethWei, _ := web3.EtherToWei("2.5")
gweiWei, _ := web3.GweiToWei("30")
ethDisplay, _ := web3.WeiToEther(ethWei)
gweiDisplay, _ := web3.WeiToGwei(gweiWei)Available units: Wei, Kwei, Mwei, Gwei, Szabo, Finney, Ether, Kether, Mether, Gether, Tether
Pre-defined chain IDs for popular networks:
// Instead of magic numbers
txParams.SetChainID(big.NewInt(1))
// Use typed chain IDs
txParams.SetChainID(web3.ChainMainnet)
txParams.SetChainID(web3.ChainGoerli)
txParams.SetChainID(web3.ChainPolygon)
txParams.SetChainID(web3.ChainArbitrum)
// Get network information
config, err := web3.GetNetworkConfig(web3.ChainMainnet)
fmt.Printf("Network: %s, Currency: %s\n", config.Name, config.Currency)
// Check network type
isTestnet := web3.IsTestnet(web3.ChainGoerli) // true
isMainnet := web3.IsMainnet(web3.ChainMainnet) // trueStandard gas limits for common operations:
// Predefined gas limits
txParams.SetGas(web3.GasLimitTransfer.Uint64()) // 21,000
txParams.SetGas(web3.GasLimitTokenTransfer.Uint64()) // 65,000
txParams.SetGas(web3.GasLimitTokenApproval.Uint64()) // 50,000
txParams.SetGas(web3.GasLimitContractDeploy.Uint64()) // 500,000Smart gas pricing with predefined levels:
// Gas price optimization levels
optimal, err := web3.GetOptimalGasPrice(ctx, client, web3.GasPriceStandard) // +10%
rapid, err := web3.GetOptimalGasPrice(ctx, client, web3.GasPriceRapid) // +50%
// Levels: GasPriceSlow, GasPriceStandard, GasPriceFast, GasPriceRapidPre-defined addresses for popular contracts:
// Common Ethereum addresses
wethContract := web3.WETHMainnet.String()
usdcContract := web3.USDCMainnet.String()
usdtContract := web3.USDTMainnet.String()
// Special addresses
zeroAddr := web3.ZeroAddress.String()
burnAddr := web3.BurnAddress.String()
// Address validation helpers
if web3.IsZeroAddress(someAddress) {
fmt.Println("Cannot send to zero address")
}Type-safe ERC-20 function signatures:
// Instead of hardcoded strings
data, err := web3.EncodeABI("balanceOf(address)", address)
// Use typed signatures
data, err := web3.EncodeABI(web3.FuncBalanceOf.String(), address)
data, err := web3.EncodeABI(web3.FuncTransfer.String(), to, amount)
data, err := web3.EncodeABI(web3.FuncApprove.String(), spender, amount)High-level transaction builders:
// Simple ETH transfer
ethTx := web3.NewSimpleTransfer("0xRecipient", "1.5", web3.ChainMainnet)
// Token transfer
tokenTx, err := web3.NewTokenTransfer(
web3.USDCMainnet.String(),
"0xRecipient",
amount,
web3.ChainMainnet,
)
// Token approval
approvalTx, err := web3.NewTokenApproval(
web3.USDCMainnet.String(),
spenderAddress,
amount,
web3.ChainMainnet,
)Type-safe transaction status checking:
// Instead of string comparisons
if receipt.Status == "0x1" {
fmt.Println("Success")
}
// Use typed status
if web3.IsTransactionSuccess(receipt) {
fmt.Println("Transaction successful!")
}
if web3.IsTransactionFailure(receipt) {
fmt.Println("Transaction failed!")
}// Before: Hardcoded strings everywhere
balance, _ := client.Eth().GetBalance(ctx, addr, "latest")
weiVal, _ := web3.ToWei("1.5", "ether")
gasPrice, _ := web3.ToWei("20", "gwei")
tx := web3.NewTransactionParams().
SetTo(recipient).
SetValue(weiVal).
SetGas(21000).
SetGasPrice(gasPrice).
SetChainID(big.NewInt(1))
// After: Type-safe constants
balance, _ := client.Eth().GetBalance(ctx, addr, web3.BlockLatest)
weiVal, _ := web3.ToWei("1.5", web3.Ether)
gasPrice, _ := web3.ToWei("20", web3.Gwei)
tx := web3.NewTransactionParams().
SetTo(recipient).
SetValue(weiVal).
SetGas(web3.GasLimitTransfer.Uint64()).
SetGasPrice(gasPrice).
SetChainID(web3.ChainMainnet)- Type Safety: Compile-time checking prevents typos
- IDE Support: Autocomplete and IntelliSense
- Maintainability: Centralized constant definitions
- Readability: Self-documenting code
- Consistency: Standardized values across the library
This library is enhanced with go-blockchain-helper, providing advanced blockchain utilities and optimized implementations.
// Enhanced parsing with error handling
ethAmount, err := web3.ParseEther("2.5")
gweiAmount, err := web3.ParseUnits("20", 9) // Custom decimals
// Advanced formatting with precision control
formatted := web3.FormatEther(weiAmount, 4) // 4 decimal places
customFormat := web3.FormatUnits(amount, 6) // USDC with 6 decimals// Create typed token instances
token := web3.NewERC20Token(contractAddr, "USD Coin", "USDC", 6)
// Enhanced encoding with proper error handling
transferData, err := web3.EncodeERC20Transfer(token, recipient, amount)
approveData, err := web3.EncodeERC20Approve(token, spender, amount)
// Simplified transaction creation
tokenTx, err := web3.NewTokenTransfer(
web3.USDCMainnet.String(),
recipient,
amount,
web3.ChainMainnet,
)// Create NFT token instances
nft := web3.NewERC721Token(contractAddr, "BoredApeYachtClub", "BAYC")
// NFT operations
transferData, err := web3.EncodeERC721Transfer(nft, from, to, tokenId)
approveData, err := web3.EncodeERC721Approve(nft, approved, tokenId)
setApprovalData, err := web3.EncodeERC721SetApprovalForAll(nft, operator, true)// Smart transaction creation with automatic estimation
tx, err := web3.CreateTransactionWithEstimate(
recipient,
value,
data,
web3.ChainMainnet,
)
// Enhanced fee calculation
fee := web3.CalculateTransactionFee(gasLimit, gasPrice)// Enhanced token balance queries
balance, err := web3.GetTokenBalance(ctx, client, tokenContract, address)
allowance, err := web3.GetTokenAllowance(ctx, client, tokenContract, owner, spender)
// Advanced ABI encoding with type inference
abiParams := []web3.ABIParam{{Type: "address"}, {Type: "uint256"}}
data, err := web3.EncodeFunctionCallAdvanced(signature, abiParams, params)// Create event monitor for real-time processing
monitor := web3.CreateEventMonitor()
// Parse common events
transferEvent, err := web3.ParseTransferEvent(eventData)// Comprehensive address validation
isValid := web3.ValidateAddress(address)
isZero := web3.IsZeroAddress(address)
isBurn := web3.IsBurnAddress(address)
// Private key utilities
address, err := web3.PrivateKeyToAddressHelper(privateKey)- Zero External Dependencies:
go-blockchain-helperuses only Go standard library - Enhanced Performance: Optimized implementations for common operations
- Professional Features: Production-ready ERC20/ERC721 support
- Better Error Handling: Comprehensive error information
- Type Safety: Strongly-typed token and transaction structures
- Advanced Utilities: Event processing, enhanced validation, and more
// Before: Basic implementations
weiValue, _ := web3.ToWei("1", web3.Ether)
balance, _ := client.Eth().GetBalance(ctx, addr, web3.BlockLatest)
// After: Enhanced with go-blockchain-helper
ethValue, err := web3.ParseEther("1") // Better parsing
balance, err := web3.GetTokenBalance(ctx, client, token, addr) // Enhanced queriesThe integration maintains full backward compatibility while providing enhanced functionality for professional blockchain development.
The library provides comprehensive unit conversion similar to web3.js:
// Convert from Ether to Wei
weiValue, err := web3.ToWei("1.5", "ether")
if err != nil {
log.Fatal(err)
}
fmt.Printf("1.5 ETH = %s Wei\n", weiValue.String())
// Convert from Gwei to Wei
gasPriceWei, err := web3.ToWei("20", "gwei")
fmt.Printf("20 Gwei = %s Wei\n", gasPriceWei.String())
// Support for all units
units := []string{"wei", "kwei", "mwei", "gwei", "szabo", "finney", "ether"}
for _, unit := range units {
wei, _ := web3.ToWei("1", unit)
fmt.Printf("1 %s = %s wei\n", unit, wei.String())
}// Convert Wei to Ether for display
weiAmount := big.NewInt(1500000000000000000) // 1.5 ETH in wei
ethValue, err := web3.FromWei(weiAmount, "ether")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Wei to ETH: %s\n", ethValue) // "1.5"
// Convert Wei to Gwei (useful for gas prices)
gasPriceWei := big.NewInt(20000000000)
gasPriceGwei, _ := web3.FromWei(gasPriceWei, "gwei")
fmt.Printf("Gas price: %s Gwei\n", gasPriceGwei) // "20"addresses := []string{
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // valid
"0xInvalidAddress", // invalid
"d8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // missing 0x prefix
"0x123", // too short
}
for _, addr := range addresses {
if web3.IsAddress(addr) {
fmt.Printf("%s is a valid address\n", addr)
} else {
fmt.Printf("%s is invalid\n", addr)
}
}// Convert integers to hex
hexInt := web3.ToHex(12345) // "0x3039"
hexBigInt := web3.ToHex(big.NewInt(12345)) // "0x3039"
// Convert strings to hex
hexString := web3.ToHex("hello") // "0x68656c6c6f"
// Convert byte arrays to hex
data := []byte{0x12, 0x34, 0x56}
hexBytes := web3.ToHex(data) // "0x123456"// Convert hex string to big.Int
hexValue := "0x3039"
value, err := web3.FromHex(hexValue)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Hex %s = %d decimal\n", hexValue, value.Int64()) // "12345"// Left padding (useful for hex values)
padded := web3.PadLeft("abc", 8, "0") // "00000abc"
hexPadded := web3.PadLeft("1a2b", 8, "0") // "00001a2b"
// Right padding
rightPadded := web3.PadRight("abc", 8, "0") // "abc00000"
// Common use case: padding addresses or hex values
address := "0x123"
paddedAddr := "0x" + web3.PadLeft(address[2:], 40, "0")
fmt.Printf("Padded address: %s\n", paddedAddr)| Unit Name | Aliases | Wei Value | Common Use |
|---|---|---|---|
wei |
- | 1 | Smallest unit, precise calculations |
kwei |
babbage, femtoether |
10³ | - |
mwei |
lovelace, picoether |
10⁶ | - |
gwei |
shannon, nanoether, nano |
10⁹ | Gas prices |
szabo |
microether, micro |
10¹² | - |
finney |
milliether, milli |
10¹⁵ | Small transactions |
ether |
eth |
10¹⁸ | Standard currency unit |
kether |
grand |
10²¹ | Large amounts |
mether |
- | 10²⁴ | Very large amounts |
gether |
- | 10²⁷ | Extremely large amounts |
tether |
- | 10³⁰ | Theoretical amounts |
// Common conversions
oneEth, _ := web3.ToWei("1", "ether") // 1000000000000000000 wei
oneGwei, _ := web3.ToWei("1", "gwei") // 1000000000 wei
gasPrice, _ := web3.ToWei("20", "gwei") // 20000000000 wei (20 Gwei)
// Display conversions
weiAmount := big.NewInt(1500000000000000000)
ethDisplay, _ := web3.FromWei(weiAmount, "ether") // "1.5"
gweiDisplay, _ := web3.FromWei(weiAmount, "gwei") // "1500000000"The library provides detailed error information for RPC failures:
balance, err := client.Eth().GetBalance(ctx, "invalid-address", "latest")
if err != nil {
// Check if it's an RPC error
if rpcErr, ok := err.(*web3.RPCError); ok {
fmt.Printf("RPC Error %d: %s\n", rpcErr.Code, rpcErr.Message)
if rpcErr.Data != "" {
fmt.Printf("Additional data: %s\n", rpcErr.Data)
}
} else {
// Handle other errors (network, parsing, etc.)
fmt.Printf("Other error: %v\n", err)
}
}// Timeout handling
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
result, err := client.Eth().GetBlockNumber(ctx)
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Request timed out")
}
return
}
// Network error handling
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
fmt.Println("Cannot connect to Ethereum node")
} else if strings.Contains(err.Error(), "invalid response") {
fmt.Println("Invalid response from node")
}
}func monitorTransaction(client *web3.Client, txHash string) error {
ctx := context.Background()
// Check if transaction exists
tx, err := client.Eth().GetTransactionByHash(ctx, txHash)
if err != nil {
return fmt.Errorf("transaction not found: %w", err)
}
fmt.Printf("Transaction found: %s -> %s\n", tx.From, tx.To)
fmt.Printf("Value: %s ETH\n", mustFromWei(tx.Value, "ether"))
// Wait for confirmation
for {
receipt, err := client.Eth().GetTransactionReceipt(ctx, txHash)
if err != nil {
time.Sleep(5 * time.Second)
continue
}
if receipt.Status == "0x1" {
fmt.Printf("✅ Transaction confirmed in block %s\n", receipt.BlockNumber)
fmt.Printf("Gas used: %s\n", receipt.GasUsed)
return nil
} else {
fmt.Printf("❌ Transaction failed\n")
return fmt.Errorf("transaction failed")
}
}
}
func mustFromWei(weiHex, unit string) string {
wei, _ := web3.FromHex(weiHex)
eth, _ := web3.FromWei(wei, unit)
return eth
}func getOptimalGasPrice(client *web3.Client) (*big.Int, error) {
ctx := context.Background()
// Get current gas price
currentGas, err := client.Eth().GetGasPrice(ctx)
if err != nil {
return nil, err
}
// Add 10% buffer for faster processing
buffer := new(big.Int).Div(currentGas, big.NewInt(10))
optimalGas := new(big.Int).Add(currentGas, buffer)
fmt.Printf("Current gas price: %s Gwei\n", mustFromWei(fmt.Sprintf("0x%x", currentGas), "gwei"))
fmt.Printf("Optimal gas price: %s Gwei\n", mustFromWei(fmt.Sprintf("0x%x", optimalGas), "gwei"))
return optimalGas, nil
}func getMultipleBalances(client *web3.Client, addresses []string) (map[string]*big.Int, error) {
ctx := context.Background()
balances := make(map[string]*big.Int)
// Use goroutines for concurrent requests
type result struct {
address string
balance *big.Int
err error
}
results := make(chan result, len(addresses))
for _, addr := range addresses {
go func(address string) {
balance, err := client.Eth().GetBalance(ctx, address, "latest")
results <- result{address, balance, err}
}(addr)
}
// Collect results
for i := 0; i < len(addresses); i++ {
res := <-results
if res.err != nil {
return nil, res.err
}
balances[res.address] = res.balance
}
return balances, nil
}go-web3/
├── client.go # Core RPC client implementation
├── eth.go # Ethereum-specific methods
├── utils.go # Utility functions
├── example/
│ └── main.go # Usage examples
├── go.mod # Go module definition
├── README.md # This documentation
└── LICENSE # MIT license
- Infura:
https://mainnet.infura.io/v3/YOUR_PROJECT_ID - Alchemy:
https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY - QuickNode:
https://your-endpoint.quiknode.pro/YOUR_API_KEY/
- Goerli:
https://goerli.infura.io/v3/YOUR_PROJECT_ID - Sepolia:
https://sepolia.infura.io/v3/YOUR_PROJECT_ID
- Hardhat:
http://localhost:8545 - Ganache:
http://localhost:7545
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- 📖 Documentation: This README
- 💻 Examples: See
example/main.go - 🐛 Issues: Open an issue on GitHub
- 💡 Feature Requests: Open an issue with the enhancement label
Made with ❤️ for the Ethereum Go community