RoutingFeeDialogContent.tsx raw
1 import { ExternalLinkIcon } from "lucide-react";
2 import React from "react";
3 import { toast } from "sonner";
4 import ExternalLink from "src/components/ExternalLink";
5 import { Input } from "src/components/ui/input";
6 import { Label } from "src/components/ui/label";
7 import { useChannels } from "src/hooks/useChannels";
8 import { Channel, UpdateChannelRequest } from "src/types";
9 import { request } from "src/utils/request";
10 import {
11 AlertDialogAction,
12 AlertDialogCancel,
13 AlertDialogContent,
14 AlertDialogDescription,
15 AlertDialogFooter,
16 AlertDialogHeader,
17 AlertDialogTitle,
18 } from "./ui/alert-dialog";
19
20 type Props = {
21 channel: Channel;
22 };
23
24 export function RoutingFeeDialogContent({ channel }: Props) {
25 const currentBaseFeeSats: number = Math.floor(
26 channel.forwardingFeeBaseMsat / 1000
27 );
28 const currentFeePPM: number = channel.forwardingFeeProportionalMillionths;
29
30 const [baseFeeSats, setBaseFeeSats] = React.useState(
31 currentBaseFeeSats !== undefined ? currentBaseFeeSats.toString() : ""
32 );
33 const [
34 forwardingFeeProportionalMillionths,
35 setForwardingFeeProportionalMillionths,
36 ] = React.useState(
37 currentFeePPM !== undefined ? currentFeePPM.toString() : ""
38 );
39 const { mutate: reloadChannels } = useChannels();
40
41 async function updateFee() {
42 try {
43 const forwardingFeeBaseMsat = +baseFeeSats * 1000;
44
45 console.info(
46 `🎬 Updating channel ${channel.id} with ${channel.remotePubkey}`
47 );
48
49 await request(
50 `/api/peers/${channel.remotePubkey}/channels/${channel.id}`,
51 {
52 method: "PATCH",
53 headers: {
54 "Content-Type": "application/json",
55 },
56 body: JSON.stringify({
57 forwardingFeeBaseMsat: forwardingFeeBaseMsat,
58 forwardingFeeProportionalMillionths:
59 +forwardingFeeProportionalMillionths,
60 } as UpdateChannelRequest),
61 }
62 );
63
64 await reloadChannels();
65 toast("Successfully updated channel");
66 } catch (error) {
67 console.error(error);
68 toast.error("Something went wrong", {
69 description: "" + error,
70 });
71 }
72 }
73
74 const handleSubmit = (_e: React.FormEvent) => {
75 // NOTE: some weird behavior due to this dialog being triggered from a dropdown
76 //e.preventDefault();
77 updateFee();
78 };
79
80 return (
81 <AlertDialogContent>
82 <AlertDialogHeader>
83 <AlertDialogTitle>Update Channel Routing Fee</AlertDialogTitle>
84 <AlertDialogDescription>
85 <p className="mb-4">
86 Adjust the fee you charge for each payment routed through this
87 channel. A high fee (e.g. 100,000 sats) can be set to prevent
88 unwanted routing. No matter the fee, you can still receive
89 payments.{" "}
90 </p>
91 <form id="routing-fee-form" onSubmit={handleSubmit}>
92 <Label htmlFor="baseFee" className="block mb-2">
93 Base Routing Fee (sats)
94 </Label>
95 <Input
96 id="baseFee"
97 name="baseFee"
98 type="number"
99 required
100 autoFocus
101 min={0}
102 value={baseFeeSats}
103 onChange={(e) => {
104 setBaseFeeSats(e.target.value.trim());
105 }}
106 />
107 <Label htmlFor="ppmFee" className="block mt-4 mb-2">
108 PPM Fee (1 PPM = 1 per 1 million sats)
109 </Label>
110 <Input
111 id="ppmFee"
112 name="ppmFee"
113 type="number"
114 required
115 min={0}
116 value={forwardingFeeProportionalMillionths}
117 onChange={(e) => {
118 setForwardingFeeProportionalMillionths(e.target.value.trim());
119 }}
120 />
121 </form>
122 <ExternalLink
123 to="https://guides.getalby.com/user-guide/alby-hub/faq/how-can-i-change-routing-fees#understanding-routing-fees-and-alby-hub"
124 className="underline flex items-center mt-4"
125 >
126 Learn more about routing fees
127 <ExternalLinkIcon className="size-4 ml-2" />
128 </ExternalLink>
129 </AlertDialogDescription>
130 </AlertDialogHeader>
131 <AlertDialogFooter>
132 <AlertDialogCancel>Cancel</AlertDialogCancel>
133 <AlertDialogAction
134 disabled={
135 (parseInt(baseFeeSats) || 0) === currentBaseFeeSats &&
136 (parseInt(forwardingFeeProportionalMillionths) || 0) ===
137 currentFeePPM
138 }
139 type="submit"
140 form="routing-fee-form"
141 onClick={handleSubmit}
142 >
143 Confirm
144 </AlertDialogAction>
145 </AlertDialogFooter>
146 </AlertDialogContent>
147 );
148 }
149