package main import ( "bufio" "context" "crypto/ecdsa" "fmt" "log" "math/big" "os" "strconv" "strings" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" ) const ( keyFileName = "keys.txt" nodeURLTxtName = "nodeURL.txt" ) func main() { // 1. 读取 nodeURL.txt nodeURLs, err := readLinesNoBlank(nodeURLTxtName) if err != nil { log.Fatalf("读取%s失败: %v", nodeURLTxtName, err) } if len(nodeURLs) == 0 { log.Fatalf("%s 文件为空,请填入至少一个节点URL(每行一个)后重试。", nodeURLTxtName) } // 2. 展示节点列表,提示选择 fmt.Println("可用网络节点列表:") for i, url := range nodeURLs { fmt.Printf(" %d. %s\n", i+1, url) } fmt.Printf("\n输入要使用的节点编号 (1-%d): ", len(nodeURLs)) var urlIndex int reader := bufio.NewReader(os.Stdin) for { fmt.Printf("请输入要使用的节点编号 (1-%d): ", len(nodeURLs)) line, err := reader.ReadString('\n') if err != nil { fmt.Println("读取输入失败,请重试。") continue } line = strings.TrimSpace(line) // 检查line是否只包含数字 num, err := strconv.Atoi(line) if err != nil || num < 1 || num > len(nodeURLs) { fmt.Printf("输入无效,请输入 1 到 %d 之间的数字。\n", len(nodeURLs)) continue } urlIndex = num - 1 break } nodeURL := nodeURLs[urlIndex] fmt.Printf("\n当前使用节点: %s\n\n", nodeURL) // 检查keys.txt是否存在 if _, err := os.Stat(keyFileName); os.IsNotExist(err) { file, err := os.Create(keyFileName) if err != nil { log.Fatalf("创建%s失败: %v", keyFileName, err) } defer file.Close() fmt.Printf("没有%s文件,已创建,请填入你的key,每行一个,然后重新运行本程序。\n", keyFileName) return } // 读取keys.txt myKeyArr, err := readLinesNoBlank(keyFileName) if err != nil { log.Fatalf("打开%s失败: %v", keyFileName, err) } if len(myKeyArr) == 0 { fmt.Printf("%s 为空,请至少填入一个私钥(每行一个),然后重新运行本程序。\n", keyFileName) return } // 连接以太坊节点 client, err := ethclient.Dial(nodeURL) if err != nil { log.Fatalf("连接节点失败: %v", err) } // 查询每个key对应地址的余额 for i, key := range myKeyArr { privateKey, err := crypto.HexToECDSA(key) if err != nil { fmt.Printf("钱包 %d: 私钥解析失败,跳过。错误:%v\n\n", i+1, err) continue // 跳过无效私钥 } publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { fmt.Printf("钱包 %d: 公钥转化失败,跳过。\n\n", i+1) continue } address := crypto.PubkeyToAddress(*publicKeyECDSA) fmt.Printf("钱包 %d , 钱包地址: %s\n", i+1, address.Hex()) balance, err := client.BalanceAt(context.Background(), address, nil) if err != nil { fmt.Printf("钱包 %d: 查询余额失败,跳过。错误:%v\n\n", i+1, err) continue } nonce, err := client.PendingNonceAt(context.Background(), address) if err != nil { fmt.Printf("钱包 %d: 查询Nonce失败,跳过。错误:%v\n\n", i+1, err) continue } fmt.Printf("余额(wei): %s\n", balance.String()) etherValue := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(1e18)) fmt.Printf("余额(Token): %f\n", etherValue) fmt.Printf("Nonce: %d\n\n", nonce) } } // 读取文件每行,过滤空行和首尾空白 func readLinesNoBlank(filename string) ([]string, error) { file, err := os.Open(filename) if err != nil { return nil, err } defer file.Close() var lines []string scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line != "" { lines = append(lines, line) } } return lines, scanner.Err() }