Essential Go Tools for Ethereum Development

·

When building decentralized applications or interacting with the Ethereum blockchain using Go, having a reliable set of utility functions is crucial. These tools streamline common tasks such as address validation, unit conversion, gas cost estimation, and signature parsing—operations that are foundational in blockchain development. This guide dives into practical Go utilities tailored for Ethereum developers, offering clean, reusable code snippets and explanations that enhance both efficiency and accuracy.

Whether you're transferring tokens, verifying smart contract interactions, or debugging transactions, these functions serve as a robust foundation. Built on widely used libraries like github.com/ethereum/go-ethereum/common and github.com/shopspring/decimal, they ensure compatibility and precision across your projects.

Core Utility Functions for Ethereum in Go

Validate Ethereum Addresses

One of the first steps in secure blockchain interaction is validating addresses. An invalid address can lead to irreversible fund loss.

valid := util.IsValidAddress("0x323b5d4c32345ced77393b3530b1eed0f346429d")
fmt.Println(valid) // true

The IsValidAddress function checks whether an input string or common.Address type matches the standard 40-character hexadecimal format prefixed with 0x. It uses regular expression matching to ensure correctness and supports multiple input types via Go’s interface system.

👉 Discover powerful tools to streamline your Ethereum development workflow.

Detect Zero Addresses

A zero address (0x0) often indicates uninitialized or invalid wallet references. Checking for it prevents logical errors in smart contract calls.

zeroed := util.IsZeroAddress("0x0")
fmt.Println(zeroed) // true

This function converts the input into a standard common.Address and compares its byte representation against a fully zeroed 20-byte array. It's especially useful when validating user inputs or ensuring contract initialization safety.

Convert Between Ether and Wei

Handling value transfers requires precise unit conversion. Ethereum uses wei as the smallest denomination, where 1 ETH = 10¹⁸ wei.

Convert decimal (e.g., 0.02 ETH) to wei:

wei := util.ToWei(0.02, 18)
fmt.Println(wei) // 20000000000000000

Convert wei back to decimal form:

wei := new(big.Int)
wei.SetString("20000000000000000", 10)
eth := util.ToDecimal(wei, 18)
fmt.Println(eth) // 0.02

These functions use the decimal.Decimal package to avoid floating-point inaccuracies during arithmetic operations—a critical feature for financial calculations.

Calculate Transaction Gas Costs

Estimating transaction costs helps users understand fees before broadcasting transactions.

gasLimit := uint64(21000)
gasPrice := new(big.Int)
gasPrice.SetString("2000000000", 10) // 2 gwei
gasCost := util.CalcGasCost(gasLimit, gasPrice)
fmt.Println(gasCost) // 42000000000000 (wei)

CalcGasCost multiplies gas limit by gas price to return total cost in wei. You can later convert this to ETH using ToDecimal for user-friendly display.

Extract Signature Components (R, S, V)

Digital signatures in Ethereum consist of three components: R, S, and V. Parsing them is essential for verifying message authenticity or recovering signer addresses.

sig := "0x789a80053e4927d0a898db8e065e948f5cf086e32f9ccaa54c1908e22ac430c62621578113ddbb62d509bf6049b8fb544ab06d36f916685a2eb8e57ffadde02301"
r, s, v := util.SigRSV(sig)
fmt.Println(hexutil.Encode(r[:])[2:]) // R value
fmt.Println(hexutil.Encode(s[:])[2:]) // S value
fmt.Println(v) // V value (recovery ID)

This function decodes the hex signature, splits it into segments, and returns properly formatted byte arrays along with the corrected V value (adjusted by adding 27).

Complete Implementation

Below is the full implementation of the utility package:

package util

import (
 "math/big"
 "reflect"
 "regexp"
 "strconv"
 "github.com/ethereum/go-ethereum/common"
 "github.com/ethereum/go-ethereum/common/hexutil"
 "github.com/shopspring/decimal"
)

// IsValidAddress validate hex address
func IsValidAddress(iaddress interface{}) bool {
 re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$")
 switch v := iaddress.(type) {
 case string:
  return re.MatchString(v)
 case common.Address:
  return re.MatchString(v.Hex())
 default:
  return false
 }
}

