Send a payment
Send over Lightning
Wavelength uses a send swap to route a Lightning payment from your Ark balance.
Call prepare({ invoice }) from useWalletPrepareSend() to get a fee quote
and a single-use sendIntentId. Pass the result to sendPrepared() from
useWalletSend() to execute.
The two-step flow lets you show the user the fee before committing. If the
user cancels, discard the quote and call nothing. For a one-step path, call
send({ invoice }) from useWalletSend() instead.
The quote returned by prepare() expires at expiresAtUnix. If the user
waits too long before confirming, call prepare() again to get a fresh
quote before calling sendPrepared(). Check feeKnown before displaying
expectedFeeSat: when feeKnown is false the fee is only an estimate, and
warning may contain additional detail to show the user.
import { useWalletPrepareSend, useWalletSend } from '@lightninglabs/wavelength-react';
function SendLightning() { const { prepare, prepareData: quote, resetPrepare } = useWalletPrepareSend(); const { sendPrepared } = useWalletSend();
const doPrepare = async (bolt11: string) => { // prepare returns a fee quote and sendIntentId; prepareData holds it after. await prepare({ invoice: bolt11 }); };
const confirm = async () => { if (!quote) return; // sendPrepared dispatches the payment. await sendPrepared(quote); resetPrepare(); };
if (quote) { return ( <div> <p> Fee: {quote.expectedFeeSat} sats {!quote.feeKnown && ' (estimate)'} </p> {quote.warning && <p role="alert">{quote.warning}</p>} <button onClick={confirm}>Confirm payment</button> <button onClick={resetPrepare}>Cancel</button> </div> ); }
return <button onClick={() => doPrepare('lnbc...')}>Pay invoice</button>;}Send on-chain (cooperative leave)
An on-chain send performs a cooperative leave: the Ark server co-signs a transaction that moves your VTXO value to a plain Bitcoin address. This is faster and cheaper than a unilateral exit because the round happens in one confirmation.
Pass onchainAddress and amountSat to send() from useWalletSend(). The
returned SendResult includes the activity entry and actualAmountSat.
import { useWalletSend } from '@lightninglabs/wavelength-react';
function SendOnChain() { const { send, sendPending } = useWalletSend();
const handleSend = async (address: string, amountSat: number) => { await send({ onchainAddress: address, amountSat, }); };
return ( <button onClick={() => handleSend('bc1q...', 50_000)} disabled={sendPending}> Send on-chain </button> );}To drain the wallet to an address, set sweepAll instead of amountSat. The
daemon snapshots the live VTXO set at prepare time and spends exactly that set,
so quote and dispatch cannot disagree about what “all” means.
await send({ onchainAddress: address, sweepAll: true });Handle errors
send and sendPrepared from useWalletSend() throw a WavelengthError on
failure and set the hook’s sendError, which is always Error | null. Check
err.code for a machine-readable reason. Display err.message to the user.
Errors thrown during prepare (from useWalletPrepareSend()) are safe to
retry. Errors thrown during sendPrepared may indicate the payment was
already sent; check the activity feed before retrying.
import { useWalletSend, WavelengthError } from '@lightninglabs/wavelength-react';
function SafeSend() { const { send, sendError, resetSend } = useWalletSend();
const pay = async (bolt11: string) => { resetSend(); try { await send({ invoice: bolt11 }); } catch (err) { if (err instanceof WavelengthError) { console.error('Payment failed:', err.code, err.message); } } };
return ( <> <button onClick={() => pay('lnbc...')}>Pay</button> {sendError && <p role="alert">{sendError.message}</p>} </> );}