React 18 + Vite frontend for Crypnight chess puzzle platform with Phantom wallet integration, real-time WebSocket gameplay, and responsive chessboard UI.
The frontend provides:
- Authentication: Phantom wallet connection via Supabase
- Solo Mode: Timer-based puzzle solving with streak tracking
- Duel Mode: Real-time matchmaking, tier selection, stake confirmation, per-player independent puzzles
- Game UI: React Chessboard with move validation, turn indicators, lives display
- WebSocket: Real-time opponent moves, puzzle progression, game state sync
- Framework: React 18, Vite
- UI Library: TailwindCSS, Lucide React icons
- Chess: chess.js for validation, react-chessboard for board rendering
- HTTP: Axios
- Wallet: Phantom wallet adapter for Solana
- Node.js 18+
- Phantom wallet (browser extension)
- Backend running on localhost:5000
npm install
npm run devApp runs on http://localhost:5173.
VITE_BACKEND_PORT=5000
VITE_DUEL_ESCROW_PDA=8iXhZUk7ZdumVE8aWiHqmMnDHdTtTbjj8YRmiAf4vdKxsrc/
├── components/
│ ├── gameModes/
│ │ ├── Solo.jsx # Solo mode UI and gameplay
│ │ ├── Duel.jsx # Duel mode UI and gameplay
│ │ └── Dashboard.jsx # Stats and mode selection
│ ├── auth/
│ │ ├── LoginPage.jsx # Phantom wallet connection
│ │ └── ProtectedRoute.jsx # Auth guard wrapper
│ └── common/
│ ├── Navbar.jsx # Top navigation
│ └── LoadingSpinner.jsx # Loading states
├── hooks/
│ ├── useDuelSocket.js # WebSocket duel connection and messaging
│ ├── useAuth.js # Authentication state
│ └── useLocalStorage.js # Persist user preferences
├── context/
│ ├── AuthContext.jsx # Global auth state
├── api/
│ ├── axios.js # API client with auth headers
├── App.jsx # Main router and layout
└── main.jsx # Entry point
Complete duel flow with state machine:
- tier_select: User chooses stake tier (Beginner → Grandmaster)
- queuing: Waiting for opponent match
- match_found: Opponent details shown, "Confirm & Stake" button
- waiting_both: Both players' deposit status, "Start Duel" button
- game: Real-time chess gameplay with 3-minute timer
- ended: Results, stats, and payout confirmation
Features:
- Tier-based stakes: 0.05 – 0.50 SOL
- Phantom wallet integration for stake confirmation
- Per-player lives display (3 circles, green=alive, gray=used)
- Turn indicator (white dot/black dot for whose move)
- Left sidebar: your progress + opponent progress
- Board blur when you run out of lives
- Timer countdown (3 minutes)
Solo mode puzzle racing:
- Puzzle rating selection (100–2800 ELO)
- 3-minute timer per session
- Streak tracking and reward multiplier
- Real-time puzzle progression
- Magic Block integration for settlement
WebSocket hook managing duel connection and all game messaging:
const {
socket,
joinQueue, // (tier) → queue:join
confirmDeposit, // (matchId, txSig) → deposit:confirm
startDuel, // (matchId) → duel:start
submitMove, // (matchId, move) → move:submit
onMatchFound, // callback(data) → match:found
onDuelStart, // callback(data) → duel:start
onPuzzleSolved, // callback(data) → puzzle:solved
onPuzzleFailed, // callback(data) → puzzle:failed
onOpponentReply, // callback(data) → opponent events
onDuelEnded, // callback(data) → duel:ended
} = useDuelSocket();Queue & Match:
- Select tier → joinQueue(tier)
- Backend matches players, sends match:found
- Show opponent details, "Confirm & Stake" button
Deposit:
- Click "Confirm & Stake" → sendTransaction via Phantom
- confirmDeposit(matchId, txSignature) sent to backend
- When both deposited, both receive both:deposited
- "Start Duel" button enabled
Game:
- startDuel(matchId) → receive puzzle + timer
- Drag pieces or click to move
- submitMove(matchId, move) sent to backend
- Backend validates move against solution
- Correct move: receive next auto-move in solution sequence
- Wrong move: lose 1 life, new puzzle loaded for you only
- 0 lives: board blurs, wait for opponent
- Timer expires or opponent out of lives: game ends
- Settlement called, winner/draw determined
- Only your color pieces draggable (playerColor check)
- No moves if you've used all lives (playerLives <= 0)
- Move validation happens on backend (solution validation)
- Set by puzzle FEN's second token (w=white, b=black)
- Chessboard boardOrientation prop: "white" or "black"
- 3 green circles = all lives remaining
- Grayed out circle = life used
- 0 remaining = elimination (board blurs, "Waiting for opponent")
- Derives from FEN's second token
- Displays colored dot: white or black
- Shows "White to move" or "Black to move"
- Phantom wallet connection sets
auth_tokenhttpOnly cookie - Axios client automatically includes cookie in all requests
| Endpoint | Purpose |
|---|---|
| POST /api/user/register | Create user on first Phantom login |
| GET /api/user/profile | Fetch user stats |
| GET /api/leaderboard | Fetch top 100 players |
| POST /api/duel/settle | Call after game ends (timer or elimination) |
// Auto-connects on mount
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const url = `${protocol}//${window.location.hostname}:5000/ws/duel`;- Open two browsers (or incognito + normal window)
- Both connect Phantom wallet
- Both select same tier (e.g., "Beginner")
- Both click "Confirm & Stake" → approve Phantom transaction
- Both see "Start Duel" button enabled
- Both click "Start Duel" → puzzle loads
- Play moves on both sides
- Verify:
- Opponent's moves auto-play on your board
- Lives decrement on wrong moves
- New puzzle loads after solving
- Board blurs when you reach 0 lives
- Play until both players have solved same number of puzzles
- Let timer expire
- Both should see "Draw" result
- Verify stakes returned to both wallets (settlement endpoint called)
- One player makes 3 wrong moves
- That player reaches 0 lives
- That player's board should blur with "Waiting for opponent to finish"
- Other player continues playing and wins
- Winner sees victory screen
Address: DKoawaEk5pJj1npwNYXeCCPF3Uqzxahokq67NY387qbK
Off-chain puzzle settling via Magic Block.
Address: EzkHbrB8bTWPVevB9X1AySwt2fAtjqEnZkAnihjZfcEc
Treasury: 6kcSoKW35mTzhf8YCyGwwzPZJBMEH7cVNEZQcohZdgJk
Manages duel escrows and settlement.
Address: 8iXhZUk7ZdumVE8aWiHqmMnDHdTtTbjj8YRmiAf4vdKx
Demo escrow for testing duel stakes on Devnet.
npm run devnpm run build
npm run previewDeploy to Vercel or Netlify:
# Vercel
vercel deploy --prodUpdate env vars in hosting platform (VITE_BACKEND_PORT, VITE_DUEL_ESCROW_PDA).
| Issue | Cause | Fix |
|---|---|---|
| Phantom not connecting | Wallet not installed/enabled | Install extension, reload page |
| Can't find opponent | Backend not running or RPC issue | Check backend logs, verify RPC URL |
| Moves not validating | Backend move:submit handler failing | Check backend logs, verify puzzle FEN correct |
| Board not blurring when out of lives | Event not received | Check browser DevTools WebSocket tab |
| Timer not counting down | useEffect not running | Check console for errors in game state |
- Backend README – API and WebSocket protocol
- Contracts README – Solana programs
- Root README – Overview