package usdtman import ( "context" "encoding/json" "fmt" "io" "net/http" "strings" "sync" "time" ) // USDTMan USDT 监听管理器 type USDTMan struct { mu sync.RWMutex addresses []string apiKey string running bool ctx context.Context cancel context.CancelFunc onPaymentComplete func(*USDTPayment) minConfirmations int64 processedTxns map[string]bool // 已处理的交易 txnMutex sync.RWMutex } // USDTPayment USDT 收款信息 type USDTPayment struct { Address string Amount float64 TxID string BlockNumber int64 Timestamp int64 From string Confirmations int64 } // TronGridTransaction 交易信息 type TronGridTransaction struct { Ret []map[string]interface{} `json:"ret"` TxID string `json:"txID"` BlockNumber int64 `json:"blockNumber"` BlockTimeStamp int64 `json:"block_timestamp"` RawData struct { Contract []struct { Parameter struct { Value struct { Amount int64 `json:"amount"` To string `json:"to_address"` From string `json:"owner_address"` } `json:"value"` } `json:"parameter"` } `json:"contract"` } `json:"raw_data"` } // TronGridAccountTransactions 账户交易列表 type TronGridAccountTransactions struct { Success bool `json:"success"` Data []TronGridTransaction `json:"data"` Meta struct { PageSize int `json:"page_size"` Fingerprint string `json:"fingerprint"` } `json:"meta"` } // TronGridBlockInfo 区块信息 type TronGridBlockInfo struct { BlockHeader struct { RawData struct { Number int64 `json:"number"` } `json:"raw_data"` } `json:"block_header"` } const ( TronGridAPI = "https://api.trongrid.io" USDTContractAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" // USDT TRC20 ) // NewUSDTMan 创建监听管理器 func NewUSDTMan(addresses []string, apiKey string) *USDTMan { ctx, cancel := context.WithCancel(context.Background()) return &USDTMan{ addresses: addresses, apiKey: apiKey, ctx: ctx, cancel: cancel, minConfirmations: 6, processedTxns: make(map[string]bool), } } // OnPaymentComplete 设置收款回调 func (m *USDTMan) OnPaymentComplete(callback func(*USDTPayment)) { m.mu.Lock() defer m.mu.Unlock() m.onPaymentComplete = callback } // Start 开始监听 func (m *USDTMan) Start() error { m.mu.Lock() if m.running { m.mu.Unlock() return fmt.Errorf("monitor already running") } m.running = true m.mu.Unlock() go m.monitorLoop() return nil } // Stop 停止监听 func (m *USDTMan) Stop() { m.mu.Lock() if !m.running { m.mu.Unlock() return } m.running = false m.mu.Unlock() m.cancel() } // monitorLoop 监听循环 func (m *USDTMan) monitorLoop() { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { case <-m.ctx.Done(): return case <-ticker.C: m.checkAllAddresses() } } } // checkAllAddresses 检查所有地址 func (m *USDTMan) checkAllAddresses() { m.mu.RLock() addresses := make([]string, len(m.addresses)) copy(addresses, m.addresses) m.mu.RUnlock() for _, address := range addresses { m.checkAddress(address) } } // checkAddress 检查单个地址的交易 func (m *USDTMan) checkAddress(address string) { transactions, err := m.getUSDTTransactions(address) if err != nil { fmt.Printf("Error checking address %s: %v\n", address, err) return } currentBlock, err := m.getCurrentBlock() if err != nil { fmt.Printf("Error getting current block: %v\n", err) return } for _, txn := range transactions { // 检查是否已处理 m.txnMutex.RLock() processed := m.processedTxns[txn.TxID] m.txnMutex.RUnlock() if processed { continue } // 计算确认数 confirmations := currentBlock - txn.BlockNumber if confirmations < m.minConfirmations { continue } // 解析交易数据 payment := m.parseTransaction(&txn, address, confirmations) if payment == nil { continue } // 标记为已处理 m.txnMutex.Lock() m.processedTxns[txn.TxID] = true m.txnMutex.Unlock() // 触发回调 m.mu.RLock() callback := m.onPaymentComplete m.mu.RUnlock() if callback != nil { callback(payment) } } } // getUSDTTransactions 获取地址的 USDT 交易 func (m *USDTMan) getUSDTTransactions(address string) ([]TronGridTransaction, error) { url := fmt.Sprintf("%s/v1/accounts/%s/transactions/trc20?limit=20&contract_address=%s&only_to=true", TronGridAPI, address, USDTContractAddress) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } if m.apiKey != "" { req.Header.Set("TRON-PRO-API-KEY", m.apiKey) } client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode != 200 { return nil, fmt.Errorf("TronGrid API error: %d %s", resp.StatusCode, string(body)) } var result TronGridAccountTransactions if err := json.Unmarshal(body, &result); err != nil { return nil, err } return result.Data, nil } // getCurrentBlock 获取当前区块高度 func (m *USDTMan) getCurrentBlock() (int64, error) { url := fmt.Sprintf("%s/wallet/getnowblock", TronGridAPI) req, err := http.NewRequest("GET", url, nil) if err != nil { return 0, err } if m.apiKey != "" { req.Header.Set("TRON-PRO-API-KEY", m.apiKey) } client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return 0, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return 0, err } if resp.StatusCode != 200 { return 0, fmt.Errorf("TronGrid API error: %d", resp.StatusCode) } var blockInfo TronGridBlockInfo if err := json.Unmarshal(body, &blockInfo); err != nil { return 0, err } return blockInfo.BlockHeader.RawData.Number, nil } // parseTransaction 解析交易 func (m *USDTMan) parseTransaction(txn *TronGridTransaction, targetAddress string, confirmations int64) *USDTPayment { if len(txn.Ret) == 0 || txn.Ret[0]["contractRet"] != "SUCCESS" { return nil } if len(txn.RawData.Contract) == 0 { return nil } contract := txn.RawData.Contract[0] value := contract.Parameter.Value // 转换地址格式 to := value.To from := value.From // 检查是否是目标地址 if !strings.EqualFold(to, targetAddress) { return nil } // USDT 是 6 位小数 amount := float64(value.Amount) / 1000000.0 return &USDTPayment{ Address: targetAddress, Amount: amount, TxID: txn.TxID, BlockNumber: txn.BlockNumber, Timestamp: txn.BlockTimeStamp, From: from, Confirmations: confirmations, } } // GetAddresses 获取所有监听地址 func (m *USDTMan) GetAddresses() []string { m.mu.RLock() defer m.mu.RUnlock() addresses := make([]string, len(m.addresses)) copy(addresses, m.addresses) return addresses } // AddAddress 添加监听地址 func (m *USDTMan) AddAddress(address string) { m.mu.Lock() defer m.mu.Unlock() // 检查是否已存在 for _, addr := range m.addresses { if addr == address { return } } m.addresses = append(m.addresses, address) } // RemoveAddress 移除监听地址 func (m *USDTMan) RemoveAddress(address string) { m.mu.Lock() defer m.mu.Unlock() for i, addr := range m.addresses { if addr == address { m.addresses = append(m.addresses[:i], m.addresses[i+1:]...) return } } }