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
118 changes: 118 additions & 0 deletions frontend/src/components/RoutingFeeDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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 { useCSRF } from "src/hooks/useCSRF";
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();
const { data: csrf } = useCSRF();

async function updateFee() {
try {
if (!csrf) {
throw new Error("csrf not loaded");
}

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: {
"X-CSRF-Token": csrf,
"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 Forwarding Fee</AlertDialogTitle>
<AlertDialogDescription>
<p className="mb-4">
Adjust the fee you charge for each payment passing through your
channel.{" "}
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
<span className="text-primary font-medium">Current fee:</span>{" "}
{currentFee} sats
</p>
<Label htmlFor="fee" className="block mb-2">
Base Forwarding 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) == 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
61 changes: 4 additions & 57 deletions frontend/src/screens/channels/Channels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ 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";
import { useCSRF } from "../../hooks/useCSRF.ts";

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 @@ -109,51 +109,6 @@ export default function Channels() {
loadNodeStats();
}, [loadNodeStats]);

async function editChannel(channel: Channel) {
try {
if (!csrf) {
throw new Error("csrf not loaded");
}

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: {
"X-CSRF-Token": csrf,
"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 {
if (!csrf) {
Expand Down Expand Up @@ -546,17 +501,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