import { AlertTriangleIcon, ExternalLinkIcon, EyeIcon, Link2Icon, TriangleAlertIcon, } from "lucide-react"; import React, { useState } from "react"; import { useNavigate, useSearchParams } from "react-router"; import ExternalLink from "src/components/ExternalLink"; import Loading from "src/components/Loading"; import MnemonicDialog from "src/components/mnemonic/MnemonicDialog"; import PasswordInput from "src/components/password/PasswordInput"; import SettingsHeader from "src/components/SettingsHeader"; import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "src/components/ui/alert-dialog"; import { Badge } from "src/components/ui/badge"; import { Button } from "src/components/ui/button"; import { Checkbox } from "src/components/ui/checkbox"; import { toast } from "sonner"; import { LoadingButton } from "src/components/ui/custom/loading-button"; import { Label } from "src/components/ui/label"; import { Separator } from "src/components/ui/separator"; import { UpgradeDialog } from "src/components/UpgradeDialog"; import { useAlbyMe } from "src/hooks/useAlbyMe"; import { useInfo } from "src/hooks/useInfo"; import { useMigrateLDKStorage } from "src/hooks/useMigrateLDKStorage"; import { InfoResponse, MnemonicResponse } from "src/types"; import { request } from "src/utils/request"; export default function Backup() { const { data: info, hasMnemonic, hasChannelManagement, hasNodeBackup, } = useInfo(); const { data: me } = useAlbyMe(); const [unlockPassword, setUnlockPassword] = useState(""); const [decryptedMnemonic, setDecryptedMnemonic] = useState(""); const [loading, setLoading] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false); const navigate = useNavigate(); const onSubmitPassword = async (e: React.FormEvent) => { e.preventDefault(); try { setLoading(true); const result = await request("/api/mnemonic", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ unlockPassword }), }); setDecryptedMnemonic(result?.mnemonic ?? ""); setIsDialogOpen(true); } catch { toast.error("Incorrect password", { description: "Failed to decrypt recovery phrase.", }); } finally { setLoading(false); } }; return ( <> Backup your recovery phrase {hasChannelManagement && " and channel states"}. These backups are for disaster recovery only. {hasNodeBackup && " To migrate your node, please use the migration tool."}{" "} Learn more about backups } />
{hasMnemonic && ( <>

Recovery Phrase

Your recovery phrase is a group of 12 random words that back up your wallet{" "} {info?.backendType === "LDK" ? "on-chain balance" : "balance"} . Using them is the only way to recover access to your wallet on another machine or when you lose your unlock password.

Important If you lose access to your Hub and do not have your recovery phrase, you will lose access to your funds. {info?.backendType === "CASHU" && }

Enter your unlock password to view your recovery phrase.

{!!unlockPassword && (
)}
View Recovery Phrase
)} {hasChannelManagement && (

Channels Backup

Your lightning balance can only be recovered on-chain by closing your lightning channels. In case of recovery of your Alby Hub a request will be sent to your peers to close your existing channels. To recover the funds from these channels, a channel backup needs to be created every time you open a new channel.

Automatic Channels Backup

{info?.albyAccountConnected ? ( Active ) : ( Recommended )}
{info?.albyAccountConnected ? ( <>

Your channel state is backed up automatically after each channel creation. Using an external recovery tool and your recovery phrase, you can recover your funds from channels to your on-chain balance as long as your channel partners are online.

{info?.vssSupported && ( <>

Dynamic Channels Backup With Instant Recovery

{me?.subscription.plan_code && info.ldkVssEnabled ? ( Active ) : ( Alby Cloud )}

When enabled, your channels state is dynamically updated and stored end-to-end encrypted by Alby's Versioned Storage Service. This allows you to recover your lightning balance with your recovery phrase alone, without having to close your channels.

{!info.ldkVssEnabled && (!me?.subscription.plan_code ? ( ) : ( ))} )} ) : (

Link your Alby Account to enable automatic channel backups after each channel creation.

Manual Channels Backup

Active

To backup your channels state manually, without Alby Account linked, follow the {" "} manual backups guide

)}
)} {!hasMnemonic && !hasChannelManagement && !info?.vssSupported && (

No recovery phrase or channel state backup present.

)}
); } type Props = { info: InfoResponse; }; function DynamicChannelsBackupDialog({ info }: Props) { const [open, setOpen] = useState(false); const [searchParams] = useSearchParams(); React.useEffect(() => { if (searchParams.get("dynamic") === "true") { setOpen(true); } }, [searchParams]); const { isMigratingStorage, migrateLDKStorage } = useMigrateLDKStorage(); return ( Enable Dynamic Channels Backup Alby Hub Restart Required

By enabling dynamic channel backups, your channels state is dynamically updated and stored end-to-end encrypted by Alby's Versioned Storage Service. This allows you to recover your lightning balance with your recovery phrase alone, without having to close your channels.

As part of enabling dynamic channels backup your hub will be shut down, and you will need to enter your unlock password to start it again.

Please ensure you have no pending payments or channel closures before continuing.

Cancel migrateLDKStorage("VSS")}> Confirm
); } function CashuMnemonicWarning() { const [mnemonicMatches, setMnemonicMatches] = React.useState(); React.useEffect(() => { (async () => { try { const result: { matches: boolean } | undefined = await request( "/api/command", { method: "POST", body: JSON.stringify({ command: "checkmnemonic" }), headers: { "Content-Type": "application/json", }, } ); setMnemonicMatches(result?.matches); } catch (error) { console.error(error); } })(); }, []); if (mnemonicMatches === undefined) { return ; } if (mnemonicMatches) { return null; } return ( Your Cashu wallet uses a different recovery phrase

Please send your funds to a different wallet, then go to settings{" "} {"->"} debug tools {"->"} execute node command {"->"}{" "} reset. You will then receive a fresh cashu wallet with the correct recovery phrase.

); }