fix: resolve chain tokens dynamically in block listeners

- resolve EVM ERC20 tokens by chain_tokens contract_address instead of hardcoded contracts
This commit is contained in:
line-6000
2026-05-11 13:40:07 +08:00
parent 7c9e3e123e
commit 84972ea0f0
2 changed files with 89 additions and 51 deletions
+22 -51
View File
@@ -195,64 +195,31 @@ func TryProcessEvmERC20Transfer(chainNetwork string, contract common.Address, to
} }
}() }()
var usdt, usdc common.Address
var polygonUsdcE common.Address
switch chainNetwork {
case mdb.NetworkEthereum:
usdt = common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7")
usdc = common.HexToAddress("0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
case mdb.NetworkBsc:
usdt = common.HexToAddress("0x55d398326f99059fF775485246999027B3197955")
usdc = common.HexToAddress("0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d")
case mdb.NetworkPolygon:
usdt = common.HexToAddress("0xc2132D05D31c914a87C6611C10748AEb04B58e8F")
usdc = common.HexToAddress("0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359")
polygonUsdcE = common.HexToAddress("0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174")
case mdb.NetworkPlasma:
// USDT0(官方),6 decimals;链上暂无与 ETH 同级的 Circle USDC 部署,仅匹配 USDT 订单
usdt = common.HexToAddress("0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb")
default:
return
}
var tokenSym string
switch {
case contract == usdt:
tokenSym = "USDT"
case contract == usdc || (polygonUsdcE != (common.Address{}) && contract == polygonUsdcE):
tokenSym = "USDC"
default:
net := evmChainLogLabel(chainNetwork)
log.Sugar.Warnf("[%s-WS] skip unsupported contract %s", net, contract.Hex())
return
}
net := evmChainLogLabel(chainNetwork) net := evmChainLogLabel(chainNetwork)
tokenConfig, err := data.GetEnabledChainTokenByContract(chainNetwork, contract.Hex())
if err != nil {
log.Sugar.Warnf("[%s-WS] load chain token contract=%s: %v", net, contract.Hex(), err)
return
}
if tokenConfig == nil || tokenConfig.ID == 0 {
log.Sugar.Warnf("[%s-WS] skip unconfigured contract %s", net, contract.Hex())
return
}
tokenSym := strings.ToUpper(strings.TrimSpace(tokenConfig.Symbol))
if tokenSym == "" {
log.Sugar.Warnf("[%s-WS] skip contract %s with empty token symbol", net, contract.Hex())
return
}
walletAddr := strings.ToLower(toAddr.Hex()) walletAddr := strings.ToLower(toAddr.Hex())
if rawValue == nil || rawValue.Sign() <= 0 { if rawValue == nil || rawValue.Sign() <= 0 {
log.Sugar.Infof("[%s-%s][%s] skip non-positive or nil amount", net, tokenSym, walletAddr) log.Sugar.Infof("[%s-%s][%s] skip non-positive or nil amount", net, tokenSym, walletAddr)
return return
} }
decimals := tokenConfig.Decimals
chainTokens, err := data.ListChainTokens(chainNetwork) if decimals < 0 {
if err != nil { decimals = 0
log.Sugar.Warnf("[%s-%s] load chain tokens: %v", net, tokenSym, err)
return
} }
var tokenConfig *mdb.ChainToken pow := decimal.New(1, int32(decimals))
for _, t := range chainTokens {
if strings.EqualFold(t.Symbol, tokenSym) {
tokenConfig = &t
break
}
}
if tokenConfig == nil || !tokenConfig.Enabled {
log.Sugar.Warnf("[%s-%s] token not enabled or configured in chain_tokens", net, tokenSym)
return
}
pow := decimal.New(1, int32(tokenConfig.Decimals))
log.Sugar.Warnf("tokenConfig.Decimals %d pow %s", tokenConfig.Decimals, pow.String())
decimalQuant := decimal.NewFromBigInt(rawValue, 0) decimalQuant := decimal.NewFromBigInt(rawValue, 0)
amount := math.MustParsePrecFloat64(decimalQuant.Div(pow).InexactFloat64(), 2) amount := math.MustParsePrecFloat64(decimalQuant.Div(pow).InexactFloat64(), 2)
@@ -260,6 +227,10 @@ func TryProcessEvmERC20Transfer(chainNetwork string, contract common.Address, to
log.Sugar.Warnf("[%s-%s][%s] skip non-positive amount %.2f", net, tokenSym, walletAddr, amount) log.Sugar.Warnf("[%s-%s][%s] skip non-positive amount %.2f", net, tokenSym, walletAddr, amount)
return return
} }
if tokenConfig.MinAmount > 0 && amount < tokenConfig.MinAmount {
log.Sugar.Debugf("[%s-%s][%s] skip below min amount hash=%s amount=%.2f min=%.2f", net, tokenSym, walletAddr, txHash, amount, tokenConfig.MinAmount)
return
}
log.Sugar.Debugf("[%s-%s][%s] processing transfer hash=%s amount=%.2f", net, tokenSym, walletAddr, txHash, amount) log.Sugar.Debugf("[%s-%s][%s] processing transfer hash=%s amount=%.2f", net, tokenSym, walletAddr, txHash, amount)
+67
View File
@@ -1,14 +1,17 @@
package service package service
import ( import (
"math/big"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/GMWalletApp/epusdt/internal/testutil" "github.com/GMWalletApp/epusdt/internal/testutil"
"github.com/GMWalletApp/epusdt/model/dao" "github.com/GMWalletApp/epusdt/model/dao"
"github.com/GMWalletApp/epusdt/model/data"
"github.com/GMWalletApp/epusdt/model/mdb" "github.com/GMWalletApp/epusdt/model/mdb"
"github.com/GMWalletApp/epusdt/notify" "github.com/GMWalletApp/epusdt/notify"
"github.com/ethereum/go-ethereum/common"
) )
func TestSendPaymentNotificationUsesLatestOrderUpdatedAt(t *testing.T) { func TestSendPaymentNotificationUsesLatestOrderUpdatedAt(t *testing.T) {
@@ -77,3 +80,67 @@ func TestSendPaymentNotificationUsesLatestOrderUpdatedAt(t *testing.T) {
t.Fatal("timed out waiting for notification") t.Fatal("timed out waiting for notification")
} }
} }
func TestTryProcessEvmERC20TransferUsesChainTokenContract(t *testing.T) {
cleanup := testutil.SetupTestDatabases(t)
defer cleanup()
const (
tradeID = "T202605110001"
orderID = "ORD202605110001"
tokenSym = "FOO"
amount = 12.34
)
contract := common.HexToAddress("0x1111111111111111111111111111111111111111")
receiveAddress := common.HexToAddress("0x2222222222222222222222222222222222222222")
if err := dao.Mdb.Create(&mdb.ChainToken{
Network: mdb.NetworkEthereum,
Symbol: tokenSym,
ContractAddress: contract.Hex(),
Decimals: 8,
Enabled: true,
}).Error; err != nil {
t.Fatalf("seed chain token: %v", err)
}
order := &mdb.Orders{
TradeId: tradeID,
OrderId: orderID,
Amount: 100,
Currency: "CNY",
ActualAmount: amount,
Token: tokenSym,
Network: mdb.NetworkEthereum,
ReceiveAddress: strings.ToLower(receiveAddress.Hex()),
Status: mdb.StatusWaitPay,
}
if err := dao.Mdb.Create(order).Error; err != nil {
t.Fatalf("seed order: %v", err)
}
if err := data.LockTransaction(mdb.NetworkEthereum, order.ReceiveAddress, order.Token, order.TradeId, order.ActualAmount, time.Hour); err != nil {
t.Fatalf("lock transaction: %v", err)
}
rawValue := big.NewInt(1_234_000_000) // 12.34 with 8 decimals
TryProcessEvmERC20Transfer(mdb.NetworkEthereum, contract, receiveAddress, rawValue, "0xfoo-hash", time.Now().UnixMilli())
paid, err := data.GetOrderInfoByTradeId(tradeID)
if err != nil {
t.Fatalf("load paid order: %v", err)
}
if paid.Status != mdb.StatusPaySuccess {
t.Fatalf("order status = %d, want %d", paid.Status, mdb.StatusPaySuccess)
}
if paid.BlockTransactionId != "0xfoo-hash" {
t.Fatalf("block transaction id = %q, want %q", paid.BlockTransactionId, "0xfoo-hash")
}
lockTradeID, err := data.GetTradeIdByWalletAddressAndAmountAndToken(mdb.NetworkEthereum, receiveAddress.Hex(), tokenSym, amount)
if err != nil {
t.Fatalf("lookup released lock: %v", err)
}
if lockTradeID != "" {
t.Fatalf("runtime lock still exists for trade_id=%q", lockTradeID)
}
}