feat: fix Lighter V2 integration and improve error handling

- Fix Lighter API field name mismatches (position/size, avg_entry_price/entry_price, sign/side)
- Fix GetBalance return format to match standard fields (totalWalletBalance, totalUnrealizedProfit)
- Fix GetPositions return format to match standard fields (positionAmt, markPrice, unRealizedProfit)
- Add API Key Index field to frontend with explanation
- Update Lighter referral link
- Disable Lighter testnet (mainnet only)
- Add load error tracking for better error messages
- Remove old Lighter V1 implementation files
- Remove test credentials from test files
This commit is contained in:
tinkle-community
2025-12-14 20:50:10 +08:00
parent abaffaddb9
commit 4725548a55
22 changed files with 749 additions and 1774 deletions

View File

@@ -636,7 +636,8 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
asterPrivateKey?: string,
lighterWalletAddr?: string,
lighterPrivateKey?: string,
lighterApiKeyPrivateKey?: string
lighterApiKeyPrivateKey?: string,
lighterApiKeyIndex?: number
) => {
try {
if (exchangeId) {
@@ -662,6 +663,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
lighter_wallet_addr: lighterWalletAddr || '',
lighter_private_key: lighterPrivateKey || '',
lighter_api_key_private_key: lighterApiKeyPrivateKey || '',
lighter_api_key_index: lighterApiKeyIndex || 0,
},
},
}
@@ -688,6 +690,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
lighter_wallet_addr: lighterWalletAddr || '',
lighter_private_key: lighterPrivateKey || '',
lighter_api_key_private_key: lighterApiKeyPrivateKey || '',
lighter_api_key_index: lighterApiKeyIndex || 0,
}
await toast.promise(api.createExchangeEncrypted(createRequest), {

View File

@@ -19,7 +19,7 @@ const EXCHANGE_REGISTRATION_LINKS: Record<string, { url: string; hasReferral?: b
bybit: { url: 'https://partner.bybit.com/b/83856', hasReferral: true },
hyperliquid: { url: 'https://app.hyperliquid.xyz/join/AITRADING', hasReferral: true },
aster: { url: 'https://www.asterdex.com/en/referral/fdfc0e', hasReferral: true },
lighter: { url: 'https://lighter.xyz', hasReferral: false },
lighter: { url: 'https://app.lighter.xyz/?referral=68151432', hasReferral: true },
}
import type { TraderConfigData } from '../types'

View File

@@ -44,7 +44,8 @@ interface ExchangeConfigModalProps {
asterPrivateKey?: string,
lighterWalletAddr?: string,
lighterPrivateKey?: string,
lighterApiKeyPrivateKey?: string
lighterApiKeyPrivateKey?: string,
lighterApiKeyIndex?: number
) => Promise<void>
onDelete: (exchangeId: string) => void
onClose: () => void
@@ -88,8 +89,8 @@ export function ExchangeConfigModal({
// LIGHTER 特定字段
const [lighterWalletAddr, setLighterWalletAddr] = useState('')
const [lighterPrivateKey, setLighterPrivateKey] = useState('')
const [lighterApiKeyPrivateKey, setLighterApiKeyPrivateKey] = useState('')
const [lighterApiKeyIndex, setLighterApiKeyIndex] = useState(0)
// 安全输入状态
const [secureInputTarget, setSecureInputTarget] = useState<
@@ -127,7 +128,7 @@ export function ExchangeConfigModal({
bitget: { url: 'https://www.bitget.com/referral/register?from=referral&clacCode=c8a43172', hasReferral: true },
hyperliquid: { url: 'https://app.hyperliquid.xyz/join/AITRADING', hasReferral: true },
aster: { url: 'https://www.asterdex.com/en/referral/fdfc0e', hasReferral: true },
lighter: { url: 'https://lighter.xyz', hasReferral: false },
lighter: { url: 'https://app.lighter.xyz/?referral=68151432', hasReferral: true },
}
// 如果是编辑现有交易所,初始化表单数据
@@ -149,8 +150,8 @@ export function ExchangeConfigModal({
// LIGHTER 字段
setLighterWalletAddr(selectedExchange.lighterWalletAddr || '')
setLighterPrivateKey('') // Don't load existing private key for security
setLighterApiKeyPrivateKey('') // Don't load existing API key for security
setLighterApiKeyIndex(selectedExchange.lighterApiKeyIndex || 0)
}
}, [editingExchangeId, selectedExchange])
@@ -237,8 +238,8 @@ export function ExchangeConfigModal({
setAsterPrivateKey(trimmed)
}
if (secureInputTarget === 'lighter') {
setLighterPrivateKey(trimmed)
toast.success(t('lighterPrivateKeyImported', language))
setLighterApiKeyPrivateKey(trimmed)
toast.success(t('lighterApiKeyImported', language))
}
// 仅在开发环境输出调试信息
if (import.meta.env.DEV) {
@@ -316,22 +317,23 @@ export function ExchangeConfigModal({
asterPrivateKey.trim()
)
} else if (currentExchangeType === 'lighter') {
if (!lighterWalletAddr.trim() || !lighterPrivateKey.trim()) return
if (!lighterWalletAddr.trim() || !lighterApiKeyPrivateKey.trim()) return
await onSave(
exchangeId,
exchangeType,
trimmedAccountName,
lighterPrivateKey.trim(),
'', // apiKey not used for Lighter
'',
'',
testnet,
undefined, // hyperliquidWalletAddr
undefined, // asterUser
undefined, // asterSigner
undefined, // asterPrivateKey
lighterWalletAddr.trim(),
undefined,
undefined,
undefined,
lighterWalletAddr.trim(),
lighterPrivateKey.trim(),
lighterApiKeyPrivateKey.trim()
'', // lighterPrivateKey (L1) no longer needed
lighterApiKeyPrivateKey.trim(),
lighterApiKeyIndex
)
} else {
// 默认情况其他CEX交易所
@@ -1049,13 +1051,36 @@ export function ExchangeConfigModal({
{/* LIGHTER 特定配置 */}
{currentExchangeType === 'lighter' && (
<>
{/* Info banner */}
<div
className="p-3 rounded mb-4"
style={{
background: 'rgba(240, 185, 11, 0.1)',
border: '1px solid rgba(240, 185, 11, 0.3)',
}}
>
<div className="flex items-start gap-2">
<span style={{ color: '#F0B90B', fontSize: '16px' }}>🔐</span>
<div className="flex-1">
<div className="text-sm font-semibold mb-1" style={{ color: '#F0B90B' }}>
{language === 'zh' ? 'Lighter API Key 配置' : 'Lighter API Key Setup'}
</div>
<div className="text-xs" style={{ color: '#848E9C', lineHeight: '1.5' }}>
{language === 'zh'
? '请在 Lighter 网站生成 API Key然后填写钱包地址、API Key 私钥和索引。'
: 'Generate an API Key on the Lighter website, then enter your wallet address, API Key private key, and index.'}
</div>
</div>
</div>
</div>
{/* L1 Wallet Address */}
<div className="mb-4">
<label
className="block text-sm font-semibold mb-2"
style={{ color: '#EAECEF' }}
>
{t('lighterWalletAddress', language)}
{t('lighterWalletAddress', language)} *
</label>
<input
type="text"
@@ -1075,13 +1100,13 @@ export function ExchangeConfigModal({
</div>
</div>
{/* L1 Private Key */}
{/* API Key Private Key */}
<div className="mb-4">
<label
className="block text-sm font-semibold mb-2"
style={{ color: '#EAECEF' }}
>
{t('lighterPrivateKey', language)}
{t('lighterApiKeyPrivateKey', language)} *
<button
type="button"
onClick={() => setSecureInputTarget('lighter')}
@@ -1091,32 +1116,6 @@ export function ExchangeConfigModal({
{t('secureInputButton', language)}
</button>
</label>
<input
type="password"
value={lighterPrivateKey}
onChange={(e) => setLighterPrivateKey(e.target.value)}
placeholder={t('enterLighterPrivateKey', language)}
className="w-full px-3 py-2 rounded font-mono text-sm"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
required
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
{t('lighterPrivateKeyDesc', language)}
</div>
</div>
{/* API Key Private Key */}
<div className="mb-4">
<label
className="block text-sm font-semibold mb-2"
style={{ color: '#EAECEF' }}
>
{t('lighterApiKeyPrivateKey', language)}
</label>
<input
type="password"
value={lighterApiKeyPrivateKey}
@@ -1128,36 +1127,49 @@ export function ExchangeConfigModal({
border: '1px solid #2B3139',
color: '#EAECEF',
}}
required
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
{t('lighterApiKeyPrivateKeyDesc', language)}
</div>
<div className="text-xs mt-2 p-2 rounded" style={{
background: '#1E2329',
border: '1px solid #2B3139',
color: '#F0B90B'
}}>
💡 {t('lighterApiKeyOptionalNote', language)}
</div>
</div>
{/* V1/V2 Status Display */}
<div className="mb-4 p-3 rounded" style={{
background: lighterApiKeyPrivateKey ? '#0F3F2E' : '#3F2E0F',
border: '1px solid ' + (lighterApiKeyPrivateKey ? '#10B981' : '#F59E0B')
}}>
<div className="flex items-center gap-2">
<div className="text-sm font-semibold" style={{
color: lighterApiKeyPrivateKey ? '#10B981' : '#F59E0B'
}}>
{lighterApiKeyPrivateKey ? '✅ LIGHTER V2' : '⚠️ LIGHTER V1'}
</div>
</div>
{/* API Key Index */}
<div className="mb-4">
<label
className="block text-sm font-semibold mb-2 flex items-center gap-2"
style={{ color: '#EAECEF' }}
>
{language === 'zh' ? 'API Key 索引' : 'API Key Index'}
<Tooltip content={
language === 'zh'
? 'Lighter 允许每个账户创建多个 API Key最多256个。索引值对应您创建的第几个 API Key从0开始计数。如果您只创建了一个 API Key请使用默认值 0。'
: 'Lighter allows creating multiple API Keys per account (up to 256). The index corresponds to which API Key you created, starting from 0. If you only created one API Key, use the default value 0.'
}>
<HelpCircle
className="w-4 h-4 cursor-help"
style={{ color: '#F0B90B' }}
/>
</Tooltip>
</label>
<input
type="number"
min={0}
max={255}
value={lighterApiKeyIndex}
onChange={(e) => setLighterApiKeyIndex(parseInt(e.target.value) || 0)}
placeholder="0"
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
{lighterApiKeyPrivateKey
? t('lighterV2Description', language)
: t('lighterV1Description', language)
}
{language === 'zh'
? '默认为 0。如果您在 Lighter 创建了多个 API Key请填写对应的索引号0-255。'
: 'Default is 0. If you created multiple API Keys on Lighter, enter the corresponding index (0-255).'}
</div>
</div>
</>
@@ -1201,7 +1213,7 @@ export function ExchangeConfigModal({
!asterSigner.trim() ||
!asterPrivateKey.trim())) ||
(currentExchangeType === 'lighter' &&
(!lighterWalletAddr.trim() || !lighterPrivateKey.trim())) ||
(!lighterWalletAddr.trim() || !lighterApiKeyPrivateKey.trim())) ||
(currentExchangeType === 'bybit' &&
(!apiKey.trim() || !secretKey.trim())) ||
(selectedTemplate?.type === 'cex' &&