/** * CoinRush Wallet Modals V2 - Ultra Premium * Psychology-driven UX with gamification elements * Includes Cryptocurrency Payment Options (Demo Mode) */ const { useState, useEffect, useRef, useCallback, useMemo } = React; // ============================================ // UTILITIES // ============================================ const formatBalance = (cents) => ((cents || 0) / 100).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const formatCurrency = (value) => { const num = parseFloat(value) || 0; return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }; // Bonus tiers for gamification const BONUS_TIERS = [ { min: 0, max: 50, bonus: 0, label: 'No bonus' }, { min: 50, max: 100, bonus: 2, label: '+2% Bonus' }, { min: 100, max: 250, bonus: 5, label: '+5% Bonus' }, { min: 250, max: 500, bonus: 8, label: '+8% Bonus' }, { min: 500, max: Infinity, bonus: 10, label: '+10% Bonus' }, ]; const getBonusTier = (amount) => { return BONUS_TIERS.find(t => amount >= t.min && amount < t.max) || BONUS_TIERS[0]; }; const getNextTier = (amount) => { const currentIndex = BONUS_TIERS.findIndex(t => amount >= t.min && amount < t.max); return BONUS_TIERS[currentIndex + 1] || null; }; // ============================================ // PAYMENT METHODS CONFIGURATION // ============================================ const PAYMENT_METHODS = { crypto: [ { id: 'btc', name: 'Bitcoin', symbol: 'BTC', color: '#F7931A', network: 'Bitcoin Network', minDeposit: 0.0001, confirmations: 3, estimatedTime: '10-60 min', address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', popular: true }, { id: 'eth', name: 'Ethereum', symbol: 'ETH', color: '#627EEA', network: 'ERC-20', minDeposit: 0.001, confirmations: 12, estimatedTime: '2-5 min', address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8EdE8', popular: true }, { id: 'usdt', name: 'Tether', symbol: 'USDT', color: '#26A17B', network: 'TRC-20', networks: ['ERC-20', 'TRC-20', 'BEP-20'], minDeposit: 10, confirmations: 1, estimatedTime: '1-5 min', address: 'TN8sL5FFvJGT3c8PN7V6PPLZ5WVfYqZU3e', popular: true }, { id: 'usdc', name: 'USD Coin', symbol: 'USDC', color: '#2775CA', network: 'ERC-20', networks: ['ERC-20', 'Solana', 'Polygon'], minDeposit: 10, confirmations: 12, estimatedTime: '2-5 min', address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8EdE8' }, { id: 'ltc', name: 'Litecoin', symbol: 'LTC', color: '#BFBBBB', network: 'Litecoin', minDeposit: 0.01, confirmations: 6, estimatedTime: '5-30 min', address: 'LcHK6vhPSr8E8KcexNokiEwQi6hh8Zc8XV' }, { id: 'sol', name: 'Solana', symbol: 'SOL', color: '#9945FF', network: 'Solana', minDeposit: 0.1, confirmations: 1, estimatedTime: '< 1 min', address: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU' }, { id: 'doge', name: 'Dogecoin', symbol: 'DOGE', color: '#C2A633', network: 'Dogecoin', minDeposit: 50, confirmations: 6, estimatedTime: '10-30 min', address: 'DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L' }, { id: 'bnb', name: 'BNB', symbol: 'BNB', color: '#F3BA2F', network: 'BEP-20', minDeposit: 0.01, confirmations: 15, estimatedTime: '2-5 min', address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8EdE8' } ], fiat: [ { id: 'card', name: 'Credit/Debit Card', icon: 'card', fee: '2.5%', instant: true }, { id: 'bank', name: 'Bank Transfer', icon: 'bank', fee: 'Free', instant: false } ] }; // ============================================ // ICONS - Refined SVGs // ============================================ const CloseIcon = () => ( ); const CheckIcon = () => ( ); const ShieldCheckIcon = () => ( ); const LockIcon = () => ( ); const ArrowDownIcon = () => ( ); const ArrowUpIcon = () => ( ); const WalletIcon = () => ( ); const SparklesIcon = () => ( ); const InfoIcon = () => ( ); const SpinnerIcon = ({ size = 24 }) => ( ); const GiftIcon = () => ( ); // Cryptocurrency Icons const BitcoinIcon = ({ size = 24 }) => ( ); const EthereumIcon = ({ size = 24 }) => ( ); const TetherIcon = ({ size = 24 }) => ( ); const UsdcIcon = ({ size = 24 }) => ( ); const LitecoinIcon = ({ size = 24 }) => ( ); const SolanaIcon = ({ size = 24 }) => ( ); const DogeIcon = ({ size = 24 }) => ( ); const BnbIcon = ({ size = 24 }) => ( ); const CardIcon = ({ size = 24 }) => ( ); const BankIcon = ({ size = 24 }) => ( ); const CopyIcon = ({ size = 16 }) => ( ); const QrCodeIcon = ({ size = 20 }) => ( ); // Additional icons for premium UI (replacing emojis) const ShieldIcon = ({ size = 16 }) => ( ); const ZapIcon = ({ size = 16 }) => ( ); const TrendingUpIcon = ({ size = 16 }) => ( ); const AlertTriangleIcon = ({ size = 14 }) => ( ); const StarIcon = ({ size = 16 }) => ( ); const CelebrationIcon = ({ size = 18 }) => ( ); const CoinsIcon = ({ size = 16 }) => ( ); const ClockIcon = ({ size = 16 }) => ( ); // Get crypto icon component const getCryptoIcon = (id, size = 24) => { const icons = { btc: BitcoinIcon, eth: EthereumIcon, usdt: TetherIcon, usdc: UsdcIcon, ltc: LitecoinIcon, sol: SolanaIcon, doge: DogeIcon, bnb: BnbIcon }; const IconComponent = icons[id]; return IconComponent ? : null; }; // ============================================ // 2FA SETUP COMPONENT // ============================================ function TwoFactorSetup({ onSetupComplete, onCancel }) { const [step, setStep] = useState('loading'); const [setupData, setSetupData] = useState(null); const [confirmCode, setConfirmCode] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); const inputRefs = useRef([]); useEffect(() => { initSetup(); }, []); useEffect(() => { if (step === 'setup' && inputRefs.current[0]) { inputRefs.current[0].focus(); } }, [step]); const initSetup = async () => { try { const res = await fetch('/api/2fa/setup', { method: 'POST', credentials: 'include' }); if (!res.ok) throw new Error('Failed to initialize 2FA'); const data = await res.json(); setSetupData(data); setStep('setup'); } catch (err) { setError(err.message); setStep('error'); } }; const handleConfirm = async () => { if (confirmCode.length !== 6) { setError('Enter all 6 digits'); return; } setIsLoading(true); setError(''); try { const res = await fetch('/api/2fa/confirm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ code: confirmCode }) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Invalid code. Please try again.'); setStep('success'); setTimeout(() => onSetupComplete(), 1500); } catch (err) { setError(err.message || 'Invalid code. Please try again.'); setConfirmCode(''); inputRefs.current[0]?.focus(); } finally { setIsLoading(false); } }; const handleCodeInput = (index, value) => { if (!/^\d*$/.test(value)) return; const newCode = confirmCode.split(''); newCode[index] = value; const updatedCode = newCode.join('').slice(0, 6); setConfirmCode(updatedCode); setError(''); if (value && index < 5) inputRefs.current[index + 1]?.focus(); if (updatedCode.length === 6) setTimeout(() => handleConfirm(), 100); }; const handleKeyDown = (index, e) => { if (e.key === 'Backspace' && !confirmCode[index] && index > 0) { inputRefs.current[index - 1]?.focus(); } }; const handlePaste = (e) => { e.preventDefault(); const pasted = e.clipboardData.getData('text').replace(/\D/g, '').slice(0, 6); setConfirmCode(pasted); if (pasted.length === 6) setTimeout(() => handleConfirm(), 100); }; if (step === 'loading') { return (

