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 (
<>
{
setShowUnlockPasswordDialog(open);
if (!open) {
setUnlockPassword("");
}
}}
>
>
);
}
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
) : (
)}
>
);
}