fix(ui): remove duplicate exchange configuration fields (Aster & Hyperliquid) (#921)

* fix(ui): remove duplicate Aster exchange form rendering
修復 Aster 交易所配置表單重複渲染問題。
Issue:
- Aster 表單代碼在 AITradersPage.tsx 中出現兩次(lines 2334 和 2559)
- 導致用戶界面顯示 6 個輸入欄位(應該是 3 個)
- 用戶體驗混亂
Fix:
- 刪除重複的 Aster 表單代碼塊(lines 2559-2710,共 153 行)
- 保留第一個表單塊(lines 2334-2419)
- 修復 prettier 格式問題
Result:
- Aster 配置現在正確顯示 3 個欄位:user, signer, private key
- Lint 檢查通過
- Hyperliquid Agent Wallet 翻譯已存在無需修改
Technical:
- 刪除了完全重複的 JSX 條件渲染塊
- 移除空白行以符合 prettier 規範
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
* fix(ui): remove legacy Hyperliquid single private key field
修復 Hyperliquid 配置頁面顯示舊版私鑰欄位的問題。
Issue:
- Hyperliquid 配置同時顯示舊版和新版欄位
- 舊版:單一「私钥」欄位(不安全,已廢棄)
- 新版:「代理私钥」+「主钱包地址」(Agent Wallet 安全模式)
- 用戶看到重複的欄位配置,造成混淆
Root Cause:
- AITradersPage.tsx 存在兩個 Hyperliquid 條件渲染塊
- Lines 2302-2332: 舊版單私鑰模式(應刪除)
- Lines 2424-2557: 新版 Agent Wallet 模式(正確)
Fix:
- 刪除舊版 Hyperliquid 單私鑰欄位代碼塊(lines 2302-2332,共 32 行)
- 保留新版 Agent Wallet 配置(代理私鑰 + 主錢包地址)
- 移除 `t('privateKey')` 和 `t('hyperliquidPrivateKeyDesc')` 舊版翻譯引用
Result:
- Hyperliquid 配置現在只顯示正確的 Agent Wallet 欄位
- 安全提示 banner 正確顯示
- 用戶體驗改善,不再混淆
Technical Details:
- 新版使用 `apiKey` 儲存 Agent Private Key
- 新版使用 `hyperliquidWalletAddr` 儲存 Main Wallet Address
- 符合 Hyperliquid Agent Wallet 最佳安全實踐
Related:
- 之前已修復 Aster 重複渲染問題(commit 5462eba0)
- Hyperliquid 翻譯 key 已存在於 translations.ts (lines 206-216, 1017-1027)
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
* fix(i18n): add missing Hyperliquid Agent Wallet translation keys
補充 Hyperliquid 代理錢包配置的翻譯文本,修復前端顯示 key 名稱的問題。
Changes:
- 新增 8 個英文翻譯 key (Agent Wallet 配置說明)
- 新增 8 個中文翻譯 key (代理錢包配置說明)
- 修正 Hyperliquid 配置頁面顯示問題(從顯示 key 名稱改為顯示翻譯文本)
Technical Details:
- hyperliquidAgentWalletTitle: Banner 標題
- hyperliquidAgentWalletDesc: 安全說明文字
- hyperliquidAgentPrivateKey: 代理私鑰欄位標籤
- hyperliquidMainWalletAddress: 主錢包地址欄位標籤
- 相應的 placeholder 和 description 文本
Related Issue: 用戶反饋前端顯示 key 名稱而非翻譯文本
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
---------
Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com>
Co-authored-by: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
0xYYBB | ZYY | Bobo
2025-11-12 09:59:57 +08:00
committed by GitHub
parent 1e0da2ee39
commit 70a6218704
3 changed files with 30 additions and 191 deletions

View File

@@ -2298,39 +2298,6 @@ function ExchangeConfigModal({
</>
)}
{/* Hyperliquid 交易所的字段 */}
{selectedExchange.id === 'hyperliquid' && (
<>
<div>
<label
className="block text-sm font-semibold mb-2"
style={{ color: '#EAECEF' }}
>
{t('privateKey', language)}
</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder={t('enterPrivateKey', language)}
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
required
/>
<div
className="text-xs mt-1"
style={{ color: '#848E9C' }}
>
{t('hyperliquidPrivateKeyDesc', language)}
</div>
</div>
</>
)}
{/* Aster 交易所的字段 */}
{selectedExchange.id === 'aster' && (
<>
@@ -2555,160 +2522,6 @@ function ExchangeConfigModal({
</div>
</>
)}
{/* Aster 交易所的字段 */}
{selectedExchange.id === 'aster' && (
<>
<div>
<label
className="block text-sm font-semibold mb-2 flex items-center gap-2"
style={{ color: '#EAECEF' }}
>
{t('user', language)}
<Tooltip content={t('asterUserDesc', language)}>
<HelpCircle
className="w-4 h-4 cursor-help"
style={{ color: '#F0B90B' }}
/>
</Tooltip>
</label>
<input
type="text"
value={asterUser}
onChange={(e) => setAsterUser(e.target.value)}
placeholder={t('enterUser', language)}
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
required
/>
</div>
<div>
<label
className="block text-sm font-semibold mb-2 flex items-center gap-2"
style={{ color: '#EAECEF' }}
>
{t('signer', language)}
<Tooltip content={t('asterSignerDesc', language)}>
<HelpCircle
className="w-4 h-4 cursor-help"
style={{ color: '#F0B90B' }}
/>
</Tooltip>
</label>
<input
type="text"
value={asterSigner}
onChange={(e) => setAsterSigner(e.target.value)}
placeholder={t('enterSigner', language)}
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
required
/>
</div>
<div>
<label
className="block text-sm font-semibold mb-2 flex items-center gap-2"
style={{ color: '#EAECEF' }}
>
{t('privateKey', language)}
<Tooltip content={t('asterPrivateKeyDesc', language)}>
<HelpCircle
className="w-4 h-4 cursor-help"
style={{ color: '#F0B90B' }}
/>
</Tooltip>
</label>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<input
type="text"
value={maskSecret(asterPrivateKey)}
readOnly
placeholder={t('enterPrivateKey', language)}
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
/>
<button
type="button"
onClick={() => setSecureInputTarget('aster')}
className="px-3 py-2 rounded text-xs font-semibold transition-all hover:scale-105"
style={{
background: '#F0B90B',
color: '#000',
whiteSpace: 'nowrap',
}}
>
{asterPrivateKey
? t('secureInputReenter', language)
: t('secureInputButton', language)}
</button>
{asterPrivateKey && (
<button
type="button"
onClick={() => setAsterPrivateKey('')}
className="px-3 py-2 rounded text-xs font-semibold transition-all hover:scale-105"
style={{
background: '#1B1F2B',
color: '#848E9C',
whiteSpace: 'nowrap',
}}
>
{t('secureInputClear', language)}
</button>
)}
</div>
{asterPrivateKey && (
<div className="text-xs" style={{ color: '#848E9C' }}>
{t('secureInputHint', language)}
</div>
)}
</div>
</div>
<div
className="p-4 rounded"
style={{
background: 'rgba(240, 185, 11, 0.1)',
border: '1px solid rgba(240, 185, 11, 0.2)',
}}
>
<div
className="text-sm font-semibold mb-2"
style={{ color: '#F0B90B' }}
>
<span className="inline-flex items-center gap-1">
<AlertTriangle className="w-4 h-4" />{' '}
{t('securityWarning', language)}
</span>
</div>
<div
className="text-xs space-y-1"
style={{ color: '#848E9C' }}
>
{selectedExchange.id === 'aster' && (
<div>{t('asterUsdtWarning', language)}</div>
)}
<div>{t('exchangeConfigWarning1', language)}</div>
<div>{t('exchangeConfigWarning2', language)}</div>
<div>{t('exchangeConfigWarning3', language)}</div>
</div>
</div>
</>
)}
</>
)}
</div>

View File

@@ -202,6 +202,18 @@ export const translations = {
'Hyperliquid uses private key for trading authentication',
hyperliquidWalletAddressDesc:
'Wallet address corresponding to the private key',
// Hyperliquid Agent Wallet (New Security Model)
hyperliquidAgentWalletTitle: 'Hyperliquid Agent Wallet Configuration',
hyperliquidAgentWalletDesc:
'Use Agent Wallet for secure trading: Agent wallet signs transactions (balance ~0), Main wallet holds funds (never expose private key)',
hyperliquidAgentPrivateKey: 'Agent Private Key',
enterHyperliquidAgentPrivateKey: 'Enter Agent wallet private key',
hyperliquidAgentPrivateKeyDesc:
'Agent wallet private key for signing transactions (keep balance near 0 for security)',
hyperliquidMainWalletAddress: 'Main Wallet Address',
enterHyperliquidMainWalletAddress: 'Enter Main wallet address',
hyperliquidMainWalletAddressDesc:
'Main wallet address that holds your trading funds (never expose its private key)',
asterUserDesc:
'Main wallet address - The EVM wallet address you use to log in to Aster (Note: Only EVM wallets are supported, Solana wallets are not supported)',
asterSignerDesc:
@@ -1000,6 +1012,18 @@ export const translations = {
enterPassphrase: '输入Passphrase (OKX必填)',
hyperliquidPrivateKeyDesc: 'Hyperliquid 使用私钥进行交易认证',
hyperliquidWalletAddressDesc: '与私钥对应的钱包地址',
// Hyperliquid 代理钱包 (新安全模型)
hyperliquidAgentWalletTitle: 'Hyperliquid 代理钱包配置',
hyperliquidAgentWalletDesc:
'使用代理钱包安全交易:代理钱包用于签名(餘額~0主钱包持有资金永不暴露私钥',
hyperliquidAgentPrivateKey: '代理私钥',
enterHyperliquidAgentPrivateKey: '输入代理钱包私钥',
hyperliquidAgentPrivateKeyDesc:
'代理钱包私钥用于签名交易为了安全应保持余额接近0',
hyperliquidMainWalletAddress: '主钱包地址',
enterHyperliquidMainWalletAddress: '输入主钱包地址',
hyperliquidMainWalletAddressDesc:
'持有交易资金的主钱包地址(永不暴露其私钥)',
asterUserDesc:
'主钱包地址 - 您用于登录 Aster 的 EVM 钱包地址(注意:仅支持 EVM 钱包,不支持 Solana 钱包)',
asterSignerDesc:
@@ -1080,9 +1104,11 @@ export const translations = {
promptTemplateNof1: 'NoF1 英文框架',
promptTemplateTaroLong: 'Taro 长仓',
promptDescDefault: '📊 默认稳健策略',
promptDescDefaultContent: '最大化夏普比率,平衡风险收益,适合新手和长期稳定交易',
promptDescDefaultContent:
'最大化夏普比率,平衡风险收益,适合新手和长期稳定交易',
promptDescAdaptive: '🛡️ 保守策略 (v6.0.0)',
promptDescAdaptiveContent: '严格风控BTC 强制确认,高胜率优先,适合保守型交易者',
promptDescAdaptiveContent:
'严格风控BTC 强制确认,高胜率优先,适合保守型交易者',
promptDescAdaptiveRelaxed: '⚡ 激进策略 (v6.0.0)',
promptDescAdaptiveRelaxedContent:
'高频交易BTC 可选确认,追求交易机会,适合波动市场',
@@ -1092,7 +1118,8 @@ export const translations = {
promptDescNof1Content:
'Hyperliquid 交易所专用,英文提示词,风险调整回报最大化',
promptDescTaroLong: '📈 Taro 长仓策略',
promptDescTaroLongContent: '数据驱动决策,多维度验证,持续学习进化,长仓专用',
promptDescTaroLongContent:
'数据驱动决策,多维度验证,持续学习进化,长仓专用',
// Loading & Error
loading: '加载中...',

View File

@@ -157,7 +157,6 @@ export const api = {
if (!res.ok) throw new Error('更新模型配置失败')
},
// 交易所配置接口
async getExchangeConfigs(): Promise<Exchange[]> {
const res = await httpClient.get(`${API_BASE}/exchanges`, getAuthHeaders())