import { Connection, PublicKey } from '@solana/web3.js'; // Types export interface TradingBalance { totalValue: number; availableBalance: number; marginUsed: number; unrealizedPnl: number; positions: { symbol: string; size: number; notionalValue: number; unrealizedPnl: number; side: 'long' | 'short'; }[]; } export interface Position { symbol: string; side: 'long' | 'short'; size: number; entryPrice: number; markPrice: number; unrealizedPnl: number; notionalValue: number; } export interface OrderRequest { symbol: string; side: 'buy' | 'sell'; type: 'market' | 'limit'; amount: number; price?: number; } export interface OrderResponse { success: boolean; orderId?: string; error?: string; } class DriftTradingService { private connection: Connection; private readonly accountPDA = '7LonnWut5i3h36xyMA5jbwnGFbnzXUPY2dsPfNaSsrTk'; constructor() { this.connection = new Connection( process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com', 'confirmed' ); } async initialize(): Promise { console.log('🚀 Initializing Direct Drift Trading Service...'); console.log('✅ Direct service ready - no SDK subscriptions needed'); } private async getAccountData(): Promise { try { const accountInfo = await this.connection.getAccountInfo( new PublicKey(this.accountPDA) ); if (!accountInfo) { throw new Error('Account not found'); } return accountInfo.data; } catch (error) { console.error('❌ Failed to fetch account data:', error); throw error; } } private parseAccountData(data: Buffer) { try { // Extract USDC balance at offset 106 const usdcBalance = data.readBigInt64LE(106); const usdcValue = Number(usdcBalance) / 1_000_000; // USDC has 6 decimals // Extract SOL position at offset 432 (most current/accurate location) const solPosition = data.readBigInt64LE(432); const solAmount = Number(solPosition) / 1_000_000_000; // SOL has 9 decimals // Estimate SOL price (you could fetch this from an oracle) const solPrice = 17.11; // Current approximate price const solValue = solAmount * solPrice; const totalValue = usdcValue + solValue; console.log(`💰 Parsed account data:`); console.log(` USDC Balance: $${usdcValue.toFixed(2)}`); console.log(` SOL Position: ${solAmount.toFixed(6)} SOL`); console.log(` SOL Value: $${solValue.toFixed(2)} (@ $${solPrice})`); console.log(` Total Value: $${totalValue.toFixed(2)}`); return { usdcBalance: usdcValue, solPosition: solAmount, solPrice, solValue, totalValue }; } catch (error) { console.error('❌ Failed to parse account data:', error); throw error; } } async getTradingBalance(): Promise { try { const data = await this.getAccountData(); const parsed = this.parseAccountData(data); // Calculate unrealized PnL (SOL value minus some baseline) const unrealizedPnl = parsed.solValue - (parsed.solPosition * 15); // Assuming $15 entry price return { totalValue: parsed.totalValue, availableBalance: parsed.usdcBalance, marginUsed: 0, // Not available without full account parsing unrealizedPnl, positions: parsed.solPosition > 0 ? [{ symbol: 'SOL', size: parsed.solPosition, notionalValue: parsed.solValue, unrealizedPnl, side: 'long' as const }] : [] }; } catch (error) { console.error('❌ Failed to get trading balance:', error); throw error; } } async getPositions(): Promise { try { const data = await this.getAccountData(); const parsed = this.parseAccountData(data); if (parsed.solPosition <= 0) { return []; } const unrealizedPnl = parsed.solValue - (parsed.solPosition * 15); // Assuming $15 entry return [{ symbol: 'SOL', side: 'long', size: parsed.solPosition, entryPrice: 15, // Estimated entry price markPrice: parsed.solPrice, unrealizedPnl, notionalValue: parsed.solValue }]; } catch (error) { console.error('❌ Failed to get positions:', error); throw error; } } async placeOrder(orderRequest: OrderRequest): Promise { console.log('📝 Order placement not implemented in direct mode:', orderRequest); return { success: false, error: 'Order placement requires SDK integration' }; } async disconnect(): Promise { console.log('✅ Direct service disconnected (no cleanup needed)'); } } // Export singleton instance export const driftTradingService = new DriftTradingService();