🚀 Fix Drift Protocol integration - Connection now working
✅ Key fixes: - Bypass problematic SDK subscription that caused 410 Gone errors - Use direct account verification without subscription - Add fallback modes for better reliability - Switch to Helius RPC endpoint for better rate limits - Implement proper error handling and retry logic 🔧 Technical changes: - Enhanced drift-trading.ts with no-subscription approach - Added Drift API endpoints (/api/drift/login, /balance, /positions) - Created DriftAccountStatus and DriftTradingPanel components - Updated Dashboard.tsx to show Drift account status - Added comprehensive test scripts for debugging 📊 Results: - Connection Status: Connected ✅ - Account verification: Working ✅ - Balance retrieval: Working ✅ (21.94 total collateral) - Private key authentication: Working ✅ - User account: 3dG7wayp7b9NBMo92D2qL2sy1curSC4TTmskFpaGDrtA 🌐 RPC improvements: - Using Helius RPC for better reliability - Added fallback RPC options in .env - Eliminated rate limiting issues
This commit is contained in:
258
components/DriftAccountStatus.tsx
Normal file
258
components/DriftAccountStatus.tsx
Normal file
@@ -0,0 +1,258 @@
|
||||
"use client"
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
interface LoginStatus {
|
||||
isLoggedIn: boolean
|
||||
publicKey: string
|
||||
userAccountExists: boolean
|
||||
error?: string
|
||||
}
|
||||
|
||||
interface AccountBalance {
|
||||
totalCollateral: number
|
||||
freeCollateral: number
|
||||
marginRequirement: number
|
||||
accountValue: number
|
||||
leverage: number
|
||||
availableBalance: number
|
||||
}
|
||||
|
||||
interface Position {
|
||||
symbol: string
|
||||
side: 'LONG' | 'SHORT'
|
||||
size: number
|
||||
entryPrice: number
|
||||
markPrice: number
|
||||
unrealizedPnl: number
|
||||
marketIndex: number
|
||||
marketType: 'PERP' | 'SPOT'
|
||||
}
|
||||
|
||||
export default function DriftAccountStatus() {
|
||||
const [loginStatus, setLoginStatus] = useState<LoginStatus | null>(null)
|
||||
const [balance, setBalance] = useState<AccountBalance | null>(null)
|
||||
const [positions, setPositions] = useState<Position[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const fetchAccountData = async () => {
|
||||
try {
|
||||
setError(null)
|
||||
|
||||
// Step 1: Login/Check status
|
||||
const loginRes = await fetch('/api/drift/login', { method: 'POST' })
|
||||
const loginData = await loginRes.json()
|
||||
setLoginStatus(loginData)
|
||||
|
||||
if (!loginData.isLoggedIn) {
|
||||
setError(loginData.error || 'Login failed')
|
||||
return
|
||||
}
|
||||
|
||||
// Step 2: Fetch balance
|
||||
const balanceRes = await fetch('/api/drift/balance')
|
||||
if (balanceRes.ok) {
|
||||
const balanceData = await balanceRes.json()
|
||||
setBalance(balanceData)
|
||||
} else {
|
||||
const errorData = await balanceRes.json()
|
||||
setError(errorData.error || 'Failed to fetch balance')
|
||||
}
|
||||
|
||||
// Step 3: Fetch positions
|
||||
const positionsRes = await fetch('/api/drift/positions')
|
||||
if (positionsRes.ok) {
|
||||
const positionsData = await positionsRes.json()
|
||||
setPositions(positionsData.positions || [])
|
||||
} else {
|
||||
const errorData = await positionsRes.json()
|
||||
console.warn('Failed to fetch positions:', errorData.error)
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Unknown error occurred')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setRefreshing(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true)
|
||||
await fetchAccountData()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchAccountData()
|
||||
}, [])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="card card-gradient">
|
||||
<div className="flex items-center justify-center p-6">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-400"></div>
|
||||
<span className="ml-3 text-gray-400">Loading Drift account...</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Login Status */}
|
||||
<div className="card card-gradient">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-bold text-white flex items-center">
|
||||
<span className="text-xl mr-2">🌊</span>
|
||||
Drift Account Status
|
||||
</h2>
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
disabled={refreshing}
|
||||
className="btn-secondary btn-sm"
|
||||
>
|
||||
{refreshing ? '🔄' : '🔃'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-500/20 border border-red-500/50 rounded-lg p-4 mb-4">
|
||||
<p className="text-red-400 text-sm">❌ {error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loginStatus && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-400">Connection Status</span>
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
||||
loginStatus.isLoggedIn
|
||||
? 'bg-green-500/20 text-green-400'
|
||||
: 'bg-red-500/20 text-red-400'
|
||||
}`}>
|
||||
{loginStatus.isLoggedIn ? '🟢 Connected' : '🔴 Disconnected'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-400">Wallet Address</span>
|
||||
<span className="text-white font-mono text-sm">
|
||||
{loginStatus.publicKey ?
|
||||
`${loginStatus.publicKey.slice(0, 6)}...${loginStatus.publicKey.slice(-6)}`
|
||||
: 'N/A'
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-gray-400">User Account</span>
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
||||
loginStatus.userAccountExists
|
||||
? 'bg-green-500/20 text-green-400'
|
||||
: 'bg-yellow-500/20 text-yellow-400'
|
||||
}`}>
|
||||
{loginStatus.userAccountExists ? 'Initialized' : 'Not Found'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Account Balance */}
|
||||
{balance && loginStatus?.isLoggedIn && (
|
||||
<div className="card card-gradient">
|
||||
<h3 className="text-lg font-bold text-white mb-4 flex items-center">
|
||||
<span className="text-xl mr-2">💰</span>
|
||||
Account Balance
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-gray-400 text-sm">Total Collateral</p>
|
||||
<p className="text-xl font-bold text-green-400">
|
||||
${balance.totalCollateral.toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-400 text-sm">Available Balance</p>
|
||||
<p className="text-xl font-bold text-blue-400">
|
||||
${balance.availableBalance.toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-400 text-sm">Margin Used</p>
|
||||
<p className="text-xl font-bold text-yellow-400">
|
||||
${balance.marginRequirement.toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-400 text-sm">Leverage</p>
|
||||
<p className="text-xl font-bold text-purple-400">
|
||||
{balance.leverage.toFixed(2)}x
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open Positions */}
|
||||
{loginStatus?.isLoggedIn && (
|
||||
<div className="card card-gradient">
|
||||
<h3 className="text-lg font-bold text-white mb-4 flex items-center">
|
||||
<span className="text-xl mr-2">📊</span>
|
||||
Open Positions ({positions.length})
|
||||
</h3>
|
||||
|
||||
{positions.length === 0 ? (
|
||||
<div className="text-center py-6">
|
||||
<p className="text-gray-400">No open positions</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{positions.map((position, index) => (
|
||||
<div key={index} className="bg-gray-800/50 rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center">
|
||||
<span className="font-bold text-white">{position.symbol}</span>
|
||||
<span className={`ml-2 px-2 py-1 rounded text-xs font-medium ${
|
||||
position.side === 'LONG'
|
||||
? 'bg-green-500/20 text-green-400'
|
||||
: 'bg-red-500/20 text-red-400'
|
||||
}`}>
|
||||
{position.side}
|
||||
</span>
|
||||
</div>
|
||||
<span className={`font-bold ${
|
||||
position.unrealizedPnl >= 0 ? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
{position.unrealizedPnl >= 0 ? '+' : ''}${position.unrealizedPnl.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-400">Size: </span>
|
||||
<span className="text-white">{position.size.toFixed(4)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">Entry: </span>
|
||||
<span className="text-white">${position.entryPrice.toFixed(2)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">Mark: </span>
|
||||
<span className="text-white">${position.markPrice.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user