用 Go 语言精准获取以太坊地址 ETH 余额:完整实战与避坑指南

·

标签:Go、以太坊、ethclient、ETH 余额、精度、Wei、Ether、区块链开发、以太坊 API


上期我们已把主网节点信息拿到手,本期聚焦「只凭地址就能查到的 ETH 余额」。注意:查询对象为 原生 ETH 而非 ERC-20 代币,也不要求你掌握私钥。跟着本文一步步操作,3 分钟把余额“挖”出来!

准备工作:依赖与网络

  1. 安装 go-ethereum 官方库:go get github.com/ethereum/go-ethereum/ethclient
  2. 获取公开节点或自建 RPC(本文用 Infura 做演示)。
  3. 终端设置环境变量 export INFURA_API_KEY="你的Key"

搞定之后,用 ethclient.Dial(url) 连接主网,共三步即可查询。


核心 API:client.BalanceAt 解析

import (
    "context"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
    "math/big"
)

ctx := context.Background()
addr := common.HexToAddress("0xd8dA...6045")
bn := big.NewInt(18382246)             // 指定区块高度
balance, _ := client.BalanceAt(ctx, addr, bn)

👉 想要零门槛体验链上查询?去这里获取主网余额实时数据


换单位?先把精度的坑填上!

在钱包里常见的“935.143007 ETH”听起来很优雅,但链上记录是整整 935143001746486974073 Wei。转换时必须选用支持大整数的数据结构。

方法一:标准库 big.Float

bf := new(big.Float).SetInt(balance)
bf.Quo(bf, big.NewFloat(1e18))

输出结果会像 935.1430017——没错,它已经四舍五入了一次!

方法二:decimal 库(推荐)

import "github.com/shopspring/decimal"

wei := decimal.RequireFromString(balance.String())
eth := wei.Div(decimal.New(1, 18)) // 即 1e18

decimal 基于 十进制浮点 存算,连微小尾差都能挽回,财务对账、DEX 比价场景都建议用它。


参数场景演练:选择区块号 or 不选?

需求调用例子
当前余额BalanceAt(ctx, addr, nil)
分叉时余额BalanceAt(ctx, addr, big.NewInt(1920000))
合约部署前状况传入部署区块高度减 1 即可

快速比对不同高度的余额变化,用来 追踪大额转账、检测黑客攻击 非常高效。


完整可运行示例

把下方代码保存为 main.go,直接 go run . 体验:

package main

import (
    "context"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
    decimal "github.com/shopspring/decimal"
    "log"
    "math/big"
    "os"
)

func main() {
    apiKey := os.Getenv("INFURA_API_KEY")
    if apiKey == "" {
        log.Fatal("请先设置 INFURA_API_KEY 环境变量")
    }

    client, err := ethclient.Dial("https://mainnet.infura.io/v3/" + apiKey)
    if err != nil {
        log.Fatalf("无法连接节点: %v", err)
    }

    ctx := context.Background()
    addr := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    block := big.NewInt(18382246)

    wei, err := client.BalanceAt(ctx, addr, block)
    if err != nil {
        log.Fatalf("查询失败: %v", err)
    }
    log.Printf("余额(Wei): %v", wei)

    eth := decimal.RequireFromString(wei.String()).Div(decimal.New(1, 18))
    log.Printf("余额(Ether, decimal): %v", eth)
}

运行输出示例:

2025/06/23 12:34:56 余额(Wei): 935143001746486974073
2025/06/23 12:34:56 余额(Ether, decimal): 935.143001746486974073

精度 丝毫无损,对接报表或前端显示时再继续四舍五入即可。

👉 现在就把脚本塞进你的监控流水线,实时警报资产异动


FAQ:最常见疑问一次说清

Q1:我只拿到私钥,能从私钥反向查地址吗?
A:可以。使用 go-ethereum/crypto 中的 ecdsa 套件可从私钥推导出 common.Address,随后再用本文方法查余额。

Q2:能不能一次查 n 个地址的余额?
A:BalanceAt 只能一次查一个。可并发调用,或用批次 RPC 封装(如 eth_getBalance 的多请求 JSON-RPC)。

Q3:查询速率有限制怎么办?
A:无论你用 Infura、Alchemy 还是自建节点,都会有限流。建议本地缓存、批量同步、或者在项目中接入 WebSocket 订阅 newHeads,仅在区块变动时刷新余额,减少 80% 以上请求。

Q4:Decimal 会不会拖慢性能?
A:decimal 使用大整数表示,比 big.Float 稍慢,但在日常百万级精度需求场景差距微乎其微,精度优先时完全值得。


案例实战:3 秒查出黑客出逃前余额

2024 年底,某项目被盗。开发者在得知黑客地址后,直接用脚本:

全程不到 3 秒,为法务取证争取了时间。精度问题?decimal 控场无误。


结语与进阶阅读

本文通过 balanceAt 让一个 HTTP 请求就能把链上 ETH 余额握在手中;配合环境变量、错误处理与并发池,你在生产环境也已稳如老狗。
下一步:

Keep coding,余额不落空!