'use client'; import { useState, useEffect } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { getChallenge, verifySignature, getMe, getUserCount, type AuthResponse } from '@/lib/api'; type Asset = 'btc' | 'ltc' | 'xmr' | 'wow' | 'grin'; const COINS: { id: Asset; name: string; icon: string }[] = [ { id: 'btc', name: 'Bitcoin', icon: '/coins/bitcoin.svg' }, { id: 'ltc', name: 'Litecoin', icon: '/coins/litecoin.svg' }, { id: 'xmr', name: 'Monero', icon: '/coins/monero.svg' }, { id: 'wow', name: 'Wownero', icon: '/coins/wownero.svg' }, { id: 'grin', name: 'Grin', icon: '/coins/grin.svg' }, ]; type Step = 'initial' | 'choose-coin' | 'signing' | 'logged-in'; export default function Home() { const [step, setStep] = useState('initial'); const [selectedCoin, setSelectedCoin] = useState(null); const [error, setError] = useState(null); const [user, setUser] = useState(null); const [hasExtension, setHasExtension] = useState(null); const [checkingSession, setCheckingSession] = useState(true); const [userCount, setUserCount] = useState(null); // Check for extension on mount useEffect(() => { const check = () => setHasExtension(!!window.smirk); // Check immediately and after a delay (extension might inject late) check(); const timeout = setTimeout(check, 500); return () => clearTimeout(timeout); }, []); // Fetch user count on mount useEffect(() => { getUserCount() .then((data) => setUserCount(data.count)) .catch(() => {}); // Silently ignore errors }, []); const handleConnect = async () => { if (!window.smirk) { setError('Smirk extension not found'); return; } try { setError(null); // Connect to extension (get public keys) await window.smirk.connect(); setStep('choose-coin'); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to connect'); } }; const handleCoinSelect = async (coin: Asset) => { if (!window.smirk) return; setSelectedCoin(coin); setStep('signing'); setError(null); try { // Get challenge from backend const challenge = await getChallenge(window.location.origin); // Sign with the selected coin const result = await window.smirk.signMessage(challenge.challenge); const sig = result.signatures.find((s) => s.asset === coin); if (!sig) { throw new Error(`No signature found for ${coin}`); } // Verify with backend const auth = await verifySignature(challenge.challenge_id, { asset: coin, signature: sig.signature, public_key: sig.publicKey, }); // Store token and show logged in localStorage.setItem('smirk_token', auth.access_token); localStorage.setItem('smirk_refresh', auth.refresh_token); setUser(auth.user); setStep('logged-in'); } catch (err) { setError(err instanceof Error ? err.message : 'Authentication failed'); setStep('choose-coin'); } }; const handleLogout = () => { localStorage.removeItem('smirk_token'); localStorage.removeItem('smirk_refresh'); setUser(null); setSelectedCoin(null); setStep('initial'); }; // Check for existing session on mount useEffect(() => { const token = localStorage.getItem('smirk_token'); if (token) { // Validate token with /auth/me getMe(token) .then((userData) => { setUser({ id: userData.id, telegram_id: userData.telegram_id, telegram_username: userData.telegram_username, }); setStep('logged-in'); }) .catch(() => { // Token invalid or expired - clear it localStorage.removeItem('smirk_token'); localStorage.removeItem('smirk_refresh'); }) .finally(() => setCheckingSession(false)); } else { setCheckingSession(false); } }, []); // Show loading while checking session if (checkingSession) { return (
); } return (
{/* Logo */}

SMIRK WALLET

Smirk

smirk.ca$h

{/* Only show user count before logging in */} {!user && userCount !== null && userCount > 0 && ( {userCount.toLocaleString()}{' '} {userCount === 1 ? 'smirker' : 'smirkers'} and counting )}
{/* Initial state - Connect button */} {step === 'initial' && (
{hasExtension === false && ( )}
)} {/* Choose coin step */} {step === 'choose-coin' && (

Choose your coin to sign in

Pick your favorite cryptocurrency to authenticate

{COINS.map((coin) => ( ))}
)} {/* Signing step */} {step === 'signing' && (

Signing with {selectedCoin?.toUpperCase()}...

Check your extension

)} {/* Logged in state */} {step === 'logged-in' && user && (

Logged in!

Signed in with {selectedCoin?.toUpperCase()}

User ID

{user.id}

{user.telegram_username && ( <>

Telegram

@{user.telegram_username}

)}
Tips Settings
)} {/* Error display */} {error && (

{error}

)}
); }