fix bugs
This commit is contained in:
@@ -56,8 +56,8 @@ func main() {
|
|||||||
|
|
||||||
// 设置收款回调
|
// 设置收款回调
|
||||||
uman.OnPaymentComplete(func(payment *usdtman.USDTPayment) {
|
uman.OnPaymentComplete(func(payment *usdtman.USDTPayment) {
|
||||||
log.Printf("💰 收到 USDT: %s -> %.6f USDT (确认数: %d, TxID: %s)",
|
log.Printf("💰 收到 USDT: %s -> %s USDT (确认数: %d, TxID: %s)",
|
||||||
payment.From, payment.Amount, payment.Confirmations, payment.TxID)
|
payment.From, payment.GetAmountString(), payment.Confirmations, payment.TxID)
|
||||||
|
|
||||||
paymentLock.Lock()
|
paymentLock.Lock()
|
||||||
paymentEvents = append(paymentEvents, *payment)
|
paymentEvents = append(paymentEvents, *payment)
|
||||||
@@ -69,6 +69,10 @@ func main() {
|
|||||||
broadcastPayment(payment)
|
broadcastPayment(payment)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// test code
|
||||||
|
uman.AddAddress("TWwGSYwpSzT6GTBr4AQw9QF6m4VVui3UGc") // tronlink trc20 gasfree 地址
|
||||||
|
uman.Start()
|
||||||
|
|
||||||
http.HandleFunc("/start", startMonitor)
|
http.HandleFunc("/start", startMonitor)
|
||||||
http.HandleFunc("/stop", stopMonitor)
|
http.HandleFunc("/stop", stopMonitor)
|
||||||
http.HandleFunc("/add-address", addAddress)
|
http.HandleFunc("/add-address", addAddress)
|
||||||
@@ -222,7 +226,8 @@ func broadcastPayment(payment *usdtman.USDTPayment) {
|
|||||||
message := map[string]interface{}{
|
message := map[string]interface{}{
|
||||||
"type": "usdt_payment",
|
"type": "usdt_payment",
|
||||||
"address": payment.Address,
|
"address": payment.Address,
|
||||||
"amount": payment.Amount,
|
"amount": payment.GetAmountFloat(),
|
||||||
|
"amountRaw": payment.Amount.String(),
|
||||||
"from": payment.From,
|
"from": payment.From,
|
||||||
"txId": payment.TxID,
|
"txId": payment.TxID,
|
||||||
"block": payment.BlockNumber,
|
"block": payment.BlockNumber,
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
data.data.payments.map(p =>
|
data.data.payments.map(p =>
|
||||||
'<tr><td>' + new Date(p.Timestamp).toLocaleString() + '</td>' +
|
'<tr><td>' + new Date(p.Timestamp).toLocaleString() + '</td>' +
|
||||||
'<td>' + p.Address.substring(0, 10) + '...</td>' +
|
'<td>' + p.Address.substring(0, 10) + '...</td>' +
|
||||||
'<td>' + p.Amount.toFixed(6) + ' USDT</td>' +
|
'<td>' + (p.Amount / 1000000).toFixed(6) + ' USDT</td>' +
|
||||||
'<td>' + p.From.substring(0, 10) + '...</td>' +
|
'<td>' + p.From.substring(0, 10) + '...</td>' +
|
||||||
'<td>' + p.Confirmations + '</td>' +
|
'<td>' + p.Confirmations + '</td>' +
|
||||||
'<td><a href="https://tronscan.org/#/transaction/' + p.TxID + '" target="_blank">' +
|
'<td><a href="https://tronscan.org/#/transaction/' + p.TxID + '" target="_blank">' +
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -43,7 +44,7 @@ type USDTMan struct {
|
|||||||
// USDTPayment USDT 收款信息
|
// USDTPayment USDT 收款信息
|
||||||
type USDTPayment struct {
|
type USDTPayment struct {
|
||||||
Address string
|
Address string
|
||||||
Amount float64
|
Amount *big.Int // 原始金额(micro USDT,10^-6),例如 13260000 = 13.26 USDT
|
||||||
TxID string
|
TxID string
|
||||||
BlockNumber int64
|
BlockNumber int64
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
@@ -51,23 +52,57 @@ type USDTPayment struct {
|
|||||||
Confirmations int64
|
Confirmations int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// TronGridTransaction 交易信息
|
// GetAmountFloat 获取浮点数金额(USDT)
|
||||||
|
func (p *USDTPayment) GetAmountFloat() float64 {
|
||||||
|
divisor := new(big.Float).SetInt64(1000000)
|
||||||
|
amount := new(big.Float).SetInt(p.Amount)
|
||||||
|
result := new(big.Float).Quo(amount, divisor)
|
||||||
|
f, _ := result.Float64()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAmountString 获取字符串金额(USDT,保留6位小数)
|
||||||
|
func (p *USDTPayment) GetAmountString() string {
|
||||||
|
return fmt.Sprintf("%.6f", p.GetAmountFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON 自定义 JSON 序列化
|
||||||
|
func (p *USDTPayment) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(struct {
|
||||||
|
Address string `json:"Address"`
|
||||||
|
Amount float64 `json:"Amount"`
|
||||||
|
AmountRaw string `json:"AmountRaw"`
|
||||||
|
TxID string `json:"TxID"`
|
||||||
|
BlockNumber int64 `json:"BlockNumber"`
|
||||||
|
Timestamp int64 `json:"Timestamp"`
|
||||||
|
From string `json:"From"`
|
||||||
|
Confirmations int64 `json:"Confirmations"`
|
||||||
|
}{
|
||||||
|
Address: p.Address,
|
||||||
|
Amount: p.GetAmountFloat(),
|
||||||
|
AmountRaw: p.Amount.String(),
|
||||||
|
TxID: p.TxID,
|
||||||
|
BlockNumber: p.BlockNumber,
|
||||||
|
Timestamp: p.Timestamp,
|
||||||
|
From: p.From,
|
||||||
|
Confirmations: p.Confirmations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TronGridTransaction TRC20 交易信息
|
||||||
type TronGridTransaction struct {
|
type TronGridTransaction struct {
|
||||||
Ret []map[string]interface{} `json:"ret"`
|
TransactionID string `json:"transaction_id"`
|
||||||
TxID string `json:"txID"`
|
BlockTimestamp int64 `json:"block_timestamp"`
|
||||||
BlockNumber int64 `json:"blockNumber"`
|
From string `json:"from"`
|
||||||
BlockTimeStamp int64 `json:"block_timestamp"`
|
To string `json:"to"`
|
||||||
RawData struct {
|
Type string `json:"type"`
|
||||||
Contract []struct {
|
Value string `json:"value"`
|
||||||
Parameter struct {
|
TokenInfo struct {
|
||||||
Value struct {
|
Symbol string `json:"symbol"`
|
||||||
Amount int64 `json:"amount"`
|
Address string `json:"address"`
|
||||||
To string `json:"to_address"`
|
Decimals int `json:"decimals"`
|
||||||
From string `json:"owner_address"`
|
Name string `json:"name"`
|
||||||
} `json:"value"`
|
} `json:"token_info"`
|
||||||
} `json:"parameter"`
|
|
||||||
} `json:"contract"`
|
|
||||||
} `json:"raw_data"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TronGridAccountTransactions 账户交易列表
|
// TronGridAccountTransactions 账户交易列表
|
||||||
@@ -75,8 +110,8 @@ type TronGridAccountTransactions struct {
|
|||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Data []TronGridTransaction `json:"data"`
|
Data []TronGridTransaction `json:"data"`
|
||||||
Meta struct {
|
Meta struct {
|
||||||
PageSize int `json:"page_size"`
|
At int64 `json:"at"`
|
||||||
Fingerprint string `json:"fingerprint"`
|
PageSize int `json:"page_size"`
|
||||||
} `json:"meta"`
|
} `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,28 +256,51 @@ func (m *USDTMan) checkAddress(address string) {
|
|||||||
for _, txn := range transactions {
|
for _, txn := range transactions {
|
||||||
// 检查是否已处理
|
// 检查是否已处理
|
||||||
m.txnMutex.RLock()
|
m.txnMutex.RLock()
|
||||||
processed := m.processedTxns[txn.TxID]
|
processed := m.processedTxns[txn.TransactionID]
|
||||||
m.txnMutex.RUnlock()
|
m.txnMutex.RUnlock()
|
||||||
|
|
||||||
if processed {
|
if processed {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取交易的区块号(需要通过 transaction_id 查询)
|
||||||
|
blockNumber, err := m.getTransactionBlock(txn.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error getting transaction block: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 计算确认数
|
// 计算确认数
|
||||||
confirmations := currentBlock - txn.BlockNumber
|
confirmations := currentBlock - blockNumber
|
||||||
if confirmations < m.minConfirmations {
|
if confirmations < m.minConfirmations {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析交易数据
|
// 检查是否是转入该地址
|
||||||
payment := m.parseTransaction(&txn, address, confirmations)
|
if !strings.EqualFold(txn.To, address) {
|
||||||
if payment == nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析金额(使用 big.Int)
|
||||||
|
amount := new(big.Int)
|
||||||
|
amount, ok := amount.SetString(txn.Value, 10)
|
||||||
|
if !ok || amount.Sign() <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payment := &USDTPayment{
|
||||||
|
Address: address,
|
||||||
|
Amount: amount,
|
||||||
|
TxID: txn.TransactionID,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
Timestamp: txn.BlockTimestamp,
|
||||||
|
From: txn.From,
|
||||||
|
Confirmations: confirmations,
|
||||||
|
}
|
||||||
|
|
||||||
// 标记为已处理
|
// 标记为已处理
|
||||||
m.txnMutex.Lock()
|
m.txnMutex.Lock()
|
||||||
m.processedTxns[txn.TxID] = true
|
m.processedTxns[txn.TransactionID] = true
|
||||||
m.txnMutex.Unlock()
|
m.txnMutex.Unlock()
|
||||||
|
|
||||||
// 触发回调
|
// 触发回调
|
||||||
@@ -293,6 +351,46 @@ func (m *USDTMan) getUSDTTransactions(address string) ([]TronGridTransaction, er
|
|||||||
return result.Data, nil
|
return result.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getTransactionBlock 获取交易的区块号
|
||||||
|
func (m *USDTMan) getTransactionBlock(txID string) (int64, error) {
|
||||||
|
url := fmt.Sprintf("%s/wallet/gettransactioninfobyid?value=%s", TronGridAPI, txID)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := m.httpClient.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 result struct {
|
||||||
|
BlockNumber int64 `json:"blockNumber"`
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("body", string(body))
|
||||||
|
if err := json.Unmarshal(body, &result); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.BlockNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getCurrentBlock 获取当前区块高度
|
// getCurrentBlock 获取当前区块高度
|
||||||
func (m *USDTMan) getCurrentBlock() (int64, error) {
|
func (m *USDTMan) getCurrentBlock() (int64, error) {
|
||||||
url := fmt.Sprintf("%s/wallet/getnowblock", TronGridAPI)
|
url := fmt.Sprintf("%s/wallet/getnowblock", TronGridAPI)
|
||||||
@@ -329,41 +427,7 @@ func (m *USDTMan) getCurrentBlock() (int64, error) {
|
|||||||
return blockInfo.BlockHeader.RawData.Number, nil
|
return blockInfo.BlockHeader.RawData.Number, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTransaction 解析交易
|
// parseTransaction 解析交易(已废弃,直接在 checkAddress 中处理)
|
||||||
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 获取所有监听地址
|
// GetAddresses 获取所有监听地址
|
||||||
func (m *USDTMan) GetAddresses() []string {
|
func (m *USDTMan) GetAddresses() []string {
|
||||||
|
|||||||
Reference in New Issue
Block a user