Skip to main content
An Arkade wallet can pay Lightning invoices and receive Lightning payments. A solver, a service that holds bitcoin on Lightning and within Arkade, carries each payment across. The swap client finds the solver, checks its quote, and moves the funds, so your code makes one call: pay with an invoice, or receive with an amount. When you pay an invoice, your bitcoin waits in an Arkade contract, called the lockup. The solver can claim the lockup only after it pays the invoice. If it never pays, the bitcoin comes back to you.

Setup

Install the SDK, the swap client, and nostr-tools, which the client uses to reach the solver:
You need an Arkade Wallet. To create one, see Create Your Wallet. During the developer preview, one solver serves Lightning on mainnet. Its card lists what it trades, its limits, and how to reach it. The client finds most solvers in a public registry, but the registry does not list this one yet. Save its card as beta-solver.card.json next to your code, and pin it with discovery.localCards:
Without the card, pay and receive throw UnsupportedRoute.
This card currently limits each payment to between 335 and 200,000 sats. The solver publishes its card on its relay. Load the latest card before you deploy, because its terms can change.
Keep one client running for the life of your app. The client claims and refunds swaps itself, but only while it runs. It derives each swap’s secrets from your wallet seed, saves each swap before it funds anything or returns an invoice, and registers the lockup with your wallet. Give it durable storage, as Choose Storage shows, so it resumes swaps after a restart. Your app shows the invoice and the outcome, and stores nothing.

Pay an Invoice

Pass the BOLT11 invoice to pay, with the most you agree to pay in fees:
pay gets a quote from the solver, checks it, and funds the lockup. The recipient gets exactly the invoice amount. You lock a little more, and the difference is the solver’s fee. maxFee caps that fee, in BTC. The payment ends with one of two outcomes:
  • paid: the solver paid the invoice and claimed the lockup. To claim, the solver needs the invoice’s preimage, which it gets only by paying the invoice.
  • refunded: the solver never paid, and your bitcoin came back. See Refund a Stalled Send.
pay also accepts an Arkade address. That is a plain wallet payment, not a swap, so pay returns { kind: 'payment', txid }. The snippet checks result.kind for this reason.

Catch Errors Before Money Moves

pay throws these errors before it funds anything:
  • AmbiguousDestination: the invoice expired, is for another network, or has no amount. The client contacted no solver.
  • AmountMismatch: you passed amount with an invoice that sets one. Leave amount out, even when the numbers match.
  • SwapRefusal: the solver declined, for example because the amount is outside its limits. The error carries the reason. Show it, or ask again.
  • MaxFeeExceeded: the quoted fee is more than maxFee.
  • QuoteVerificationFailed: the quote failed a check, such as an unsafe refund window or a lockup address that differs from the one the client derives.
  • InsufficientFunds: the wallet holds too little to fund the lockup.

Refund a Stalled Send

If the solver never pays the invoice, your bitcoin stays in the lockup until the quote’s refundLocktime passes. On the developer-preview solver, that is about a week after you pay. The client then submits the refund itself, and the outcome is refunded. If the client is not running at that time, it submits the refund when it next starts. If the outcome is needs_recovery instead, the swap needs your attention. See Recover a Swept Swap.

Receive a Payment

Keep the client running until the payer pays. The client claims the payment, so it must be running when the payment arrives. If the claim window closes first, the outcome is lapsed.
Pass receive the number of sats you want in your Arkade wallet. It returns a Lightning invoice to show the payer:
The invoice asks the payer for a little more than amount. The difference is the solver’s fee. receive returns only after it saves the swap, so this client can always claim the invoice you show. Do not show an invoice from quote alone: its claim secret is not saved yet. The invoice is a hold invoice. When the payer pays, the solver funds a lockup in Arkade with the sats you receive. Your client claims the lockup, which reveals the preimage the solver needs to settle the Lightning payment. Follow the swap until it reaches claimed or lapsed:
  • claimed: the sats are in your Arkade wallet.
  • lapsed: no payment reached your Arkade wallet. Show it as an expired request.
A receive never ends refunded. None of your money left your wallet, so nothing comes back.

LNURL and Lightning Addresses

To accept payments to a Lightning address (name@yourdomain) or an LNURL, your server answers a callback from the payer’s wallet with an invoice. The callback gives amount in millisatoshis (LUD-06): what the payer sends over Lightning. The invoice you return must ask for exactly that amount, or the payer’s wallet refuses it. Use quote and accept here instead of receive, because receive makes an invoice for more than its amount. Quote with amountOn: 'give', which fixes the amount the payer sends. Then call accept before you return the invoice as pr, so the client saves the claim secret before anyone sees the invoice:
Your Arkade wallet receives a little less than the payer sends. The difference is the solver’s fee. accept does not check a fee ceiling: only pay, receive, and exchange enforce maxFee. Compare quote.fee.amount with your limit before you call accept. To receive while the wallet is offline, run lnurl-server. With OFFLINE_SELF_CLAIM on, it claims each lockup itself, so the payment reaches the Arkade wallet while the wallet is closed. It can also give the wallet a Lightning address. See Lightning Services.

Want to learn more?

Swap Client

Storage, errors, outcomes, and recovery for every route.

Markets and Quotes

The model underneath: how quotes are requested, priced, and committed.

Corridor Swaps

The underlying contract: paired HTLCs, timeouts, and recovery.