Skip to content
84 changes: 84 additions & 0 deletions frontend/src/components/FeeRateField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { AlertTriangleIcon, ExternalLinkIcon } from "lucide-react";
import ExternalLink from "src/components/ExternalLink";
import { Button } from "src/components/ui/button";
import { Input } from "src/components/ui/input";
import { Label } from "src/components/ui/label";

type RecommendedFees = {
fastestFee: number;
halfHourFee: number;
economyFee: number;
minimumFee: number;
};

type FeeRateFieldProps = {
feeRate: string;
onFeeRateChange: (value: string) => void;
recommendedFees?: RecommendedFees;
hasMempoolError?: boolean;
mempoolUrl?: string;
};

export function FeeRateField({
feeRate,
onFeeRateChange,
recommendedFees,
hasMempoolError,
mempoolUrl,
}: FeeRateFieldProps) {
return (
<div className="grid gap-2">
<Label htmlFor="fee-rate">On-chain Fee Rate (sat/vB)</Label>
{hasMempoolError && (
<div className="text-muted-foreground text-xs flex gap-1 items-center">
<AlertTriangleIcon className="h-3 w-3" />
Failed to fetch fee estimates. Try refreshing the page.
</div>
)}
<Input
id="fee-rate"
type="number"
value={feeRate}
step={1}
required
min={recommendedFees?.minimumFee || 1}
onChange={(e) => {
onFeeRateChange(e.target.value);
}}
/>
{recommendedFees && (
<div className="flex items-center mt-2 gap-4">
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
onFeeRateChange(recommendedFees.economyFee.toString())
}
>
Low priority: {recommendedFees.economyFee}
</Button>
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
onFeeRateChange(recommendedFees.fastestFee.toString())
}
>
High priority: {recommendedFees.fastestFee}
</Button>
{mempoolUrl && (
<ExternalLink
to={mempoolUrl}
className="text-muted-foreground text-sm underline flex items-center gap-2"
>
View on Mempool
<ExternalLinkIcon className="w-4 h-4" />
</ExternalLink>
)}
</div>
)}
</div>
);
}
59 changes: 8 additions & 51 deletions frontend/src/screens/wallet/WithdrawOnchainFunds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { toast } from "sonner";
import { AnchorReserveAlert } from "src/components/AnchorReserveAlert";
import AppHeader from "src/components/AppHeader";
import ExternalLink from "src/components/ExternalLink";
import { FeeRateField } from "src/components/FeeRateField";
import { FixedFloatButton } from "src/components/FixedFloatButton";
import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount";
import Loading from "src/components/Loading";
Expand Down Expand Up @@ -257,57 +258,13 @@ export default function WithdrawOnchainFunds() {
{(info?.backendType === "LDK" || info?.backendType === "LND") && (
<>
{showAdvanced && (
<div className="grid gap-2">
<Label htmlFor="fee-rate">Fee Rate (Sat/vB)</Label>
{mempoolError && (
<div className="text-muted-foreground text-xs flex gap-1 items-center">
<AlertTriangleIcon className="h-3 w-3" />
Failed to fetch fee estimates. Try refreshing the page.
</div>
)}
<Input
id="fee-rate"
type="number"
value={feeRate}
step={1}
required
min={recommendedFees?.minimumFee || 1}
onChange={(e) => {
setFeeRate(e.target.value);
}}
/>
{recommendedFees && (
<div className="flex items-center mt-2 gap-4">
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
setFeeRate(recommendedFees.economyFee.toString())
}
>
Low priority: {recommendedFees.economyFee}
</Button>{" "}
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
setFeeRate(recommendedFees.fastestFee.toString())
}
>
High priority: {recommendedFees.fastestFee}
</Button>{" "}
<ExternalLink
to={info?.mempoolUrl}
className="text-sm text-muted-foreground underline flex items-center gap-2"
>
View on Mempool
<ExternalLinkIcon className="w-4 h-4" />
</ExternalLink>
</div>
)}
</div>
<FeeRateField
feeRate={feeRate}
onFeeRateChange={setFeeRate}
recommendedFees={recommendedFees}
hasMempoolError={Boolean(mempoolError)}
mempoolUrl={info?.mempoolUrl}
/>
)}
{!showAdvanced && (
<Button
Expand Down
85 changes: 20 additions & 65 deletions frontend/src/screens/wallet/send/Onchain.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import {
AlertTriangleIcon,
ExternalLinkIcon,
InfoIcon,
PencilIcon,
XIcon,
} from "lucide-react";
import { InfoIcon, PencilIcon, XIcon } from "lucide-react";
import React from "react";
import { Link, useLocation, useNavigate } from "react-router";
import { toast } from "sonner";
import { AnchorReserveAlert } from "src/components/AnchorReserveAlert";
import AppHeader from "src/components/AppHeader";
import { CurrencyInputField } from "src/components/CurrencyInputField";
import ExternalLink from "src/components/ExternalLink";
import { FeeRateField } from "src/components/FeeRateField";
import { InsufficientLightningBalanceAlert } from "src/components/InsufficientLightningBalanceAlert";
import Loading from "src/components/Loading";
import { MempoolAlert } from "src/components/MempoolAlert";
import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert";
import { Button } from "src/components/ui/button";
import { LinkButton } from "src/components/ui/custom/link-button";
import { LoadingButton } from "src/components/ui/custom/loading-button";
import { Input } from "src/components/ui/input";
import { Label } from "src/components/ui/label";
import { Switch } from "src/components/ui/switch";
import { ONCHAIN_DUST_SATS } from "src/constants";
Expand Down Expand Up @@ -134,6 +126,12 @@ function OnchainForm({
}
}, [recommendedFees]);

React.useEffect(() => {
if (mempoolError) {
setEditFee(true);
}
}, [mempoolError]);

const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
Expand Down Expand Up @@ -212,8 +210,9 @@ function OnchainForm({
<div className="grid gap-2 text-sm border-t pt-6">
{!editFee ? (
<div className="flex items-center justify-between">
<p className="text-muted-foreground">On-chain Fee Rate</p>
<div
<Label>On-chain Fee Rate (sat/vB)</Label>
Comment thread
im-adithya marked this conversation as resolved.
Outdated
<button
type="button"
className="flex items-center gap-2 cursor-pointer"
onClick={() => setEditFee(true)}
>
Expand All @@ -223,60 +222,16 @@ function OnchainForm({
<Loading className="w-4 h-4" />
)}
<PencilIcon className="w-4 h-4" />
</div>
</button>
</div>
) : (
<div className="grid gap-2">
<Label htmlFor="fee-rate">Fee Rate (Sat/vB)</Label>
{mempoolError && (
<div className="text-muted-foreground text-xs flex gap-1 items-center">
<AlertTriangleIcon className="h-3 w-3" />
Failed to fetch fee estimates. Try refreshing the page.
</div>
)}
<Input
id="fee-rate"
type="number"
value={feeRate}
step={1}
required
min={recommendedFees?.minimumFee || 1}
onChange={(e) => {
setFeeRate(e.target.value);
}}
/>
{recommendedFees && (
<div className="flex items-center mt-2 gap-4">
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
setFeeRate(recommendedFees.economyFee.toString())
}
>
Low priority: {recommendedFees.economyFee}
</Button>{" "}
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
setFeeRate(recommendedFees.fastestFee.toString())
}
>
High priority: {recommendedFees.fastestFee}
</Button>{" "}
<ExternalLink
to={info?.mempoolUrl}
className="text-muted-foreground underline flex items-center gap-2"
>
View on Mempool
<ExternalLinkIcon className="w-4 h-4" />
</ExternalLink>
</div>
)}
</div>
<FeeRateField
feeRate={feeRate}
onFeeRateChange={setFeeRate}
recommendedFees={recommendedFees}
hasMempoolError={Boolean(mempoolError)}
mempoolUrl={info?.mempoolUrl}
/>
)}
</div>
Comment thread
im-adithya marked this conversation as resolved.
{amountSat && +amountSat < 10_000 && (
Expand Down Expand Up @@ -388,7 +343,7 @@ function SwapForm({
</div>
<div className="grid gap-2 text-sm border-t pt-6">
<div className="flex items-center justify-between">
<p className="text-muted-foreground">On-chain Fee Rate</p>
<Label>On-chain Fee Rate</Label>
<p>
{recommendedFees?.fastestFee ? (
<p>{recommendedFees?.fastestFee} sat/vB</p>
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/screens/wallet/swap/SwapInStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default function SwapInStatus() {
const [searchParams] = useSearchParams();

const isInternalSwap = searchParams.has("internal", "true");
const feeRate = searchParams.get("feeRate") || "";
const [, setPaidWithAlbyHub] = React.useState(false);

useEffect(() => {
Expand All @@ -70,6 +71,7 @@ export default function SwapInStatus() {
const payload: RedeemOnchainFundsRequest = {
toAddress: swap.lockupAddress,
amountSat: swap.sendAmountSat,
feeRate: +feeRate,
Comment thread
im-adithya marked this conversation as resolved.
Outdated
};
const response = await request<RedeemOnchainFundsResponse>(
"/api/wallet/redeem-onchain-funds",
Expand All @@ -93,7 +95,7 @@ export default function SwapInStatus() {
setPaying(false);
}
})();
}, [swap]);
}, [feeRate, swap]);

React.useEffect(() => {
// only auto-redeem while the swap is still awaiting its on-chain deposit,
Expand Down
Loading
Loading