/** * Automated Profit Withdrawal UI * * Configure automatic withdrawals of trading profits */ 'use client' import { useState, useEffect } from 'react' interface WithdrawalSettings { ENABLE_AUTO_WITHDRAWALS: boolean WITHDRAWAL_INTERVAL_HOURS: number WITHDRAWAL_PROFIT_PERCENT: number MIN_WITHDRAWAL_AMOUNT: number MIN_ACCOUNT_BALANCE: number WITHDRAWAL_DESTINATION: string LAST_WITHDRAWAL_TIME: string | null TOTAL_WITHDRAWN: number } interface AccountStats { currentBalance: number totalInvested: number totalPnL: number totalWithdrawn: number availableProfit: number nextWithdrawalAmount: number nextWithdrawalTime: string | null } export default function WithdrawalsPage() { const [settings, setSettings] = useState(null) const [stats, setStats] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [withdrawing, setWithdrawing] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null) useEffect(() => { loadSettings() loadStats() }, []) const loadSettings = async () => { try { const response = await fetch('/api/withdrawals/settings') const data = await response.json() setSettings(data.settings) setLoading(false) } catch (error) { setMessage({ type: 'error', text: 'Failed to load withdrawal settings' }) setLoading(false) } } const loadStats = async () => { try { const response = await fetch('/api/withdrawals/stats') const data = await response.json() setStats(data) } catch (error) { console.error('Failed to load stats:', error) } } const saveSettings = async () => { setSaving(true) setMessage(null) try { const response = await fetch('/api/withdrawals/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings), }) const data = await response.json() if (data.success) { setMessage({ type: 'success', text: 'Withdrawal settings saved!' }) loadStats() // Reload stats to show updated next withdrawal } else { setMessage({ type: 'error', text: data.error || 'Failed to save settings' }) } } catch (error) { setMessage({ type: 'error', text: 'Network error' }) } setSaving(false) } const triggerManualWithdrawal = async () => { if (!confirm('Withdraw profits now? This will withdraw based on your configured percentage.')) { return } setWithdrawing(true) setMessage(null) try { const response = await fetch('/api/withdrawals/execute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, }) const data = await response.json() if (data.success) { setMessage({ type: 'success', text: `Withdrawal successful! ${data.amount} USDC sent to your wallet. TX: ${data.signature.substring(0, 8)}...` }) loadStats() loadSettings() } else { setMessage({ type: 'error', text: data.error || 'Withdrawal failed' }) } } catch (error) { setMessage({ type: 'error', text: 'Network error' }) } setWithdrawing(false) } if (loading || !settings || !stats) { return (
Loading...
) } return (
{/* Header */}

💰 Automated Withdrawals

Configure automatic profit withdrawal from your trading account

{/* Message */} {message && (
{message.text}
)} {/* Account Stats */}

📊 Account Statistics

Current Balance
${stats.currentBalance.toFixed(2)}
Total Invested
${stats.totalInvested.toFixed(2)}
= 0 ? 'bg-green-500/20 border-green-500/30' : 'bg-red-500/20 border-red-500/30' } rounded-xl p-6 border`}>
= 0 ? 'text-green-300' : 'text-red-300'} text-sm mb-2`}> Trading P&L
${stats.totalPnL >= 0 ? '+' : ''}{stats.totalPnL.toFixed(2)}
Available Profit
${stats.availableProfit.toFixed(2)}
(Balance - Total Invested)
Total Withdrawn
${stats.totalWithdrawn.toFixed(2)}
{stats.nextWithdrawalAmount > 0 && settings.ENABLE_AUTO_WITHDRAWALS && (
Next Scheduled Withdrawal
${stats.nextWithdrawalAmount.toFixed(2)}
{stats.nextWithdrawalTime && (
Next withdrawal: {new Date(stats.nextWithdrawalTime).toLocaleString()}
)}
)}
{/* Withdrawal Settings */}

âš™ī¸ Withdrawal Configuration

{/* Enable Auto Withdrawals */}
Enable Automatic Withdrawals
Automatically withdraw profits on schedule
{/* Withdrawal Interval */}
setSettings({ ...settings, WITHDRAWAL_INTERVAL_HOURS: parseFloat(e.target.value) })} className="flex-1 bg-white/10 border border-white/30 rounded-xl px-4 py-3 text-white text-lg" min="1" step="1" />
{settings.WITHDRAWAL_INTERVAL_HOURS === 24 ? '(Daily)' : settings.WITHDRAWAL_INTERVAL_HOURS === 168 ? '(Weekly)' : settings.WITHDRAWAL_INTERVAL_HOURS === 720 ? '(Monthly)' : `(Every ${settings.WITHDRAWAL_INTERVAL_HOURS}h)`}
{/* Profit Percentage */}
setSettings({ ...settings, WITHDRAWAL_PROFIT_PERCENT: parseFloat(e.target.value) })} className="flex-1 bg-white/10 border border-white/30 rounded-xl px-4 py-3 text-white text-lg" min="0" max="100" step="5" />
{settings.WITHDRAWAL_PROFIT_PERCENT}%
Withdraw {settings.WITHDRAWAL_PROFIT_PERCENT}% of available profit. {stats.availableProfit > 0 && ( {' '}Next withdrawal: ${(stats.availableProfit * settings.WITHDRAWAL_PROFIT_PERCENT / 100).toFixed(2)} )}
setSettings({ ...settings, WITHDRAWAL_PROFIT_PERCENT: parseFloat(e.target.value) })} className="w-full mt-3" min="0" max="100" step="5" />
{/* Minimum Withdrawal Amount */}
setSettings({ ...settings, MIN_WITHDRAWAL_AMOUNT: parseFloat(e.target.value) })} className="w-full bg-white/10 border border-white/30 rounded-xl px-4 py-3 text-white text-lg" min="0" step="10" />
Skip withdrawal if amount is below this threshold
{/* Minimum Account Balance */}
setSettings({ ...settings, MIN_ACCOUNT_BALANCE: parseFloat(e.target.value) })} className="w-full bg-white/10 border border-white/30 rounded-xl px-4 py-3 text-white text-lg" min="0" step="50" />
Never withdraw if it would drop account below this amount (safety buffer)
{/* Destination Wallet */}
setSettings({ ...settings, WITHDRAWAL_DESTINATION: e.target.value })} className="w-full bg-white/10 border border-white/30 rounded-xl px-4 py-3 text-white text-sm font-mono" placeholder="Your Solana wallet address" readOnly />
Using wallet from bot configuration. To change, update WALLET_PUBLIC_KEY in .env
{/* Action Buttons */}
{/* Warning */} {stats.availableProfit <= 0 && (
âš ī¸ No profits available for withdrawal. Current P&L: ${stats.totalPnL.toFixed(2)}
)} {/* Info Box */}

â„šī¸ How It Works

  • ✅ Available Profit = Current Balance - Total Invested
  • ✅ Withdrawal Amount = Available Profit × Percentage
  • ✅ Runs automatically on schedule when enabled
  • ✅ Skips withdrawal if amount below minimum threshold
  • ✅ Never withdraws if it would drop account below minimum balance
  • ✅ Telegram notifications for all withdrawals
  • ✅ Full transaction history tracked in database
{/* Back Link */}
← Back to Dashboard
) }