Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: edit routing fee dialog #427

Merged
merged 14 commits into from
Aug 21, 2024
Merged
112 changes: 112 additions & 0 deletions frontend/src/components/RoutingFeeDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { ExternalLinkIcon } from "lucide-react";
import React from "react";
import ExternalLink from "src/components/ExternalLink";
import { Input } from "src/components/ui/input";
import { Label } from "src/components/ui/label";
import { toast } from "src/components/ui/use-toast";
import { useChannels } from "src/hooks/useChannels";
import { Channel, UpdateChannelRequest } from "src/types";
import { request } from "src/utils/request";
import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "./ui/alert-dialog";

type Props = {
channel: Channel;
};

export function RoutingFeeDialog({ channel }: Props) {
const currentFee: number = React.useMemo(() => {
return Math.floor(channel.forwardingFeeBaseMsat / 1000);
}, [channel.forwardingFeeBaseMsat]);
const [forwardingFee, setForwardingFee] = React.useState(
currentFee ? currentFee.toString() : ""
);
const { mutate: reloadChannels } = useChannels();

async function updateFee() {
try {
const forwardingFeeBaseMsat = +forwardingFee * 1000;

console.info(
`🎬 Updating channel ${channel.id} with ${channel.remotePubkey}`
);

await request(
`/api/peers/${channel.remotePubkey}/channels/${channel.id}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
forwardingFeeBaseMsat: forwardingFeeBaseMsat,
} as UpdateChannelRequest),
}
);

await reloadChannels();
toast({ title: "Sucessfully updated channel" });
} catch (error) {
console.error(error);
toast({
variant: "destructive",
description: "Something went wrong: " + error,
});
}
}

return (
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Update Channel Routing Fee</AlertDialogTitle>
<AlertDialogDescription>
<p className="mb-4">
Adjust the fee you charge for each payment passing through your
channel. A high fee (e.g. 100,000 sats) can be set to prevent
unwanted routing.{" "}
<span className="text-primary font-medium">Current fee:</span>{" "}
{currentFee} sats
</p>
<Label htmlFor="fee" className="block mb-2">
Routing Fee (sats)
</Label>
<Input
id="fee"
name="fee"
type="number"
required
autoFocus
min={0}
value={forwardingFee}
onChange={(e) => {
setForwardingFee(e.target.value.trim());
}}
/>
<ExternalLink
to="https://guides.getalby.com/user-guide/v/alby-account-and-browser-extension/alby-hub/faq-alby-hub/how-can-i-change-routing-fees#understanding-routing-fees-and-alby-hub"
className="underline flex items-center mt-4"
>
Learn more about routing fees
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
<ExternalLinkIcon className="w-4 h-4 ml-2" />
</ExternalLink>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
disabled={(parseInt(forwardingFee) || 0) == currentFee}
onClick={updateFee}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
);
}
35 changes: 25 additions & 10 deletions frontend/src/components/channels/ChannelDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {
MoreHorizontal,
Trash2,
} from "lucide-react";
import React from "react";
import { CloseChannelDialog } from "src/components/CloseChannelDialog";
import ExternalLink from "src/components/ExternalLink";
import { RoutingFeeDialog } from "src/components/RoutingFeeDialog";
import {
AlertDialog,
AlertDialogTrigger,
Expand All @@ -22,14 +24,19 @@ import { Channel } from "src/types";
type ChannelDropdownMenuProps = {
alias: string;
channel: Channel;
editChannel(channel: Channel): void;
};

export function ChannelDropdownMenu({
alias,
channel,
editChannel,
}: ChannelDropdownMenuProps) {
const [dialog, setDialog] = React.useState<"close" | "routingFee" | null>(
null
);

const openCloseDialog = () => setDialog("close");
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
const openRoutingFeeDialog = () => setDialog("routingFee");

return (
<AlertDialog>
<DropdownMenu>
Expand Down Expand Up @@ -58,23 +65,31 @@ export function ChannelDropdownMenu({
</ExternalLink>
</DropdownMenuItem>
{channel.public && (
<AlertDialogTrigger asChild>
<DropdownMenuItem
className="flex flex-row items-center gap-2 cursor-pointer"
onClick={openRoutingFeeDialog}
>
<HandCoins className="h-4 w-4" />
Set Routing Fee
</DropdownMenuItem>
</AlertDialogTrigger>
)}
<AlertDialogTrigger asChild>
<DropdownMenuItem
className="flex flex-row items-center gap-2 cursor-pointer"
onClick={() => editChannel(channel)}
onClick={openCloseDialog}
>
<HandCoins className="h-4 w-4" />
Set Routing Fee
</DropdownMenuItem>
)}
<AlertDialogTrigger asChild>
<DropdownMenuItem className="flex flex-row items-center gap-2 cursor-pointer">
<Trash2 className="h-4 w-4 text-destructive" />
Close Channel
</DropdownMenuItem>
</AlertDialogTrigger>
</DropdownMenuContent>
</DropdownMenu>
<CloseChannelDialog alias={alias} channel={channel} />
{dialog === "close" && (
<CloseChannelDialog alias={alias} channel={channel} />
)}
{dialog === "routingFee" && <RoutingFeeDialog channel={channel} />}
</AlertDialog>
);
}
13 changes: 2 additions & 11 deletions frontend/src/components/channels/ChannelsCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,9 @@ import { Channel, Node } from "src/types";
type ChannelsCardsProps = {
channels?: Channel[];
nodes?: Node[];
editChannel(channel: Channel): void;
};

export function ChannelsCards({
channels,
nodes,
editChannel,
}: ChannelsCardsProps) {
export function ChannelsCards({ channels, nodes }: ChannelsCardsProps) {
if (!channels?.length) {
return null;
}
Expand Down Expand Up @@ -61,11 +56,7 @@ export function ChannelsCards({
<div className="flex-1 whitespace-nowrap text-ellipsis overflow-hidden">
{alias}
</div>
<ChannelDropdownMenu
alias={alias}
channel={channel}
editChannel={editChannel}
/>
<ChannelDropdownMenu alias={alias} channel={channel} />
</div>
</CardTitle>
<Separator className="mt-5" />
Expand Down
13 changes: 2 additions & 11 deletions frontend/src/components/channels/ChannelsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ import { ChannelDropdownMenu } from "./ChannelDropdownMenu";
type ChannelsTableProps = {
channels?: Channel[];
nodes?: Node[];
editChannel(channel: Channel): void;
};

export function ChannelsTable({
channels,
nodes,
editChannel,
}: ChannelsTableProps) {
export function ChannelsTable({ channels, nodes }: ChannelsTableProps) {
if (channels && !channels.length) {
return null;
}
Expand Down Expand Up @@ -157,11 +152,7 @@ export function ChannelsTable({
<ChannelWarning channel={channel} />
</TableCell>
<TableCell>
<ChannelDropdownMenu
alias={alias}
channel={channel}
editChannel={editChannel}
/>
<ChannelDropdownMenu alias={alias} channel={channel} />
</TableCell>
</TableRow>
);
Expand Down
56 changes: 4 additions & 52 deletions frontend/src/screens/channels/Channels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ import { useRedeemOnchainFunds } from "src/hooks/useRedeemOnchainFunds.ts";
import { useSyncWallet } from "src/hooks/useSyncWallet.ts";
import { copyToClipboard } from "src/lib/clipboard.ts";
import { cn } from "src/lib/utils.ts";
import { Channel, Node, UpdateChannelRequest } from "src/types";
import { Channel, Node } from "src/types";
import { request } from "src/utils/request";

export default function Channels() {
useSyncWallet();
const { data: channels, mutate: reloadChannels } = useChannels();
const { data: channels } = useChannels();
const { data: nodeConnectionInfo } = useNodeConnectionInfo();
const { data: balances } = useBalances();
const { data: albyBalance, mutate: reloadAlbyBalance } = useAlbyBalance();
Expand Down Expand Up @@ -108,46 +108,6 @@ export default function Channels() {
loadNodeStats();
}, [loadNodeStats]);

async function editChannel(channel: Channel) {
try {
const forwardingFeeBaseSats = prompt(
"Enter base forwarding fee in sats",
Math.floor(channel.forwardingFeeBaseMsat / 1000).toString()
);

if (!forwardingFeeBaseSats) {
return;
}

const forwardingFeeBaseMsat = +forwardingFeeBaseSats * 1000;

console.info(
`🎬 Updating channel ${channel.id} with ${channel.remotePubkey}`
);

await request(
`/api/peers/${channel.remotePubkey}/channels/${channel.id}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
forwardingFeeBaseMsat: forwardingFeeBaseMsat,
} as UpdateChannelRequest),
}
);
await reloadChannels();
toast({ title: "Sucessfully updated channel" });
} catch (error) {
console.error(error);
toast({
variant: "destructive",
description: "Something went wrong: " + error,
});
}
}

