From aab98a1bceffef4f067e1c9cfe381c8ed59308f0 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Thu, 25 Jun 2026 15:11:18 +0700 Subject: [PATCH] feat: add swaps and on-chain payments --- README.md | 1 + SKILL.md | 5 +- references/payments.md | 7 +++ references/swaps.md | 80 ++++++++++++++++++++++++++++++ references/unsupported-features.md | 3 -- 5 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 references/swaps.md diff --git a/README.md b/README.md index 3b2ef51..45ec488 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ npx skills add getAlby/hub-skill - [Debug Tools](./references/debug-tools.md) - [Channels](./references/channels.md) - [Payments](./references/payments.md) +- [Swaps](./references/swaps.md) - [Apps](./references/apps.md) - [LSP](./references/lsp.md) - [JIT Channels](./references/jit-channels.md) diff --git a/SKILL.md b/SKILL.md index 7673b4c..bb8c64c 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,10 +1,10 @@ --- name: alby-hub-skill -description: Manage a self-custodial Alby Hub lightning node via @getalby/hub-cli — setup, authentication, channels, LSP, backups, lightning and on-chain payments, and creating budgeted, scoped NWC app connections that give agents and apps controlled, revocable access to the wallet. +description: Manage a self-custodial Alby Hub lightning node via @getalby/hub-cli — setup, authentication, channels, LSP, backups, lightning and on-chain payments, swaps, and creating budgeted, scoped NWC app connections that give agents and apps controlled, revocable access to the wallet. license: Apache-2.0 metadata: author: getAlby - version: "0.1.4" + version: "0.1.5" --- # Alby Hub Agent Skill @@ -32,6 +32,7 @@ Use this skill to manage an Alby Hub lightning node via the CLI. - [JIT Channels: Default first-receive flow on LDK — a channel opens just-in-time on the first payment, fee deducted from it (LDK only)](./references/jit-channels.md) - [Channels: Open/close channels, peers, connect-peer, node connection info](./references/channels.md) - [Payments: Pay/make invoices, transactions, lookup, balances, wallet address](./references/payments.md) +- [Swaps: Swap on-chain bitcoin ↔ lightning (swap in / swap out), powered by boltz.exchange — confirm amounts and addresses before swapping](./references/swaps.md) - [Apps: NWC app management — create-app, list apps](./references/apps.md) - [QR Codes: Display invoices and NWC connection strings as QR codes using qrencode](./references/qrcodes.md) - [Custom Node Commands: Backend-specific node commands](./references/custom-node-commands.md) diff --git a/references/payments.md b/references/payments.md index faa1b09..2bd18b3 100644 --- a/references/payments.md +++ b/references/payments.md @@ -11,6 +11,12 @@ npx -y @getalby/hub-cli@0.5.0 get-balances # Get an on-chain deposit address npx -y @getalby/hub-cli@0.5.0 get-onchain-address +# Send an on-chain payment from the hub's on-chain wallet (confirm with human if channels are open and amount may cause anchor reserves may be drained) +npx -y @getalby/hub-cli@0.5.0 pay-onchain bc1... --amount 100000 + +# Sweep the entire on-chain balance to an address (confirm with human if any channels are open - anchor reserves will be drained) +npx -y @getalby/hub-cli@0.5.0 pay-onchain bc1... --all + # Pay a BOLT11 invoice npx -y @getalby/hub-cli@0.5.0 pay-invoice lnbc... @@ -45,3 +51,4 @@ On **LDK**, if the hub has no channel yet, a `make-invoice` for an amount above - `--amount` for `pay-invoice` is in millisatoshis (msat). Use for zero-amount invoices only. - `--amount` for `make-invoice` is in millisatoshis. - `get-balances` returns both lightning (channel) and on-chain balances. This is the **hub wallet** balance — it is **not** the balance of any individual app. +- `pay-onchain` `--amount` is in **sats**, and spends from the **on-chain** wallet (not lightning). Use `--all` to sweep the whole on-chain balance instead of `--amount`. The address is **not validated for you** — read the full address back to the user and get explicit confirmation before sending; an on-chain payment is irreversible. diff --git a/references/swaps.md b/references/swaps.md new file mode 100644 index 0000000..ceba884 --- /dev/null +++ b/references/swaps.md @@ -0,0 +1,80 @@ +# Swaps + +Swap between **on-chain bitcoin** and **lightning**, powered by [boltz.exchange](https://boltz.exchange): + +- **Swap in** — on-chain bitcoin → lightning balance. +- **Swap out** — lightning balance → on-chain bitcoin (the hub's own on-chain wallet, or an external address). + +Swaps are **bitcoin ↔ bitcoin**. The only "other cryptocurrency" path is via FixedFloat (see the end of this page). + +## ⚠️ Fund safety — confirm before every swap + +A swap moves real funds and **cannot be undone**. Before running any swap command, **always confirm with the user in plain language**: + +1. **The direction** (in or out) and **the amount** in **satoshis** (sats). +2. **For swap out:** the destination — the hub's own on-chain wallet, or an **external on-chain address**. If external, **read the full address back to the user and get explicit confirmation** — a wrong address means irreversible loss of funds, and the address is not validated for you. Payment will be initiated immediately and cannot be reversed. +3. **For swap in:** how it will be funded — from the **hub's on-chain wallet** or an **external wallet** (see the flow below). +4. **That fees apply** (see below). + +Only run the command once the user has confirmed. Never guess the amount or an address. + +## Swap in (on-chain → lightning) + +This is a **two-step** flow: + +1. **Generate the swap** with `swap-in --amount `. This returns the swap details, including: + - `lockupAddress` — the on-chain bitcoin address to deposit to. + - `sendAmountSat` — the **exact** on-chain amount to deposit (this is higher than the requested amount; it includes the boltz fees). + - `id` — the swap ID, and `state` (starts as `PENDING`). + + **Show the user the deposit address and the exact amount.** You can display it as a QR code (`bitcoin:?amount=`, where `amountInBTC = sendAmountSat / 100000000`) — see [QR Codes](./qrcodes.md). + +2. **Fund the deposit.** Send exactly `sendAmountSat` to `lockupAddress` from any on-chain wallet — the user's external wallet, or the hub's own on-chain balance. The user sends the shown amount to the shown address themselves; there is no swap-specific CLI step for this. You can offer to pay with the hub's on-chain funds, but the human should confirm before any action is taken. + +Once the deposit confirms on-chain, boltz pays the hub's lightning invoice and the swap reaches `SUCCESS`. Track progress with `lookup-swap `. + +```bash +# 1. Generate a swap to receive 100,000 sats on lightning +npx -y @getalby/hub-cli@0.5.0 swap-in --amount 100000 + +# 2. Send the shown sendAmountSat to the shown lockupAddress from any on-chain wallet +``` + +## Swap out (lightning → on-chain) + +`swap-out` spends from the hub's lightning balance and pays the boltz invoice automatically, then claims the on-chain funds to the destination — there is no second step. + +```bash +# Swap lightning out into the hub's own on-chain wallet (amount received on-chain, in sats) +npx -y @getalby/hub-cli@0.5.0 swap-out --amount 100000 + +# Swap lightning out to an external on-chain address +npx -y @getalby/hub-cli@0.5.0 swap-out --amount 100000 --destination bc1... +``` + +- `--amount` is the amount **received on-chain**, in **sats**. boltz adds its fees on top, so the lightning amount actually spent (`sendAmountSat`) is higher. +- Omitting `--destination` swaps into the hub's own on-chain wallet; providing it sends to an external on-chain address. + +## Checking a swap + +```bash +# Look up a swap by its swap ID (state is PENDING, SUCCESS, FAILED or REFUNDED) +npx -y @getalby/hub-cli@0.5.0 lookup-swap +``` + +## Before swapping + +- Check balances first with `get-balances`. Swap **out** needs enough **lightning** spendable balance (amount + fees); swap **in** funded from the hub needs enough **on-chain** spendable balance (`sendAmountSat` + on-chain fees). +- Fees are `albyServiceFee% + boltzServiceFee% + on-chain fees`. boltz also enforces minimum and maximum swap amounts; an out-of-range amount is rejected with an error message. + +## Swap to/from another cryptocurrency + +To swap into or out of a **different cryptocurrency** (e.g. an external ETH/USDT address), there is no boltz swap — it goes through [FixedFloat](https://ff.io): + +- **Out to another coin:** send the user `https://ff.io/?from=BTCLN&ref=qnnjvywb`. They choose the target coin and address; FixedFloat returns a **lightning invoice**; pay it from the hub with `pay-invoice` (see [Payments](./payments.md)) after confirming the amount. +- **In from another coin:** create a lightning invoice with `make-invoice`, then send the user `https://ff.io/?to=BTCLN&address=&ref=qnnjvywb` to pay it with their other cryptocurrency. + +## Notes + +- All swap amounts are in **sats**, not millisats (unlike `pay-invoice` / `make-invoice`). +- If a swap shows `FAILED`, advise the user that locked-up funds can be recovered via the **Swap Refund** tool in the Alby Hub web interface (Settings → Debug Tools). CLI refunds are not yet supported. diff --git a/references/unsupported-features.md b/references/unsupported-features.md index 936dfeb..49a22b9 100644 --- a/references/unsupported-features.md +++ b/references/unsupported-features.md @@ -4,12 +4,9 @@ The hub CLI is experimental and incomplete. The following features are **not** a ## Bitcoin & liquidity -- Swaps (manual swaps in/out) - Auto-swaps - Buy bitcoin -- Exchange bitcoin with stablecoin / crypto - Pay for a channel with stablecoin / crypto -- Send on-chain payments ## Node & wallet management