// IsZeroAddress validate if it's a 0 address
func IsZeroAddress(iaddress interface{}) bool {
 var address common.Address
 switch v := iaddress.(type) {
 case string:
  address = common.HexToAddress(v)
 case common.Address:
  address = v
 default:
  return false
 }
 zeroAddressBytes := common.FromHex("0x0000000000000000000000000000000000000000")
 addressBytes := address.Bytes()
 return reflect.DeepEqual(addressBytes, zeroAddressBytes)
}

// ToDecimal wei to decimals
func ToDecimal(ivalue interface{}, decimals int) decimal.Decimal {
 value := new(big.Int)
 switch v := ivalue.(type) {
 case string:
  value.SetString(v, 10)
 case *big.Int:
  value = v
 }
 mul := decimal.NewFromFloat(float64(10)).Pow(decimal.NewFromFloat(float64(decimals)))
 num, _ := decimal.NewFromString(value.String())
 result := num.Div(mul)
 return result
}

// ToWei decimals to wei
func ToWei(iamount interface{}, decimals int) *big.Int {
 amount := decimal.NewFromFloat(0)
 switch v := iamount.(type) {
 case string:
  amount, _ = decimal.NewFromString(v)
 case float64:
  amount = decimal.NewFromFloat(v)
 case int64:
  amount = decimal.NewFromFloat(float64(v))
 case decimal.Decimal:
  amount = v
 case *decimal.Decimal:
  amount = *v
 }
 mul := decimal.NewFromFloat(float64(10)).Pow(decimal.NewFromFloat(float64(decimals)))
 result := amount.Mul(mul)
 wei := new(big.Int)
 wei.SetString(result.String(), 10)
 return wei
}

// CalcGasCost calculate gas cost given gas limit (units) and gas price (wei)
func CalcGasCost(gasLimit uint64, gasPrice *big.Int) *big.Int {
 gasLimitBig := big.NewInt(int64(gasLimit))
 return gasLimitBig.Mul(gasLimitBig, gasPrice)
}

// SigRSV signatures R S V returned as arrays
func SigRSV(isig interface{}) ([32]byte, [32]byte, uint8) {
 var sig []byte
 switch v := isig.(type) {
 case []byte:
  sig = v
 case string:
  sig, _ = hexutil.Decode(v)
 }
 sigstr := common.Bytes2Hex(sig)
 rS := sigstr[0:64]
 sS := sigstr[64:128]
 R := [32]byte{}
 S := [32]byte{}
 copy(R[:], common.FromHex(rS))
 copy(S[:], common.FromHex(sS))
 vStr := sigstr[128:130]
 vI, _ := strconv.Atoi(vStr)
 V := uint8(vI + 27)
 return R, S, V
}

For testing, refer to util_test.go in the original repository to verify behavior under various edge cases.

👉 Access advanced blockchain development resources to boost your coding efficiency.

Frequently Asked Questions

Q: Can I use these utilities in production applications?
A: Yes. These functions are based on battle-tested libraries and follow Ethereum standards (e.g., EIP-55). However, always test thoroughly in staging environments before deployment.

Q: Why does the V value in signatures need adjustment by +27?
A: The +27 adjustment aligns with older ECDSA signature standards used in early Ethereum clients. Modern implementations may use different recovery values (e.g., EIP-155), so ensure compatibility with your signing method.

Q: How do I handle different ERC-20 token decimals?
A: Pass the token’s specific decimals value (often found in its contract) into ToWei or ToDecimal. For example, USDC uses 6 decimals instead of ETH’s 18.

Q: Is floating-point input safe in ToWei?
A: While supported for convenience, prefer string inputs ("0.02") over float64 to avoid precision loss due to IEEE 754 limitations.

Q: What dependencies are required?
A: You'll need github.com/ethereum/go-ethereum/common and github.com/shopspring/decimal. Install via Go modules:
go get github.com/ethereum/go-ethereum/common
go get github.com/shopspring/decimal

Q: Can I extend these functions for EIP-1559 transactions?
A: Absolutely. While CalcGasCost works for legacy pricing, you can expand it to compute effective gas prices using maxFeePerGas and maxPriorityFeePerGas.

Ethereum development in Go becomes significantly smoother with well-crafted helper functions. From validating inputs to parsing cryptographic signatures, these tools cover fundamental needs while promoting code reusability and correctness.

👉 Enhance your Ethereum toolkit with cutting-edge development platforms.