import { ArrowDownUpIcon, ClipboardPasteIcon, ClockIcon, CopyIcon, MoveRightIcon, XCircleIcon, } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import AppHeader from "src/components/AppHeader"; import { CurrencyInputField } from "src/components/CurrencyInputField"; import ExternalLink from "src/components/ExternalLink"; import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount"; import Loading from "src/components/Loading"; import PasswordInput from "src/components/password/PasswordInput"; import ResponsiveLinkButton from "src/components/ResponsiveLinkButton"; import { AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "src/components/ui/alert-dialog"; import { Button } from "src/components/ui/button"; import { LoadingButton } from "src/components/ui/custom/loading-button"; import { Input } from "src/components/ui/input"; import { Label } from "src/components/ui/label"; import { RadioGroup, RadioGroupItem } from "src/components/ui/radio-group"; import { useAutoSwapsConfig, useSwapInfo } from "src/hooks/useSwaps"; import { copyToClipboard } from "src/lib/clipboard"; import { AutoSwapConfig, AutoSwapRequest } from "src/types"; import { request } from "src/utils/request"; export default function AutoSwap() { const { data: swapConfig } = useAutoSwapsConfig(); if (!swapConfig) { return ; } return (
} />
{swapConfig.enabled ? ( ) : ( )}
); } function AutoSwapOutForm() { const { mutate } = useAutoSwapsConfig(); const { data: swapInfo } = useSwapInfo("out"); const [isInternalSwap, setInternalSwap] = useState(true); const [balanceThresholdSat, setBalanceThresholdSat] = useState(""); const [swapAmountSat, setSwapAmountSat] = useState(""); const [destination, setDestination] = useState(""); const [externalType, setExternalType] = useState<"address" | "xpub">( "address" ); const [unlockPassword, setUnlockPassword] = useState(""); const [showUnlockPasswordDialog, setShowUnlockPasswordDialog] = useState(false); const [loading, setLoading] = useState(false); const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (Number(swapAmountSat) > Number(balanceThresholdSat)) { toast.info( "Balance threshold must be greater than or equal to swap amount" ); return; } if (externalType === "xpub" && !isInternalSwap) { setShowUnlockPasswordDialog(true); return; } await submitAutoSwap(); }; const onConfirmUnlockPassword = async (e: React.FormEvent) => { e.preventDefault(); await submitAutoSwap(unlockPassword); }; const submitAutoSwap = async (password?: string) => { try { setLoading(true); const payload: AutoSwapRequest = { swapAmountSat: parseInt(swapAmountSat), balanceThresholdSat: parseInt(balanceThresholdSat), destination, destinationType: !isInternalSwap ? externalType : undefined, unlockPassword: password, }; await request("/api/autoswap", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }); setUnlockPassword(""); setShowUnlockPasswordDialog(false); toast("Auto swap enabled successfully"); await mutate(); } catch (error) { toast("Failed to save auto swap settings", { description: (error as Error).message, }); } finally { setLoading(false); } }; const paste = async () => { const text = await navigator.clipboard.readText(); setDestination(text.trim()); }; if (!swapInfo) { return ; } return ( <>

Lightning On-chain

Setup automatic swap of lightning funds into your on-chain balance every time a set threshold is reached.

Swaps will be made once per hour

{ setDestination(""); setInternalSwap(!isInternalSwap); }} className="flex gap-4 flex-row" >
{!isInternalSwap && (
{ setExternalType(value as "address" | "xpub"); setDestination(""); }} className="flex gap-4 flex-row" >

Send to the same address each time

Generate new addresses from extended public key

setDestination(e.target.value)} required />

{externalType === "address" ? "Enter a Bitcoin address to receive swapped funds" : "Enter an XPUB to automatically generate new addresses for each swap. You will be asked to enter your unlock password to encrypt it safely"}

)}

{swapInfo.albyServiceFee + swapInfo.boltzServiceFee}% + on-chain fees

Begin Auto Swap

powered by{" "} boltz.exchange

{ setShowUnlockPasswordDialog(open); if (!open) { setUnlockPassword(""); } }} >
Confirm Auto Swap Setup

Please enter your unlock password to encrypt and securely store the XPUB.

Cancel
); } function ActiveSwapOutConfig({ swapConfig }: { swapConfig: AutoSwapConfig }) { const { mutate } = useAutoSwapsConfig(); const { data: swapInfo } = useSwapInfo("out"); const [loading, setLoading] = useState(false); const onDeactivate = async () => { try { setLoading(true); await request(`/api/autoswap`, { method: "DELETE", headers: { "Content-Type": "application/json", }, }); toast("Deactivated auto swap successfully"); await mutate(); } catch (error) { toast.error("Deactivating auto swaps failed", { description: (error as Error).message, }); } finally { setLoading(false); } }; return ( <>

Active Lightning On-chain Swap

Alby Hub will try to perform a swap every time the balance reaches the threshold.

Swaps will be made once per hour

Type Lightning to On-chain
Destination
{swapConfig.destination || "On-chain Balance"}
{swapConfig.destination && ( copyToClipboard(swapConfig.destination)} /> )}
Lightning balance threshold
Swap amount
Fee {swapInfo ? ( {swapInfo.albyServiceFee + swapInfo.boltzServiceFee}% + on-chain fees ) : ( )}
); }