Setting Up Security

Generating your secure key...

); } if (step === 'error') { return (

Setup Failed

{error}

); } if (step === 'success') { return (

2FA Enabled!

Your account is now protected

); } return (

Enable 2FA Protection

Add an extra layer of security

1

Get Google Authenticator

Download from App Store or Play Store

2

Scan QR Code

Tap + in the app, then scan below

{setupData?.qr_code_data_url ? ( QR Code ) : (
)}
Can't scan? Enter manually {setupData?.secret}
3

Enter 6-Digit Code

From your authenticator app

{[0, 1, 2, 3, 4, 5].map(i => ( inputRefs.current[i] = el} type="text" inputMode="numeric" maxLength={1} value={confirmCode[i] || ''} onChange={e => handleCodeInput(i, e.target.value)} onKeyDown={e => handleKeyDown(i, e)} className={`code-input ${confirmCode.length === 6 ? 'success' : (error ? 'error' : '')}`} disabled={isLoading} /> ))}
{error && confirmCode.length < 6 &&

{error}

}
); } // ============================================ // 2FA VERIFY COMPONENT // ============================================ function TwoFactorVerify({ onVerify, onCancel, isLoading, amount, type = 'withdraw' }) { const [code, setCode] = useState(''); const [error, setError] = useState(''); const inputRefs = useRef([]); useEffect(() => { inputRefs.current[0]?.focus(); }, []); const handleCodeInput = (index, value) => { if (!/^\d*$/.test(value)) return; const newCode = code.split(''); newCode[index] = value; const updatedCode = newCode.join('').slice(0, 6); setCode(updatedCode); setError(''); if (value && index < 5) inputRefs.current[index + 1]?.focus(); if (updatedCode.length === 6) handleSubmit(updatedCode); }; const handleKeyDown = (index, e) => { if (e.key === 'Backspace' && !code[index] && index > 0) { inputRefs.current[index - 1]?.focus(); } }; const handlePaste = (e) => { e.preventDefault(); const pasted = e.clipboardData.getData('text').replace(/\D/g, '').slice(0, 6); setCode(pasted); if (pasted.length === 6) handleSubmit(pasted); }; const handleSubmit = async (codeValue = code) => { if (codeValue.length !== 6) return; try { await onVerify(codeValue); } catch (err) { setError(err.message || 'Invalid code'); setCode(''); inputRefs.current[0]?.focus(); } }; return (

