From 045834dcbe32ed21b218069a0c167cacb12cbb3f Mon Sep 17 00:00:00 2001 From: tangmengqiu <1124090103@qq.com> Date: Mon, 3 Nov 2025 23:15:38 -0500 Subject: [PATCH] =?UTF-8?q?feat(hyperliquid):=20Auto-generate=20wallet=20a?= =?UTF-8?q?ddress=20from=20private=20key=20Enable=20automatic=20wallet=20a?= =?UTF-8?q?ddress=20generation=20from=20private=20key=20for=20Hyperliquid?= =?UTF-8?q?=20exchange,=20simplifying=20user=20onboarding=20and=20reducing?= =?UTF-8?q?=20configuration=20errors.=20Backend=20Changes=20(trader/hyperl?= =?UTF-8?q?iquid=5Ftrader.go):=20-=20Import=20crypto/ecdsa=20package=20for?= =?UTF-8?q?=20ECDSA=20public=20key=20operations=20-=20Enable=20wallet=20ad?= =?UTF-8?q?dress=20auto-generation=20when=20walletAddr=20is=20empty=20-=20?= =?UTF-8?q?Use=20crypto.PubkeyToAddress()=20to=20derive=20address=20from?= =?UTF-8?q?=20private=20key=20-=20Add=20logging=20for=20both=20auto-genera?= =?UTF-8?q?ted=20and=20manually=20provided=20addresses=20Frontend=20Change?= =?UTF-8?q?s=20(web/src/components/AITradersPage.tsx):=20-=20Remove=20wall?= =?UTF-8?q?et=20address=20required=20validation=20(only=20private=20key=20?= =?UTF-8?q?required)=20-=20Update=20button=20disabled=20state=20to=20only?= =?UTF-8?q?=20check=20private=20key=20-=20Add=20"Optional"=20label=20to=20?= =?UTF-8?q?wallet=20address=20field=20-=20Add=20dynamic=20placeholder=20wi?= =?UTF-8?q?th=20bilingual=20hint=20-=20Show=20context-aware=20helper=20tex?= =?UTF-8?q?t=20based=20on=20input=20state=20-=20Remove=20HTML=20required?= =?UTF-8?q?=20attribute=20from=20input=20field=20Translation=20Updates=20(?= =?UTF-8?q?web/src/i18n/translations.ts):=20-=20Add=20'optional'=20transla?= =?UTF-8?q?tion=20(EN:=20"Optional",=20ZH:=20"=E5=8F=AF=E9=80=89")=20-=20A?= =?UTF-8?q?dd=20'hyperliquidWalletAddressAutoGenerate'=20translation=20=20?= =?UTF-8?q?=20EN:=20"Leave=20blank=20to=20automatically=20generate=20walle?= =?UTF-8?q?t=20address=20from=20private=20key"=20=20=20ZH:=20"=E7=95=99?= =?UTF-8?q?=E7=A9=BA=E5=B0=86=E8=87=AA=E5=8A=A8=E4=BB=8E=E7=A7=81=E9=92=A5?= =?UTF-8?q?=E7=94=9F=E6=88=90=E9=92=B1=E5=8C=85=E5=9C=B0=E5=9D=80"=20Benef?= =?UTF-8?q?its:=20=E2=9C=85=20Simplified=20UX=20-=20Users=20only=20need=20?= =?UTF-8?q?to=20provide=20private=20key=20=E2=9C=85=20Error=20prevention?= =?UTF-8?q?=20-=20Auto-generated=20address=20always=20matches=20private=20?= =?UTF-8?q?key=20=E2=9C=85=20Backward=20compatible=20-=20Manual=20address?= =?UTF-8?q?=20input=20still=20supported=20=E2=9C=85=20Better=20UX=20-=20Cl?= =?UTF-8?q?ear=20visual=20indicators=20for=20optional=20fields=20Technical?= =?UTF-8?q?=20Details:=20-=20Uses=20Ethereum=20standard=20ECDSA=20public?= =?UTF-8?q?=20key=20to=20address=20conversion=20-=20Implementation=20was?= =?UTF-8?q?=20already=20present=20but=20commented=20out=20(lines=2037-43)?= =?UTF-8?q?=20-=20No=20database=20schema=20changes=20required=20(hyperliqu?= =?UTF-8?q?id=5Fwallet=5Faddr=20already=20nullable)=20-=20Fallback=20behav?= =?UTF-8?q?ior:=20manual=20input=20>=20auto-generation=20Co-Authored-By:?= =?UTF-8?q?=20tinkle-community=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- trader/hyperliquid_trader.go | 20 +++++++++++++------- web/src/components/AITradersPage.tsx | 16 ++++++++++------ web/src/i18n/translations.ts | 4 ++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/trader/hyperliquid_trader.go b/trader/hyperliquid_trader.go index c189dbdc..0c7684d3 100644 --- a/trader/hyperliquid_trader.go +++ b/trader/hyperliquid_trader.go @@ -2,6 +2,7 @@ package trader import ( "context" + "crypto/ecdsa" "encoding/json" "fmt" "log" @@ -34,13 +35,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 41b3cdc2..f3364551 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -1201,7 +1201,7 @@ 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; + if (!apiKey.trim()) return; // 只验证私钥,钱包地址可选(会自动生成) await onSave(selectedExchangeId, apiKey.trim(), '', testnet, hyperliquidWalletAddr.trim()); } else if (selectedExchange?.id === 'aster') { if (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim()) return; @@ -1360,18 +1360,22 @@ function ExchangeConfigModal({
setHyperliquidWalletAddr(e.target.value)} - placeholder={t('enterWalletAddress', language)} + placeholder="0x... (留空将自动从私钥生成 / Leave blank to auto-generate)" className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} - required />
- {t('hyperliquidWalletAddressDesc', language)} + {hyperliquidWalletAddr.trim() + ? t('hyperliquidWalletAddressDesc', language) + : t('hyperliquidWalletAddressAutoGenerate', language)}
@@ -1468,10 +1472,10 @@ function ExchangeConfigModal({