import { AlertTriangleIcon, ExternalLinkIcon } from "lucide-react"; import React from "react"; import { toast } from "sonner"; import ExternalLink from "src/components/ExternalLink"; import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount"; import Loading from "src/components/Loading"; import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert"; import { LoadingButton } from "src/components/ui/custom/loading-button"; import { Input } from "src/components/ui/input"; import { Label } from "src/components/ui/label"; import { useBalances } from "src/hooks/useBalances"; import { useChannels } from "src/hooks/useChannels"; import { request } from "src/utils/request"; import { AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "./ui/alert-dialog"; type Props = { receiveThroughNodePubkey: string; closeDialog(): void; }; export function RebalanceChannelDialogContent({ receiveThroughNodePubkey, closeDialog, }: Props) { const [amountSat, setAmountSat] = React.useState(""); const { data: channels, mutate: reloadChannels } = useChannels(); const { mutate: reloadBalances } = useBalances(); const [isRebalancing, setRebalancing] = React.useState(false); const inputRef = React.useRef(null); React.useEffect(() => { setTimeout(() => { // for some reason `autoFocus` is not working on this input inputRef.current?.focus(); }, 100); }, [inputRef]); if (!channels) { return ; } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setRebalancing(true); try { if (!channels) { throw new Error("channels not loaded"); } const response = await request<{ totalFeeSat: number }>( `/api/channels/rebalance`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ receiveThroughNodePubkey, amountSat: parseInt(amountSat), }), } ); if (!response) { throw new Error("No rebalance response received"); } await Promise.all([reloadChannels(), reloadBalances()]); toast( "Successfully rebalanced channels. Total fee: " + response.totalFeeSat + " sats" ); closeDialog(); } catch (error) { console.error(error); toast.error("" + error); } setRebalancing(false); } return (
Rebalance In

Rebalance funds from other channels into this channel.

channel.remotePubkey === receiveThroughNodePubkey ) .map((channel) => channel.localSpendableBalanceSat) ) + 1 ) )} max={Math.floor( Math.max( ...channels .filter( (channel) => channel.remotePubkey === receiveThroughNodePubkey ) .map((channel) => channel.remoteBalanceSat) ) )} value={amountSat} onChange={(e) => { setAmountSat(e.target.value.trim()); }} />

Fee: 0.5% {!!amountSat && ( <>  ( ) )}{" "} + routing fees

Learn more about rebalancing between channels Rebalancing is in beta Funds may be rebalanced out of unexpected channels. {channels.filter( (channel) => channel.remotePubkey === receiveThroughNodePubkey ).length > 1 && ( Multiple channels with same counterparty Funds may be rebalanced to an unexpected channel. )} {channels.some( (channel) => channel.remotePubkey !== receiveThroughNodePubkey && channel.localSpendableBalanceMsat < (channels.find( (other) => other.remotePubkey === receiveThroughNodePubkey )?.localSpendableBalanceMsat || 0) ) && ( You have another channel with less funds Consider choosing a channel with less local balance to rebalance into. )}
Cancel Confirm
); }