Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion app/conf/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Debug = false
const (
Bsc = "bsc" // Binance Smart Chain
Tron = "tron"
Ton = "ton" // The Open Network
Aptos = "aptos"
Solana = "solana"
Xlayer = "xlayer"
Expand All @@ -30,6 +31,7 @@ const (
UsdtSolana = "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
SolSplToken = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
UsdtAptos = "0x357b0b74bc833e95a115ad22604854d6b0fca151cecd94111770e5d6ffc9dc2b"
UsdtTon = "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" // EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs
UsdcErc20 = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
UsdcPolygon = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
UsdcXlayer = "0x74b7f16337b8972027f6196a17a631ac6de26d22"
Expand All @@ -50,6 +52,7 @@ const (
UsdtArbitrumDecimals = -6 // USDT Arbitrum小数位数
UsdtAptosDecimals = -6 // USDT Aptos小数位数
UsdtSolanaDecimals = -6 // USDT Solana小数位数
UsdtTonDecimals = -6 // USDT Ton 小数位数

UsdcEthDecimals = -6 // USDC ERC20小数位数
UsdcPolygonDecimals = -6 // USDC Polygon小数位数
Expand All @@ -61,6 +64,8 @@ const (
UsdcAptosDecimals = -6 // USDC Aptos小数位数
UsdcSolanaDecimals = -6 // USDC Solana小数位数

BscBnbDecimals = -18 // BSC BNB 小数位数
TronTrxDecimals = -6 // Tron TRX 小数位数
TonTonDecimals = -9 // Ton TON 小数位数
EthereumEthDecimals = -18 // Ethereum ETH 小数位数
BscBnbDecimals = -18 // BSC BNB 小数位数
)
13 changes: 12 additions & 1 deletion app/handler/admin/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ func (Order) List(ctx *gin.Context) {
db = db.Where("status = ?", *req.Status)
}
if req.Address != "" {
db = db.Where("address like ?", "%"+req.Address+"%")
address := model.NormalizeKnownAddress(req.Address)
db = db.Where("address like ?", "%"+address+"%")
}
if req.TradeType != "" {
db = db.Where("trade_type = ?", req.TradeType)
Expand All @@ -154,6 +155,9 @@ func (Order) List(ctx *gin.Context) {

return
}
for i := range data {
formatAdminOrderAddress(&data[i].Order)
}

base.Response(ctx, 200, data, total)
}
Expand All @@ -179,12 +183,19 @@ func (Order) Detail(ctx *gin.Context) {
return
}

formatAdminOrderAddress(&o)

base.Ok(ctx, detail{
Order: o,
TxUrl: o.GetTxUrl(),
})
}

func formatAdminOrderAddress(order *model.Order) {
order.Address = model.FormatTradeAddress(order.TradeType, order.Address)
order.FromAddress = model.FormatTradeAddress(order.TradeType, order.FromAddress)
}

func (Order) Paid(ctx *gin.Context) {
var req paidReq
if err := ctx.ShouldBindJSON(&req); err != nil {
Expand Down
10 changes: 2 additions & 8 deletions app/handler/admin/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ func (Wallet) Add(ctx *gin.Context) {
return
}

// 非大小写敏感的地址,统一转为小写存储
if !model.AddrCaseSens(model.TradeType(wallet.TradeType)) {
wallet.MatchAddr = strings.ToLower(wallet.MatchAddr)
}
wallet.MatchAddr = model.NormalizeTradeAddress(model.TradeType(wallet.TradeType), wallet.Address)

if err := model.Db.Create(&wallet).Error; err != nil {
base.Error(ctx, err)
Expand Down Expand Up @@ -164,10 +161,7 @@ func (Wallet) Mod(ctx *gin.Context) {
return
}

// 非大小写敏感的地址,统一转为小写存储
if !model.AddrCaseSens(model.TradeType(w.TradeType)) {
w.MatchAddr = strings.ToLower(w.Address)
}
w.MatchAddr = model.NormalizeTradeAddress(model.TradeType(w.TradeType), w.Address)

if err := model.Db.Save(&w).Error; err != nil {
base.Error(ctx, err)
Expand Down
6 changes: 3 additions & 3 deletions app/handler/epusdt/epusdt.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (Epusdt) UpdateOrder(ctx *gin.Context) {
"status": newOrder.Status,
"amount": newOrder.Money,
"actual_amount": newOrder.Amount,
"token": newOrder.Address,
"token": model.FormatOrderPaymentAddress(newOrder),
"expiration_time": uint64(newOrder.ExpiredAt.Sub(time.Now()).Seconds()),
"payment_url": model.CheckoutCounter(host, newOrder.TradeId),
}))
Expand Down Expand Up @@ -321,7 +321,7 @@ func (Epusdt) CreateTransaction(ctx *gin.Context) {
"status": order.Status,
"amount": order.Money,
"actual_amount": order.Amount,
"token": order.Address,
"token": model.FormatOrderPaymentAddress(order),
"expiration_time": uint64(order.ExpiredAt.Sub(time.Now()).Seconds()),
"payment_url": model.CheckoutCounter(utils.GetRequestHost(ctx.Request), order.TradeId),
}))
Expand Down Expand Up @@ -377,7 +377,7 @@ func (Epusdt) CheckoutCounter(ctx *gin.Context) {
ctx.HTML(200, string(order.TradeType+".html"), gin.H{
"http_host": uri.Host,
"amount": order.Amount,
"address": order.Address,
"address": model.FormatOrderPaymentAddress(order),
"expire": int64(order.ExpiredAt.Sub(time.Now()).Seconds()),
"return_url": order.ReturnUrl,
"usdt_rate": order.Rate,
Expand Down
96 changes: 96 additions & 0 deletions app/model/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package model

import (
"strings"

"github.com/v03413/bepusdt/app/conf"
"github.com/v03413/bepusdt/app/utils"
)

type networkAddressCodec func(string) (string, bool)

var networkAddressNormalizers = map[Network]networkAddressCodec{
conf.Ton: utils.NormalizeTonAddress,
}

var networkAddressFormatters = map[Network]networkAddressCodec{
conf.Ton: utils.FormatTonAddress,
}

// normalizeNetworkAddress 按网络将地址转换为内部匹配和索引使用的标准形式。
func normalizeNetworkAddress(network Network, address string) (string, bool) {
address = strings.TrimSpace(address)
if address == "" {
return "", false
}

if normalize, ok := networkAddressNormalizers[network]; ok {
return normalize(address)
}

return address, true
}

// NormalizeKnownAddress 使用已注册的网络规范化器尝试转换地址。
func NormalizeKnownAddress(address string) string {
address = strings.TrimSpace(address)
if address == "" {
return ""
}

for _, normalize := range networkAddressNormalizers {
if normalized, ok := normalize(address); ok {
return normalized
}
}

return address
}

// FormatNetworkAddress 按网络将内部地址转换为适合用户查看的展示格式。
func FormatNetworkAddress(network Network, address string) string {
address = strings.TrimSpace(address)
if address == "" {
return ""
}

if format, ok := networkAddressFormatters[network]; ok {
if formatted, ok := format(address); ok {
return formatted
}
}

return address
}

// NormalizeTradeAddress 按交易类型将地址转换为订单匹配使用的标准形式。
func NormalizeTradeAddress(t TradeType, address string) string {
address = strings.TrimSpace(address)
if c, ok := registry[t]; ok {
if normalized, ok := normalizeNetworkAddress(c.Network, address); ok {
address = normalized
}
}
// 非大小写敏感的地址,统一转为小写存储
if !AddrCaseSens(t) {
return strings.ToLower(address)
}

return address
}

// FormatTradeAddress 按交易类型将内部地址转换为适合用户查看和付款的展示格式。
func FormatTradeAddress(t TradeType, address string) string {
if c, ok := registry[t]; ok {
return FormatNetworkAddress(c.Network, address)
}

return strings.TrimSpace(address)
}

// FormatOrderPaymentAddress 返回订单对外展示的付款地址。
func FormatOrderPaymentAddress(order Order) string {
address := order.Address

return FormatTradeAddress(order.TradeType, address)
}
18 changes: 17 additions & 1 deletion app/model/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ var defaultConf = map[ConfKey]string{
AtomUSDT: "0.01",
AtomUSDC: "0.01",
AtomTRX: "0.01",
AtomBNB: "0.00001",
AtomTON: "0.0001",
AtomETH: "0.000001",
AtomBNB: "0.00001",
MonitorMinAmount: "0.01",
PaymentMinAmount: "0.01",
PaymentMaxAmount: "99999",
Expand All @@ -36,6 +37,8 @@ var defaultConf = map[ConfKey]string{
RpcEndpointBase: "https://base-public.nodies.app/",
RpcEndpointAptos: "https://aptos-rest.publicnode.com/",
RpcEndpointPlasma: "https://rpc.plasma.to/",
TonCenterV3Endpoint: "https://toncenter.com/api/v3",
TonCenterV3ApiKey: "",
NotifyMaxRetry: "10",
BlockHeightMaxDiff: "1000",
BlockOffsetConfirm: "0",
Expand Down Expand Up @@ -230,6 +233,19 @@ func GetTronGridApiKeys() []string {
return strings.Split(GetK(RpcEndpointTronGridApiKey), ",")
}

func GetTonCenterV3ApiKeys() []string {
items := strings.Split(GetC(TonCenterV3ApiKey), ",")
keys := make([]string, 0, len(items))
for _, item := range items {
key := strings.TrimSpace(item)
if key != "" {
keys = append(keys, key)
}
}

return keys
}

func FillDefaultConf() {
var existKeys []string
Db.Model(&Conf{}).Pluck("k", &existKeys)
Expand Down
8 changes: 6 additions & 2 deletions app/model/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ const (
AtomUSDT ConfKey = "atom_usdt"
AtomUSDC ConfKey = "atom_usdc"
AtomTRX ConfKey = "atom_trx"
AtomBNB ConfKey = "atom_bnb"
AtomTON ConfKey = "atom_ton"
AtomETH ConfKey = "atom_eth"
AtomBNB ConfKey = "atom_bnb"

MonitorMinAmount ConfKey = "monitor_min_amount" // 监控最小金额,低于此金额的入账不进行通知
PaymentMinAmount ConfKey = "payment_min_amount"
Expand All @@ -62,6 +63,8 @@ const (
RpcEndpointAptos ConfKey = "rpc_endpoint_aptos" // APTOS RPC节点
RpcEndpointTron ConfKey = "rpc_endpoint_tron" // TRON RPC节点
RpcEndpointTronGridApiKey ConfKey = "rpc_endpoint_tron_grid_api_key" // TRON RPC节点 TronGrid Api Key
TonCenterV3Endpoint ConfKey = "ton_center_v3_endpoint" // TON Center V3 节点
TonCenterV3ApiKey ConfKey = "ton_center_v3_api_key" // TON Center V3 Api Key

RateSyncCoingeckoApiUrl ConfKey = "rate_sync_coingecko_api_url" // 汇率同步 Coingecko Api URL
RateSyncCoingeckoApiKey ConfKey = "rate_sync_coingecko_api_key" // 汇率同步 Coingecko Api Key
Expand Down Expand Up @@ -96,8 +99,9 @@ const (
USDT Crypto = "USDT"
USDC Crypto = "USDC"
TRX Crypto = "TRX"
BNB Crypto = "BNB"
TON Crypto = "TON"
ETH Crypto = "ETH"
BNB Crypto = "BNB"
)
const (
Classic MatchMode = "classic" // 经典模式,精确匹配
Expand Down
2 changes: 2 additions & 0 deletions app/model/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (

BscBnb TradeType = "bsc.bnb"
EthereumEth TradeType = "ethereum.eth"
TonTon TradeType = "ton.ton"
TronTrx TradeType = "tron.trx"

UsdtTrc20 TradeType = "usdt.trc20"
Expand All @@ -43,6 +44,7 @@ const (
UsdtAptos TradeType = "usdt.aptos"
UsdcAptos TradeType = "usdc.aptos"
UsdtPlasma TradeType = "usdt.plasma"
UsdtTon TradeType = "usdt.ton"
)

const (
Expand Down
31 changes: 29 additions & 2 deletions app/model/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ var supportCrypto = map[Crypto]CoinId{
USDT: "tether",
USDC: "usd-coin",
TRX: "tron",
BNB: "binancecoin",
TON: "the-open-network",
ETH: "ethereum",
BNB: "binancecoin",
}

// TradeType 交易类型,当下类型开始增多,现在这里统一管理、尽量收缩配置项
Expand Down Expand Up @@ -129,6 +130,17 @@ var registry = map[TradeType]TradeTypeConf{
ExplorerFmt: "https://arbiscan.io/tx/%s",
EndpointKey: RpcEndpointArbitrum,
},
UsdtTon: {
Alias: "USDT・Ton",
NetworkName: "Ton",
Network: conf.Ton,
Crypto: USDT,
Contract: conf.UsdtTon,
Decimal: conf.UsdtTonDecimals,
AmountRange: usdGeneralRange,
ExplorerFmt: "https://tonviewer.com/transaction/%s",
EndpointKey: TonCenterV3Endpoint,
},
UsdcErc20: {
Alias: "USDC・ERC20",
NetworkName: "ERC20",
Expand Down Expand Up @@ -236,7 +248,7 @@ var registry = map[TradeType]TradeTypeConf{
Network: conf.Tron,
Crypto: TRX,
Native: true,
Decimal: -6,
Decimal: conf.TronTrxDecimals,
AmountRange: Range{
MinAmount: decimal.NewFromFloat(0.1),
MaxAmount: decimal.NewFromFloat(1000000),
Expand All @@ -245,6 +257,21 @@ var registry = map[TradeType]TradeTypeConf{
EndpointKey: RpcEndpointTron,
AddrCaseSens: true,
},
TonTon: {
Alias: "TON・Ton",
NetworkName: "Ton",
Network: conf.Ton,
Crypto: TON,
Native: true,
Decimal: conf.TonTonDecimals,
AmountRange: Range{
MinAmount: decimal.NewFromFloat(0.0001),
MaxAmount: decimal.NewFromFloat(1000000),
},
ExplorerFmt: "https://tonviewer.com/transaction/%s",
EndpointKey: TonCenterV3Endpoint,
AddrCaseSens: true,
},
EthereumEth: {
Alias: "Ethereum・Eth",
NetworkName: "Ethereum",
Expand Down
6 changes: 5 additions & 1 deletion app/model/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ func StartBuildOrder(p OrderParams) (Order, error) {
if !utils.IsValidTronAddress(p.Address) &&
!utils.IsValidEvmAddress(p.Address) &&
!utils.IsValidSolanaAddress(p.Address) &&
!utils.IsValidAptosAddress(p.Address) {
!utils.IsValidAptosAddress(p.Address) &&
!utils.IsValidTonAddress(p.Address) {

return order, fmt.Errorf("钱包地址格式错误:%s", p.Address)
}
}
if _, ok := registry[p.TradeType]; !ok {
return order, fmt.Errorf("不支持的交易类型:%s", p.TradeType)
}
if p.Address != "" {
p.Address = NormalizeTradeAddress(p.TradeType, p.Address)
}
if _, ok := supportFiat[p.Fiat]; !ok {
return order, fmt.Errorf("不支持的法币类型:%s", p.Fiat)
}
Expand Down
Loading