axiom-checker/main.go
2026-01-21 04:17:41 +02:00

475 lines
11 KiB
Go

package main
import (
"flag"
"fmt"
"os"
"sort"
"strings"
"sync"
"time"
)
type Mode string
const (
ModePrivateKeys Mode = "private-keys"
ModeAxiom Mode = "axiom"
ModeBoth Mode = "both"
)
type CheckResult struct {
Line string
Balance float64
}
type Statistics struct {
TotalBalance float64
KeysWithBalance int
EmptyKeys int
TotalKeys int
Duration time.Duration
Mode string
}
var (
results []CheckResult
resultsMutex sync.Mutex
stats Statistics
)
func addResult(line string, balance float64) {
resultsMutex.Lock()
defer resultsMutex.Unlock()
results = append(results, CheckResult{Line: line, Balance: balance})
stats.TotalBalance += balance
if balance > 0 {
stats.KeysWithBalance++
} else {
stats.EmptyKeys++
}
}
func main() {
addressFlag := flag.String("address", "", "Check specific address")
flag.Parse()
printHeader()
cfg, err := loadConfig()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
os.Exit(1)
}
initProxies()
if *addressFlag != "" {
checkSingleAddress(*addressFlag, cfg)
return
}
for {
choice, err := showMainMenu(cfg)
if err != nil {
fmt.Fprintf(os.Stderr, "\nCancelled\n")
os.Exit(1)
}
switch choice {
case "settings":
if err := showSettingsMenu(cfg); err != nil {
fmt.Fprintf(os.Stderr, "\nSettings cancelled\n")
continue
}
case "start":
initApiKeyWorkers(cfg)
fmt.Println()
printConfigSummary(cfg)
moduleManager := NewModuleManager(cfg)
enabledModules := moduleManager.GetEnabledModules()
if cfg.EnableSolanaCheck || len(enabledModules) > 0 {
colorPrint(ColorGreen, "Active modules: ")
modules := []string{}
if cfg.EnableSolanaCheck {
modules = append(modules, "Solana")
}
for _, m := range enabledModules {
modules = append(modules, m.Name())
}
for i, m := range modules {
if i > 0 {
fmt.Print(", ")
}
colorPrint(ColorCyan, m)
}
fmt.Println()
}
fmt.Printf("Loaded proxies: %d\n", len(proxies))
keys := loadPrivateKeys()
if len(keys) == 0 {
fmt.Fprintln(os.Stderr, "\nNo keys found!\nAdd to: private_keys.txt or keys/*.txt")
continue
}
stats.TotalKeys = len(keys)
runChecker(keys, cfg, moduleManager)
fmt.Println("\nPress Enter to return to main menu...")
fmt.Scanln()
printHeader()
case "exit":
colorPrintln(ColorYellow, "\nGoodbye!")
os.Exit(0)
}
}
}
func printHeader() {
colorPrintln(ColorCyan, "")
colorPrintln(ColorCyan, " ___ _ ___ _ _ ")
colorPrintln(ColorCyan, " / _ \\__ _(_)___ _ __ ___ / __| |_ ___ __| |_____ _ _ ")
colorPrintln(ColorCyan, " | (_| \\ \\ / / _ \\ ' \\/ -_) | (__| ' \\/ -_) _| / / -_) '_|")
colorPrintln(ColorCyan, " \\___/_\\_\\_\\\\___/_|_|_\\___| \\___|_||_\\___\\__|_\\_\\___|_| ")
fmt.Print(" ")
colorPrintln(ColorYellow, "by @septum")
colorPrintln(ColorCyan, "")
}
func checkSingleAddress(address string, cfg *Config) {
if strings.HasPrefix(address, "0x") || strings.HasPrefix(address, "0X") {
balance, details, err := getDeBankBalance(address)
if err != nil {
fmt.Printf("[evm address][debank] %s - Error: %v\n", address, err)
return
}
fmt.Printf("[evm address][debank] %s - $%.2f\n", address, balance)
if details != "" {
fmt.Print(details)
}
} else {
portfolio, err := getPortfolioValue(address, cfg)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
tokenStr := ""
if portfolio.Value > 0 {
if portfolio.TokenCount > 10 {
tokenStr = " (>10 tokens)"
} else {
tokenStr = fmt.Sprintf(" (%d tokens)", portfolio.TokenCount)
}
}
valueStr := "0.00"
if portfolio.Value > 0 {
valueStr = fmt.Sprintf("%.2f", portfolio.Value)
}
fmt.Printf("[solana address][jup.ag] %s - $%s%s\n", address, valueStr, tokenStr)
}
}
func runChecker(keys []string, cfg *Config, moduleManager *ModuleManager) {
outputFile := fmt.Sprintf("results_%s.txt", time.Now().Format("2006-01-02_15-04-05"))
fmt.Printf("\n%s\n", strings.Repeat("━", 60))
fmt.Printf("Keys to check: %d\n", len(keys))
fmt.Printf("Output file: %s\n", outputFile)
fmt.Printf("%s\n\n", strings.Repeat("━", 60))
startTime := time.Now()
processKeys(keys, cfg, moduleManager)
stats.Duration = time.Since(startTime)
sort.Slice(results, func(i, j int) bool { return results[i].Balance > results[j].Balance })
if err := writeResults(outputFile); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write results: %v\n", err)
return
}
fmt.Printf("\n\n")
colorPrintln(ColorGreen, fmt.Sprintf("✅ Results saved to: %s", outputFile))
printSummary()
}
func processKeys(keys []string, cfg *Config, moduleManager *ModuleManager) {
tracker := NewProgressTracker(len(keys))
display := NewStatusDisplay(tracker)
display.Start()
defer display.Stop()
for i := 0; i < len(keys); i += cfg.Threads {
end := min(i+cfg.Threads, len(keys))
var wg sync.WaitGroup
for _, key := range keys[i:end] {
wg.Add(1)
go func(pk string) {
defer wg.Done()
checkKey(pk, cfg, moduleManager, tracker)
}(key)
}
wg.Wait()
}
}
func checkKey(pk string, cfg *Config, moduleManager *ModuleManager, tracker *ProgressTracker) {
defer func() {
if r := recover(); r != nil {
line := fmt.Sprintf("Error: %v", r)
addResult(line, 0)
tracker.Increment(0)
}
}()
var line string
totalBalance := 0.0
if cfg.EnableSolanaCheck {
kp, err := keypairFromSecretKey(pk)
if err != nil {
addResult(fmt.Sprintf("Error: %v", err), 0)
tracker.Increment(0)
return
}
addr := kp.PublicKeyBase58()
pkBalance, _ := getPortfolioValue(addr, cfg)
line += fmt.Sprintf("[Private Key] %s\n", pk)
line += fmt.Sprintf(" [SOL] %s - $%.2f", addr, pkBalance.Value)
if pkBalance.TokenCount > 0 {
line += fmt.Sprintf(" (%d tokens)", pkBalance.TokenCount)
}
line += "\n"
totalBalance += pkBalance.Value
} else {
line += fmt.Sprintf("[Private Key] %s\n", pk)
}
moduleBalance, moduleDetails := moduleManager.CheckAll(pk)
if moduleDetails != "" {
line += moduleDetails
totalBalance += moduleBalance
}
if totalBalance > 0 {
line += fmt.Sprintf(" %s[TOTAL: $%.2f]%s\n", ColorYellow, totalBalance, ColorReset)
}
addResult(line, totalBalance)
tracker.Increment(totalBalance)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func printSummary() {
fmt.Println("\n" + strings.Repeat("=", 60))
fmt.Println("SUMMARY")
fmt.Println(strings.Repeat("=", 60))
fmt.Printf("Mode: %s\n", stats.Mode)
fmt.Printf("Total Balance: $%.2f\n", stats.TotalBalance)
fmt.Printf("Keys with Balance: %d\n", stats.KeysWithBalance)
fmt.Printf("Empty Keys: %d\n", stats.EmptyKeys)
fmt.Printf("Total Keys: %d\n", stats.TotalKeys)
fmt.Printf("Duration: %s\n", stats.Duration.Round(time.Second))
fmt.Println(strings.Repeat("=", 60))
}
func writeResults(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
fmt.Fprintf(f, "RESULTS\n")
fmt.Fprintf(f, "Generated: %s\n\n", time.Now().Format("2006-01-02 15:04:05"))
fmt.Fprintf(f, "Mode: %s\n", stats.Mode)
fmt.Fprintf(f, "Total Balance: $%.2f\n", stats.TotalBalance)
fmt.Fprintf(f, "Accounts with Balance: %d\n", stats.KeysWithBalance)
fmt.Fprintf(f, ":0.01$> accounts%d\n", stats.EmptyKeys)
fmt.Fprintf(f, "Total Keys: %d\n", stats.TotalKeys)
fmt.Fprintf(f, "Duration: %s\n", stats.Duration.Round(time.Second))
fmt.Fprintf(f, "\n%s\n\n", strings.Repeat("=", 80))
// Write sorted results
for _, result := range results {
fmt.Fprintln(f, result.Line)
}
return nil
}
func check(pk string, mode Mode, cfg *Config) {
defer func() {
if r := recover(); r != nil {
line := fmt.Sprintf("Error: %v", r)
fmt.Println(line)
addResult(line, 0)
}
}()
if mode == ModePrivateKeys {
kp, err := keypairFromSecretKey(pk)
if err != nil {
line := fmt.Sprintf("Error: %v", err)
fmt.Println(line)
addResult(line, 0)
return
}
addr := kp.PublicKeyBase58()
portfolio, err := getPortfolioValue(addr, cfg)
if err != nil {
line := fmt.Sprintf("Error: %v", err)
fmt.Println(line)
addResult(line, 0)
return
}
tokenStr := ""
if portfolio.Value > 0 {
if portfolio.TokenCount > 10 {
tokenStr = " (>10 tokens)"
} else {
tokenStr = fmt.Sprintf(" (%d tokens)", portfolio.TokenCount)
}
}
valueStr := "0.00"
if portfolio.Value > 0 {
valueStr = fmt.Sprintf("%.2f", portfolio.Value)
}
line := fmt.Sprintf("[private key][jup.ag] %s | %s - $%s%s", pk, addr, valueStr, tokenStr)
fmt.Println(line)
addResult(line, portfolio.Value)
return
}
ax, err := getAxiomWallet(pk)
if err != nil {
line := fmt.Sprintf("Error: %v", err)
fmt.Println(line)
addResult(line, 0)
return
}
if ax.IsNewUser {
line := fmt.Sprintf("[axiom][jup.ag] %s | %s - NEW USER (skipped)", pk, ax.Sol)
fmt.Println(line)
addResult(line, 0)
return
}
if mode == ModeAxiom {
portfolio, err := getPortfolioValue(ax.Sol, cfg)
if err != nil {
line := fmt.Sprintf("Error: %v", err)
fmt.Println(line)
addResult(line, 0)
return
}
tokenStr := ""
if portfolio.Value > 0 {
if portfolio.TokenCount > 10 {
tokenStr = " (>10 tokens)"
} else {
tokenStr = fmt.Sprintf(" (%d tokens)", portfolio.TokenCount)
}
}
valueStr := "0.00"
if portfolio.Value > 0 {
valueStr = fmt.Sprintf("%.2f", portfolio.Value)
}
line := fmt.Sprintf("[axiom][jup.ag] %s | %s - $%s%s", pk, ax.Sol, valueStr, tokenStr)
fmt.Println(line)
addResult(line, portfolio.Value)
return
}
if mode == ModeBoth {
kp, err := keypairFromSecretKey(pk)
if err != nil {
line := fmt.Sprintf("Error: %v", err)
fmt.Println(line)
addResult(line, 0)
return
}
addr := kp.PublicKeyBase58()
var p1, p2 *PortfolioValue
var wg sync.WaitGroup
var err1, err2 error
wg.Add(2)
go func() {
defer wg.Done()
p1, err1 = getPortfolioValue(addr, cfg)
}()
go func() {
defer wg.Done()
p2, err2 = getPortfolioValue(ax.Sol, cfg)
}()
wg.Wait()
if err1 != nil || err2 != nil {
line := "Error fetching portfolios"
fmt.Println(line)
addResult(line, 0)
return
}
total := p1.Value + p2.Value
count := p1.TokenCount + p2.TokenCount
tokenStr := ""
if total > 0 {
if count > 10 {
tokenStr = " (>10 tokens)"
} else {
tokenStr = fmt.Sprintf(" (%d tokens)", count)
}
}
valueStr := "0.00"
if total > 0 {
valueStr = fmt.Sprintf("%.2f", total)
}
line := fmt.Sprintf("[pk+axiom][jup.ag] %s | %s | %s - $%s%s", pk, addr, ax.Sol, valueStr, tokenStr)
fmt.Println(line)
addResult(line, total)
}
}
func tern(cond bool, t, f string) string {
if cond {
return t
}
return f
}