import { NextResponse } from 'next/server' // Helper function to get market index from symbol function getMarketIndex(symbol) { const marketMap = { 'SOL': 0, 'BTC': 1, 'ETH': 2, 'APT': 3, 'AVAX': 4, 'BNB': 5, 'MATIC': 6, 'ARB': 7, 'DOGE': 8, 'OP': 9 } const index = marketMap[symbol.toUpperCase()] if (index === undefined) { throw new Error(`Unsupported symbol: ${symbol}`) } return index } // Helper function to get symbol from market index function getSymbolFromMarketIndex(marketIndex) { const symbols = ['SOL', 'BTC', 'ETH', 'APT', 'AVAX', 'BNB', 'MATIC', 'ARB', 'DOGE', 'OP'] return symbols[marketIndex] || `UNKNOWN_${marketIndex}` } // Helper function to get trading balance with better error handling async function getTradingBalance(driftClient) { try { const userAccount = await driftClient.getUserAccount() if (!userAccount) { throw new Error('User account is null') } console.log('📊 Raw user account data keys:', Object.keys(userAccount)) // Get all spot positions const spotPositions = userAccount.spotPositions || [] const usdcPosition = spotPositions.find(pos => pos.marketIndex === 0) // USDC is usually index 0 // Convert BigNumber values to regular numbers const BN = (await import('bn.js')).default // Get collateral info - convert from BN to number const totalCollateral = userAccount.totalCollateral ? (userAccount.totalCollateral instanceof BN ? userAccount.totalCollateral.toNumber() / 1e6 : parseFloat(userAccount.totalCollateral.toString()) / 1e6) : 0 const freeCollateral = userAccount.freeCollateral ? (userAccount.freeCollateral instanceof BN ? userAccount.freeCollateral.toNumber() / 1e6 : parseFloat(userAccount.freeCollateral.toString()) / 1e6) : 0 // Get USDC balance const usdcBalance = usdcPosition && usdcPosition.scaledBalance ? (usdcPosition.scaledBalance instanceof BN ? usdcPosition.scaledBalance.toNumber() / 1e6 : parseFloat(usdcPosition.scaledBalance.toString()) / 1e6) : 0 console.log('💰 Parsed balances:', { totalCollateral, freeCollateral, usdcBalance, spotPositionsCount: spotPositions.length }) return { totalCollateral: totalCollateral.toString(), freeCollateral: freeCollateral.toString(), usdcBalance: usdcBalance.toString(), marginRatio: userAccount.marginRatio ? userAccount.marginRatio.toString() : '0', accountExists: true, spotPositions: spotPositions.map(pos => ({ marketIndex: pos.marketIndex, balance: pos.scaledBalance ? (pos.scaledBalance instanceof BN ? pos.scaledBalance.toNumber() / 1e6 : parseFloat(pos.scaledBalance.toString()) / 1e6) : 0 })) } } catch (error) { throw new Error(`Balance retrieval failed: ${error.message}`) } } export async function POST(request) { try { console.log('🌊 Drift leverage trading endpoint...') // Check if environment is configured if (!process.env.SOLANA_PRIVATE_KEY) { return NextResponse.json({ success: false, error: 'Drift not configured - missing SOLANA_PRIVATE_KEY' }, { status: 400 }) } const { action = 'get_balance', symbol = 'SOL', amount, side, leverage = 1, stopLoss = true, takeProfit = true, stopLossPercent = 2, // Default 2% takeProfitPercent = 4 // Default 4% } = await request.json() // Import Drift SDK components const { DriftClient, initialize } = await import('@drift-labs/sdk') const { Connection, Keypair } = await import('@solana/web3.js') // Initialize connection with Helius const heliusApiKey = '5e236449-f936-4af7-ae38-f15e2f1a3757' const rpcUrl = `https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}` const connection = new Connection(rpcUrl, 'confirmed') console.log('🌐 Using mainnet with Helius RPC') const privateKeyArray = JSON.parse(process.env.SOLANA_PRIVATE_KEY) const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)) // Create wallet using manual interface (most reliable) const wallet = { publicKey: keypair.publicKey, signTransaction: async (tx) => { if (typeof tx.partialSign === 'function') { tx.partialSign(keypair) } else if (typeof tx.sign === 'function') { tx.sign([keypair]) } return tx }, signAllTransactions: async (txs) => { return txs.map(tx => { if (typeof tx.partialSign === 'function') { tx.partialSign(keypair) } else if (typeof tx.sign === 'function') { tx.sign([keypair]) } return tx }) } } console.log('🔐 Connecting to Drift with wallet:', keypair.publicKey.toString()) // Initialize Drift SDK const env = 'mainnet-beta' const sdkConfig = initialize({ env }) const driftClient = new DriftClient({ connection, wallet, programID: sdkConfig.DRIFT_PROGRAM_ID, opts: { commitment: 'confirmed', }, }) try { // Subscribe to drift client await driftClient.subscribe() console.log('✅ Connected to Drift successfully') // Handle action let result = {} if (action === 'get_balance') { try { // Simple and direct approach console.log('🔍 Getting user account...') const userAccount = await driftClient.getUserAccount() if (userAccount) { console.log('✅ User account found, getting balance...') result = await getTradingBalance(driftClient) console.log('✅ Balance retrieved successfully') } else { console.log('❌ User account is null') result = { message: 'User account exists but returns null', accountExists: false } } } catch (error) { console.log('❌ Error getting user account:', error.message) // Check wallet SOL balance as fallback const walletBalance = await connection.getBalance(keypair.publicKey) const solBalance = walletBalance / 1e9 result = { message: 'Cannot access user account data', error: error.message, solBalance: solBalance, walletAddress: keypair.publicKey.toString(), suggestion: 'Account may need to be accessed through Drift UI first or deposit USDC directly' } } } else if (action === 'place_order') { // Place a leverage order with stop loss and take profit if (!amount || !side) { result = { error: 'Missing required parameters: amount and side' } } else { try { const { OrderType, PositionDirection, OrderTriggerCondition } = await import('@drift-labs/sdk') const BN = (await import('bn.js')).default const marketIndex = getMarketIndex(symbol) // Get current market price for stop loss/take profit calculations const perpMarketAccount = driftClient.getPerpMarketAccount(marketIndex) const currentPrice = Number(perpMarketAccount.amm.lastMarkPriceTwap) / 1e6 console.log(`📊 Current ${symbol} price: $${currentPrice}`) // For perpetual futures: amount is USD position size, apply leverage // Example: $32 position with 10x leverage = $320 position value const leveragedPositionSize = amount * leverage console.log(`💰 Applying ${leverage}x leverage: $${amount} → $${leveragedPositionSize}`) // Convert leveraged USD position to SOL base asset amount const solTokenAmount = leveragedPositionSize / currentPrice const baseAssetAmount = new BN(Math.floor(solTokenAmount * 1e9)) console.log(`💰 Position size conversion:`, { usdPositionSize: amount, leverage: leverage, leveragedPositionSize: leveragedPositionSize, solPrice: currentPrice, solTokenAmount: solTokenAmount, calculatedBaseAsset: solTokenAmount * 1e9, flooredBaseAsset: Math.floor(solTokenAmount * 1e9), baseAssetAmount: baseAssetAmount.toString() }) // Determine direction const direction = side.toLowerCase() === 'buy' ? PositionDirection.LONG : PositionDirection.SHORT console.log(`📊 Placing ${side} order:`, { symbol, marketIndex, usdAmount: amount, solAmount: solTokenAmount, leverage, currentPrice, baseAssetAmount: baseAssetAmount.toString() }) // 1. Place main perpetual market order console.log('🚀 Placing main market order...') const mainOrderTx = await driftClient.placePerpOrder({ orderType: OrderType.MARKET, marketIndex, direction, baseAssetAmount, reduceOnly: false, }) console.log('✅ Main order placed:', mainOrderTx) // Wait for main order to fill await new Promise(resolve => setTimeout(resolve, 5000)) // 2. Calculate stop loss and take profit prices using config percentages // NO ARTIFICIAL MINIMUMS: AI can freely choose appropriate percentages const stopLossPercentCalc = stopLossPercent / 100 // Use exact percentage from AI analysis const takeProfitPercentCalc = takeProfitPercent / 100 // Use exact percentage from AI analysis let stopLossPrice, takeProfitPrice if (direction === PositionDirection.LONG) { stopLossPrice = currentPrice * (1 - stopLossPercentCalc) takeProfitPrice = currentPrice * (1 + takeProfitPercentCalc) } else { stopLossPrice = currentPrice * (1 + stopLossPercentCalc) takeProfitPrice = currentPrice * (1 - takeProfitPercentCalc) } console.log(`🎯 Risk management:`, { stopLossPrice: stopLossPrice.toFixed(4), takeProfitPrice: takeProfitPrice.toFixed(4), stopLossPercent: `${stopLossPercentCalc * 100}%`, takeProfitPercent: `${takeProfitPercentCalc * 100}%`, priceDifference: Math.abs(currentPrice - stopLossPrice).toFixed(4) }) let stopLossTx = null, takeProfitTx = null // 3. Place stop loss order if (stopLoss) { try { console.log('🛡️ Placing stop loss order...') const stopLossTriggerPrice = new BN(Math.floor(stopLossPrice * 1e6)) const stopLossOrderPrice = direction === PositionDirection.LONG ? new BN(Math.floor(stopLossPrice * 0.995 * 1e6)) // LONG: order below trigger : new BN(Math.floor(stopLossPrice * 1.005 * 1e6)) // SHORT: order above trigger console.log(`🛡️ Stop Loss Details:`, { orderType: 'TRIGGER_LIMIT', triggerPrice: (stopLossTriggerPrice.toNumber() / 1e6).toFixed(4), orderPrice: (stopLossOrderPrice.toNumber() / 1e6).toFixed(4), direction: direction === PositionDirection.LONG ? 'SHORT' : 'LONG', baseAssetAmount: baseAssetAmount.toString(), currentPrice: currentPrice, stopLossPrice: stopLossPrice }) stopLossTx = await driftClient.placePerpOrder({ orderType: OrderType.TRIGGER_LIMIT, marketIndex, direction: direction === PositionDirection.LONG ? PositionDirection.SHORT : PositionDirection.LONG, baseAssetAmount, price: stopLossOrderPrice, triggerPrice: stopLossTriggerPrice, triggerCondition: direction === PositionDirection.LONG ? OrderTriggerCondition.BELOW : OrderTriggerCondition.ABOVE, reduceOnly: true, }) console.log('✅ Stop loss placed:', stopLossTx) } catch (slError) { console.warn('⚠️ Stop loss failed:', slError.message) console.warn('🛡️ Stop loss failure details:', { stopLossPrice, currentPrice, priceDiff: Math.abs(currentPrice - stopLossPrice), percentDiff: ((Math.abs(currentPrice - stopLossPrice) / currentPrice) * 100).toFixed(2) + '%', error: slError.message }) } } // 4. Place take profit order if (takeProfit) { try { console.log('🎯 Placing take profit order...') const takeProfitTriggerPrice = new BN(Math.floor(takeProfitPrice * 1e6)) const takeProfitOrderPrice = new BN(Math.floor(takeProfitPrice * 1.005 * 1e6)) // 0.5% slippage for execution console.log('🎯 Take Profit Details:', { takeProfitPrice: takeProfitPrice.toFixed(4), triggerPrice: (Number(takeProfitTriggerPrice) / 1e6).toFixed(4), orderPrice: (Number(takeProfitOrderPrice) / 1e6).toFixed(4), baseAssetAmount: baseAssetAmount.toString() }) takeProfitTx = await driftClient.placePerpOrder({ orderType: OrderType.TRIGGER_LIMIT, marketIndex, direction: direction === PositionDirection.LONG ? PositionDirection.SHORT : PositionDirection.LONG, baseAssetAmount, price: takeProfitOrderPrice, triggerPrice: takeProfitTriggerPrice, reduceOnly: true, }) console.log('✅ Take profit placed successfully:', takeProfitTx) } catch (tpError) { console.error('❌ Take profit placement failed:', { error: tpError.message, code: tpError.code, logs: tpError.logs || 'No logs available' }) } } // 5. Get final position after all orders const userAccount = await driftClient.getUserAccount() const position = userAccount.perpPositions.find(pos => pos.marketIndex === marketIndex && !pos.baseAssetAmount.isZero()) // 6. Create learning record for AI feedback loop try { const { PrismaClient } = await import('@prisma/client') const prisma = new PrismaClient() // Create trade record for learning const tradeRecord = await prisma.trade.create({ data: { userId: 'default-user', // Use existing user symbol: symbol, side: side.toLowerCase(), amount: amount, price: currentPrice, entryPrice: currentPrice, stopLoss: stopLoss ? stopLossPrice : null, takeProfit: takeProfit ? takeProfitPrice : null, leverage: leverage, timeframe: '1h', // Default timeframe status: 'EXECUTED', driftTxId: mainOrderTx, isAutomated: true, tradingMode: 'REAL', executionTime: new Date(), learningData: JSON.stringify({ stopLossTransactionId: stopLossTx, takeProfitTransactionId: takeProfitTx, stopLossPercent, takeProfitPercent, marketIndex, orderExecutionData: { mainOrderSuccess: !!mainOrderTx, stopLossSuccess: !!stopLossTx, takeProfitSuccess: !!takeProfitTx, platform: 'DRIFT_PROTOCOL' } }) } }) console.log(`📚 Created learning record for trade: ${tradeRecord.id}`) await prisma.$disconnect() } catch (learningError) { console.warn('⚠️ Failed to create learning record:', learningError.message) } result = { success: true, transactionId: mainOrderTx, stopLossTransactionId: stopLossTx, takeProfitTransactionId: takeProfitTx, symbol, side, amount, leverage, currentPrice, stopLossPrice: stopLoss ? stopLossPrice : null, takeProfitPrice: takeProfit ? takeProfitPrice : null, riskManagement: { stopLoss: !!stopLossTx, takeProfit: !!takeProfitTx, stopLossPercent, takeProfitPercent }, position: position ? { marketIndex: position.marketIndex, baseAssetAmount: position.baseAssetAmount.toString(), quoteAssetAmount: position.quoteAssetAmount.toString(), avgEntryPrice: (Number(position.quoteAssetAmount) / Number(position.baseAssetAmount) * 1e9).toFixed(4) } : null } } catch (orderError) { console.log('❌ Failed to place order:', orderError.message) result = { success: false, error: 'Failed to place order', details: orderError.message } } } } else { result = { message: `Action ${action} not yet implemented` } } // Clean up connection await driftClient.unsubscribe() return NextResponse.json({ success: true, action, result, timestamp: Date.now() }) } catch (driftError) { console.error('❌ Drift trading error:', driftError) try { await driftClient.unsubscribe() } catch (cleanupError) { console.warn('⚠️ Cleanup error:', cleanupError.message) } return NextResponse.json({ success: false, error: 'Drift trading failed', details: driftError.message }, { status: 500 }) } } catch (error) { console.error('❌ Trading API error:', error) return NextResponse.json({ success: false, error: 'Internal server error', details: error.message }, { status: 500 }) } }