修复tron
This commit is contained in:
parent
9a27923d69
commit
cb0cacdcc5
@ -1312,7 +1312,8 @@ func socialRequirementMetricDefinitions(section string) []MetricDefinition {
|
|||||||
default:
|
default:
|
||||||
return []MetricDefinition{
|
return []MetricDefinition{
|
||||||
{Metric: "app_download_users", Definition: "客户端 app_first_open 去重人数;不代表应用商店真实下载量。"},
|
{Metric: "app_download_users", Definition: "客户端 app_first_open 去重人数;不代表应用商店真实下载量。"},
|
||||||
{Metric: "registered_users", Definition: "UserRegistered 服务端事实去重人数。"},
|
// registered_users 保留历史字段兼容,业务展示统一为“新增用户”;资格和幂等边界由 UserRegistered 生产、消费链路保证。
|
||||||
|
{Metric: "registered_users", Definition: "注册成功的新用户人数:仅统计资料完成后由 user-service 发布的 UserRegistered 服务端事实,按用户去重;未完成资料、quick_account、game_robot 不计,MQ 重投不重复。"},
|
||||||
{Metric: "new_user_d1_retention_rate", Definition: "新普通用户次日活跃留存。"},
|
{Metric: "new_user_d1_retention_rate", Definition: "新普通用户次日活跃留存。"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1421,7 +1422,7 @@ func socialRequirementColumns(section string) []SocialRequirementColumn {
|
|||||||
default:
|
default:
|
||||||
return []SocialRequirementColumn{
|
return []SocialRequirementColumn{
|
||||||
{Key: "app_download_users", Label: "下载APP人数", Type: "count", Tooltip: "客户端 app_first_open 去重人数"},
|
{Key: "app_download_users", Label: "下载APP人数", Type: "count", Tooltip: "客户端 app_first_open 去重人数"},
|
||||||
{Key: "registered_users", Label: "注册成功用户", Type: "count"},
|
{Key: "registered_users", Label: "新增用户", Type: "count", Tooltip: "注册成功的新用户人数:资料完成后产生 UserRegistered 服务端事实并按用户去重;未完成资料、quick_account、game_robot 不计,MQ 重投不重复"},
|
||||||
{Key: "new_host_users", Label: "新主播用户", Type: "count"},
|
{Key: "new_host_users", Label: "新主播用户", Type: "count"},
|
||||||
{Key: "new_normal_users", Label: "新普通用户", Type: "count"},
|
{Key: "new_normal_users", Label: "新普通用户", Type: "count"},
|
||||||
{Key: "registration_success_rate", Label: "注册成功率", Type: "ratio"},
|
{Key: "registration_success_rate", Label: "注册成功率", Type: "ratio"},
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
package trongrid
|
package trongrid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -32,7 +36,8 @@ func New(cfg config.TronGridConfig) *Client {
|
|||||||
httpClient: &http.Client{Timeout: timeout},
|
httpClient: &http.Client{Timeout: timeout},
|
||||||
apiBaseURL: strings.TrimRight(strings.TrimSpace(cfg.APIBaseURL), "/"),
|
apiBaseURL: strings.TrimRight(strings.TrimSpace(cfg.APIBaseURL), "/"),
|
||||||
apiKey: strings.TrimSpace(cfg.APIKey),
|
apiKey: strings.TrimSpace(cfg.APIKey),
|
||||||
contractAddress: strings.ToLower(strings.TrimSpace(cfg.USDTContractAddress)),
|
// Base58Check 区分大小写;保存原始形式,比较时再统一解码为账户 Hex。
|
||||||
|
contractAddress: strings.TrimSpace(cfg.USDTContractAddress),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +47,13 @@ func (c *Client) VerifyUSDTTransfer(ctx context.Context, txHash string, toAddres
|
|||||||
return "", xerr.New(xerr.Unavailable, "tron grid client is not configured")
|
return "", xerr.New(xerr.Unavailable, "tron grid client is not configured")
|
||||||
}
|
}
|
||||||
txHash = strings.TrimSpace(txHash)
|
txHash = strings.TrimSpace(txHash)
|
||||||
toAddress = strings.ToLower(strings.TrimSpace(toAddress))
|
|
||||||
if txHash == "" || toAddress == "" || amountMinor <= 0 {
|
if txHash == "" || toAddress == "" || amountMinor <= 0 {
|
||||||
return "", xerr.New(xerr.InvalidArgument, "usdt transfer verification request is incomplete")
|
return "", xerr.New(xerr.InvalidArgument, "usdt transfer verification request is incomplete")
|
||||||
}
|
}
|
||||||
|
expectedAddress, ok := normalizeTronAddress(toAddress)
|
||||||
|
if !ok {
|
||||||
|
return "", xerr.New(xerr.InvalidArgument, "tron receive address is invalid")
|
||||||
|
}
|
||||||
// H5 商品金额以 USD cent 保存,USDT 使用 6 位小数;1 cent = 0.01 USDT = 10,000 atomic units。
|
// H5 商品金额以 USD cent 保存,USDT 使用 6 位小数;1 cent = 0.01 USDT = 10,000 atomic units。
|
||||||
requiredAtomicAmount := amountMinor * 10_000
|
requiredAtomicAmount := amountMinor * 10_000
|
||||||
events, rawJSON, err := c.listTransferEvents(ctx, txHash)
|
events, rawJSON, err := c.listTransferEvents(ctx, txHash)
|
||||||
@ -59,7 +67,10 @@ func (c *Client) VerifyUSDTTransfer(ctx context.Context, txHash string, toAddres
|
|||||||
if !c.contractMatches(event.ContractAddress) {
|
if !c.contractMatches(event.ContractAddress) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.ToLower(strings.TrimSpace(event.Result.To)) != toAddress {
|
// TronGrid 的 event result.to 可能返回 0x/41 Hex,而后台保存的是 Base58Check。
|
||||||
|
// 两端先归一为去除 TRON 网络前缀的 20-byte Hex,避免同一地址因编码形式不同被误判。
|
||||||
|
eventAddress, validAddress := normalizeTronAddress(event.Result.To)
|
||||||
|
if !validAddress || eventAddress != expectedAddress {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
value, err := strconv.ParseInt(strings.TrimSpace(event.Result.Value), 10, 64)
|
value, err := strconv.ParseInt(strings.TrimSpace(event.Result.Value), 10, 64)
|
||||||
@ -102,15 +113,66 @@ func (c *Client) listTransferEvents(ctx context.Context, txHash string) ([]trans
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) contractMatches(value string) bool {
|
func (c *Client) contractMatches(value string) bool {
|
||||||
value = strings.ToLower(strings.TrimSpace(value))
|
configured, configuredOK := normalizeTronAddress(c.contractAddress)
|
||||||
if value == "" || c.contractAddress == "" {
|
actual, actualOK := normalizeTronAddress(value)
|
||||||
return false
|
return configuredOK && actualOK && configured == actual
|
||||||
}
|
}
|
||||||
if value == c.contractAddress {
|
|
||||||
return true
|
const tronBase58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
// normalizeTronAddress 接受后台常用的 Base58Check,以及 TronGrid 可能返回的
|
||||||
|
// 0x + 20-byte、41 + 20-byte 或裸 20-byte Hex,统一输出账户主体的 40 位小写 Hex。
|
||||||
|
// Base58 输入必须通过网络前缀和双 SHA-256 checksum 校验,避免把格式正确但已损坏的地址用于放币判断。
|
||||||
|
func normalizeTronAddress(value string) (string, bool) {
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
if value == "" {
|
||||||
|
return "", false
|
||||||
}
|
}
|
||||||
// TronGrid 的不同接口可能返回 base58 或 hex 合约地址;这里保留主网 USDT hex 兼容,避免官方返回格式差异导致误拒。
|
hexValue := strings.TrimPrefix(strings.ToLower(value), "0x")
|
||||||
return c.contractAddress == "tr7nhqjekqxgtci8q8zyk4pyl8otszgjlj6t" && value == "a614f803b6fd780986a42c78ec9c7f77e6ded13c"
|
if len(hexValue) == 42 && strings.HasPrefix(hexValue, "41") {
|
||||||
|
hexValue = hexValue[2:]
|
||||||
|
}
|
||||||
|
if len(hexValue) == 40 {
|
||||||
|
if _, err := hex.DecodeString(hexValue); err == nil {
|
||||||
|
return hexValue, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload, ok := decodeTronBase58Check(value)
|
||||||
|
if !ok || len(payload) != 21 || payload[0] != 0x41 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(payload[1:]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTronBase58Check(value string) ([]byte, bool) {
|
||||||
|
number := new(big.Int)
|
||||||
|
base := big.NewInt(58)
|
||||||
|
for _, char := range value {
|
||||||
|
index := strings.IndexRune(tronBase58Alphabet, char)
|
||||||
|
if index < 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
number.Mul(number, base)
|
||||||
|
number.Add(number, big.NewInt(int64(index)))
|
||||||
|
}
|
||||||
|
decoded := number.Bytes()
|
||||||
|
leadingZeroes := 0
|
||||||
|
for leadingZeroes < len(value) && value[leadingZeroes] == '1' {
|
||||||
|
leadingZeroes++
|
||||||
|
}
|
||||||
|
if leadingZeroes > 0 {
|
||||||
|
decoded = append(make([]byte, leadingZeroes), decoded...)
|
||||||
|
}
|
||||||
|
if len(decoded) < 5 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
payload, checksum := decoded[:len(decoded)-4], decoded[len(decoded)-4:]
|
||||||
|
firstHash := sha256.Sum256(payload)
|
||||||
|
secondHash := sha256.Sum256(firstHash[:])
|
||||||
|
if !bytes.Equal(checksum, secondHash[:4]) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return payload, true
|
||||||
}
|
}
|
||||||
|
|
||||||
type transferEvent struct {
|
type transferEvent struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user