| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- package main
- import (
- "bufio"
- "context"
- "crypto/ecdsa"
- "fmt"
- "math/big"
- "os"
- "strings"
- "sync"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethclient"
- "github.com/therecipe/qt/core"
- "github.com/therecipe/qt/widgets"
- )
- const (
- keyFileName = "keys.txt"
- nodeURLTxtName = "nodeURL.txt"
- )
- type window struct {
- *widgets.QMainWindow
- chainCB *widgets.QComboBox
- outputTE *widgets.QTextEdit
- }
- type queryResult struct {
- index int
- message string
- }
- func main() {
- app := widgets.NewQApplication(len(os.Args), os.Args)
- // 1.5 倍全局放大
- app.SetStyleSheet(`
- * {
- font-size: 15pt;
- padding: 6px;
- }
- QPushButton {
- min-height: 34px;
- }
- `)
- win := &window{QMainWindow: widgets.NewQMainWindow(nil, 0)}
- win.SetWindowTitle("多链余额查询器")
- win.SetMinimumSize2(1050, 750) // 700*1.5 , 500*1.5
- central := widgets.NewQWidget(nil, 0)
- layout := widgets.NewQVBoxLayout2(central)
- // ---- 1. 读取节点列表 ----
- nodeURLs, err := readLinesNoBlank(nodeURLTxtName)
- if err != nil {
- popupFatal(fmt.Sprintf("读取 %s 失败: %v", nodeURLTxtName, err))
- }
- if len(nodeURLs) == 0 {
- popupFatal(fmt.Sprintf("%s 为空,请至少填一个节点 URL", nodeURLTxtName))
- }
- // ---- 2. 下拉框 ----
- layout.AddWidget(widgets.NewQLabel2("选择节点:", nil, 0), 0, 0)
- win.chainCB = widgets.NewQComboBox(nil)
- for _, u := range nodeURLs {
- win.chainCB.AddItem(u, core.NewQVariant1(u))
- }
- layout.AddWidget(win.chainCB, 0, 0)
- // ---- 3. 按钮区域 ----
- btnLayout := widgets.NewQHBoxLayout()
- queryBtn := widgets.NewQPushButton2("查询余额", nil)
- queryBtn.ConnectClicked(func(bool) { win.query() })
- btnLayout.AddWidget(queryBtn, 0, 0)
- clearBtn := widgets.NewQPushButton2("清除输出", nil)
- clearBtn.ConnectClicked(func(bool) { win.outputTE.Clear() })
- btnLayout.AddWidget(clearBtn, 0, 0)
- layout.AddLayout(btnLayout, 0)
- // ---- 4. 输出框 ----
- win.outputTE = widgets.NewQTextEdit(nil)
- win.outputTE.SetReadOnly(true)
- layout.AddWidget(win.outputTE, 0, 0)
- win.SetCentralWidget(central)
- win.Show()
- widgets.QApplication_Exec()
- }
- // ============ 查询逻辑 ============
- func (w *window) query() {
- w.outputTE.Clear()
- // 当前选中节点
- idx := w.chainCB.CurrentIndex()
- if idx < 0 {
- w.log("未选择节点")
- return
- }
- nodeURL := w.chainCB.CurrentText()
- w.log("当前节点: %s", nodeURL)
- // 读取 keys
- keys, err := readLinesNoBlank(keyFileName)
- if err != nil {
- w.log("读取 %s 失败: %v", keyFileName, err)
- return
- }
- if len(keys) == 0 {
- w.log("%s 为空,请先填入私钥(每行一个)", keyFileName)
- return
- }
- // 连接
- client, err := ethclient.Dial(nodeURL)
- if err != nil {
- w.log("连接节点失败: %v", err)
- return
- }
- defer client.Close()
- var wg sync.WaitGroup
- results := make([]string, len(keys))
- for i, hexKey := range keys {
- wg.Add(1)
- go func(i int, hexKey string) {
- defer wg.Done()
- privateKey, err := crypto.HexToECDSA(hexKey)
- if err != nil {
- results[i] = fmt.Sprintf("钱包 %d: 私钥解析失败,跳过。err=%v", i+1, err)
- return
- }
- publicKey, ok := privateKey.Public().(*ecdsa.PublicKey)
- if !ok {
- results[i] = fmt.Sprintf("钱包 %d: 公钥转换失败,跳过", i+1)
- return
- }
- addr := crypto.PubkeyToAddress(*publicKey)
- balance, err := client.BalanceAt(context.Background(), addr, nil)
- if err != nil {
- results[i] = fmt.Sprintf("钱包 %d (%s): 查余额失败,err=%v", i+1, addr.Hex(), err)
- return
- }
- nonce, err := client.PendingNonceAt(context.Background(), addr)
- if err != nil {
- results[i] = fmt.Sprintf("钱包 %d (%s): 查 nonce 失败,err=%v", i+1, addr.Hex(), err)
- return
- }
- ether := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(1e18))
- results[i] = fmt.Sprintf("钱包 %d %s \n余额(wei): %s\n余额Token %.6f\nNonce: %d",
- i+1, addr.Hex(), balance.String(), ether, nonce)
- }(i, hexKey)
- }
- wg.Wait()
- // 全部结束后按顺序输出
- for _, res := range results {
- w.log(res)
- }
- }
- // ============ 工具函数 ============
- func readLinesNoBlank(filename string) ([]string, error) {
- f, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- var out []string
- sc := bufio.NewScanner(f)
- for sc.Scan() {
- line := strings.TrimSpace(sc.Text())
- if line != "" {
- out = append(out, line)
- }
- }
- return out, sc.Err()
- }
- func (w *window) log(format string, a ...interface{}) {
- s := fmt.Sprintf(format, a...)
- w.outputTE.Append(s + "\n")
- }
- func popupFatal(msg string) {
- widgets.QMessageBox_Critical(nil, "错误", msg, widgets.QMessageBox__Ok, widgets.QMessageBox__Ok)
- os.Exit(1)
- }
|