- Replace hardcoded localhost URLs with dynamic host detection from request headers - Supports both development (localhost:3001) and Docker (localhost:9000 -> 3000) environments - Uses host header to determine correct protocol and port for internal API calls - Updated execute-dex, validate, and orders APIs to use dynamic baseUrl - Ensures proper API communication in containerized environments
278 lines
8.7 KiB
JavaScript
278 lines
8.7 KiB
JavaScript
import { NextResponse } from 'next/server'
|
|
|
|
export async function POST(request) {
|
|
try {
|
|
const body = await request.json()
|
|
const {
|
|
symbol,
|
|
side,
|
|
amount,
|
|
amountUSD,
|
|
stopLoss,
|
|
takeProfit,
|
|
useRealDEX = false,
|
|
tradingPair,
|
|
quickSwap = false,
|
|
closePosition = false,
|
|
fromCoin,
|
|
toCoin
|
|
} = body
|
|
|
|
// Get the base URL from the request
|
|
const host = request.headers.get('host') || 'localhost:3000'
|
|
const protocol = host.includes('localhost') ? 'http' : 'https'
|
|
const baseUrl = `${protocol}://${host}`
|
|
|
|
console.log('🔄 Execute DEX trade request:', {
|
|
symbol,
|
|
side,
|
|
amount,
|
|
amountUSD,
|
|
stopLoss,
|
|
takeProfit,
|
|
useRealDEX,
|
|
tradingPair,
|
|
quickSwap
|
|
})
|
|
|
|
// Validate inputs
|
|
if (!symbol || !side || !amount) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: 'Missing required fields: symbol, side, amount'
|
|
},
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
if (!['BUY', 'SELL'].includes(side.toUpperCase())) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: 'Invalid side. Must be BUY or SELL'
|
|
},
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
if (amount <= 0) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: 'Amount must be greater than 0'
|
|
},
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
// Validate balance before proceeding (skip for position closing)
|
|
if (!closePosition) {
|
|
console.log('🔍 Validating wallet balance before DEX trade...')
|
|
|
|
try {
|
|
const validationResponse = await fetch(`${baseUrl}/api/trading/validate`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
symbol,
|
|
side,
|
|
amount,
|
|
amountUSD,
|
|
tradingMode: 'SPOT',
|
|
fromCoin,
|
|
toCoin
|
|
})
|
|
})
|
|
|
|
const validationResult = await validationResponse.json()
|
|
|
|
if (!validationResult.success) {
|
|
console.log('❌ DEX balance validation failed:', validationResult.message)
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: validationResult.error,
|
|
message: validationResult.message,
|
|
validation: validationResult
|
|
}, { status: validationResponse.status })
|
|
}
|
|
|
|
console.log('✅ DEX balance validation passed')
|
|
} catch (validationError) {
|
|
console.error('❌ DEX balance validation error:', validationError)
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'VALIDATION_FAILED',
|
|
message: 'Could not validate wallet balance for DEX trade. Please try again.'
|
|
}, { status: 500 })
|
|
}
|
|
}
|
|
|
|
// For now, simulate the trade until Jupiter integration is fully tested
|
|
if (!useRealDEX) {
|
|
console.log('🎮 Executing SIMULATED trade (real DEX integration available)')
|
|
|
|
// Simulate realistic execution
|
|
const currentPrice = symbol === 'SOL' ? 166.75 : symbol === 'BTC' ? 121819 : 3041.66
|
|
const priceImpact = amount > 10 ? 0.005 : 0.001
|
|
const executedPrice = side === 'BUY'
|
|
? currentPrice * (1 + priceImpact)
|
|
: currentPrice * (1 - priceImpact)
|
|
|
|
// Simulate network delay
|
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
|
|
const result = {
|
|
success: true,
|
|
trade: {
|
|
txId: `sim_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`,
|
|
orderId: `order_${Date.now()}`,
|
|
symbol: symbol.toUpperCase(),
|
|
side: side.toUpperCase(),
|
|
amount: amount,
|
|
executedPrice: executedPrice,
|
|
timestamp: Date.now(),
|
|
status: 'FILLED',
|
|
dex: 'SIMULATION',
|
|
stopLoss: stopLoss,
|
|
takeProfit: takeProfit,
|
|
monitoring: !!(stopLoss || takeProfit)
|
|
},
|
|
message: `${side.toUpperCase()} order for ${amount} ${symbol} simulated at $${executedPrice.toFixed(4)}`
|
|
}
|
|
|
|
if (stopLoss || takeProfit) {
|
|
result.message += ` with TP/SL monitoring`
|
|
}
|
|
|
|
return NextResponse.json(result)
|
|
}
|
|
|
|
// Real DEX execution (Jupiter)
|
|
console.log('🚀 Executing REAL trade on Jupiter DEX')
|
|
|
|
try {
|
|
// Dynamic import to avoid build issues
|
|
const { jupiterDEXService } = await import('../../../../lib/jupiter-dex-service')
|
|
|
|
if (!jupiterDEXService.isConfigured()) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'Jupiter DEX service not configured',
|
|
message: 'Wallet not initialized for real trading'
|
|
}, { status: 503 })
|
|
}
|
|
|
|
const tradeResult = await jupiterDEXService.executeTrade({
|
|
symbol: symbol.toUpperCase(),
|
|
side: side.toUpperCase(),
|
|
amount: parseFloat(amount),
|
|
stopLoss: stopLoss ? parseFloat(stopLoss) : undefined,
|
|
takeProfit: takeProfit ? parseFloat(takeProfit) : undefined,
|
|
tradingPair: tradingPair,
|
|
quickSwap: quickSwap
|
|
})
|
|
|
|
if (!tradeResult.success) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: tradeResult.error || 'Trade execution failed',
|
|
dex: 'JUPITER'
|
|
}, { status: 500 })
|
|
}
|
|
|
|
const tradeResponse = {
|
|
success: true,
|
|
trade: {
|
|
txId: tradeResult.txId,
|
|
orderId: tradeResult.orderId,
|
|
symbol: symbol.toUpperCase(),
|
|
side: side.toUpperCase(),
|
|
amount: amount,
|
|
timestamp: Date.now(),
|
|
status: stopLoss || takeProfit ? 'MONITORING' : 'FILLED',
|
|
dex: 'JUPITER',
|
|
stopLoss: stopLoss,
|
|
takeProfit: takeProfit,
|
|
monitoring: !!(stopLoss || takeProfit)
|
|
},
|
|
message: `${side.toUpperCase()} order executed on Jupiter DEX${stopLoss || takeProfit ? ' with TP/SL monitoring' : ''}`
|
|
}
|
|
|
|
// Add trade to history with clear spot swap indication
|
|
try {
|
|
// Use localhost for internal container communication
|
|
await fetch(`${baseUrl}/api/trading/history`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
action: 'add',
|
|
symbol: tradingPair || `${fromCoin || symbol}/${toCoin || 'USDC'}`,
|
|
side: side.toUpperCase(),
|
|
amount: amount,
|
|
price: 168.1, // Get from actual execution
|
|
type: 'SPOT_SWAP', // Clearly indicate this is a spot swap
|
|
status: 'executed',
|
|
txId: tradeResult.txId,
|
|
dex: 'JUPITER',
|
|
tradingMode: 'SPOT',
|
|
notes: closePosition ? 'Position closing trade' : `Spot swap: ${amount} ${fromCoin || symbol} → ${toCoin || 'USDC'}`
|
|
})
|
|
})
|
|
console.log('✅ Spot trade added to history')
|
|
} catch (error) {
|
|
console.error('❌ Failed to add trade to history:', error)
|
|
}
|
|
|
|
// DON'T create positions for spot swaps - they're instant settlements
|
|
// Only leverage/perpetual trades should create positions
|
|
// For spot trades, we only need trade history entries
|
|
|
|
// Note: Position creation is intentionally removed for spot trades
|
|
// If this trade had stop loss or take profit, those would be handled as separate limit orders
|
|
console.log('✅ Spot trade completed - no position created (instant settlement)')
|
|
|
|
return NextResponse.json(tradeResponse)
|
|
|
|
} catch (error) {
|
|
console.error('❌ Jupiter DEX execution failed:', error)
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'Jupiter DEX execution failed',
|
|
message: error.message,
|
|
dex: 'JUPITER'
|
|
}, { status: 500 })
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ Trade execution API error:', error)
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
error: 'Internal server error',
|
|
message: error.message
|
|
},
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function GET() {
|
|
return NextResponse.json({
|
|
message: 'Enhanced Trading Execute API - Real DEX Integration Available',
|
|
endpoints: {
|
|
POST: '/api/trading/execute-dex - Execute trades on real DEX with TP/SL'
|
|
},
|
|
parameters: {
|
|
symbol: 'string (required) - Trading symbol (SOL, BTC, ETH)',
|
|
side: 'string (required) - BUY or SELL',
|
|
amount: 'number (required) - Amount to trade',
|
|
stopLoss: 'number (optional) - Stop loss price',
|
|
takeProfit: 'number (optional) - Take profit price',
|
|
useRealDEX: 'boolean (optional) - true for Jupiter DEX, false for simulation'
|
|
},
|
|
supportedDEX: ['Jupiter (Solana)', 'Simulation'],
|
|
features: ['Stop Loss Orders', 'Take Profit Orders', 'Real-time Monitoring']
|
|
})
|
|
}
|