"use client"; /** * mavi create + 24-word backup + MetaMask-style 6-blank confirm quiz. * All secrets stay in this browser. */ import { buildRecoveryQuiz, createWallet, MAVI_DEFAULT_WORD_COUNT, MAVI_MIN_PASSWORD_LENGTH, MAVI_QUIZ_BLANK_COUNT, quizAnswersMatch, quizProgress, type CreatedWallet, type RecoveryQuizItem, } from "@krypco/mavi-core"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useCallback, useMemo, useState } from "react"; import { MaviWalletLogo } from "@/components/wallet/wallet-logos"; import { Button, buttonVariants } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { ErrorBanner } from "@/components/ui/error-banner"; import { PasswordInput } from "@/components/ui/password-input"; import { saveNewVault, shortAddress } from "@/lib/mavi/storage"; import { cn } from "@/lib/utils"; type Step = "setup" | "backup" | "confirm" | "done"; export function CreateWalletFlow() { const router = useRouter(); const [step, setStep] = useState("setup"); const [password, setPassword] = useState(""); const [password2, setPassword2] = useState(""); const [wallet, setWallet] = useState(null); const [revealed, setRevealed] = useState(false); const [quiz, setQuiz] = useState([]); /** answers keyed by word index in the full phrase */ const [answers, setAnswers] = useState>({}); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const words = useMemo( () => (wallet ? wallet.mnemonic.split(" ") : []), [wallet], ); const setupValid = password.length >= MAVI_MIN_PASSWORD_LENGTH && password === password2; const progress = quizProgress(quiz, answers); const quizComplete = quiz.length > 0 && progress.filled === progress.total; const startQuiz = useCallback((mnemonic: string) => { const q = buildRecoveryQuiz(mnemonic, MAVI_QUIZ_BLANK_COUNT); setQuiz(q); setAnswers({}); }, []); const onGenerate = useCallback(() => { setError(null); if (password.length < MAVI_MIN_PASSWORD_LENGTH) { setError( `Password must be at least ${MAVI_MIN_PASSWORD_LENGTH} characters.`, ); return; } if (password !== password2) { setError("Passwords do not match."); return; } try { const w = createWallet(MAVI_DEFAULT_WORD_COUNT); setWallet(w); setRevealed(false); setQuiz([]); setAnswers({}); setStep("backup"); } catch (e) { setError(e instanceof Error ? e.message : "Could not create wallet."); } }, [password, password2]); const goToConfirm = useCallback(() => { if (!wallet) return; setError(null); startQuiz(wallet.mnemonic); setStep("confirm"); }, [startQuiz, wallet]); const onConfirmBackup = useCallback(async () => { if (!wallet) return; setError(null); if (!quizAnswersMatch(quiz, answers)) { setError( "Some words are wrong. Pick the correct word for each blank, or go back and check your list.", ); return; } setBusy(true); try { await saveNewVault(password, wallet); setStep("done"); } catch (e) { setError(e instanceof Error ? e.message : "Could not save wallet."); } finally { setBusy(false); } }, [answers, password, quiz, wallet]); function pickChoice(index: number, word: string) { setAnswers((prev) => ({ ...prev, [index]: word })); setError(null); } return (
{error ? ( {error} ) : null} {step === "setup" ? (
Create mavi
Set a password that locks mavi on{" "} this browser only. You will get a 24-word{" "} recovery phrase — never sent to krypco servers.
Recovery phrase: 24 words . Next you write them down, then confirm{" "} 6 random words (like MetaMask).

Anyone with your 24 words can control this wallet. krypco cannot reset them for you.

) : null} {step === "backup" && wallet ? ( Write down your Secret Recovery Phrase 24 words, in order. Store them offline. Do not email them or post them online. {!revealed ? (

Phrase is hidden until you are ready to write it down.

) : (
    {words.map((w, i) => (
  1. {i + 1}. {w}
  2. ))}
)}
Next you will confirm{" "} 6 random words from this list (different words each time you try).
) : null} {step === "confirm" && wallet && quiz.length > 0 ? ( Confirm Secret Recovery Phrase Select each word in the order it was presented to you.

{progress.filled} of {progress.total}

{quiz.map((q) => { const selected = answers[q.index]; return (

Word #{q.wordNumber}

{q.choices.map((choice) => { const isSelected = selected === choice; return ( ); })}
); })}
) : null} {step === "done" && wallet ? (
mavi is ready
Your wallet is encrypted on this browser. Use your password to unlock later. Keep your paper backup safe.

Address

{wallet.address}

Short form: {shortAddress(wallet.address)}

mavi wallet
) : null}
); } function StepDots({ step }: { step: Step }) { const order: Step[] = ["setup", "backup", "confirm", "done"]; const labels = ["Password", "Write phrase", "Confirm 6", "Ready"]; const idx = order.indexOf(step); return (
    {labels.map((label, i) => (
  1. {i + 1} {label} {i < labels.length - 1 ? ( ) : null}
  2. ))}
); }