From 04e0cbad83c6af445d78d1c553537424a105b298 Mon Sep 17 00:00:00 2001 From: wqqqqqq Date: Tue, 30 Dec 2025 23:25:08 +0800 Subject: [PATCH 1/5] feat: implement coinank free base coin interface (#1293) - implement coinank free base coin interface in coinank_api.BaseCoinSymbols --- provider/coinank/coinank_api/base_coin.go | 50 +++++++++++++++++++ .../coinank/coinank_api/base_coin_test.go | 43 ++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 provider/coinank/coinank_api/base_coin.go create mode 100644 provider/coinank/coinank_api/base_coin_test.go diff --git a/provider/coinank/coinank_api/base_coin.go b/provider/coinank/coinank_api/base_coin.go new file mode 100644 index 00000000..2316f031 --- /dev/null +++ b/provider/coinank/coinank_api/base_coin.go @@ -0,0 +1,50 @@ +package coinank_api + +import ( + "context" + "encoding/json" + "nofx/provider/coinank" + "nofx/provider/coinank/coinank_enum" +) + +// BaseCoinSymbols get base coin from coinank free open api , all params is optional +func BaseCoinSymbols(ctx context.Context, exchangeName coinank_enum.Exchange, symbol string, baseCoin string) ([]BaseCoinResponse, error) { + paramsMap := make(map[string]string, 3) + if symbol != "" { + paramsMap["symbol"] = symbol + } + if baseCoin != "" { + paramsMap["baseCoin"] = baseCoin + } + if exchangeName != "" { + paramsMap["exchangeName"] = string(exchangeName) + } + resp, err := get(ctx, "/api/baseCoin/symbols/open", paramsMap) + if err != nil { + return nil, err + } + var result coinank.CoinankResponse[[]BaseCoinResponse] + err = json.Unmarshal([]byte(resp), &result) + if err != nil { + return nil, err + } + if !result.Success { + return nil, coinank.HttpError + } + return result.Data, nil +} + +type BaseCoinResponse struct { + Symbol string `json:"symbol"` + BaseCoin string `json:"baseCoin"` + ExchangeName string `json:"exchangeName"` + ProductType string `json:"productType"` + SymbolType string `json:"symbolType"` + PricePrecision string `json:"pricePrecision"` + DeliveryType string `json:"deliveryType"` + ExpireAt int `json:"expireAt"` + UpdateAt int `json:"updateAt"` + Hot bool `json:"hot"` + Price float64 `json:"price"` + PriceChangeH24 float64 `json:"priceChangeH24"` +} diff --git a/provider/coinank/coinank_api/base_coin_test.go b/provider/coinank/coinank_api/base_coin_test.go new file mode 100644 index 00000000..2249fa89 --- /dev/null +++ b/provider/coinank/coinank_api/base_coin_test.go @@ -0,0 +1,43 @@ +package coinank_api + +import ( + "context" + "encoding/json" + "testing" +) + +func TestBaseCoinSymbolsNoArgs(t *testing.T) { + resp, err := BaseCoinSymbols(context.TODO(), "", "", "") + if err != nil { + t.Error(err) + } + res, err := json.Marshal(resp) + if err != nil { + t.Error(err) + } + t.Logf("%s", res) +} + +func TestBaseCoinSymbolsBTC(t *testing.T) { + resp, err := BaseCoinSymbols(context.TODO(), "", "", "BTC") + if err != nil { + t.Error(err) + } + res, err := json.Marshal(resp) + if err != nil { + t.Error(err) + } + t.Logf("%s", res) +} + +func TestBaseCoinSymbolsBTCUSDT(t *testing.T) { + resp, err := BaseCoinSymbols(context.TODO(), "", "BTCUSDT", "") + if err != nil { + t.Error(err) + } + res, err := json.Marshal(resp) + if err != nil { + t.Error(err) + } + t.Logf("%s", res) +} From 8f540ae17ca2741216d2fa19d44524681a88a880 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Wed, 31 Dec 2025 12:56:19 +0800 Subject: [PATCH 2/5] feat: improve Hyperliquid agent wallet config UX and guidance --- .../traders/ExchangeConfigModal.tsx | 129 ++++++++---------- web/src/i18n/translations.ts | 33 +++-- 2 files changed, 75 insertions(+), 87 deletions(-) diff --git a/web/src/components/traders/ExchangeConfigModal.tsx b/web/src/components/traders/ExchangeConfigModal.tsx index d492ac7b..92642b93 100644 --- a/web/src/components/traders/ExchangeConfigModal.tsx +++ b/web/src/components/traders/ExchangeConfigModal.tsx @@ -915,33 +915,39 @@ export function ExchangeConfigModal({ {/* Hyperliquid 交易所的字段 */} {currentExchangeType === 'hyperliquid' && ( <> - {/* 安全提示 banner */} + {/* 操作指引 banner */}
-
- - 🔐 - -
-
- {t('hyperliquidAgentWalletTitle', language)} -
-
- {t('hyperliquidAgentWalletDesc', language)} -
-
+
+ {t('hyperliquidAgentWalletTitle', language)}
+
+ {t('hyperliquidAgentWalletDesc', language)} +
+ + + {t('hyperliquidAgentWalletLinkText', language)} +
{/* Agent Private Key 字段 */} @@ -952,57 +958,36 @@ export function ExchangeConfigModal({ > {t('hyperliquidAgentPrivateKey', language)} -
-
- - - {apiKey && ( - +
+ setApiKey(e.target.value)} + placeholder={t( + 'enterHyperliquidAgentPrivateKey', + language )} -
- {apiKey && ( -
- {t('secureInputHint', language)} -
- )} + className="w-full px-3 py-2 rounded" + style={{ + background: '#0B0E11', + border: '1px solid #2B3139', + color: '#EAECEF', + }} + required + /> +
Date: Wed, 31 Dec 2025 13:19:01 +0800 Subject: [PATCH 3/5] fix: remove unused maskSecret function --- web/src/components/traders/ExchangeConfigModal.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/web/src/components/traders/ExchangeConfigModal.tsx b/web/src/components/traders/ExchangeConfigModal.tsx index 92642b93..3f24206a 100644 --- a/web/src/components/traders/ExchangeConfigModal.tsx +++ b/web/src/components/traders/ExchangeConfigModal.tsx @@ -248,17 +248,6 @@ export function ExchangeConfigModal({ setSecureInputTarget(null) } - // 掩盖敏感数据显示 - const maskSecret = (secret: string) => { - if (!secret || secret.length === 0) return '' - if (secret.length <= 8) return '*'.repeat(secret.length) - return ( - secret.slice(0, 4) + - '*'.repeat(Math.max(secret.length - 8, 4)) + - secret.slice(-4) - ) - } - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (isSaving) return From 445c3aad69454904f0c76cdfc9231ac88050e9b6 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Wed, 31 Dec 2025 13:19:58 +0800 Subject: [PATCH 4/5] feat: add pprof server for memory profiling --- main.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/main.go b/main.go index 56879d33..6cb087ce 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main import ( + "net/http" + _ "net/http/pprof" "nofx/api" "nofx/auth" "nofx/backtest" @@ -139,6 +141,14 @@ func main() { } } + // Start pprof server for profiling (port 6060) + go func() { + logger.Info("📊 Starting pprof server on :6060") + if err := http.ListenAndServe(":6060", nil); err != nil { + logger.Warnf("⚠️ pprof server error: %v", err) + } + }() + // Start API server server := api.NewServer(traderManager, st, cryptoService, backtestManager, cfg.APIServerPort) go func() { From 1af4589320aef748ac2af3d5526f0978bf0b9fb5 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Thu, 1 Jan 2026 13:23:36 +0800 Subject: [PATCH 5/5] fix: improve Hyperliquid order execution compatibility --- trader/hyperliquid_trader.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/trader/hyperliquid_trader.go b/trader/hyperliquid_trader.go index 0f1d3ca9..48a94aba 100644 --- a/trader/hyperliquid_trader.go +++ b/trader/hyperliquid_trader.go @@ -2078,8 +2078,7 @@ func (t *HyperliquidTrader) GetTrades(startTime time.Time, limit int) ([]TradeRe return trades, nil } -// defaultBuilder is the builder info for order routing -var defaultBuilder = &hyperliquid.BuilderInfo{ - Builder: "0x891dc6f05ad47a3c1a05da55e7a7517971faaf0d", - Fee: 10, -} +// defaultBuilder is nil to avoid requiring users to approve builder fee +// Previously used builder address: 0x891dc6f05ad47a3c1a05da55e7a7517971faaf0d +// If builder fee is needed in the future, implement approval flow first +var defaultBuilder *hyperliquid.BuilderInfo = nil