mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 10:16:15 +00:00
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:
@@ -195,64 +195,31 @@ func TryProcessEvmERC20Transfer(chainNetwork string, contract common.Address, to
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var usdt, usdc common.Address
|
net := evmChainLogLabel(chainNetwork)
|
||||||
var polygonUsdcE common.Address
|
tokenConfig, err := data.GetEnabledChainTokenByContract(chainNetwork, contract.Hex())
|
||||||
switch chainNetwork {
|
if err != nil {
|
||||||
case mdb.NetworkEthereum:
|
log.Sugar.Warnf("[%s-WS] load chain token contract=%s: %v", net, contract.Hex(), err)
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
|
if tokenConfig == nil || tokenConfig.ID == 0 {
|
||||||
var tokenSym string
|
log.Sugar.Warnf("[%s-WS] skip unconfigured contract %s", net, contract.Hex())
|
||||||
switch {
|
return
|
||||||
case contract == usdt:
|
}
|
||||||
tokenSym = "USDT"
|
tokenSym := strings.ToUpper(strings.TrimSpace(tokenConfig.Symbol))
|
||||||
case contract == usdc || (polygonUsdcE != (common.Address{}) && contract == polygonUsdcE):
|
if tokenSym == "" {
|
||||||
tokenSym = "USDC"
|
log.Sugar.Warnf("[%s-WS] skip contract %s with empty token symbol", net, contract.Hex())
|
||||||
default:
|
|
||||||
net := evmChainLogLabel(chainNetwork)
|
|
||||||
log.Sugar.Warnf("[%s-WS] skip unsupported contract %s", net, contract.Hex())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
net := evmChainLogLabel(chainNetwork)
|
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user