import { AlertCircleIcon, AlertTriangleIcon, CopyIcon, ExternalLinkIcon, } from "lucide-react"; import React from "react"; import { toast } from "sonner"; import { SwapAlert } from "src/components/channels/SwapAlert"; import ExternalLink from "src/components/ExternalLink"; import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount"; import { MempoolAlert } from "src/components/MempoolAlert"; import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert"; import { Button } from "src/components/ui/button"; import { Label } from "src/components/ui/label"; import { RadioGroup, RadioGroupItem } from "src/components/ui/radio-group"; import { useBalances } from "src/hooks/useBalances"; import { useChannels } from "src/hooks/useChannels"; import { useInfo } from "src/hooks/useInfo"; import { copyToClipboard } from "src/lib/clipboard"; import { Channel, CloseChannelResponse } from "src/types"; import { request } from "src/utils/request"; import { AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "./ui/alert-dialog"; type Props = { alias: string; channel: Channel; }; export function CloseChannelDialogContent({ alias, channel }: Props) { const [closeType, setCloseType] = React.useState("normal"); const [step, setStep] = React.useState(channel.active ? 2 : 1); const [fundingTxId, setFundingTxId] = React.useState(""); const { data: info } = useInfo(); const { mutate: reloadBalances } = useBalances(); const { data: channels, mutate: reloadChannels } = useChannels(); const onContinue = () => { setStep(step + 1); }; const copy = (text: string) => { copyToClipboard(text); }; async function closeChannel() { try { console.info(`🎬 Closing channel with ${channel.remotePubkey}`); const closeChannelResponse = await request( `/api/peers/${channel.remotePubkey}/channels/${channel.id}?force=${ closeType === "force" }`, { method: "DELETE", headers: { "Content-Type": "application/json", }, } ); if (!closeChannelResponse) { throw new Error("Error closing channel"); } const closedChannel = channels?.find( (c) => c.id === channel.id && c.remotePubkey === channel.remotePubkey ); console.info("Closed channel", closedChannel); if (closedChannel) { setFundingTxId(closedChannel.fundingTxId); setStep(step + 1); } toast("Successfully closed channel"); } catch (error) { console.error(error); toast.error("Something went wrong: " + error); } } return ( {step === 1 && ( <> Are you sure you want to close the channel with {alias}? This channel is inactive. Some channels require up to 6 onchain confirmations before they are usable. Cancel )} {step === 2 && ( <> Are you sure you want to close the channel with {alias}?
Closing this channel will move{" "} {" "} in this channel to your on-chain balance and reduce your receive limit by{" "} .

Node ID

{channel.remotePubkey}

Channel ID

{channel.id}

Cancel )} {step === 3 && ( <> Select mode of channel closure
{closeType === "force" && ( Heads up! Your channel balance will be locked for up to two weeks if you force close )} setCloseType(closeType === "normal" ? "force" : "normal") } className="mt-2" >

Attempt to settle with your channel partner for a quick, low-cost closure. Your funds should be available on-chain within an hour. If your partner is offline or agreement cannot be met, a force closure will be initiated.

You close the channel alone. Your funds may be locked for up to two weeks and may incur higher fees. Only try this if a normal closure does not work.

Learn more about closing channels
Cancel )} {step === 4 && ( <> Channel closed successfully

Funding Transaction Id

{fundingTxId}

{ copy(fundingTxId); }} />
View on Mempool
{ await reloadChannels(); await reloadBalances(); }} > Done )}
); }