diff --git a/trader/hyperliquid_trader.go b/trader/hyperliquid_trader.go index c189dbdc..5ae48a16 100644 --- a/trader/hyperliquid_trader.go +++ b/trader/hyperliquid_trader.go @@ -2,10 +2,12 @@ package trader import ( "context" + "crypto/ecdsa" "encoding/json" "fmt" "log" "strconv" + "strings" "github.com/ethereum/go-ethereum/crypto" "github.com/sonirico/go-hyperliquid" @@ -22,6 +24,9 @@ type HyperliquidTrader struct { // NewHyperliquidTrader 创建Hyperliquid交易器 func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testnet bool) (*HyperliquidTrader, error) { + // 去掉私钥的 0x 前缀(如果有,不区分大小写) + privateKeyHex = strings.TrimPrefix(strings.ToLower(privateKeyHex), "0x") + // 解析私钥 privateKey, err := crypto.HexToECDSA(privateKeyHex) if err != nil { @@ -34,13 +39,18 @@ func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testnet bool) apiURL = hyperliquid.TestnetAPIURL } - // // 从私钥生成钱包地址 - // pubKey := privateKey.Public() - // publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey) - // if !ok { - // return nil, fmt.Errorf("无法转换公钥") - // } - // walletAddr := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + // 从私钥生成钱包地址(如果未提供) + if walletAddr == "" { + pubKey := privateKey.Public() + publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("无法转换公钥") + } + walletAddr = crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + log.Printf("✓ 从私钥自动生成钱包地址: %s", walletAddr) + } else { + log.Printf("✓ 使用提供的钱包地址: %s", walletAddr) + } ctx := context.Background() diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx index 45cb1955..6f8d761c 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -123,10 +123,9 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { e.asterPrivateKey && e.asterPrivateKey.trim() !== ''; } - // Hyperliquid 只需要私钥(作为apiKey)和钱包地址 + // Hyperliquid 只需要私钥(作为apiKey),钱包地址会自动从私钥生成 if (e.id === 'hyperliquid') { - return e.apiKey && e.apiKey.trim() !== '' && - e.hyperliquidWalletAddr && e.hyperliquidWalletAddr.trim() !== ''; + return e.apiKey && e.apiKey.trim() !== ''; } // Binance 等其他交易所需要 apiKey 和 secretKey @@ -1164,8 +1163,6 @@ function ExchangeConfigModal({ const [testnet, setTestnet] = useState(false); const [showGuide, setShowGuide] = useState(false); - // Hyperliquid 特定字段 - const [hyperliquidWalletAddr, setHyperliquidWalletAddr] = useState(''); // Aster 特定字段 const [asterUser, setAsterUser] = useState(''); @@ -1182,10 +1179,7 @@ function ExchangeConfigModal({ setSecretKey(selectedExchange.secretKey || ''); setPassphrase(''); // Don't load existing passphrase for security setTestnet(selectedExchange.testnet || false); - - // Hyperliquid 字段 - setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || ''); - + // Aster 字段 setAsterUser(selectedExchange.asterUser || ''); setAsterSigner(selectedExchange.asterSigner || ''); @@ -1202,8 +1196,8 @@ function ExchangeConfigModal({ if (!apiKey.trim() || !secretKey.trim()) return; await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet); } else if (selectedExchange?.id === 'hyperliquid') { - if (!apiKey.trim() || !hyperliquidWalletAddr.trim()) return; - await onSave(selectedExchangeId, apiKey.trim(), '', testnet, hyperliquidWalletAddr.trim()); + if (!apiKey.trim()) return; // 只验证私钥,钱包地址自动从私钥生成 + await onSave(selectedExchangeId, apiKey.trim(), '', testnet, ''); // 传空字符串,后端自动生成地址 } else if (selectedExchange?.id === 'aster') { if (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim()) return; await onSave(selectedExchangeId, '', '', testnet, undefined, asterUser.trim(), asterSigner.trim(), asterPrivateKey.trim()); @@ -1370,24 +1364,6 @@ function ExchangeConfigModal({ {t('hyperliquidPrivateKeyDesc', language)} - -
- - setHyperliquidWalletAddr(e.target.value)} - placeholder={t('enterWalletAddress', language)} - className="w-full px-3 py-2 rounded" - style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} - required - /> -
- {t('hyperliquidWalletAddressDesc', language)} -
-
)} @@ -1485,7 +1461,7 @@ function ExchangeConfigModal({ !selectedExchange || (selectedExchange.id === 'binance' && (!apiKey.trim() || !secretKey.trim())) || (selectedExchange.id === 'okx' && (!apiKey.trim() || !secretKey.trim() || !passphrase.trim())) || - (selectedExchange.id === 'hyperliquid' && (!apiKey.trim() || !hyperliquidWalletAddr.trim())) || + (selectedExchange.id === 'hyperliquid' && !apiKey.trim()) || // 只验证私钥,钱包地址可选 (selectedExchange.id === 'aster' && (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim())) || (selectedExchange.type === 'cex' && selectedExchange.id !== 'hyperliquid' && selectedExchange.id !== 'aster' && selectedExchange.id !== 'binance' && selectedExchange.id !== 'okx' && (!apiKey.trim() || !secretKey.trim())) }