Verify {type === 'deposit' ? 'Deposit' : 'Withdrawal'}

{type === 'deposit' ? 'Depositing' : 'Withdrawing'} ${formatBalance(amount)}

Enter your 6-digit authenticator code

{[0, 1, 2, 3, 4, 5].map(i => ( inputRefs.current[i] = el} type="text" inputMode="numeric" maxLength={1} value={code[i] || ''} onChange={e => handleCodeInput(i, e.target.value)} onKeyDown={e => handleKeyDown(i, e)} className={`code-input ${error ? 'error' : ''}`} disabled={isLoading} /> ))}
{error &&

{error}

}
); } // ============================================ // CRYPTO DEPOSIT VIEW COMPONENT // ============================================ function CryptoDepositView({ crypto, onBack, amount }) { const [copied, setCopied] = useState(false); const [selectedNetwork, setSelectedNetwork] = useState(crypto.network); const handleCopyAddress = () => { navigator.clipboard.writeText(crypto.address); setCopied(true); setTimeout(() => setCopied(false), 2000); }; // Generate simple QR code placeholder (in production, use a real QR library) const generateQRPlaceholder = () => { return (
QR Code
Scan with your wallet
); }; return (
{getCryptoIcon(crypto.id, 48)}

Deposit {crypto.name}

Send {crypto.symbol} to the address below

{crypto.networks && (
{crypto.networks.map(net => ( ))}

Sending assets via wrong network may result in loss of funds

)} {generateQRPlaceholder()}
{crypto.address}
Minimum Deposit {crypto.minDeposit} {crypto.symbol}
Confirmations {crypto.confirmations} blocks
Estimated Time {crypto.estimatedTime}
Network Fee Paid by you
DEMO MODE

This is a demonstration. No real cryptocurrency transactions are processed.

); } // ============================================ // DEPOSIT MODAL // ============================================ function DepositModal({ isOpen, onClose, userBalance, onBalanceUpdate }) { const [step, setStep] = useState('form'); const [paymentType, setPaymentType] = useState('crypto'); // 'crypto' or 'fiat' const [selectedCrypto, setSelectedCrypto] = useState(null); const [pendingCrypto, setPendingCrypto] = useState(null); // Track crypto selection during 2FA flow const [amount, setAmount] = useState(''); const [twoFaStatus, setTwoFaStatus] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [newBalance, setNewBalance] = useState(0); const modalRef = useRef(null); const inputRef = useRef(null); const QUICK_AMOUNTS = [10, 25, 50, 100, 250, 500]; const amountNum = parseFloat(amount || 0); const amountInCents = amountNum * 100; const minDeposit = 100; const isValidAmount = amountInCents >= minDeposit; const requires2FA = amountInCents >= 5000; // Bonus tier calculation const currentTier = getBonusTier(amountNum); const nextTier = getNextTier(amountNum); const bonusAmount = (amountNum * currentTier.bonus / 100).toFixed(2); const totalWithBonus = (amountNum + parseFloat(bonusAmount)).toFixed(2); useEffect(() => { if (isOpen) { check2FAStatus(); document.body.style.overflow = 'hidden'; setTimeout(() => inputRef.current?.focus(), 100); } else { document.body.style.overflow = ''; setStep('form'); setPaymentType('crypto'); setSelectedCrypto(null); setPendingCrypto(null); setAmount(''); setError(''); } return () => { document.body.style.overflow = ''; }; }, [isOpen]); const check2FAStatus = async () => { try { const res = await fetch('/api/2fa/status', { credentials: 'include' }); if (res.ok) setTwoFaStatus(await res.json()); } catch (err) { console.error('2FA check failed:', err); } }; const handleBackdropClick = (e) => { if (e.target === modalRef.current) onClose(); }; const handleSelectCrypto = (crypto) => { if (!isValidAmount) { setError('Please enter a valid deposit amount first (minimum $1.00)'); return; } // For crypto deposits >= $50, require 2FA first if (requires2FA) { setPendingCrypto(crypto); if (!twoFaStatus?.enabled) { setStep('2fa-setup'); return; } setStep('2fa-verify'); return; } setSelectedCrypto(crypto); setStep('crypto-deposit'); }; const handleDeposit = () => { if (!isValidAmount) { setError('Minimum deposit is $1.00'); return; } if (requires2FA) { if (!twoFaStatus?.enabled) { setStep('2fa-setup'); return; } setStep('2fa-verify'); return; } processDeposit(); }; const processDeposit = async (twoFaCode = null) => { setStep('processing'); setIsLoading(true); try { const res = await fetch('/api/wallet/deposit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ amount: amountInCents, two_factor_code: twoFaCode }) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Deposit failed'); setNewBalance(data.new_balance); setStep('success'); // Update balance with floating animation (positive delta) if (window.updateCoinRushBalance) { window.updateCoinRushBalance(data.new_balance, amountInCents); } if (typeof window.refreshUserBalance === 'function') window.refreshUserBalance(); } catch (err) { setError(err.message); setStep('error'); } finally { setIsLoading(false); } }; if (!isOpen) return null; // Handle 2FA verification - for crypto, proceed to crypto-deposit; for fiat, process deposit const handleTwoFaVerified = (twoFaCode) => { if (pendingCrypto) { // Crypto deposit - after 2FA, show the crypto deposit view setSelectedCrypto(pendingCrypto); setPendingCrypto(null); setStep('crypto-deposit'); } else { // Fiat deposit - process immediately processDeposit(twoFaCode); } }; const renderContent = () => { switch (step) { case 'crypto-deposit': return ( <>
{ setSelectedCrypto(null); setStep('form'); }} amount={amountNum} />
); case '2fa-setup': return { check2FAStatus(); setStep('2fa-verify'); }} onCancel={() => { setPendingCrypto(null); setStep('form'); }} />; case '2fa-verify': return { setPendingCrypto(null); setStep('form'); }} isLoading={isLoading} type="deposit" />; case 'processing': return (

Processing Deposit

Adding funds to your account...

); case 'success': return (

Deposit Complete!

+${formatCurrency(amount)}

{currentTier.bonus > 0 && (

+${bonusAmount} bonus added!

)}

New balance: ${formatBalance(newBalance)}

); case 'error': return (

Deposit Failed

{error}

); default: return ( <>

Add Funds

Balance: ${formatBalance(userBalance)}
{/* Payment Method Tabs */}
{/* Crypto Payment Options */} {paymentType === 'crypto' && (
{/* Inline Amount Input */}
Amount
$ { setAmount(e.target.value); setError(''); }} min="1" step="1" />
{QUICK_AMOUNTS.map(val => ( ))}
{error &&
{error}
}
{PAYMENT_METHODS.crypto.map(crypto => ( ))}
Secure & Private
Low Fees
Fast Processing
)} {/* Fiat Payment Options */} {paymentType === 'fiat' && ( <>
Amount to Deposit {currentTier.bonus > 0 && ( {currentTier.label} )}
$ { const val = e.target.value.replace(/[^0-9.]/g, ''); if (val.split('.').length <= 2) { setAmount(val); setError(''); } }} className={`amount-input ${amount ? 'has-value' : ''}`} />
Min: $1.00 {currentTier.bonus > 0 && +${bonusAmount} bonus}
{QUICK_AMOUNTS.map(amt => { const tier = getBonusTier(amt); return ( ); })}
{/* Bonus tier progress */} {amountNum >= 10 && nextTier && (
Current Bonus {currentTier.bonus > 0 ? currentTier.label : 'None'}

Deposit ${nextTier.min} to unlock {nextTier.label}

)} {requires2FA && (
{twoFaStatus?.enabled ? '2FA Verification Required' : '2FA Setup Required'}
{twoFaStatus?.enabled ? 'Confirm with your authenticator app' : 'Deposits over $50 require 2FA protection'}
)} {error &&

{error}

} )}

