import { CircleAlertIcon, CircleCheckIcon, CircleHelpIcon, CircleXIcon, 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"; import FormattedFiatAmount from "src/components/FormattedFiatAmount"; import Loading from "src/components/Loading"; import LottieLoading from "src/components/LottieLoading"; import QRCode from "src/components/QRCode"; import { Button } from "src/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "src/components/ui/card"; import { ExternalLinkButton } from "src/components/ui/custom/external-link-button"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "src/components/ui/tooltip"; 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"; export default function SwapInStatus() { const { data: info } = useInfo(); useSyncWallet(); // ensure funds show up on node page after swap completes 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 [, 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, }; 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 onchain funds", response); } catch (error) { console.error(error); toast.error("Failed to redeem onchain funds", { description: "" + error, }); setPaying(false); } })(); }, [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 ; } const copyPaymentHash = () => { copyToClipboard(swap.paymentHash); }; const copyAddress = () => { copyToClipboard(swap.lockupAddress); }; const copyAmount = () => { copyToClipboard(swap.sendAmountSat.toString()); }; const swapStatus = swap.state; const statusText = { SUCCESS: "Swap Successful", FAILED: "Swap Failed", REFUNDED: "Swap Refunded", PENDING: swap.lockupTxId ? "Waiting for confirmation" : isInternalSwap ? "Depositing on-chain funds" : "Waiting for deposit", }; return (
{swapStatus === "PENDING" && } {statusText[swapStatus]} Swap ID: {swap.id}{" "} { copyToClipboard(swap.id); }} /> {swapStatus === "SUCCESS" ? ( <>

) : ( <> {(swapStatus === "REFUNDED" || swapStatus === "FAILED") && ( )} {swapStatus === "PENDING" && (swap.lockupTxId || isInternalSwap ? ( ) : ( ))}

{!swap.lockupTxId && !isInternalSwap && ( )}
{!swap.lockupTxId && !isInternalSwap && (
{swap.state !== "FAILED" && ( )} {swap.state === "PENDING" && ( Open in External Wallet )}
)} )} {/* We only show status screen once bitcoin is locked up */} {swap.lockupTxId ? (
{swapStatus === "SUCCESS" && ( <>
Funds received via lightning

Onchain deposit confirmed

View
)} {swapStatus === "PENDING" && ( <>

Waiting for 1 on-chain confirmation...

View
)} {swapStatus === "REFUNDED" && ( <>

Refund initiated

View
)} {(swapStatus === "FAILED" || swapStatus === "REFUNDED") && ( <>

Onchain deposit failed

View
Deposit usually fails when there is an amount mismatch or if Boltz failed to send the lightning payment to your node. {swapStatus !== "REFUNDED" && " You can use the Swap Refund button in Settings -> Debug Tools to claim the locked up bitcoin."}
)}
Swap initiated
) : swapStatus === "FAILED" ? ( <>

Onchain deposit failed

View
Deposit usually fails when there is an amount mismatch or if Boltz failed to send the lightning payment to your node. You can use the Swap Refund button in Settings{" "} {"->"} Debug Tools to claim the locked up bitcoin.
) : null}
); } const Divider = ({ color }: { color: string }) => (
);