- Fixed execute-dex API to extract and forward amountUSD parameter from request body - Updated AIAnalysisPanel to pass amountUSD in executeTrade function call - Fixed port references in validation and execute-dex APIs to use current dev server port - Resolves issue where amountUSD was undefined in validation causing incorrect balance calculations - Added comprehensive logging for debugging trade data flow - Tested successfully: 5 USD trade now validates correctly instead of requiring 832.5 USD
164 lines
5.8 KiB
JavaScript
164 lines
5.8 KiB
JavaScript
import { NextResponse } from 'next/server'
|
|
|
|
export async function POST(request) {
|
|
try {
|
|
const body = await request.json()
|
|
const { symbol, side, amount, amountUSD, price, tradingMode = 'SPOT', fromCoin, toCoin } = body
|
|
|
|
console.log(`🔍 Validating trade: ${side} ${amount} ${symbol} (USD: ${amountUSD})`)
|
|
|
|
// Fetch real wallet balance from the wallet API
|
|
let walletBalance
|
|
try {
|
|
const walletResponse = await fetch('http://localhost:3001/api/wallet/balance')
|
|
const walletData = await walletResponse.json()
|
|
|
|
if (walletData.success && walletData.wallet) {
|
|
walletBalance = {
|
|
solBalance: walletData.wallet.solBalance,
|
|
usdValue: walletData.wallet.usdValue,
|
|
positions: walletData.balance.positions || []
|
|
}
|
|
console.log(`✅ Real wallet balance: ${walletBalance.solBalance} SOL ($${walletBalance.usdValue.toFixed(2)})`)
|
|
} else {
|
|
throw new Error('Failed to fetch wallet balance')
|
|
}
|
|
} catch (error) {
|
|
console.log(`⚠️ Failed to fetch real wallet balance, using fallback: ${error.message}`)
|
|
// Fallback to hardcoded values only if API fails
|
|
walletBalance = {
|
|
solBalance: 0.0728,
|
|
usdValue: 12.12,
|
|
positions: [
|
|
{ symbol: 'SOL', amount: 0.0728, price: 166.5 }
|
|
]
|
|
}
|
|
}
|
|
|
|
// Determine required balance for the trade
|
|
let requiredBalance = 0
|
|
let requiredCurrency = ''
|
|
let availableBalance = 0
|
|
|
|
if (tradingMode === 'SPOT') {
|
|
if (side.toUpperCase() === 'BUY') {
|
|
// For BUY orders, use the USD amount directly (not amount * price)
|
|
requiredBalance = amountUSD || (amount * (price || 166.5))
|
|
requiredCurrency = 'USD'
|
|
availableBalance = walletBalance.usdValue
|
|
|
|
console.log(`💰 BUY validation: Need $${requiredBalance} USD, Have $${availableBalance}`)
|
|
} else {
|
|
// For SELL orders, need the actual token amount
|
|
requiredBalance = amount
|
|
requiredCurrency = fromCoin || symbol.replace('USD', '')
|
|
|
|
// Find the token balance
|
|
const tokenPosition = walletBalance.positions.find(pos =>
|
|
pos.symbol === requiredCurrency ||
|
|
pos.symbol === symbol
|
|
)
|
|
|
|
availableBalance = tokenPosition ? tokenPosition.amount : walletBalance.solBalance
|
|
console.log(`💰 SELL validation: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)
|
|
}
|
|
} else if (tradingMode === 'PERP') {
|
|
// For perpetuals, only need margin
|
|
const leverage = 10 // Default leverage
|
|
requiredBalance = (amountUSD || (amount * (price || 166.5))) / leverage
|
|
requiredCurrency = 'USD'
|
|
availableBalance = walletBalance.usdValue
|
|
|
|
console.log(`💰 PERP validation: Need $${requiredBalance} USD margin, Have $${availableBalance}`)
|
|
}
|
|
|
|
console.log(`💰 Balance check: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)
|
|
|
|
// Validate sufficient balance
|
|
if (requiredBalance > availableBalance) {
|
|
const shortfall = requiredBalance - availableBalance
|
|
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'INSUFFICIENT_BALANCE',
|
|
message: `Insufficient balance. Need ${requiredBalance.toFixed(6)} ${requiredCurrency}, have ${availableBalance.toFixed(6)}. Shortfall: ${shortfall.toFixed(6)}`,
|
|
required: requiredBalance,
|
|
available: availableBalance,
|
|
shortfall: shortfall,
|
|
currency: requiredCurrency
|
|
}, { status: 400 })
|
|
}
|
|
|
|
// Validate minimum trade size
|
|
const minTradeUsd = 1.0 // Minimum $1 trade
|
|
const tradeValueUsd = side.toUpperCase() === 'BUY'
|
|
? requiredBalance
|
|
: amount * (price || 166.5)
|
|
|
|
if (tradeValueUsd < minTradeUsd) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'TRADE_TOO_SMALL',
|
|
message: `Trade value too small. Minimum trade: $${minTradeUsd}, your trade: $${tradeValueUsd.toFixed(2)}`,
|
|
minTradeUsd: minTradeUsd,
|
|
tradeValueUsd: tradeValueUsd
|
|
}, { status: 400 })
|
|
}
|
|
|
|
// Validate maximum trade size (safety check)
|
|
const maxTradePercent = 0.95 // Max 95% of balance per trade
|
|
const maxAllowedTrade = availableBalance * maxTradePercent
|
|
|
|
if (requiredBalance > maxAllowedTrade) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'TRADE_TOO_LARGE',
|
|
message: `Trade too large. Maximum allowed: ${maxAllowedTrade.toFixed(6)} ${requiredCurrency} (95% of balance)`,
|
|
maxAllowed: maxAllowedTrade,
|
|
requested: requiredBalance,
|
|
currency: requiredCurrency
|
|
}, { status: 400 })
|
|
}
|
|
|
|
// If we get here, the trade is valid
|
|
return NextResponse.json({
|
|
success: true,
|
|
validation: {
|
|
requiredBalance: requiredBalance,
|
|
availableBalance: availableBalance,
|
|
currency: requiredCurrency,
|
|
tradeValueUsd: tradeValueUsd,
|
|
valid: true
|
|
},
|
|
message: `Trade validation passed: ${side} ${amount} ${symbol}`
|
|
})
|
|
|
|
} catch (error) {
|
|
console.error('❌ Balance validation error:', error)
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: 'VALIDATION_ERROR',
|
|
message: 'Failed to validate trade balance: ' + error.message
|
|
}, { status: 500 })
|
|
}
|
|
}
|
|
|
|
export async function GET() {
|
|
return NextResponse.json({
|
|
message: 'Trade Balance Validation API',
|
|
description: 'Validates if wallet has sufficient balance for proposed trades',
|
|
endpoints: {
|
|
'POST /api/trading/validate': 'Validate trade against wallet balance'
|
|
},
|
|
parameters: {
|
|
symbol: 'Trading symbol (SOL, BTC, etc.)',
|
|
side: 'BUY or SELL',
|
|
amount: 'Trade amount',
|
|
price: 'Trade price (optional, uses current market price)',
|
|
tradingMode: 'SPOT or PERP',
|
|
fromCoin: 'Source currency',
|
|
toCoin: 'Target currency'
|
|
}
|
|
})
|
|
}
|