Demo Mode Active

This is a demonstration. No real money transactions are processed.

{paymentType === 'fiat' && (
)} ); } }; return (
{renderContent()}
); } // ============================================ // CRYPTO WITHDRAW VIEW COMPONENT // ============================================ function CryptoWithdrawView({ crypto, onBack, amount, onConfirm, isLoading }) { const [walletAddress, setWalletAddress] = useState(''); const [selectedNetwork, setSelectedNetwork] = useState(crypto.network); const [error, setError] = useState(''); const handleWithdraw = () => { if (!walletAddress || walletAddress.length < 20) { setError('Please enter a valid wallet address'); return; } onConfirm(walletAddress, selectedNetwork); }; return (
{getCryptoIcon(crypto.id, 48)}

Withdraw to {crypto.name}

Enter your {crypto.symbol} wallet address

{crypto.networks && (
{crypto.networks.map(net => ( ))}

Ensure you select the correct network that matches your receiving wallet

)}
{ setWalletAddress(e.target.value); setError(''); }} />
{error &&

{error}

}
Amount ${amount.toFixed(2)} USD
Network Fee ~$2.50 (varies)
You Receive ~${(amount - 2.50).toFixed(2)} in {crypto.symbol}
DEMO MODE

This is a demonstration. No real cryptocurrency will be sent.

); } // ============================================ // WITHDRAW MODAL // ============================================ function WithdrawModal({ isOpen, onClose, userBalance }) { const [step, setStep] = useState('form'); const [paymentType, setPaymentType] = useState('crypto'); const [selectedCrypto, setSelectedCrypto] = useState(null); const [pendingCrypto, setPendingCrypto] = useState(null); // Track crypto selection during 2FA flow const [amount, setAmount] = useState(''); const [twoFaStatus, setTwoFaStatus] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const modalRef = useRef(null); const inputRef = useRef(null); const QUICK_AMOUNTS = [10, 25, 50, 100, 250, 500]; const amountNum = parseFloat(amount || 0); const amountInCents = amountNum * 100; const minWithdraw = 100; const maxWithdraw = userBalance || 0; const isValidAmount = amountInCents >= minWithdraw && amountInCents <= maxWithdraw; const requires2FA = amountInCents >= 5000; // $50+ requires 2FA for all payment types useEffect(() => { if (isOpen) { check2FAStatus(); document.body.style.overflow = 'hidden'; setTimeout(() => inputRef.current?.focus(), 100); } else { document.body.style.overflow = ''; setStep('form'); setPaymentType('crypto'); setSelectedCrypto(null); setPendingCrypto(null); setAmount(''); setError(''); } return () => { document.body.style.overflow = ''; }; }, [isOpen]); const check2FAStatus = async () => { try { const res = await fetch('/api/2fa/status', { credentials: 'include' }); if (res.ok) setTwoFaStatus(await res.json()); } catch (err) { console.error('2FA check failed:', err); } }; const handleBackdropClick = (e) => { if (e.target === modalRef.current) onClose(); }; const handleSelectCrypto = (crypto) => { if (!isValidAmount) { setError('Please enter a valid withdrawal amount first'); return; } // For crypto withdrawals, also check 2FA if amount >= $50 if (requires2FA) { setPendingCrypto(crypto); // Remember which crypto was selected if (!twoFaStatus?.enabled) { setStep('2fa-setup'); return; } setStep('2fa-verify'); return; } setSelectedCrypto(crypto); setStep('crypto-withdraw'); }; const handleWithdraw = () => { if (!isValidAmount) { setError(amountInCents < minWithdraw ? 'Minimum withdrawal is $1.00' : 'Insufficient balance'); return; } if (requires2FA) { if (!twoFaStatus?.enabled) { setStep('2fa-setup'); return; } setStep('2fa-verify'); return; } processWithdrawal(); }; const processWithdrawal = async (twoFaCode = null) => { setStep('processing'); setIsLoading(true); try { const res = await fetch('/api/wallet/withdraw', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ amount: amountInCents, two_factor_code: twoFaCode }) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Withdrawal failed'); setStep('success'); // Update balance with floating animation (negative delta) const newBalance = data.new_balance !== undefined ? data.new_balance : (userBalance - amountInCents); if (window.updateCoinRushBalance) { window.updateCoinRushBalance(newBalance, -amountInCents); } if (typeof window.refreshUserBalance === 'function') window.refreshUserBalance(); } catch (err) { setError(err.message); setStep('error'); } finally { setIsLoading(false); } }; if (!isOpen) return null; // Handle 2FA verification - for crypto, proceed to crypto-withdraw; for fiat, process withdrawal const handleTwoFaVerified = (twoFaCode) => { if (pendingCrypto) { // Crypto withdrawal - after 2FA, show the crypto withdraw view setSelectedCrypto(pendingCrypto); setPendingCrypto(null); setStep('crypto-withdraw'); } else { // Fiat withdrawal - process immediately processWithdrawal(twoFaCode); } }; const renderContent = () => { switch (step) { case '2fa-setup': return { check2FAStatus(); setStep('2fa-verify'); }} onCancel={() => { setPendingCrypto(null); setStep('form'); }} />; case '2fa-verify': return { setPendingCrypto(null); setStep('form'); }} isLoading={isLoading} type="withdraw" />; case 'processing': return (

