import { ChevronDownIcon, InfoIcon } from "lucide-react"; import React, { FormEvent } from "react"; import { Link, useNavigate } from "react-router"; import { toast } from "sonner"; import AppHeader from "src/components/AppHeader"; import { ChannelPeerNote } from "src/components/channels/ChannelPeerNote"; import { ChannelPublicPrivateAlert } from "src/components/channels/ChannelPublicPrivateAlert"; import { DuplicateChannelAlert } from "src/components/channels/DuplicateChannelAlert"; import { SwapAlert } from "src/components/channels/SwapAlert"; import ExternalLink from "src/components/ExternalLink"; import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount"; import Loading from "src/components/Loading"; import { MempoolAlert } from "src/components/MempoolAlert"; import { Button } from "src/components/ui/button"; import { Checkbox } from "src/components/ui/checkbox"; import { ExternalLinkButton } from "src/components/ui/custom/external-link-button"; import { LinkButton } from "src/components/ui/custom/link-button"; import { Input } from "src/components/ui/input"; import { Label } from "src/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "src/components/ui/select"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "src/components/ui/tooltip"; import { useChannelPeerSuggestions } from "src/hooks/useChannelPeerSuggestions"; import { useChannels } from "src/hooks/useChannels"; import { useInfo } from "src/hooks/useInfo"; import { cn } from "src/lib/utils"; import useChannelOrderStore from "src/state/ChannelOrderStore"; import { Channel, LightningOrder, Network, NewChannelOrder, RecommendedChannelPeer, } from "src/types"; import LightningNetworkDarkSVG from "public/images/illustrations/lightning-network-dark.svg"; import LightningNetworkLightSVG from "public/images/illustrations/lightning-network-light.svg"; import { LSPTermsDialog } from "src/components/channels/LSPTermsDialog"; import FormattedFiatAmount from "src/components/FormattedFiatAmount"; function getPeerKey(peer: RecommendedChannelPeer) { return JSON.stringify(peer); } export default function IncreaseIncomingCapacity() { const { data: info } = useInfo(); const { data: channels } = useChannels(); if (!info?.network || !channels) { return ; } return ; } function NewChannelInternal({ network, channels, }: { network: Network; channels: Channel[]; }) { const { data: _channelPeerSuggestions, error: channelPeerSuggestionsError } = useChannelPeerSuggestions(); const navigate = useNavigate(); const presetAmounts = [1_000_000, 2_000_000, 3_000_000]; const [order, setOrder] = React.useState>({ paymentMethod: "lightning", status: "pay", amountSat: presetAmounts[0].toString(), prevChannelIds: channels.map((channel) => channel.id), isPublic: !!channels.length && channels.every((channel) => channel.public), }); const [showAdvanced, setShowAdvanced] = React.useState(false); const [selectedPeer, setSelectedPeer] = React.useState< RecommendedChannelPeer | undefined >(); React.useEffect(() => { if (channelPeerSuggestionsError) { toast.error("Failed to load channel suggestions"); navigate("/channels/outgoing"); } }, [channelPeerSuggestionsError, navigate]); const channelPeerSuggestions = React.useMemo(() => { return _channelPeerSuggestions ? [ ..._channelPeerSuggestions.filter( (peer) => peer.paymentMethod === "lightning" && peer.type === "LSPS1" ), ] : undefined; }, [_channelPeerSuggestions]); function setPublic(isPublic: boolean) { setOrder((current) => ({ ...current, isPublic, })); } const setAmountSat = React.useCallback((amountSat: string) => { setOrder((current) => ({ ...current, amountSat, })); }, []); React.useEffect(() => { if (!channelPeerSuggestions) { return; } const recommendedPeer = channelPeerSuggestions.find( (peer) => peer.network === network && peer.paymentMethod === order.paymentMethod ); setSelectedPeer(recommendedPeer); }, [network, order.paymentMethod, channelPeerSuggestions]); React.useEffect(() => { if (selectedPeer) { if ( selectedPeer.paymentMethod === "lightning" && order.paymentMethod === "lightning" ) { setOrder((current) => ({ ...current, lspType: selectedPeer.type, lspIdentifier: selectedPeer.identifier, ...(!selectedPeer.publicChannelsAllowed && { isPublic: false }), })); } } }, [order.paymentMethod, selectedPeer]); // find the best channel partner const okPartners = channelPeerSuggestions?.filter( (partner) => parseInt(order.amountSat || "0") >= partner.minimumChannelSizeSat && parseInt(order.amountSat || "0") <= partner.maximumChannelSizeSat && partner.network === network && partner.paymentMethod === "lightning" && partner.type === "LSPS1" && partner.pubkey && (partner.publicChannelsAllowed || !order.isPublic) && !channels.some((channel) => channel.remotePubkey === partner.pubkey) ); const bestPartner = okPartners?.[0]; function onSubmit(e: FormEvent) { e.preventDefault(); try { const nextOrder: Partial = { ...order }; if (!showAdvanced) { if (!channelPeerSuggestions) { throw new Error("Channel Peer suggestions not loaded"); } if (!channels) { throw new Error("Channels not loaded"); } const amountSat = parseInt(order.amountSat || "0"); if (!amountSat) { throw new Error("No amount set"); } if (!bestPartner) { toast.error("No channel partner found", { description: "No ideal channel partner found. Please choose from the advanced options to continue", }); return; } nextOrder.paymentMethod = "lightning"; if (bestPartner.paymentMethod !== "lightning") { throw new Error("Unexpected partner payment method"); } nextOrder.lspType = bestPartner.type; nextOrder.lspIdentifier = bestPartner.identifier; } useChannelOrderStore.getState().setOrder(nextOrder as NewChannelOrder); navigate("/channels/order"); } catch (error) { toast.error("Something went wrong", { description: "" + error, }); console.error(error); } } if (!channelPeerSuggestions) { return ; } const selectedPartner = showAdvanced ? selectedPeer : bestPartner; const estimatedChannelPrice = selectedPartner?.paymentMethod === "lightning" ? order.amountSat === "1000000" ? selectedPartner["feeTotalSat1m"] : order.amountSat === "2000000" ? selectedPartner["feeTotalSat2m"] : order.amountSat === "3000000" ? selectedPartner["feeTotalSat3m"] : undefined : undefined; return ( <> Open Channel with On-Chain } />

Alby Hub works with selected service providers (LSPs) which provide the best network connectivity and liquidity to receive payments.{" "} Learn more

Configure the amount of receiving capacity you need. You will only pay for the liquidity fee which will be shown in the next step.
{order.amountSat && +order.amountSat < 200_000 && (

For a smooth experience consider a opening a channel of{" "} in size or more.{" "} Learn more

)} { setAmountSat(e.target.value.trim()); }} />
{presetAmounts.map((presetAmountSat) => (
setAmountSat(presetAmountSat.toString())} >
))}
{estimatedChannelPrice && ( {" "} Estimated channel price:{" "} )} {selectedPartner?.paymentMethod === "lightning" && (

You will receive a channel from{" "} {selectedPartner.name} .{" "}

View Terms

} />
)}
{showAdvanced && ( <>
{selectedPeer && (
{selectedPeer.name === "Custom" && ( <>
)}
)}
{order.paymentMethod === "lightning" && ( )}
setPublic(!order.isPublic)} className="mr-2" disabled={selectedPeer && !selectedPeer.publicChannelsAllowed} title={ selectedPeer && !selectedPeer.publicChannelsAllowed ? "This channel partner does not support public channels." : undefined } />

Not recommended for most users.{" "} Learn more

)} {!showAdvanced && ( )} {channels?.some((channel) => channel.public !== !!order.isPublic) && ( )} {selectedPeer?.note && } {showAdvanced && ( )}

Other options

Increase Lightning Balance Buy Bitcoin
); } type NewChannelLightningProps = { order: Partial; setOrder(order: Partial): void; }; function NewChannelLightning(props: NewChannelLightningProps) { if (props.order.paymentMethod !== "lightning") { throw new Error("unexpected payment method"); } return null; }