From 76767c9d8992f35a4386aa535b3310dd440ab0d2 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Wed, 10 Jun 2026 14:59:39 +0530 Subject: [PATCH 01/10] feat: add FeeRateField component to set fee rates for swaps --- frontend/src/components/FeeRateField.tsx | 84 ++++++++++++++++++ .../screens/wallet/WithdrawOnchainFunds.tsx | 59 ++----------- frontend/src/screens/wallet/send/Onchain.tsx | 85 +++++-------------- .../src/screens/wallet/swap/SwapInStatus.tsx | 4 +- frontend/src/screens/wallet/swap/index.tsx | 72 ++++++++++++++-- 5 files changed, 180 insertions(+), 124 deletions(-) create mode 100644 frontend/src/components/FeeRateField.tsx diff --git a/frontend/src/components/FeeRateField.tsx b/frontend/src/components/FeeRateField.tsx new file mode 100644 index 000000000..3a55396ec --- /dev/null +++ b/frontend/src/components/FeeRateField.tsx @@ -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 ( +
+ + {hasMempoolError && ( +
+ + Failed to fetch fee estimates. Try refreshing the page. +
+ )} + { + onFeeRateChange(e.target.value); + }} + /> + {recommendedFees && ( +
+ + + {mempoolUrl && ( + + View on Mempool + + + )} +
+ )} +
+ ); +} diff --git a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx index 86325051e..ace8c5d53 100644 --- a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx +++ b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx @@ -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"; @@ -257,57 +258,13 @@ export default function WithdrawOnchainFunds() { {(info?.backendType === "LDK" || info?.backendType === "LND") && ( <> {showAdvanced && ( -
- - {mempoolError && ( -
- - Failed to fetch fee estimates. Try refreshing the page. -
- )} - { - setFeeRate(e.target.value); - }} - /> - {recommendedFees && ( -
- {" "} - {" "} - - View on Mempool - - -
- )} -
+ )} {!showAdvanced && ( ) : ( -
- - {mempoolError && ( -
- - Failed to fetch fee estimates. Try refreshing the page. -
- )} - { - setFeeRate(e.target.value); - }} - /> - {recommendedFees && ( -
- {" "} - {" "} - - View on Mempool - - -
- )} -
+ )} {amountSat && +amountSat < 10_000 && ( @@ -388,7 +343,7 @@ function SwapForm({
-

On-chain Fee Rate

+

{recommendedFees?.fastestFee ? (

{recommendedFees?.fastestFee} sat/vB

diff --git a/frontend/src/screens/wallet/swap/SwapInStatus.tsx b/frontend/src/screens/wallet/swap/SwapInStatus.tsx index dbb302b18..a44b56040 100644 --- a/frontend/src/screens/wallet/swap/SwapInStatus.tsx +++ b/frontend/src/screens/wallet/swap/SwapInStatus.tsx @@ -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(() => { @@ -70,6 +71,7 @@ export default function SwapInStatus() { const payload: RedeemOnchainFundsRequest = { toAddress: swap.lockupAddress, amountSat: swap.sendAmountSat, + feeRate: +feeRate, }; const response = await request( "/api/wallet/redeem-onchain-funds", @@ -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, diff --git a/frontend/src/screens/wallet/swap/index.tsx b/frontend/src/screens/wallet/swap/index.tsx index 9a050e17c..0d9f69929 100644 --- a/frontend/src/screens/wallet/swap/index.tsx +++ b/frontend/src/screens/wallet/swap/index.tsx @@ -2,6 +2,7 @@ import { ClipboardPasteIcon, ExternalLinkIcon, MoveRightIcon, + PencilIcon, RefreshCwIcon, } from "lucide-react"; import { useEffect, useState } from "react"; @@ -10,6 +11,7 @@ import { toast } from "sonner"; import { AnchorReserveAlert } from "src/components/AnchorReserveAlert"; import AppHeader from "src/components/AppHeader"; import { CurrencyInputField } from "src/components/CurrencyInputField"; +import { FeeRateField } from "src/components/FeeRateField"; import { FixedFloatButton } from "src/components/FixedFloatButton"; import { FixedFloatSwapInFlow } from "src/components/FixedFloatSwapInFlow"; import Loading from "src/components/Loading"; @@ -29,6 +31,7 @@ import { } from "src/components/ui/tabs"; import { useBalances } from "src/hooks/useBalances"; import { useInfo } from "src/hooks/useInfo"; +import { useMempoolApi } from "src/hooks/useMempoolApi"; import { useSwapInfo } from "src/hooks/useSwaps"; import { CreateInvoiceRequest, @@ -97,10 +100,30 @@ function SwapInForm() { const navigate = useNavigate(); const [swapAmountSat, setSwapAmountSat] = useState(""); + const { data: recommendedFees, error: mempoolError } = useMempoolApi<{ + fastestFee: number; + halfHourFee: number; + economyFee: number; + minimumFee: number; + }>("/v1/fees/recommended"); + const [feeRate, setFeeRate] = useState(""); + const [editFee, setEditFee] = useState(false); const [loading, setLoading] = useState(false); const [cryptoTransaction, setCryptoTransaction] = useState(null); + useEffect(() => { + if (recommendedFees?.fastestFee) { + setFeeRate(recommendedFees.fastestFee.toString()); + } + }, [recommendedFees]); + + useEffect(() => { + if (mempoolError) { + setEditFee(true); + } + }, [mempoolError]); + const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -142,7 +165,11 @@ function SwapInForm() { throw new Error("Error swapping in"); } navigate( - `/wallet/swap/in/status/${swapInResponse.swapId}${swapFrom === "internal" ? "?internal=true" : ""}` + `/wallet/swap/in/status/${swapInResponse.swapId}${ + swapFrom === "internal" + ? `?internal=true&feeRate=${encodeURIComponent(feeRate)}` + : "" + }` ); toast("Initiated swap"); } catch (error) { @@ -284,12 +311,43 @@ function SwapInForm() { /> ) : ( <> -
- -

- {swapInfo.albyServiceFee + swapInfo.boltzServiceFee}% + on-chain - fees -

+
+ {isInternalSwap && ( + <> + {!editFee ? ( +
+ + +
+ ) : ( + + )} + + )} +
+ +

+ {swapInfo.albyServiceFee + swapInfo.boltzServiceFee}% + {!isInternalSwap && " + on-chain fees"} +

+
From 7879e12c36fffefe990d9f458dffb0a73d0e8f96 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Wed, 10 Jun 2026 19:42:33 +0530 Subject: [PATCH 02/10] chore: extract toggle into FeeRateField component --- frontend/src/components/FeeRateField.tsx | 75 +++++++++++++++---- .../screens/wallet/WithdrawOnchainFunds.tsx | 48 +----------- frontend/src/screens/wallet/send/Onchain.tsx | 51 +------------ frontend/src/screens/wallet/swap/index.tsx | 49 +----------- 4 files changed, 69 insertions(+), 154 deletions(-) diff --git a/frontend/src/components/FeeRateField.tsx b/frontend/src/components/FeeRateField.tsx index 3a55396ec..0240416bf 100644 --- a/frontend/src/components/FeeRateField.tsx +++ b/frontend/src/components/FeeRateField.tsx @@ -1,8 +1,12 @@ -import { AlertTriangleIcon, ExternalLinkIcon } from "lucide-react"; +import { AlertTriangleIcon, ExternalLinkIcon, PencilIcon } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; import ExternalLink from "src/components/ExternalLink"; +import Loading from "src/components/Loading"; import { Button } from "src/components/ui/button"; import { Input } from "src/components/ui/input"; import { Label } from "src/components/ui/label"; +import { useInfo } from "src/hooks/useInfo"; +import { useMempoolApi } from "src/hooks/useMempoolApi"; type RecommendedFees = { fastestFee: number; @@ -14,22 +18,65 @@ type RecommendedFees = { type FeeRateFieldProps = { feeRate: string; onFeeRateChange: (value: string) => void; - recommendedFees?: RecommendedFees; - hasMempoolError?: boolean; - mempoolUrl?: string; }; -export function FeeRateField({ - feeRate, - onFeeRateChange, - recommendedFees, - hasMempoolError, - mempoolUrl, -}: FeeRateFieldProps) { +export function FeeRateField({ feeRate, onFeeRateChange }: FeeRateFieldProps) { + const { data: info } = useInfo(); + const { data: recommendedFees, error: mempoolError } = + useMempoolApi("/v1/fees/recommended"); + const [isEditing, setIsEditing] = useState(false); + const hasInitializedDefaultFee = useRef(false); + + useEffect(() => { + if ( + recommendedFees?.fastestFee && + !hasInitializedDefaultFee.current && + !feeRate + ) { + hasInitializedDefaultFee.current = true; + onFeeRateChange(recommendedFees.fastestFee.toString()); + } + }, [feeRate, onFeeRateChange, recommendedFees]); + + useEffect(() => { + if (mempoolError) { + setIsEditing(true); + } + }, [mempoolError]); + + if (!info || (!recommendedFees && !mempoolError)) { + return ( +
+ + +
+ ); + } + + if (!isEditing) { + return ( +
+ + {feeRate ? ( + + ) : ( + + )} +
+ ); + } + return (
- {hasMempoolError && ( + {mempoolError && (
Failed to fetch fee estimates. Try refreshing the page. @@ -68,9 +115,9 @@ export function FeeRateField({ > High priority: {recommendedFees.fastestFee} - {mempoolUrl && ( + {info.mempoolUrl && ( View on Mempool diff --git a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx index ace8c5d53..bc415ff0d 100644 --- a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx +++ b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx @@ -1,6 +1,5 @@ import { AlertTriangleIcon, - ChevronDownIcon, CopyIcon, ExternalLinkIcon, InfoIcon, @@ -34,7 +33,6 @@ import { Separator } from "src/components/ui/separator"; import { ONCHAIN_DUST_SATS } from "src/constants"; import { useBalances } from "src/hooks/useBalances"; import { useInfo } from "src/hooks/useInfo"; -import { useMempoolApi } from "src/hooks/useMempoolApi"; import { copyToClipboard } from "src/lib/clipboard"; import { @@ -46,33 +44,14 @@ import { request } from "src/utils/request"; export default function WithdrawOnchainFunds() { const { data: info } = useInfo(); const { data: balances } = useBalances(); - const { data: recommendedFees, error: mempoolError } = useMempoolApi<{ - fastestFee: number; - halfHourFee: number; - economyFee: number; - minimumFee: number; - }>("/v1/fees/recommended"); const [isLoading, setLoading] = React.useState(false); const [onchainAddress, setOnchainAddress] = React.useState(""); const [amountSat, setAmountSat] = React.useState(""); const [feeRate, setFeeRate] = React.useState(""); const [sendAll, setSendAll] = React.useState(false); - const [showAdvanced, setShowAdvanced] = React.useState(false); const [transactionId, setTransactionId] = React.useState(""); const [confirmDialogOpen, setConfirmDialogOpen] = React.useState(false); - React.useEffect(() => { - if (mempoolError) { - setShowAdvanced(true); - } - }, [mempoolError]); - - React.useEffect(() => { - if (recommendedFees?.fastestFee) { - setFeeRate(recommendedFees.fastestFee.toString()); - } - }, [recommendedFees]); - const copy = (text: string) => { copyToClipboard(text); }; @@ -158,7 +137,7 @@ export default function WithdrawOnchainFunds() { ); } - if (!info || !balances || (!recommendedFees && !mempoolError)) { + if (!info || !balances) { return ; } @@ -256,28 +235,9 @@ export default function WithdrawOnchainFunds() {

{(info?.backendType === "LDK" || info?.backendType === "LND") && ( - <> - {showAdvanced && ( - - )} - {!showAdvanced && ( - - )} - +
+ +
)}
diff --git a/frontend/src/screens/wallet/send/Onchain.tsx b/frontend/src/screens/wallet/send/Onchain.tsx index 8c741ea21..51be5ac3c 100644 --- a/frontend/src/screens/wallet/send/Onchain.tsx +++ b/frontend/src/screens/wallet/send/Onchain.tsx @@ -1,4 +1,4 @@ -import { InfoIcon, PencilIcon, XIcon } from "lucide-react"; +import { InfoIcon, XIcon } from "lucide-react"; import React from "react"; import { Link, useLocation, useNavigate } from "react-router"; import { toast } from "sonner"; @@ -16,7 +16,6 @@ import { Label } from "src/components/ui/label"; import { Switch } from "src/components/ui/switch"; import { ONCHAIN_DUST_SATS } from "src/constants"; import { useBalances } from "src/hooks/useBalances"; -import { useInfo } from "src/hooks/useInfo"; import { useMempoolApi } from "src/hooks/useMempoolApi"; import { useSwapInfo } from "src/hooks/useSwaps"; import { @@ -107,30 +106,10 @@ function OnchainForm({ setSwap: React.Dispatch>; }) { const navigate = useNavigate(); - const { data: info } = useInfo(); const { data: balances } = useBalances(); - const { data: recommendedFees, error: mempoolError } = useMempoolApi<{ - fastestFee: number; - halfHourFee: number; - economyFee: number; - minimumFee: number; - }>("/v1/fees/recommended"); const [feeRate, setFeeRate] = React.useState(""); const [isLoading, setLoading] = React.useState(false); - const [editFee, setEditFee] = React.useState(false); - - React.useEffect(() => { - if (recommendedFees?.fastestFee) { - setFeeRate(recommendedFees.fastestFee.toString()); - } - }, [recommendedFees]); - - React.useEffect(() => { - if (mempoolError) { - setEditFee(true); - } - }, [mempoolError]); const onSubmit = async (event: React.FormEvent) => { event.preventDefault(); @@ -180,7 +159,7 @@ function OnchainForm({ } }; - if (!info || !balances || (!recommendedFees && !mempoolError)) { + if (!balances) { return ; } @@ -208,31 +187,7 @@ function OnchainForm({
- {!editFee ? ( -
- - -
- ) : ( - - )} +
{amountSat && +amountSat < 10_000 && ( diff --git a/frontend/src/screens/wallet/swap/index.tsx b/frontend/src/screens/wallet/swap/index.tsx index 0d9f69929..2e1e9a6c7 100644 --- a/frontend/src/screens/wallet/swap/index.tsx +++ b/frontend/src/screens/wallet/swap/index.tsx @@ -2,7 +2,6 @@ import { ClipboardPasteIcon, ExternalLinkIcon, MoveRightIcon, - PencilIcon, RefreshCwIcon, } from "lucide-react"; import { useEffect, useState } from "react"; @@ -31,7 +30,6 @@ import { } from "src/components/ui/tabs"; import { useBalances } from "src/hooks/useBalances"; import { useInfo } from "src/hooks/useInfo"; -import { useMempoolApi } from "src/hooks/useMempoolApi"; import { useSwapInfo } from "src/hooks/useSwaps"; import { CreateInvoiceRequest, @@ -100,30 +98,11 @@ function SwapInForm() { const navigate = useNavigate(); const [swapAmountSat, setSwapAmountSat] = useState(""); - const { data: recommendedFees, error: mempoolError } = useMempoolApi<{ - fastestFee: number; - halfHourFee: number; - economyFee: number; - minimumFee: number; - }>("/v1/fees/recommended"); const [feeRate, setFeeRate] = useState(""); - const [editFee, setEditFee] = useState(false); const [loading, setLoading] = useState(false); const [cryptoTransaction, setCryptoTransaction] = useState(null); - useEffect(() => { - if (recommendedFees?.fastestFee) { - setFeeRate(recommendedFees.fastestFee.toString()); - } - }, [recommendedFees]); - - useEffect(() => { - if (mempoolError) { - setEditFee(true); - } - }, [mempoolError]); - const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -313,33 +292,7 @@ function SwapInForm() { <>
{isInternalSwap && ( - <> - {!editFee ? ( -
- - -
- ) : ( - - )} - + )}
From e6789520acb10d154c175677debb431239dc5d67 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Wed, 10 Jun 2026 19:44:11 +0530 Subject: [PATCH 03/10] chore: remove redundant info message --- .../src/screens/wallet/WithdrawOnchainFunds.tsx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx index bc415ff0d..591d445a9 100644 --- a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx +++ b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx @@ -1,9 +1,4 @@ -import { - AlertTriangleIcon, - CopyIcon, - ExternalLinkIcon, - InfoIcon, -} from "lucide-react"; +import { AlertTriangleIcon, CopyIcon, ExternalLinkIcon } from "lucide-react"; import React from "react"; import { toast } from "sonner"; import { AnchorReserveAlert } from "src/components/AnchorReserveAlert"; @@ -246,14 +241,6 @@ export default function WithdrawOnchainFunds() { open={confirmDialogOpen} > - {feeRate && ( -
- - On-chain payment will be made with{" "} - {feeRate} sat/vB fee -
- )} - From cc4577ae02959a64547d9a3a01aadaea73e4f564 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Wed, 10 Jun 2026 19:50:19 +0530 Subject: [PATCH 04/10] chore: use on-chain wording everywhere --- .../components/CloseChannelDialogContent.tsx | 2 +- .../screens/channels/CurrentChannelOrder.tsx | 2 +- .../screens/wallet/WithdrawOnchainFunds.tsx | 20 +++++++++---------- .../src/screens/wallet/swap/SwapInStatus.tsx | 10 +++++----- .../src/screens/wallet/swap/SwapOutStatus.tsx | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/CloseChannelDialogContent.tsx b/frontend/src/components/CloseChannelDialogContent.tsx index 91145b140..5e3c0668a 100644 --- a/frontend/src/components/CloseChannelDialogContent.tsx +++ b/frontend/src/components/CloseChannelDialogContent.tsx @@ -94,7 +94,7 @@ export function CloseChannelDialogContent({ alias, channel }: Props) { Are you sure you want to close the channel with {alias}? - This channel is inactive. Some channels require up to 6 onchain + This channel is inactive. Some channels require up to 6 on-chain confirmations before they are usable. diff --git a/frontend/src/screens/channels/CurrentChannelOrder.tsx b/frontend/src/screens/channels/CurrentChannelOrder.tsx index fbffb3f7d..b59b45721 100644 --- a/frontend/src/screens/channels/CurrentChannelOrder.tsx +++ b/frontend/src/screens/channels/CurrentChannelOrder.tsx @@ -302,7 +302,7 @@ function PayBitcoinChannelOrderTopup({ order }: { order: NewChannelOrder }) {

This amount includes cost for the channel opening and potential - channel onchain reserves. + channel on-chain reserves.

- You currently don't have enough sats to pay for an onchain transaction. + You currently don't have enough sats to pay for an on-chain transaction.

); } @@ -149,12 +149,12 @@ export default function WithdrawOnchainFunds() {

- Your on-chain balance will be withdrawn to the onchain bitcoin wallet + Your on-chain balance will be withdrawn to the on-chain bitcoin wallet address you specify below.

Amount

- Current onchain balance:{" "} + Current on-chain balance:{" "} @@ -202,7 +202,7 @@ export default function WithdrawOnchainFunds() { Entire wallet balance will be sent - Your entire wallet balance will be sent minus onchain + Your entire wallet balance will be sent minus on-chain transaction fees. The exact amount cannot be determined until the payment is made. @@ -214,7 +214,7 @@ export default function WithdrawOnchainFunds() { />

- + - Confirm Onchain Transaction + Confirm On-chain Transaction
diff --git a/frontend/src/screens/wallet/swap/SwapInStatus.tsx b/frontend/src/screens/wallet/swap/SwapInStatus.tsx index a44b56040..b0060a5bc 100644 --- a/frontend/src/screens/wallet/swap/SwapInStatus.tsx +++ b/frontend/src/screens/wallet/swap/SwapInStatus.tsx @@ -86,10 +86,10 @@ export default function SwapInStatus() { if (!response?.txId) { throw new Error("No address in response"); } - console.info("Redeemed onchain funds", response); + console.info("Redeemed on-chain funds", response); } catch (error) { console.error(error); - toast.error("Failed to redeem onchain funds", { + toast.error("Failed to redeem on-chain funds", { description: "" + error, }); setPaying(false); @@ -249,7 +249,7 @@ export default function SwapInStatus() {
-

Onchain deposit confirmed

+

On-chain deposit confirmed

-

Onchain deposit failed

+

On-chain deposit failed

-

Onchain deposit failed

+

On-chain deposit failed

-

Confirmed onchain

+

Confirmed on-chain

Date: Wed, 10 Jun 2026 19:55:10 +0530 Subject: [PATCH 05/10] chore: only pass fee rate if it exists --- frontend/src/screens/wallet/swap/SwapInStatus.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/screens/wallet/swap/SwapInStatus.tsx b/frontend/src/screens/wallet/swap/SwapInStatus.tsx index b0060a5bc..c409cab4d 100644 --- a/frontend/src/screens/wallet/swap/SwapInStatus.tsx +++ b/frontend/src/screens/wallet/swap/SwapInStatus.tsx @@ -71,7 +71,7 @@ export default function SwapInStatus() { const payload: RedeemOnchainFundsRequest = { toAddress: swap.lockupAddress, amountSat: swap.sendAmountSat, - feeRate: +feeRate, + ...(feeRate ? { feeRate: +feeRate } : {}), }; const response = await request( "/api/wallet/redeem-onchain-funds", From d97a5bf40cf9b75d59a4df36fb2ace8bf4226308 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Wed, 24 Jun 2026 15:00:32 +0530 Subject: [PATCH 06/10] chore: shift onchain payment to backend --- api/api.go | 5 +- api/models.go | 72 +++++++++--------- .../src/screens/wallet/swap/SwapInStatus.tsx | 76 +------------------ frontend/src/screens/wallet/swap/index.tsx | 10 ++- frontend/src/types.ts | 2 + swaps/swaps_service.go | 28 ++++++- 6 files changed, 76 insertions(+), 117 deletions(-) diff --git a/api/api.go b/api/api.go index da75b8180..a5ce2e470 100644 --- a/api/api.go +++ b/api/api.go @@ -1050,10 +1050,11 @@ func (api *api) InitiateSwapIn(ctx context.Context, initiateSwapInRequest *Initi return nil, errors.New("invalid swap amount") } - swapInResponse, err := api.svc.GetSwapsService().SwapIn(amountSat, false) + swapInResponse, err := api.svc.GetSwapsService().SwapIn(amountSat, false, initiateSwapInRequest.InternalPayment, initiateSwapInRequest.FeeRate) if err != nil { logger.Logger.WithFields(logrus.Fields{ - "amount_sat": amountSat, + "amount_sat": amountSat, + "internal_payment": initiateSwapInRequest.InternalPayment, }).WithError(err).Error("Failed to initiate swap in") return nil, err } diff --git a/api/models.go b/api/models.go index 5448465dc..f118272d0 100644 --- a/api/models.go +++ b/api/models.go @@ -169,9 +169,11 @@ type CreateLightningAddressRequest struct { } type InitiateSwapRequest struct { - SwapAmount *uint64 `json:"swapAmount"` // deprecated - SwapAmountSat *uint64 `json:"swapAmountSat"` - Destination string `json:"destination"` + SwapAmount *uint64 `json:"swapAmount"` // deprecated + SwapAmountSat *uint64 `json:"swapAmountSat"` + Destination string `json:"destination"` + InternalPayment bool `json:"internalPayment"` + FeeRate *uint64 `json:"feeRate"` } type RefundSwapRequest struct { @@ -304,38 +306,38 @@ type InfoResponseRelay struct { } type InfoResponse struct { - BackendType string `json:"backendType"` - SetupCompleted bool `json:"setupCompleted"` - OAuthRedirect bool `json:"oauthRedirect"` - Running bool `json:"running"` - Unlocked bool `json:"unlocked"` - AlbyAuthUrl string `json:"albyAuthUrl"` - NextBackupReminder string `json:"nextBackupReminder"` - AlbyUserIdentifier string `json:"albyUserIdentifier"` - AlbyAccountConnected bool `json:"albyAccountConnected"` - Version string `json:"version"` - Network string `json:"network"` - EnableAdvancedSetup bool `json:"enableAdvancedSetup"` - LdkVssEnabled bool `json:"ldkVssEnabled"` - VssSupported bool `json:"vssSupported"` - StartupState string `json:"startupState"` - StartupError string `json:"startupError"` - StartupErrorTime time.Time `json:"startupErrorTime"` - AutoUnlockPasswordSupported bool `json:"autoUnlockPasswordSupported"` - AutoUnlockPasswordEnabled bool `json:"autoUnlockPasswordEnabled"` - Currency string `json:"currency"` - BitcoinDisplayFormat string `json:"bitcoinDisplayFormat"` - Relays []InfoResponseRelay `json:"relays"` - NodeAlias string `json:"nodeAlias"` - MempoolUrl string `json:"mempoolUrl"` - ChainDataSourceType string `json:"chainDataSourceType,omitempty"` - ChainDataSourceAddress string `json:"chainDataSourceAddress,omitempty"` - JitChannelsLiquiditySource string `json:"jitChannelsLiquiditySource,omitempty"` - JitChannelsMinPaymentSizeMsat *uint64 `json:"jitChannelsMinPaymentSizeMsat,omitempty"` - JitChannelsMaxPaymentSizeMsat *uint64 `json:"jitChannelsMaxPaymentSizeMsat,omitempty"` - JitChannelsEnabled bool `json:"jitChannelsEnabled"` - HideUpdateBanner bool `json:"hideUpdateBanner"` - SupportsBolt12 bool `json:"supportsBolt12"` + BackendType string `json:"backendType"` + SetupCompleted bool `json:"setupCompleted"` + OAuthRedirect bool `json:"oauthRedirect"` + Running bool `json:"running"` + Unlocked bool `json:"unlocked"` + AlbyAuthUrl string `json:"albyAuthUrl"` + NextBackupReminder string `json:"nextBackupReminder"` + AlbyUserIdentifier string `json:"albyUserIdentifier"` + AlbyAccountConnected bool `json:"albyAccountConnected"` + Version string `json:"version"` + Network string `json:"network"` + EnableAdvancedSetup bool `json:"enableAdvancedSetup"` + LdkVssEnabled bool `json:"ldkVssEnabled"` + VssSupported bool `json:"vssSupported"` + StartupState string `json:"startupState"` + StartupError string `json:"startupError"` + StartupErrorTime time.Time `json:"startupErrorTime"` + AutoUnlockPasswordSupported bool `json:"autoUnlockPasswordSupported"` + AutoUnlockPasswordEnabled bool `json:"autoUnlockPasswordEnabled"` + Currency string `json:"currency"` + BitcoinDisplayFormat string `json:"bitcoinDisplayFormat"` + Relays []InfoResponseRelay `json:"relays"` + NodeAlias string `json:"nodeAlias"` + MempoolUrl string `json:"mempoolUrl"` + ChainDataSourceType string `json:"chainDataSourceType,omitempty"` + ChainDataSourceAddress string `json:"chainDataSourceAddress,omitempty"` + JitChannelsLiquiditySource string `json:"jitChannelsLiquiditySource,omitempty"` + JitChannelsMinPaymentSizeMsat *uint64 `json:"jitChannelsMinPaymentSizeMsat,omitempty"` + JitChannelsMaxPaymentSizeMsat *uint64 `json:"jitChannelsMaxPaymentSizeMsat,omitempty"` + JitChannelsEnabled bool `json:"jitChannelsEnabled"` + HideUpdateBanner bool `json:"hideUpdateBanner"` + SupportsBolt12 bool `json:"supportsBolt12"` } type UpdateSettingsRequest struct { diff --git a/frontend/src/screens/wallet/swap/SwapInStatus.tsx b/frontend/src/screens/wallet/swap/SwapInStatus.tsx index c409cab4d..8ae5799aa 100644 --- a/frontend/src/screens/wallet/swap/SwapInStatus.tsx +++ b/frontend/src/screens/wallet/swap/SwapInStatus.tsx @@ -6,9 +6,7 @@ import { CopyIcon, ExternalLinkIcon, } from "lucide-react"; -import React, { useEffect, useState } from "react"; import { useParams, useSearchParams } from "react-router"; -import { toast } from "sonner"; import AppHeader from "src/components/AppHeader"; import ExternalLink from "src/components/ExternalLink"; import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount"; @@ -35,12 +33,7 @@ import { useInfo } from "src/hooks/useInfo"; import { useSwap } from "src/hooks/useSwaps"; import { useSyncWallet } from "src/hooks/useSyncWallet"; import { copyToClipboard } from "src/lib/clipboard"; -import { - RedeemOnchainFundsRequest, - RedeemOnchainFundsResponse, - SwapIn, -} from "src/types"; -import { request } from "src/utils/request"; +import { SwapIn } from "src/types"; export default function SwapInStatus() { const { data: info } = useInfo(); @@ -48,76 +41,9 @@ export default function SwapInStatus() { const { swapId } = useParams() as { swapId: string }; const { data: swap } = useSwap(swapId, true); - const [isPaying, setPaying] = useState(false); const [searchParams] = useSearchParams(); const isInternalSwap = searchParams.has("internal", "true"); - const feeRate = searchParams.get("feeRate") || ""; - const [, setPaidWithAlbyHub] = React.useState(false); - - useEffect(() => { - if (isPaying && swap?.lockupTxId) { - setPaying(false); - } - }, [isPaying, swap?.lockupTxId]); - - const payWithAlbyHub = React.useCallback(() => { - (async () => { - setPaying(true); - try { - if (!swap) { - throw new Error("swap not loaded"); - } - const payload: RedeemOnchainFundsRequest = { - toAddress: swap.lockupAddress, - amountSat: swap.sendAmountSat, - ...(feeRate ? { feeRate: +feeRate } : {}), - }; - const response = await request( - "/api/wallet/redeem-onchain-funds", - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - } - ); - if (!response?.txId) { - throw new Error("No address in response"); - } - console.info("Redeemed on-chain funds", response); - } catch (error) { - console.error(error); - toast.error("Failed to redeem on-chain funds", { - description: "" + error, - }); - setPaying(false); - } - })(); - }, [feeRate, swap]); - - React.useEffect(() => { - // only auto-redeem while the swap is still awaiting its on-chain deposit, - // otherwise a refresh/revisit of ?internal=true would submit a second - // redeem request for an already-funded swap - if ( - isInternalSwap && - swap && - swap.state === "PENDING" && - !swap.lockupTxId - ) { - setPaidWithAlbyHub((current) => { - if (current) { - return current; - } - setTimeout(() => { - payWithAlbyHub(); - }, 1); - return true; - }); - } - }, [isInternalSwap, payWithAlbyHub, swap]); if (!swap) { return ; diff --git a/frontend/src/screens/wallet/swap/index.tsx b/frontend/src/screens/wallet/swap/index.tsx index 2e1e9a6c7..b25db7ab7 100644 --- a/frontend/src/screens/wallet/swap/index.tsx +++ b/frontend/src/screens/wallet/swap/index.tsx @@ -132,6 +132,12 @@ function SwapInForm() { const payload: InitiateSwapRequest = { swapAmountSat: parseInt(swapAmountSat), + ...(swapFrom === "internal" + ? { + internalPayment: true, + ...(feeRate ? { feeRate: parseInt(feeRate) } : {}), + } + : {}), }; const swapInResponse = await request("/api/swaps/in", { method: "POST", @@ -145,9 +151,7 @@ function SwapInForm() { } navigate( `/wallet/swap/in/status/${swapInResponse.swapId}${ - swapFrom === "internal" - ? `?internal=true&feeRate=${encodeURIComponent(feeRate)}` - : "" + swapFrom === "internal" ? "?internal=true" : "" }` ); toast("Initiated swap"); diff --git a/frontend/src/types.ts b/frontend/src/types.ts index d05867904..6ec9fe827 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -597,6 +597,8 @@ export type AutoSwapRequest = { export type InitiateSwapRequest = { swapAmountSat?: number; destination?: string; + internalPayment?: boolean; + feeRate?: number; }; export type LSPOrderResponse = { diff --git a/swaps/swaps_service.go b/swaps/swaps_service.go index 7a8c2f310..8580ffe1f 100644 --- a/swaps/swaps_service.go +++ b/swaps/swaps_service.go @@ -56,7 +56,7 @@ type SwapsService interface { StopAutoSwapOut() EnableAutoSwapOut(encryptionKey string) error SwapOut(amountSat uint64, destination string, autoSwap, usedXpubDerivation bool) (*SwapResponse, error) - SwapIn(amountSat uint64, autoSwap bool) (*SwapResponse, error) + SwapIn(amountSat uint64, autoSwap bool, internalPayment bool, feeRate *uint64) (*SwapResponse, error) GetSwapOutInfo() (*SwapInfo, error) GetSwapInInfo() (*SwapInfo, error) RefundSwap(swapId, address string, enableRetries bool) error @@ -379,7 +379,7 @@ func (svc *swapsService) SwapOut(amountSat uint64, destination string, autoSwap, }, nil } -func (svc *swapsService) SwapIn(amountSat uint64, autoSwap bool) (*SwapResponse, error) { +func (svc *swapsService) SwapIn(amountSat uint64, autoSwap bool, internalPayment bool, feeRate *uint64) (*SwapResponse, error) { amountMsat := amountSat * 1000 invoice, err := svc.transactionsService.MakeInvoice(svc.ctx, amountMsat, "On-chain to lightning swap", "", 0, nil, svc.lnClient, nil, nil, nil) if err != nil { @@ -498,6 +498,30 @@ func (svc *swapsService) SwapIn(amountSat uint64, autoSwap bool) (*SwapResponse, logger.Logger.WithField("swapId", swap.Id).Info("Swap created") + if internalPayment { + var lockupTxId string + lockupTxId, err = svc.lnClient.RedeemOnchainFunds(svc.ctx, swap.Address, swap.ExpectedAmount, feeRate, false) + if err != nil { + logger.Logger.WithError(err).WithFields(logrus.Fields{ + "swapId": swap.Id, + "amountSat": swap.ExpectedAmount, + }).Error("Failed to fund internal swap in") + return nil, err + } + + err = svc.db.Model(&dbSwap).Updates(&db.Swap{ + LockupTxId: lockupTxId, + }).Error + if err != nil { + logger.Logger.WithError(err).WithFields(logrus.Fields{ + "swapId": swap.Id, + "lockupTxId": lockupTxId, + }).Error("Failed to save internal swap lockup txid") + return nil, err + } + dbSwap.LockupTxId = lockupTxId + } + go svc.startSwapInListener(&dbSwap) return &SwapResponse{ From e6354d1cebaaaebbaa47c700752ca05be51a7cf2 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Wed, 24 Jun 2026 15:04:36 +0530 Subject: [PATCH 07/10] chore: remove unnecessary backendType check --- frontend/src/screens/wallet/WithdrawOnchainFunds.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx index f4106cfdb..a4a9a3d54 100644 --- a/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx +++ b/frontend/src/screens/wallet/WithdrawOnchainFunds.tsx @@ -229,11 +229,9 @@ export default function WithdrawOnchainFunds() { cannot be reversed.

- {(info?.backendType === "LDK" || info?.backendType === "LND") && ( -
- -
- )} +
+ +
Date: Wed, 24 Jun 2026 15:33:54 +0530 Subject: [PATCH 08/10] chore: minor changes --- frontend/src/screens/wallet/swap/index.tsx | 2 +- swaps/swaps_service.go | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/frontend/src/screens/wallet/swap/index.tsx b/frontend/src/screens/wallet/swap/index.tsx index b25db7ab7..c2a1fa1fb 100644 --- a/frontend/src/screens/wallet/swap/index.tsx +++ b/frontend/src/screens/wallet/swap/index.tsx @@ -135,7 +135,7 @@ function SwapInForm() { ...(swapFrom === "internal" ? { internalPayment: true, - ...(feeRate ? { feeRate: parseInt(feeRate) } : {}), + ...(feeRate ? { feeRate: +feeRate } : {}), } : {}), }; diff --git a/swaps/swaps_service.go b/swaps/swaps_service.go index 8580ffe1f..e45f3bb9c 100644 --- a/swaps/swaps_service.go +++ b/swaps/swaps_service.go @@ -499,8 +499,7 @@ func (svc *swapsService) SwapIn(amountSat uint64, autoSwap bool, internalPayment logger.Logger.WithField("swapId", swap.Id).Info("Swap created") if internalPayment { - var lockupTxId string - lockupTxId, err = svc.lnClient.RedeemOnchainFunds(svc.ctx, swap.Address, swap.ExpectedAmount, feeRate, false) + _, err = svc.lnClient.RedeemOnchainFunds(svc.ctx, swap.Address, swap.ExpectedAmount, feeRate, false) if err != nil { logger.Logger.WithError(err).WithFields(logrus.Fields{ "swapId": swap.Id, @@ -508,18 +507,6 @@ func (svc *swapsService) SwapIn(amountSat uint64, autoSwap bool, internalPayment }).Error("Failed to fund internal swap in") return nil, err } - - err = svc.db.Model(&dbSwap).Updates(&db.Swap{ - LockupTxId: lockupTxId, - }).Error - if err != nil { - logger.Logger.WithError(err).WithFields(logrus.Fields{ - "swapId": swap.Id, - "lockupTxId": lockupTxId, - }).Error("Failed to save internal swap lockup txid") - return nil, err - } - dbSwap.LockupTxId = lockupTxId } go svc.startSwapInListener(&dbSwap) From 2d8f09e5dd4cbe62664dfcb93109c9b7033468e7 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Thu, 25 Jun 2026 14:58:57 +0530 Subject: [PATCH 09/10] chore: split InitiateSwapRequest struct --- api/api.go | 4 ++-- api/models.go | 13 +++++++++---- .../src/screens/wallet/receive/ReceiveOnchain.tsx | 4 ++-- frontend/src/screens/wallet/send/Onchain.tsx | 4 ++-- frontend/src/screens/wallet/swap/index.tsx | 7 ++++--- frontend/src/types.ts | 10 +++++++--- http/http_service.go | 4 ++-- wails/wails_handlers.go | 4 ++-- 8 files changed, 30 insertions(+), 20 deletions(-) diff --git a/api/api.go b/api/api.go index a5ce2e470..82906c5e8 100644 --- a/api/api.go +++ b/api/api.go @@ -997,7 +997,7 @@ func (api *api) GetSwapOutInfo() (*SwapInfoResponse, error) { }, nil } -func (api *api) InitiateSwapOut(ctx context.Context, initiateSwapOutRequest *InitiateSwapRequest) (*swaps.SwapResponse, error) { +func (api *api) InitiateSwapOut(ctx context.Context, initiateSwapOutRequest *InitiateSwapOutRequest) (*swaps.SwapResponse, error) { lnClient := api.svc.GetLNClient() if lnClient == nil { return nil, ErrLNClientNotStarted @@ -1030,7 +1030,7 @@ func (api *api) InitiateSwapOut(ctx context.Context, initiateSwapOutRequest *Ini return swapOutResponse, nil } -func (api *api) InitiateSwapIn(ctx context.Context, initiateSwapInRequest *InitiateSwapRequest) (*swaps.SwapResponse, error) { +func (api *api) InitiateSwapIn(ctx context.Context, initiateSwapInRequest *InitiateSwapInRequest) (*swaps.SwapResponse, error) { lnClient := api.svc.GetLNClient() if lnClient == nil { return nil, ErrLNClientNotStarted diff --git a/api/models.go b/api/models.go index f118272d0..023ecfb92 100644 --- a/api/models.go +++ b/api/models.go @@ -69,8 +69,8 @@ type API interface { ListSwaps() (*ListSwapsResponse, error) GetSwapInInfo() (*SwapInfoResponse, error) GetSwapOutInfo() (*SwapInfoResponse, error) - InitiateSwapIn(ctx context.Context, initiateSwapInRequest *InitiateSwapRequest) (*swaps.SwapResponse, error) - InitiateSwapOut(ctx context.Context, initiateSwapOutRequest *InitiateSwapRequest) (*swaps.SwapResponse, error) + InitiateSwapIn(ctx context.Context, initiateSwapInRequest *InitiateSwapInRequest) (*swaps.SwapResponse, error) + InitiateSwapOut(ctx context.Context, initiateSwapOutRequest *InitiateSwapOutRequest) (*swaps.SwapResponse, error) RefundSwap(refundSwapRequest *RefundSwapRequest) error GetSwapMnemonic() string GetAutoSwapConfig() (*GetAutoSwapConfigResponse, error) @@ -168,14 +168,19 @@ type CreateLightningAddressRequest struct { AppId uint `json:"appId"` } -type InitiateSwapRequest struct { +type InitiateSwapInRequest struct { SwapAmount *uint64 `json:"swapAmount"` // deprecated SwapAmountSat *uint64 `json:"swapAmountSat"` - Destination string `json:"destination"` InternalPayment bool `json:"internalPayment"` FeeRate *uint64 `json:"feeRate"` } +type InitiateSwapOutRequest struct { + SwapAmount *uint64 `json:"swapAmount"` // deprecated + SwapAmountSat *uint64 `json:"swapAmountSat"` + Destination string `json:"destination"` +} + type RefundSwapRequest struct { SwapId string `json:"swapId"` Address string `json:"address"` diff --git a/frontend/src/screens/wallet/receive/ReceiveOnchain.tsx b/frontend/src/screens/wallet/receive/ReceiveOnchain.tsx index 0044c33e9..7e6e20858 100644 --- a/frontend/src/screens/wallet/receive/ReceiveOnchain.tsx +++ b/frontend/src/screens/wallet/receive/ReceiveOnchain.tsx @@ -16,7 +16,7 @@ import { useMempoolApi } from "src/hooks/useMempoolApi"; import { useSwapInfo } from "src/hooks/useSwaps"; import { CreateInvoiceRequest, - InitiateSwapRequest, + InitiateSwapInRequest, SwapResponse, Transaction, } from "src/types"; @@ -75,7 +75,7 @@ export default function ReceiveOnchain() { return; } - const payload: InitiateSwapRequest = { + const payload: InitiateSwapInRequest = { swapAmountSat: parseInt(swapAmountSat), }; const swapInResponse = await request("/api/swaps/in", { diff --git a/frontend/src/screens/wallet/send/Onchain.tsx b/frontend/src/screens/wallet/send/Onchain.tsx index 51be5ac3c..0447583b8 100644 --- a/frontend/src/screens/wallet/send/Onchain.tsx +++ b/frontend/src/screens/wallet/send/Onchain.tsx @@ -19,7 +19,7 @@ import { useBalances } from "src/hooks/useBalances"; import { useMempoolApi } from "src/hooks/useMempoolApi"; import { useSwapInfo } from "src/hooks/useSwaps"; import { - InitiateSwapRequest, + InitiateSwapOutRequest, RedeemOnchainFundsRequest, RedeemOnchainFundsResponse, SwapResponse, @@ -233,7 +233,7 @@ function SwapForm({ event.preventDefault(); try { setLoading(true); - const payload: InitiateSwapRequest = { + const payload: InitiateSwapOutRequest = { swapAmountSat: +amountSat, destination: address, }; diff --git a/frontend/src/screens/wallet/swap/index.tsx b/frontend/src/screens/wallet/swap/index.tsx index c2a1fa1fb..0ea37c19d 100644 --- a/frontend/src/screens/wallet/swap/index.tsx +++ b/frontend/src/screens/wallet/swap/index.tsx @@ -33,7 +33,8 @@ import { useInfo } from "src/hooks/useInfo"; import { useSwapInfo } from "src/hooks/useSwaps"; import { CreateInvoiceRequest, - InitiateSwapRequest, + InitiateSwapInRequest, + InitiateSwapOutRequest, SwapResponse, Transaction, } from "src/types"; @@ -130,7 +131,7 @@ function SwapInForm() { return; } - const payload: InitiateSwapRequest = { + const payload: InitiateSwapInRequest = { swapAmountSat: parseInt(swapAmountSat), ...(swapFrom === "internal" ? { @@ -338,7 +339,7 @@ function SwapOutForm() { try { setLoading(true); - const payload: InitiateSwapRequest = { + const payload: InitiateSwapOutRequest = { swapAmountSat: parseInt(swapAmountSat), destination, }; diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 6ec9fe827..08207e641 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -594,13 +594,17 @@ export type AutoSwapRequest = { unlockPassword?: string; }; -export type InitiateSwapRequest = { - swapAmountSat?: number; - destination?: string; +export type InitiateSwapInRequest = { + swapAmountSat: number; internalPayment?: boolean; feeRate?: number; }; +export type InitiateSwapOutRequest = { + swapAmountSat: number; + destination: string; +}; + export type LSPOrderResponse = { invoice?: string; feeSat: number; diff --git a/http/http_service.go b/http/http_service.go index b36091a62..04c4e3437 100644 --- a/http/http_service.go +++ b/http/http_service.go @@ -1479,7 +1479,7 @@ func (httpSvc *HttpService) getSwapInInfoHandler(c echo.Context) error { } func (httpSvc *HttpService) initiateSwapOutHandler(c echo.Context) error { - var initiateSwapOutRequest api.InitiateSwapRequest + var initiateSwapOutRequest api.InitiateSwapOutRequest if err := c.Bind(&initiateSwapOutRequest); err != nil { return c.JSON(http.StatusBadRequest, ErrorResponse{ Message: fmt.Sprintf("Bad request: %s", err.Error()), @@ -1497,7 +1497,7 @@ func (httpSvc *HttpService) initiateSwapOutHandler(c echo.Context) error { } func (httpSvc *HttpService) initiateSwapInHandler(c echo.Context) error { - var initiateSwapInRequest api.InitiateSwapRequest + var initiateSwapInRequest api.InitiateSwapInRequest if err := c.Bind(&initiateSwapInRequest); err != nil { return c.JSON(http.StatusBadRequest, ErrorResponse{ Message: fmt.Sprintf("Bad request: %s", err.Error()), diff --git a/wails/wails_handlers.go b/wails/wails_handlers.go index 193f3a532..a4f7d2ee5 100644 --- a/wails/wails_handlers.go +++ b/wails/wails_handlers.go @@ -1154,7 +1154,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string } return WailsRequestRouterResponse{Body: swapInInfo, Error: ""} case "/api/swaps/out": - initiateSwapOutRequest := &api.InitiateSwapRequest{} + initiateSwapOutRequest := &api.InitiateSwapOutRequest{} err := json.Unmarshal([]byte(body), initiateSwapOutRequest) if err != nil { logger.Logger.WithFields(logrus.Fields{ @@ -1175,7 +1175,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string } return WailsRequestRouterResponse{Body: swapOutResponse, Error: ""} case "/api/swaps/in": - initiateSwapInRequest := &api.InitiateSwapRequest{} + initiateSwapInRequest := &api.InitiateSwapInRequest{} err := json.Unmarshal([]byte(body), initiateSwapInRequest) if err != nil { logger.Logger.WithFields(logrus.Fields{ From 031a16ac46e3198a67a0e1736ec580574e5e9335 Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Thu, 25 Jun 2026 15:01:30 +0530 Subject: [PATCH 10/10] chore: show + onchain fees for internal swap ins too --- frontend/src/screens/wallet/swap/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/screens/wallet/swap/index.tsx b/frontend/src/screens/wallet/swap/index.tsx index 0ea37c19d..743b8fa80 100644 --- a/frontend/src/screens/wallet/swap/index.tsx +++ b/frontend/src/screens/wallet/swap/index.tsx @@ -302,8 +302,8 @@ function SwapInForm() {

- {swapInfo.albyServiceFee + swapInfo.boltzServiceFee}% - {!isInternalSwap && " + on-chain fees"} + {swapInfo.albyServiceFee + swapInfo.boltzServiceFee}% + on-chain + fees