Processing Withdrawal

Transferring your funds...

); case 'success': return (

Withdrawal Complete!

-${formatCurrency(amount)}

Funds have been transferred successfully

); case 'error': return (

Withdrawal Failed

{error}

); case 'crypto-withdraw': return ( <>
{ setSelectedCrypto(null); setStep('form'); }} amount={amountNum} onConfirm={(address, network) => processWithdrawal()} isLoading={isLoading} />
); default: return ( <>

Withdraw Funds

Available: ${formatBalance(userBalance)}
{/* Amount Section First */}
Amount to Withdraw
$ { const val = e.target.value.replace(/[^0-9.]/g, ''); if (val.split('.').length <= 2) { setAmount(val); setError(''); } }} className={`amount-input ${amount ? 'has-value' : ''}`} />
Min: $1.00 Max: ${formatBalance(maxWithdraw)}
{QUICK_AMOUNTS.filter(a => a * 100 <= maxWithdraw).map(amt => ( ))}
{/* Payment Method Tabs */}
{/* Crypto Withdrawal Options */} {paymentType === 'crypto' && (

Select cryptocurrency to receive

{PAYMENT_METHODS.crypto.map(crypto => ( ))}
{!isValidAmount && amountNum > 0 && (

{amountInCents < minWithdraw ? 'Minimum withdrawal is $1.00' : 'Insufficient balance'}

)}
)} {/* Fiat Withdrawal */} {paymentType === 'fiat' && (

Bank transfers are not yet available. Please use cryptocurrency for withdrawals.

)} {requires2FA && (
{twoFaStatus?.enabled ? '2FA Verification Required' : '2FA Setup Required'}
{twoFaStatus?.enabled ? 'Confirm with your authenticator app' : 'Withdrawals over $50 require 2FA protection'}
)} {error &&

{error}

}