async function resetRouter() {
try {
const key = prompt(
Expand Down Expand Up @@ -530,17 +490,9 @@ export default function Channels() {
)}

{isDesktop ? (
<ChannelsTable
channels={channels}
nodes={nodes}
editChannel={editChannel}
/>
<ChannelsTable channels={channels} nodes={nodes} />
) : (
<ChannelsCards
channels={channels}
nodes={nodes}
editChannel={editChannel}
/>
<ChannelsCards channels={channels} nodes={nodes} />
)}
</>
);
Expand Down
17 changes: 17 additions & 0 deletions lnclient/lnd/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,22 @@ func (svc *LNDService) ListChannels(ctx context.Context) ([]lnclient.Channel, er
channelOpeningBlockHeight := lndChannel.ChanId >> 40
confirmations := nodeInfo.BlockHeight - uint32(channelOpeningBlockHeight)

var forwardingFee uint32
if !lndChannel.Private {
channelEdge, err := svc.client.GetChanInfo(ctx, &lnrpc.ChanInfoRequest{
ChanId: lndChannel.ChanId,
})
if err != nil {
return nil, err
}

if channelEdge.Node1Pub == nodeInfo.IdentityPubkey {
forwardingFee = uint32(channelEdge.Node1Policy.FeeBaseMsat)
} else {
forwardingFee = uint32(channelEdge.Node2Policy.FeeBaseMsat)
}
}

channels[i] = lnclient.Channel{
InternalChannel: lndChannel,
LocalBalance: lndChannel.LocalBalance * 1000,
Expand All @@ -191,6 +207,7 @@ func (svc *LNDService) ListChannels(ctx context.Context) ([]lnclient.Channel, er
UnspendablePunishmentReserve: lndChannel.LocalConstraints.ChanReserveSat,
CounterpartyUnspendablePunishmentReserve: lndChannel.RemoteConstraints.ChanReserveSat,
IsOutbound: lndChannel.Initiator,
ForwardingFeeBaseMsat: forwardingFee,
}
}

Expand Down
Loading