Demo Mode

This is a demonstration. No real money or crypto is being transferred.

); } }; return (
{renderContent()}
); } // ============================================ // GLOBAL MODAL MANAGER // ============================================ function WalletModalsManager() { const [depositOpen, setDepositOpen] = useState(false); const [withdrawOpen, setWithdrawOpen] = useState(false); const [userBalance, setUserBalance] = useState(0); useEffect(() => { window.openDepositModal = () => { fetchBalance(); setDepositOpen(true); }; window.openWithdrawModal = () => { fetchBalance(); setWithdrawOpen(true); }; return () => { window.openDepositModal = null; window.openWithdrawModal = null; }; }, []); const fetchBalance = async () => { try { const res = await fetch('/me', { credentials: 'include' }); if (res.ok) { const data = await res.json(); setUserBalance(data.balance || 0); } } catch (err) { console.error('Balance fetch failed:', err); } }; return ( <> setDepositOpen(false)} userBalance={userBalance} /> setWithdrawOpen(false)} userBalance={userBalance} /> ); } // Auto-mount document.addEventListener('DOMContentLoaded', () => { let container = document.getElementById('wallet-modals-root'); if (!container) { container = document.createElement('div'); container.id = 'wallet-modals-root'; document.body.appendChild(container); } ReactDOM.render(React.createElement(WalletModalsManager), container); }); // Mount if ready if (document.readyState !== 'loading') { setTimeout(() => { let container = document.getElementById('wallet-modals-root'); if (!container) { container = document.createElement('div'); container.id = 'wallet-modals-root'; document.body.appendChild(container); } if (!container.hasChildNodes()) { ReactDOM.render(React.createElement(WalletModalsManager), container); } }, 100); } // Exports window.DepositModal = DepositModal; window.WithdrawModal = WithdrawModal; window.openDepositModal = window.openDepositModal || (() => {}); window.openWithdrawModal = window.openWithdrawModal || (() => {});