Source: https://wavelength.lightning.engineering/introduction/what-is-wavelength-sdk.md
# What is the Wavelength SDK?
## What it is
The Wavelength SDK is a TypeScript library for embedding a **self-custodial Bitcoin, Lightning, and Ark wallet** directly in your web or mobile app. Your users hold their own keys. You do not run a wallet server on their behalf. (Building a pure-native Kotlin or Swift app? The same embedded wallet ships as a [native mobile SDK](/native-ios-android/overview/) too.)
The full wallet daemon ([wavelength](https://github.com/lightninglabs/wavelength)) is compiled to run **inside your app**: to WebAssembly in the browser on web, and into the app binary on mobile. There is no separate process, no local socket, and no backend you must operate to host the wallet logic. Your app boots the daemon and talks to it through a typed client.
The Wavelength SDK ships as these packages:
Package
`@lightninglabs/wavelength-core`
Role
Shared types, the WavelengthClient interface, errors, and enums. No DOM, no transport.
Package
`@lightninglabs/wavelength-web`
Role
The browser WASM transport. Framework-agnostic: use it from vanilla JS, Vue, Svelte, or React. Re-exports core.
Package
`@lightninglabs/wavelength-react-native`
Role
The React Native transport. The daemon is compiled into the app binary. Re-exports core.
Package
`@lightninglabs/wavelength-react`
Role
React provider and hooks. Depends on core only and takes an injected client, so the same binding can run over the web or React Native transport.
**React is the primary developer experience**, but React is not required. On web, if you are not using React, install `@lightninglabs/wavelength-web`, call `createWebClient()`, and use the `WavelengthClient` methods directly. The React package is a thin binding over the same interface.
Native iOS and Android apps are covered as well: the [wavelength-mobile](https://github.com/lightninglabs/wavelength-mobile) wrappers embed the same daemon behind idiomatic Kotlin and Swift APIs, documented in the [Native iOS & Android](/native-ios-android/overview/) section.
## Mental model
Think of the Wavelength SDK as **your app talking to a local wallet daemon**, not your app talking to a remote wallet API.
Your app
WavelengthClienttyped API
Embedded daemonin your app
GatewaysArk · Swap · Esplora
1. **Your app** calls methods like `balance()`, `send()`, and `deposit()` on a `WavelengthClient`.
2. **The client** forwards those calls to the embedded daemon running in your app (on web, in a Web Worker by default, or the main thread if configured).
3. **The daemon** holds keys, tracks VTXOs, coordinates Ark rounds, and reaches out to public infrastructure when it needs chain data, mailbox relay, or Lightning swaps.
The daemon connects to three backend services configured in `RuntimeConfig`:
- **Ark operator + mailbox** (`arkServerAddress`) for rounds and VTXO/mailbox relay
- **Swap server** (`swapServerAddress`) for Lightning↔Ark atomic swaps
- **Esplora** (`walletEsploraUrl`) for chain and UTXO queries
On web, the Ark and swap addresses are REST URLs. On React Native, they are `host:port` gRPC addresses. `walletEsploraUrl` is an HTTP Esplora endpoint on both platforms.
Your app never calls those gateways directly. It only talks to the local daemon. **User keys never leave the device.** Seed generation, signing, and wallet state live on the device (OPFS in the browser; the app data directory on React Native). The gateways see protocol traffic, not your users’ private keys.
A minimal boot sequence looks like this:
```ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
const client = createWebClient({ runtimeBaseUrl: '/wavewalletdk/' });
await client.ready(); // load WASM runtime
await client.start(defaultConfig('signet')); // boot daemon, connect gateways
const { mnemonic } = await client.createWallet({ password: '…' }); // show mnemonic to the user for backup
```
```ts
import { createNativeClient, defaultConfig } from '@lightninglabs/wavelength-react-native';
const client = createNativeClient();
await client.ready(); // ready the native runtime
await client.start(defaultConfig('signet')); // boot daemon, connect gateways
const { mnemonic } = await client.createWallet({ password: '…' }); // show mnemonic to the user for backup
```
From here, use the same client for deposits, Lightning receive, sends, and activity. The [System architecture](/introduction/system-architecture/) page explains how those operations map to Ark, swaps, and on-chain exits.
---
Source: https://wavelength.lightning.engineering/introduction/the-wavelength-system.md
# The Wavelength system
## What Wavelength is
Wavelength adds self-custodial Bitcoin and Lightning payments to any app with a handful of API calls. Your users get a real wallet that can send and receive over the Lightning Network, and you never run a Lightning node, open channels, or source liquidity to make it work. If you can call an API, you can accept Bitcoin. (Stablecoin support is on the way through Taproot Assets, over the same wallet surface.)
The value is in what you do **not** have to run. Accepting Lightning payments has traditionally meant operating a node, balancing channels, sourcing liquidity, and keeping all of that healthy around the clock. Wavelength runs that machinery and gives you a small, friendly wallet surface in its place, so the capability (instant, global, low-fee payments) is available without the operational burden.
> **This does not replace running your own node**
>
> Lightning has always offered a fully self-sovereign path where you run your own node and channels, and that path is not going anywhere. Wavelength is for everyone who would rather not run infrastructure at all, without giving up control of their funds.
## What you run vs. what is managed
You run **Wavelength**, the self-custodial client (compiled from [wavelength](https://github.com/lightninglabs/wavelength)). It holds the user’s keys, tracks their balance, and builds and signs their payments. Everything the wallet talks to is managed for you.
Your app
wallet API
Wavelengththe client you run · holds keys, signs paymentswebnativeserverMCP
exit
BOLT 11
Bitcoin on-chainyours anytime, no permission
Wavelength Operatormanaged for you · coordination + settlementdeep liquidity via Loop
Lightning Network
| Piece | Who runs it | What it does |
| ----------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Wavelength** (the client) | You | Holds keys, tracks balances, builds and signs payments. Runs as an embedded SDK in a web or mobile app, as a standalone server, or as an MCP server for agents. |
| **Coordination + settlement** | Wavelength Operator | An Ark-like layer that batches off-chain transfers instantly and cheaply. It settles between users but never takes unilateral control of anyone’s funds. |
| **Liquidity via Loop** | Wavelength Operator | Deep Lightning liquidity through Loop, so payments route reliably without you sourcing, funding, or managing channels. |
| **The Lightning Network** | Open network | Everything speaks BOLT 11, so you can pay and be paid by any wallet, exchange, or app already on Lightning. |
## Self-custodial, exit anytime
Users hold their own keys. The coordination service settles transfers but never has unilateral control of anyone’s money, and a user can always move their balance back to the Bitcoin blockchain on their own, at any time, without anyone’s cooperation. The wallet exposes this directly through an `exit` command. You get the convenience of a managed payments experience with the trust guarantees of holding your own Bitcoin. See [Leaving Ark](/concepts/leaving-ark/) for how exits work in practice.
## One invoice for everything
Every payment you send and receive is a BOLT 11 invoice, the standard Lightning format. There is a single payment format to learn, and your wallet works with the entire Lightning Network the moment you integrate it. A normal Bitcoin address only shows up at the edges, when funding a wallet or moving funds back to the chain. See [Lightning payments are swaps](/concepts/lightning-payments-are-swaps/) for how a Lightning payment maps to an Ark swap under the hood.
## Ways to integrate
Wavelength meets you where you build:
- **Embedded SDK** - pull the SDK into your app to put a wallet directly inside it. It runs on web (compiled to WebAssembly, in a browser tab) and on mobile (compiled into the app binary), on React Native or on native Kotlin and Swift. Start with the [Web quickstart](/web/get-started/quickstart/), the [React Native quickstart](/react-native/get-started/quickstart/), or the [Native iOS & Android quickstart](/native-ios-android/quickstart/).
- **As a standalone client** - a single self-contained process driven over a gRPC and REST API.
- **For agents** - run the MCP server so an AI agent can drive the wallet as typed tool calls. Wallet creation and unlock stay off the agent channel, so seeds and passwords are never exposed to a model.
These docs focus on the SDK (web, React Native, and native iOS and Android). The other surfaces share the same wallet commands and are documented separately.
## Networks
Wavelength runs on signet and testnet by default, and both are open to everyone. These test networks let you build and exercise the full payment flow with coins that have no real value. Mainnet access is gated to an approved allowlist and requires an explicit opt-in. See [Networks and config](/concepts/networks-and-config/) for the details.
## Where to go next
- [What is Wavelength?](/introduction/what-is-wavelength-sdk/) - the client you run, and the packages it ships as.
- [Web quickstart](/web/get-started/quickstart/), [React Native quickstart](/react-native/get-started/quickstart/), or [Native iOS & Android quickstart](/native-ios-android/quickstart/) - a running wallet in a few minutes.
- [System architecture](/introduction/system-architecture/) - how the embedded daemon connects to the backend gateways.
---
Source: https://wavelength.lightning.engineering/introduction/system-architecture.md
# System architecture
## The embedded daemon
The Wavelength SDK embeds [wavelength](https://github.com/lightninglabs/wavelength), the same wallet daemon on every platform, and runs it wherever your app runs. On the web it is compiled to WebAssembly and runs inside the browser; you boot it with `createWebClient()` and `client.start()`. On React Native it is compiled into your app binary and runs on device; you boot it with `createNativeClient()` and `client.start()`. Either way it stays running for the session, and you drive it through one typed `WavelengthClient`.
**`WavelengthClient` is the app-facing API.** It is defined in `@lightninglabs/wavelength-core` and implemented by each transport (`@lightninglabs/wavelength-web` on the web, `@lightninglabs/wavelength-react-native` on React Native). Your code never imports Go or protobuf types. You call typed methods (`deposit`, `receive`, `send`, `balance`, and so on) and receive plain JavaScript objects.
The wire protocol between the client and the embedded daemon differs by transport, and from your app’s perspective it is an implementation detail. The React Native transport uses the daemon’s native gRPC. Browsers cannot open arbitrary gRPC connections the way a native host can, so the web transport uses **REST** against the embedded daemon’s HTTP facade instead. You still hold one `WavelengthClient` and call the same methods on either.
The daemon also owns local persistence (wallet database, swap state) and signing. Your app configures *where* the daemon connects on the network, not *how* it stores keys.
## The three backend gateways
Once started, the daemon reaches three external backend services. Configure them in `RuntimeConfig` (or use your transport package’s `defaultConfig(network)` for the public signet and testnet presets):
```ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
const client = createWebClient();
await client.ready();
await client.start(defaultConfig('signet'));
// arkServerAddress, walletEsploraUrl, swapServerAddress pre-filled for signet
```
```ts
import { createNativeClient, defaultConfig } from '@lightninglabs/wavelength-react-native';
const client = createNativeClient();
await client.ready();
await client.start(defaultConfig('signet'));
// arkServerAddress, walletEsploraUrl, swapServerAddress pre-filled for signet
```
| Field | Service | What the daemon uses it for |
| ------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `arkServerAddress` | Ark operator and mailbox edge | Ark **rounds**, VTXO lifecycle, and **mailbox relay** for out-of-round transfers. A REST URL on web; a `host:port` gRPC address on React Native. |
| `swapServerAddress` | Swap server | **Lightning↔Ark atomic swaps** (Lightning receive and send). A REST URL on web; a `host:port` gRPC address on React Native. |
| `walletEsploraUrl` | Esplora-compatible indexer | **Chain and UTXO queries**; an HTTP endpoint on both platforms that implements the Esplora `/address/:addr/utxo` API |
**Mailbox and operator share one edge.** VTXO mailbox traffic goes through the same Ark server URL. There is no separate `mailboxUrl` in `RuntimeConfig`; the redundant mailbox fields were removed so you configure a single Ark gateway.
Lightning is not a fourth gateway. Lightning send and receive are **swap operations** routed through `swapServerAddress`. On-chain visibility (boarding deposits, cooperative leaves, unilateral exits) goes through Esplora and the Ark operator as appropriate.
Set `disableSwaps: true` if you only need Ark and on-chain flows without Lightning. It suppresses the preset and any override swap fields.
## How boarding, swaps, and exits connect
Wavelength unifies three payment rails behind one balance and one activity stream. Here is how each rail uses the gateways above.
### On-chain in: boarding
1. Your app calls `deposit()` and gets a **boarding address** (a standard Bitcoin receive address).
2. The user sends on-chain BTC to that address.
3. Esplora lets the daemon see the UTXO arrive.
4. At the **next Ark round** (interval is operator-configured, often well under a minute), the deposit **boards** into Ark and becomes a **VTXO** you can spend instantly off-chain.
Until boarding completes, funds show as pending inbound balance.
### Lightning: swaps
Wavelength does not open Lightning channels in the browser. Lightning payments are **atomic swaps** between Lightning and Ark:
- **Receive Lightning** (`receive()`) starts a **receive swap**: the swap server holds a Lightning invoice; when it is paid, you receive Ark balance (a VTXO).
- **Send Lightning** (`send({ invoice })`) runs a **send swap**: you spend Ark balance; the swap server pays the BOLT-11 invoice on Lightning. For a quote-then-confirm flow instead of a single call, see `prepareSend()`/`sendPrepared()`.
Both directions go through `swapServerAddress`. Swaps can sit in a pending state while liquidity or routing resolves; the daemon tracks them in your activity stream and can refund if a swap expires.
### On-chain out: leave and exit
Spending to a normal Bitcoin address uses a **cooperative leave**: the Ark operator helps convert your VTXO into an on-chain transaction. This is the default path for `send({ onchainAddress })`. It is faster and cheaper than exiting alone.
**Unilateral exit** is the emergency fallback. If the operator is unavailable or you need to recover funds without cooperation, the daemon can execute a unilateral exit from a VTXO. It is slower, more expensive, and intended for edge cases. Use `exit()` and `exitStatus()` on `WavelengthClient` for this path; see [Unilateral exit](/guides/unilateral-exit/) for integration details. Call `getExitPlan()` first to preview readiness and backing-wallet funding requirements before triggering `exit()`.
On-chain in
Deposit
Boarding
Round
VTXO
Lightning receive
Receive swap
Swap server
VTXO
Lightning send
Send swap
Swap server
Invoice paid
On-chain send
Cooperative leave
Operator + Esplora
Emergency
Unilateral exit
On-chain
For deeper detail on each rail, see [Balances & VTXOs](/concepts/balances-and-vtxos/), [Lightning payments = swaps](/concepts/lightning-payments-are-swaps/), and [Leaving Ark](/concepts/leaving-ark/).
---
Source: https://wavelength.lightning.engineering/concepts/balances-and-vtxos.md
# Balances & VTXOs
## VTXOs
Wavelength tracks spendable funds the way Ark does: as off-chain outputs bundled into the operator’s periodic batch transactions, not as a single on-chain UTXO in your app. The sections below walk through that model, how boarding turns into live balance, and why confirmed and pending numbers can differ.
In Ark, your spendable balance is not a single on-chain UTXO in your wallet. It is a set of **Virtual Transaction Outputs (VTXOs)**: off-chain outputs created inside the operator’s periodic **batch transactions**. Each batch builds a Virtual Transaction Tree (VTXT) whose leaves are individual VTXOs assigned to participants.
A VTXO behaves like a UTXO you can spend, but it lives in Ark’s shared batch structure until you exit, refresh, or the operator sweeps expired batches. Wavelength tracks every live VTXO the daemon knows about and uses them as inputs when you send, swap, or leave.
Each VTXO carries two spend paths:
- **Collaborative path**: you and the Ark operator co-sign, so the output can move instantly inside Ark (for example through a cooperative leave or an out-of-round transfer).
- **Unilateral exit path**: you alone can broadcast after a relative timelock (CSV delay) if the operator stops cooperating.
VTXOs also have a **sweep delay** tied to their batch. Before the operator becomes eligible to sweep the batch, the VTXO should either be refreshed through a **batch swap** (getting a fresh VTXO in a newer batch) or moved to on-chain Bitcoin. This refresh is daemon-driven: Wavelength detects the approaching expiry and submits the batch swap for you, so there is no method to call yourself. Wavelength surfaces expiry pressure through balance and activity updates rather than asking you to manage raw outpoints manually.
> **Note**
>
> `balance()` and `list({ view: 'vtxos' })` read from the embedded daemon’s view of live VTXOs. The operator’s indexer is authoritative on the network side; the daemon reconciles that view as rounds complete and swaps settle.
## Confirmed vs pending
When you show balance in a UI, users usually want one “available” number. Wavelength splits the picture into confirmed and pending fields so you can explain why spendable funds lag behind what they see on-chain or in the activity feed.
`balance()` returns three satoshi fields that answer different questions:
- **`confirmedSat`**: funds you can treat as settled inside the wallet. These VTXOs are live, unencumbered, and available for send, swap, or leave.
- **`pendingInSat`**: inbound value still moving through boarding, a Lightning swap, or a round that has not finished yet. A deposit may be visible on-chain before it becomes a VTXO; a Lightning receive stays pending until the swap server funds and you claim the **virtual HTLC (vHTLC)** (see [Lightning payments are swaps](/concepts/lightning-payments-are-swaps/)).
- **`pendingOutSat`**: outbound value reserved by in-flight sends, swap funding, or leave requests. The wallet holds these amounts aside so you do not double-spend the same VTXOs while an operation is still settling.
Confirmed satoshis are what you show as “available.” Pending fields explain why the total on-screen balance can differ from what a user can spend right now, and why a payment can appear in the activity feed before it affects confirmed balance.
Pair `balance()` with the activity stream when you need per-operation detail: each **Entry** carries its own status (`pending`, `complete`, or `failed`) and a coarse phase (for example `waiting_for_payment`, `settling`, or `confirmed`) that describes where that specific operation sits in its pipeline. See the [wavelength-core reference](/reference/wavelength-core/) for the full **Balance** and **Entry** shapes.
## Boarding → rounds → VTXO lifecycle
On-chain Bitcoin enters Ark through **boarding**:
1. `deposit()` returns a **boarding address** (and an initial deposit activity entry). Send on-chain BTC to that address like any other receive address.
2. The operator watches the chain via Esplora. Once the deposit confirms and meets the operator’s policy, the wallet submits a **boarding request** for the next **round**.
3. Rounds advance on a fixed cadence (often around one minute on public test networks; roughly 90 seconds end-to-end is a reasonable UX expectation). During request collection the operator aggregates boarding inputs, VTXO creations, leaves, and batch swaps into one batch transaction.
4. When the round completes, your boarded sats become one or more **VTXOs** in the new batch. The deposit entry moves from pending toward confirmed, and confirmed balance increases.
Every VTXO you receive this way inherits the batch’s **CSV expiry** (sweep delay). As that horizon approaches, the VTXO is **refreshed** with a **batch swap**: forfeiting the old VTXO in a round in exchange for a fresh one in a newer batch, which resets the expiry clock. Wavelength handles the round interaction; you do not pick batch transaction IDs yourself. Operators can advertise a **free refresh window**: a late-lifetime span of blocks in which a pure refresh gets its fee waived. When that waiver can be reached without weakening exit safety, the daemon delays the automatic refresh into the window; the advertised width surfaces as `serverInfo.freeRefreshWindowBlocks` on [`getInfo()`](/reference/wavelength-core/#getInfo). See [Leaving Ark](/concepts/leaving-ark/) for the `exit()` / `getExitPlan()` / `sweepWallet()` APIs that apply if you choose to move funds on-chain instead of refreshing.
> **Tip**
>
> Boarding is not instant. Show pending inbound balance and the deposit entry’s phase (`waiting_for_confirmation`, then `settling`) so users know their on-chain payment is recognized but not yet spendable inside Ark.
Lightning receives and sends also end in VTXOs (via vHTLC outputs), but on-chain boarding is the path most integrators hit first: address → wait for round → live VTXO → refresh before expiry.
---
Source: https://wavelength.lightning.engineering/concepts/activity-and-events.md
# Activity & events
## The activity stream
Every deposit, payment, and exit shows up in one ordered history. Wavelength keeps that history as a stream you can snapshot once or subscribe to for live updates as operations settle.
Wallet history in Wavelength is an **activity stream**: a sequence of **Entry** objects, one per user-visible wallet operation. Deposits, Lightning receives, sends, and cooperative exits each create an entry when they start and update in place as they settle.
You can read history in two ways:
- `list({ view: 'activity' })` for a paginated snapshot. Filter with `pendingOnly` or `kinds`, and page with `limit` and the string list cursor:
```ts
const result = await client.list({
view: 'activity',
kinds: ['receive', 'deposit'],
});
const { activity } = result;
// activity.entries, activity.hasMore, activity.nextCursor
```
- `startActivity()` for a live stream that pushes updates until you call `stopActivity()`.
For UI work, prefer the live stream. Call `subscribe()` first to register a listener, then open the stream with the entry kinds you need. Persist the last numeric `Entry.cursor` you processed and pass it back to resume after that position:
```ts
await client.startActivity({
includeExisting: true,
kinds: ['send', 'receive'],
});
await client.startActivity({
kinds: ['send', 'receive'],
cursor: lastCursor,
});
```
Each update arrives as a **WavelengthEvent** with `type: 'activity'` and the changed entry as payload. Pending entries mutate (status, phase, txid, confirmation height) until they reach `complete` or `failed`.
> **Note**
>
> `stopActivity()` closes the stream but leaves your `subscribe()` listener registered. You can call `startActivity()` again later without re-subscribing.
Direct clients also receive a terminal `activityStream` event when a stream ends or fails. The client does not retry automatically, so a direct consumer chooses its own retry policy. The current bridges do not provide structured gap cursor or reason details.
`WalletEngine` manages recovery for UI consumers. It reconciles the snapshot with `list()`, retries from its last safe numeric stream cursor with bounded backoff, and resets that cursor with the runtime lifecycle. Replayed activity events trigger idempotent snapshot refreshes instead of direct row appends. This provides eventual snapshot consistency after reconnects without duplicating entries. The React `useWalletActivity` hook reads that engine snapshot, ordered newest-first, and re-renders as reconciliation completes.
## Event model
Beyond activity rows, the client emits runtime and log events on the same subscription. Narrow on the event type before reading its payload.
**WavelengthEvent** is a discriminated union. Narrow on `type` before reading `payload`:
All five event types arrive through the same `subscribe(listener)` callback registered once.
| `type` | Payload | Meaning |
| ------------------ | ------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `'activity'` | `Entry` | A wallet operation was created or updated. |
| `'activityStream'` | `{ state: 'ended' }` or `{ state: 'failed', message }` | The activity stream ended or failed without a consumer-initiated close. |
| `'log'` | `{ level, message }` | Daemon log line forwarded for debugging (`debug`, `info`, `warn`, `error`). |
| `'runtimeReady'` | none | The embedded runtime finished loading and can accept `start()`. |
| `'runtimeStopped'` | none | The runtime stopped, including an unsolicited stop surfaced by the transport. |
Activity events carry the full entry shape:
- **kind**: high-level category (see below).
- **status**: collapsed outcome: `pending`, `complete`, or `failed`.
- **amountSat** / **feeSat**: principal and fees in satoshis (plain int64 magnitudes; use the entry’s **kind** to determine direction).
- **cursor**: the monotonic stream position of this update. It is zero for entries returned outside the subscription path.
- **progress**: optional lifecycle metadata (phase, payment hash, txid, confirmation height, VTXO outpoint, and Lightning preimage).
- **request**: optional echo of what the user initiated (Lightning invoice, on-chain address, etc.).
- **failureReason** / **failureCode**: populated when status is `failed`. **failureCode** is one of:
- **`timed_out`**: the operation exceeded its deadline before completing.
- **`expired`**: the underlying invoice, address, or quote expired before it was used.
- **`refunded`**: a swap or payment failed and funds were returned to the wallet.
- **`needs_intervention`**: the operation stalled in a state that requires manual follow-up.
- **`failed`**: a generic failure not covered by the other codes.
When known, `EntryProgress.preimage` is the hex-encoded Lightning payment preimage. For a completed Lightning-backed send, it is proof of payment for the invoice.
**Entry kinds** group operations for filtering and display:
- **`deposit`**: on-chain boarding (`deposit()` address flow).
- **`receive`**: inbound Lightning receive settled through an atomic swap into Ark.
- **`send`**: outbound payment (Lightning send swap, cooperative on-chain leave, or in-Ark send).
- **`exit`**: cooperative leave of VTXOs to a plain on-chain address.
For a `send` entry, `entry.request?.type` (`'lightning'`, `'onchain'`, or `'ark'`) tells you which rail the payment actually used; when preparing a send, `PrepareSendResult.rail` reports the same thing ahead of time. See the [wavelength-core reference](/reference/wavelength-core/) for the full field list.
**Entry phases** sit one level below status and describe *where* a pending operation is in its pipeline. They do not replace status; use both. Common phases include:
- **`request_created`**: the wallet recorded the intent but nothing external has happened yet.
- **`waiting_for_payment`**: waiting for a counterparty (Lightning payer, swap funder, or boarding confirmation).
- **`waiting_for_confirmation`**: an on-chain tx is seen but not yet deep enough in the chain.
- **`payment_detected`** / **`settling`**: value is in motion through Ark, Lightning, or swap machinery.
- **`confirmed`**: terminal success from the backing subsystem’s perspective.
- **`refunding`** / **`refunded`**: swap timeout or cancellation path returning funds.
- **`failed`**: terminal failure.
You can switch on the progress phase for structured UI states, or render the phase label directly when you want the daemon’s short label without maintaining a mapping table. Field definitions live in the [wavelength-core reference](/reference/wavelength-core/).
For the React hook signatures built on this stream, see the [wavelength-react reference](/reference/wavelength-react/). For how activity and events relate to the client’s broader startup and connection state, see [Wallet lifecycle & auth](/concepts/wallet-lifecycle-and-auth/).
---
Source: https://wavelength.lightning.engineering/concepts/wallet-lifecycle-and-auth.md
# Wallet lifecycle & auth
## WalletState & phases
Before you enable send or receive, the wallet must exist, unlock, and finish syncing with the operator. The Wavelength SDK exposes that readiness at two related layers: a daemon-reported wallet state and a broader runtime phase your UI can render.
**WalletState** (on **WalletInfo**) is a lowercase string union the SDK normalizes from the daemon’s numeric wallet-state field:
| Value | Meaning |
| ----------- | -------------------------------------------------------------------- |
| `'none'` | No wallet exists yet, or the runtime has not loaded wallet metadata. |
| `'locked'` | A wallet is present but encrypted; credentials are required. |
| `'syncing'` | Unlocked and catching up with the operator, indexer, and chain view. |
| `'ready'` | Unlocked, synced, and safe to spend. |
The SDK maps the daemon’s numeric proto enum to these strings at the boundary (`walletStateFromProto`). The wallet-ready flag is also exposed and is `true` exactly when wallet state equals `'ready'`.
**RuntimePhase** is the broader lifecycle a host UI renders. It merges runtime boot/shutdown with wallet state, plus one phase the engine owns on its own:
- **Runtime-owned phases**: `loading`, `runtimeReady`, `starting`, `stopping`, `stopped`, `error`. The `WalletEngine` (from `@lightninglabs/wavelength-web`’s `createWebWalletEngine`, or the equivalent React Native factory) sets these while the runtime loads, `start()` runs, or `stop()` tears down. A failed `start()` or `stop()` lands directly on `'error'`; there is no separate failed sub-phase to branch on. `WavelengthProvider` (from `@lightninglabs/wavelength-react`) owns nothing here: it just publishes whichever engine you hand it. If you are calling `wavelength-core`/`wavelength-web` directly without an engine, you own deriving this state machine yourself.
- **Wallet-owned phases** (derived by `phaseFromInfo()` in core): `needsWallet`, `locked`, `syncing`, `ready`.
- **Engine-owned phase**: `restoring`. Unlike the wallet-owned phases, this one is never derived from `WalletInfo`; the engine enters it itself whenever `restoreWallet()` (`useWalletRestore()`’s `restore` in React) is called, and leaves it once the restored wallet reports ready. When the call sets `recoverState: true`, the server-assisted recovery scan is additionally tracked through `snapshot.recovery` for the lifetime of the scan. See [Restore a wallet](/guides/restore-a-wallet/) for the full flow.
After `createWallet()` or a successful unlock, the daemon often lands in the syncing phase while it replays local state and reconciles with the operator. The `WalletEngine` **automatically advances syncing to ready**: while its phase is `'syncing'`, it polls `refresh()` every 2000ms until the phase leaves syncing, so you do not call a separate “finish sync” API. If you are integrating `wavelength-core`/`wavelength-web` directly without an engine, you must poll `getInfo()` or subscribe to activity events yourself to detect the syncing-to-ready transition. Show a spinner on `'syncing'` (and `'restoring'`) and enable send/receive controls only on `'ready'`.
> **Caution**
>
> Treat `'none'` and `'locked'` as non-spendable. Do not infer readiness from a successful unlock alone; wait for `'ready'` (or `walletReady === true`) before exposing payment actions.
Use `getInfo()` / `useWalletInfo()` for the current wallet state, and `phaseFromInfo()` when you want a single enum for gating UI across web and future native transports. See the [wavelength-core reference](/reference/wavelength-core/) for **WalletEngine**, **WalletState**, and **RuntimePhase** definitions.
## Password vs passkey
Both paths protect the same underlying wallet seed, but they derive the encryption key differently. Choose one at setup time; after unlock, the lifecycle is the same.
**Password unlock** is the baseline flow:
1. `createWallet({ password })` generates (or imports) a mnemonic and creates the wallet database with its key material encrypted under the password.
2. `unlockWallet({ password })` opens the wallet database with that password on each session.
The password never leaves your app as plaintext on the wire; the SDK encodes it for the daemon’s unlock RPC. Users must remember the password (and back up the mnemonic if you expose it).
```ts
// After the runtime reaches 'runtimeReady' and start() has resolved:
await client.createWallet({ password });
// On a later session, unlock the existing wallet instead:
await client.unlockWallet({ password });
// Either call leaves the daemon syncing or ready; read the current phase from getInfo().
const info = await client.getInfo();
const phase = phaseFromInfo(info);
// Render UI from phase: 'syncing' shows a spinner, 'ready' unlocks send/receive.
```
In React, the same flow runs through `useWalletCreate()` and `useWalletUnlock()`, which wrap the calls above with verb-prefixed pending/error/data state (`createPending`/`createError`/`createData` and `unlockPending`/`unlockError`/`unlockData`) and refresh the engine’s snapshot afterward, so `phase` from `useWallet()` advances on its own.
**Passkey unlock** replaces the password with a **WebAuthn PRF output**:
1. During setup, a platform **PasskeyCeremony** derives a deterministic secret (`prfOutput`) bound to the user’s passkey.
2. `openWalletFromPasskey({ prfOutput })` unlocks (or creates) the wallet using that secret instead of a typed password.
```tsx
import { webPasskeyCeremony } from '@lightninglabs/wavelength-web';
import { useWalletPasskey } from '@lightninglabs/wavelength-react';
function PasskeyOnboard() {
const { create, createPending } = useWalletPasskey(webPasskeyCeremony);
// First run: register a passkey and create the wallet from its PRF output.
const handleCreate = () => create('My Wallet App');
return (
Create wallet with passkey
);
}
function PasskeyUnlock({ credentialId }: { credentialId: string }) {
const { open, openPending } = useWalletPasskey(webPasskeyCeremony);
// Returning session: assert the existing passkey to unlock.
const unlock = () => open(credentialId);
return (
Unlock with passkey
);
}
```
Passkeys tie unlock to a device and biometric or PIN verification. They remove password re-entry but introduce platform constraints (browser support, secure context, cross-origin isolation on web). The Wavelength SDK folds passkey into the engine via an injected passkey ceremony; the core client only sees the PRF bytes.
> **Note**
>
> WebAuthn registration, PRF extension availability, and credential persistence are platform mechanics. See [Use a passkey](/guides/use-a-passkey/) for browser-specific steps; this page is the shared mental model.
From the wallet lifecycle perspective both paths converge: after unlock the daemon enters syncing, then ready. A restore (either path) instead passes through the engine-owned `'restoring'` phase first; recovery tracking through `snapshot.recovery` only kicks in when the call sets `recoverState: true`. Choose password for the simplest cross-device story; choose passkey when you want passwordless return visits on a trusted device. For what the seed and passwords actually are, and how backup and recovery work in each mode, see [Keys, backup & recovery](/concepts/keys-backup-and-recovery/).
---
Source: https://wavelength.lightning.engineering/concepts/keys-backup-and-recovery.md
# Keys, backup & recovery
Every Wavelength wallet is a hierarchical-deterministic (HD) wallet rooted in a single seed. The seed’s origin depends on the auth mode (typed password or passkey), and everything else follows from it: what sits on disk, what counts as a backup, and what a user needs to get funds back on a new device. For the lifecycle states and UI phases around unlock, see [Wallet lifecycle & auth](/concepts/wallet-lifecycle-and-auth/).
## One seed, two ways to derive it
A **password wallet** gets its seed from a freshly generated (or imported) 24-word mnemonic; the password protects the wallet’s key material at rest. A **passkey wallet** inverts this: the seed itself is *derived* from the passkey, so the passkey is simultaneously the unlock credential and the root secret.
```mermaid
%%{init: {"theme":"base","themeVariables":{"fontFamily":"Inter, sans-serif","fontSize":"16px","primaryColor":"#2c2c33","primaryTextColor":"#f5f5f7","primaryBorderColor":"#15e0c2","secondaryColor":"#24242a","secondaryTextColor":"#f5f5f7","secondaryBorderColor":"#a78bfa","tertiaryColor":"#1c1c21","tertiaryTextColor":"#b6b6c0","tertiaryBorderColor":"#56c7f2","lineColor":"#8c8c96","textColor":"#f5f5f7","mainBkg":"#2c2c33","nodeBorder":"#303037","clusterBkg":"#1c1c21","clusterBorder":"#303037","titleColor":"#b6b6c0","edgeLabelBackground":"#24242a","edgeLabelTextColor":"#f5f5f7","nodeTextColor":"#f5f5f7","rectBorderRadius":"10px","clusterBorderRadius":"12px"},"flowchart":{"htmlLabels":false,"curve":"basis","padding":20,"nodeSpacing":55,"rankSpacing":60,"diagramPadding":12,"useMaxWidth":false}}}%%
flowchart TD
PK["Passkey assertion (WebAuthn PRF output)"] --> HKDF["HKDF-SHA256"]
HKDF -->|"info: wavewalletdk:seed:v1"| ENT["16-byte entropy"]
HKDF -->|"info: wavewalletdk:dbpw:v1"| DBP["wallet DB password (64-char hex)"]
ENT --> AZ["aezeed mnemonic (24 words)"]
AZ --> SEED["HD wallet seed"]
PW["User password"] --> DBP2["wallet DB password"]
MN["Generated / imported mnemonic"] --> SEED
SEED --> DB[("wallet database (OPFS / app sandbox)")]
DBP --> DB
DBP2 --> DB
```
Both paths converge on the same storage: the seed lives only inside the wallet database, encrypted under the wallet DB password and stored device-locally. On the web that is [OPFS](/web/runtime/data-and-persistence/), the browser’s origin-private file system. On React Native it is a data directory inside the app sandbox: `RuntimeConfig.dataDir`, defaulting to the platform path reported by `getDefaultDataDir()`. Either way the database never leaves the device, and no key material ever reaches the operator or any server.
## The passkey ceremony
Passkey wallets are built on the WebAuthn **PRF extension**: an authenticator holds a hidden pseudo-random function per credential, and evaluating it over a fixed input yields a stable 32-byte secret. The SDK always evaluates the PRF over the same input, `SHA-256("wavewalletdk-passkey:v1")` (exported from core as `PASSKEY_PRF_SALT_HEX`), so the same passkey produces the same secret on every device and every session. That determinism is the whole design: the PRF output *is* the wallet’s root secret.
On the web, `registerPasskeyWallet(appName)` creates the credential with `authenticatorAttachment: 'platform'`, `residentKey: 'required'`, and `userVerification: 'required'`, meaning a discoverable, biometric-gated passkey in the platform authenticator (Face ID, Touch ID, Windows Hello, or a synced password manager). Some browsers do not return a PRF result from the creation ceremony itself, so registration falls back to an immediate assertion scoped to the just-created credential to read the PRF value reliably.
`assertPasskeyPrf(credentialId?)` runs the returning-session ceremony. With a stored `credentialId` the assertion is scoped to that one credential and the OS unlocks it directly; without one, the assertion is *discoverable*, so a passkey synced from another device can be offered even though this device has never seen the wallet. The `credentialId` is not a secret; persisting it in `localStorage` just skips the credential chooser on the next unlock.
On React Native, `createNativePasskeyCeremony({ rpId })` supplies the same ceremony through the platform credential APIs: Credential Manager on Android and AuthenticationServices on iOS (iOS support is experimental and needs iOS 18 or newer). Because every platform evaluates the PRF over the same fixed input, the same passkey derives the same wallet everywhere: a wallet created in the browser opens in the app, and vice versa, provided both use the same relying-party domain.
> **Caution**
>
> Passkeys are bound to the **relying-party ID**. On the web the SDK sets it to the page’s hostname; on React Native you pass `rpId` explicitly and must associate that domain with your app (see [Passkey setup](/react-native/get-started/passkey-setup/)). If the app later moves to a different domain, existing passkeys cannot be asserted there and passkey-based recovery breaks for every user. Plan the relying-party domain as a long-term commitment, share it between your web and mobile apps so one passkey opens both, and treat the mnemonic as the domain-independent escape hatch.
## Seed derivation
The wallet runtime (the Go SDK, compiled to WASM on the web and bundled as a native library on React Native) expands the PRF output with **HKDF-SHA256** into two domain-separated secrets:
| HKDF `info` | Output | Role |
| ---------------------- | --------------------- | ----------------------------------- |
| `wavewalletdk:seed:v1` | 16 bytes | aezeed entropy (the HD wallet seed) |
| `wavewalletdk:dbpw:v1` | 32 bytes, hex-encoded | wallet DB password |
The entropy is wrapped into an [aezeed](https://github.com/lightningnetwork/lnd/tree/master/aezeed) cipher seed with a pinned version and a birthday pinned to the Bitcoin genesis date, and an **empty seed passphrase**. Pinning all three means the wallet is a pure function of the PRF output: nothing else needs to be stored or remembered for the keys to be reproducible.
Two invariants protect this contract:
- **The PRF input never changes.** The evaluation input is fixed per namespace version. If a caller ever evaluated the PRF over a different input, the same passkey would silently derive a different seed (a different wallet), and the original funds would be unreachable until the correct input is used again.
- **Short PRF outputs are rejected.** The SDK refuses PRF outputs under 32 bytes, so a platform bug or empty input cannot collapse the derivation into a low-entropy, attacker-reproducible seed.
The derived DB password plays the same role a typed password plays in a password wallet: it is the private passphrase that encrypts the wallet database’s key material at rest. Because it comes out of HKDF, it carries the full 32 bytes of entropy, far stronger than any human password. And because the derivation is deterministic, every device derives the same password without storing or syncing it, which is why a passkey wallet never shows a password prompt.
For a **password wallet**, the user’s typed password is used directly as the wallet database passphrase, and the seed comes from a mnemonic generated by the daemon (`createWallet({ password })`) or imported by the user (`createWallet({ password, mnemonic })`). An optional seed passphrase (`seedPassphrase`) can be layered on an imported mnemonic; passkey wallets always use an empty one.
## What is stored where
| Artifact | Where | Protected by |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| HD seed and derived keys | Wallet database in [OPFS](/web/runtime/data-and-persistence/) (web) or the app-sandbox data directory (React Native) | Encrypted under the wallet DB password (typed password, or HKDF-derived for passkey wallets) |
| Swap and daemon bookkeeping | SQLite databases alongside the wallet database | Origin or app-sandbox isolation (no key material) |
| Passkey private key | Platform authenticator / passkey provider | Device biometrics or PIN; provider sync encryption |
| `credentialId`, wallet-kind markers | App-level storage (`localStorage` on web, by convention) | Nothing (not secret) |
| Mnemonic | Wherever the user wrote it down | The user |
Nothing above leaves the device except the passkey itself, which the platform’s passkey provider (iCloud Keychain, Google Password Manager, 1Password, and similar) may sync end-to-end encrypted across the user’s devices. The operator sees signed protocol messages, never keys.
## Backup
A passkey wallet has two independent backup layers:
1. **The synced passkey.** If the user’s passkey provider syncs credentials, the wallet is already recoverable on any device signed into that provider: the PRF travels with the credential, and the seed is derived from it on demand. This is the primary, zero-effort layer.
2. **The mnemonic.** When a passkey wallet is created (or first imported on a device), the result includes a 24-word aezeed mnemonic. It decodes to the same entropy the passkey derives, so it recovers the same wallet *without* the passkey, on any domain, in any aezeed-compatible flow. Offer it to the user once, encourage writing it down, and do not persist it anywhere.
> **Note**
>
> Two exports of the same passkey wallet show **different 24-word phrases**. aezeed encrypts the entropy under a fresh random salt each time it renders a mnemonic, so the words differ while deciphering to the same entropy, and therefore the same wallet. Both phrases are valid backups; a re-export that differs from the recorded phrase is expected, not a bug.
A password wallet has exactly one backup layer: the mnemonic. The password cannot be recovered or reset from anywhere, and without the mnemonic a forgotten password means the funds are gone once the local database is lost.
## Recovery paths
| Scenario | Path | What comes back |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| Same device, returning session | Passkey assertion (scoped to the stored `credentialId`) + `openWalletFromPasskey()` / `unlockWallet({ password })` | Everything; the local database is intact |
| New device, synced passkey | Discoverable passkey assertion + `openWalletFromPasskey()`. The wallet state is `'none'`, so the SDK derives the seed and imports it | Keys immediately; the view of funds converges in the background |
| Lost passkey, has mnemonic | `createWallet({ password, mnemonic, recoverState: true })`; the wallet continues as a password wallet | Keys immediately; funds and history via server-assisted recovery |
| Lost passkey and mnemonic | None | Nothing; there is no custodial reset |
Recovery restores the *seed* instantly; restoring the *view* of funds takes a scan. A mnemonic restore can opt into **server-assisted recovery** with `recoverState: true`: the daemon walks the seed’s addresses and queries the operator’s indexer to rebuild boarding outputs, VTXOs, and Lightning receive history, tracked in the background through the engine’s `'restoring'` phase and `snapshot.recovery`. See [Restore a wallet](/guides/restore-a-wallet/) for the full flow. The passkey import path does not run that scan today, so an app that supports passkey recovery on a fresh device should treat the imported wallet’s view of funds as converging rather than immediate.
> **Caution**
>
> Recovery reproduces keys, not liveness obligations. VTXOs have expiries and unilateral-exit windows; a wallet that has been offline for a long time should be brought back online with enough margin to refresh or exit its VTXOs. See [Leaving Ark](/concepts/leaving-ark/).
## Security model
- **Spending requires the device.** A passkey assertion demands user verification (biometric or PIN) on an unlocked device. Anyone who can pass that check can derive the seed; there is no second factor beyond the platform’s.
- **The database at rest is only as strong as its passphrase.** For passkey wallets that is 32 bytes of HKDF output, effectively unbreakable offline. For password wallets it is the user’s password; encourage real passphrases.
- **The app is part of the trust boundary.** Script running on the wallet’s web origin, or code running inside the mobile app’s process, can drive a ceremony (still gated by a user-verification prompt) and can read the PRF output once a legitimate ceremony completes. XSS on the wallet origin or a compromised dependency in the app bundle is therefore equivalent to key compromise; treat content-security policy and dependency hygiene as wallet security, not routine hygiene.
- **The mnemonic is the root of everything.** Anyone holding the 24 words holds the wallet, independent of passkeys, devices, or domains.
---
Source: https://wavelength.lightning.engineering/concepts/lightning-payments-are-swaps.md
# Lightning payments = swaps
## Receiving over Lightning
Receiving Lightning does not mean opening a channel in your app. Wavelength routes inbound payments through an atomic swap that moves value **from Lightning into Ark**. You do **not** open or manage Lightning channels. Instead, `receive()` asks the **swap server** to act as a bridge.
The flow, at a high level:
1. The wallet builds a BOLT-11 **invoice** whose payment hash locks a **virtual HTLC (vHTLC)** on Ark.
2. A Lightning payer routes to the swap server (via a route hint embedded in the invoice).
3. The swap server **holds** the incoming Lightning HTLC and **funds a matching vHTLC** on Ark, locked to the same payment hash.
4. The wallet observes the funded vHTLC (via the operator’s indexer), **claims** it with the preimage, and the swap server uses that preimage to **settle** the Lightning side.
The vHTLC is a taproot output with the same hashlock/timelock structure as a Lightning HTLC, but settled inside Ark. Until the claim completes, the receive shows up as pending inbound balance and a receive activity entry in phases like `waiting_for_payment` and `settling`.
> **Note**
>
> Wavelength Lightning receive is swap-backed. There is no inbound channel capacity to pre-fund, but swap server liquidity and operator liveness still matter for how quickly funding appears.
## Sending over Lightning
Sending to a Lightning invoice works the same way in reverse: Ark value funds a vHTLC, and the swap server pays the invoice on Lightning once the Ark side is claimable. The swap moves value **from Ark out to Lightning**. Again, this is not a channel payment.
The flow:
1. `prepareSend({ invoice })` (or `send()` in one step) selects VTXOs and constructs a swap session.
2. The wallet **funds a vHTLC** on Ark locked to the invoice’s payment hash.
3. The swap server **claims** that vHTLC with cooperation from the operator, learns the preimage, and **pays** the BOLT-11 invoice on Lightning.
4. When the Lightning payment succeeds, the Ark side is settled and the send entry’s progress phase (`entry.progress.phase`) reaches `confirmed`, and `entry.status` becomes `complete`.
Outbound Lightning therefore consumes confirmed balance (and shows pending outbound balance while the swap runs). The **SendRail** on a prepared send is `'lightning'` when the invoice path is selected.
On-chain sends (`onchainAddress`) and cooperative leaves are separate rails; only invoice payments use the swap server.
## Pending & refunds
Swap legs can take seconds or minutes, and either side can time out. Your UI should treat activity entries as live until they reach a terminal status or refund phase. Swap operations are **long-lived and asynchronous**. An activity entry stays pending while the swap server, operator, or Lightning network has not reached a terminal state. The progress phase tells you which leg is active: waiting for a payer, detected funding, settling through Ark, and so on.
Every vHTLC encodes **refund paths** with CLTV and CSV timelocks. If the swap server never claims (receive side) or never pays the invoice (send side), the wallet can unwind cooperatively first (operator-co-signed OOR refunds) and, if needed, fall back to on-chain unilateral leaves after delays elapse.
Wavelength **arms automatic refund recovery** on pay sessions so stuck sends do not strand funds indefinitely. Entries may pass through `refunding` and `refunded` phases when a timeout fires; `failed` covers hard errors with failure reason text for display. When an entry fails, `entry.failureReason` holds a human-readable message and `entry.failureCode` holds one of `timed_out | expired | refunded | needs_intervention | failed` for programmatic handling.
> **Caution**
>
> A pending Lightning send is often waiting on swap server liquidity or routing, not a bug in your integration. Surface the entry phase to users and expect auto-refund if the invoice expires or the swap times out. See [Troubleshooting](/web/support/troubleshooting/) for the “stuck send” pattern.
You do not implement the refund ladder yourself for normal flows; the daemon drives cooperative refunds and escalates when deadlines pass. Your job is to show pending state clearly and avoid treating `prepareSend` quotes as final until the entry completes.
---
Source: https://wavelength.lightning.engineering/concepts/networks-and-config.md
# Networks & config
## Backend endpoints
Starting the embedded daemon requires backend endpoints that serve the same Bitcoin network. Your app never calls them directly. Misaligned endpoints are a common cause of stuck deposits or swaps that never fund.
`RuntimeConfig` uses one flat, camelCase shape on both platforms. The endpoint values differ by transport:
1. **`arkServerAddress`** is the Ark operator and mailbox edge. It handles rounds, boarding, VTXO relay, cooperative leave, and mailbox traffic. Use a REST URL on web and a `host:port` gRPC address on React Native. There is no separate mailbox field.
2. **`swapServerAddress`** is the swap server for Lightning to Ark atomic swaps. Use a REST URL on web and a `host:port` gRPC address on React Native.
3. **`walletEsploraUrl`** is an HTTP Esplora endpoint on both platforms for chain queries and UTXO lookups. It must implement the Esplora `/address/:addr/utxo` API (electrs or mempool.space compatible).
All configured services must point at the same **`network`**. A mismatched Esplora endpoint or swap server is a common source of “stuck deposit” or “swap never funds” bugs.
## defaultConfig presets
For public test networks, use your transport package’s `defaultConfig(network)` instead of hand-typing URLs. It returns a ready **RuntimeConfig** with canonical endpoints merged with any overrides you pass:
```ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
const client = createWebClient();
const config = defaultConfig('signet', { dataDir: 'my-app-wallet' });
await client.start(config);
```
```ts
import { createNativeClient, defaultConfig } from '@lightninglabs/wavelength-react-native';
const client = createNativeClient();
const config = defaultConfig('signet', { dataDir: 'my-app-wallet' });
await client.start(config);
```
Presets ship for:
| Network | Typical use |
| ----------- | ----------------------------------------------------------------------------------------- |
| `'signet'` | Default for docs live embeds and the demo app; public Lightning Labs test infrastructure. |
| `'testnet'` | Bitcoin testnet3 with hosted Ark/swap gateways. |
Hosted test-network gateways use TLS. The web preset uses REST URLs, while the React Native preset uses matching `host:port` gRPC addresses. `defaultConfig` accepts only these preset networks. `'mainnet'` and `'regtest'` are excluded: mainnet has no public deployment yet, and regtest’s local ports vary per development environment. Build a `RuntimeConfig` for either by hand: mainnet with your own endpoints and `allowMainnet` (see below), regtest with local endpoints and the insecure-transport flags.
Pass overrides as the second argument to change data directory, point at your own operator, or toggle advanced flags without losing the rest of the preset.
## allowMainnet
Mainnet is deliberately gated. Set **`allowMainnet: true`** together with **`network: 'mainnet'`** and your own **`arkServerAddress`**, **`swapServerAddress`**, and **`walletEsploraUrl`**. Without **`allowMainnet`**, the SDK rejects the configuration before startup.
> **Caution**
>
> `allowMainnet` is an explicit safety rail. Do not enable it in example code or demos that users might copy against production networks by accident.
Test networks ignore **`allowMainnet`**; it exists only for mainnet runs.
## Mainnet access
Signet and testnet are open to anyone. Mainnet is different: the mainnet server accepts only clients on an approved allowlist, so `allowMainnet` on its own is not enough to connect. Access is keyed to your client’s identity, which is its mailbox ID.
To request access, initialize and unlock your wallet, run `wavecli getinfo`, and copy the `identity_pubkey` value (a 66-character hex string). Submit that value through the [mainnet access request form](https://docs.google.com/forms/d/e/1FAIpQLScX-AwTYPCRqD9WI2LOtalgL25PSOJ__I6Gf4D8xW04WyWCpA/viewform). Once approved, your client can register and connect. If you re-initialize with a new seed, the identity changes and you have to request access again for the new `identity_pubkey`.
## Complete configuration
Most apps only need `defaultConfig(network)` plus an optional data directory. Do not copy this advanced example into a quickstart. It shows the flat fields available for self-hosting and specialized deployments:
```ts
const config: RuntimeConfig = {
network: 'signet',
walletType: 'btcwallet',
walletFeeUrl: 'https://fees.example.com',
walletBlockHeadersSource: 'neutrino',
walletFilterHeadersSource: 'neutrino',
walletRecoveryWindow: 250,
maxOperatorFeeSat: 100,
signingWorkers: 4,
bufferSize: 64,
};
```
### Common fields
- `network`, `allowMainnet`, `dataDir`, and `debugLevel` select the network, mainnet safety gate, storage root, and daemon log verbosity.
- `arkServerAddress`, `arkServerTlsCertPath`, and `arkServerInsecure` configure the Ark endpoint. On web, `arkServerAddress` is a REST URL and filesystem certificate paths for `arkServerTlsCertPath` are rejected. On React Native, it is a `host:port` gRPC address.
- `swapServerAddress`, `swapServerTlsCertPath`, `swapServerInsecure`, and `swapDatabaseFileName` configure the swap service and its state. The web transport accepts `swapServerTlsCertPath` only when `disableSwaps: true`, which suppresses every swap field.
- `disableSwaps: true` disables Lightning swaps and suppresses both preset and override swap fields.
- `maxOperatorFeeSat`, `signingWorkers`, and `bufferSize` are numeric runtime limits. They must be nonnegative safe integers.
### Embedded wallet backend fields
- `walletEsploraUrl`, `walletPasswordFile`, and `walletPollIntervalSeconds` apply only to `lwwallet`.
- `walletFeeUrl`, `walletBlockHeadersSource`, and `walletFilterHeadersSource` apply only to `btcwallet`.
- `walletRecoveryWindow` applies to both supported embedded backends.
`lwwallet` is the default backend. The SDK rejects incompatible backend fields and invalid numeric values before it starts the daemon. LND and eager-round-join disable are intentionally unavailable through this mobile start contract.
On regtest, set both insecure flags yourself because local web gateways speak plain HTTP. On hosted networks, leave them unset so connections stay TLS-verified. If you self-host Ark or swap infrastructure, replace the preset endpoints while keeping the network aligned with your Esplora instance. Full field documentation is in the [wavelength-core reference](/reference/wavelength-core/).
## Next steps
Once your client is configured and started, see [Handle phases and errors](/guides/handle-phases-and-errors/) for surfacing startup and runtime failures, or [Installation](/web/get-started/installation/) if you still need to add the SDK packages to your app.
---
Source: https://wavelength.lightning.engineering/concepts/leaving-ark.md
# Leaving Ark
## Cooperative leave
Most withdrawals should use the cooperative path: you forfeit VTXOs in an operator round and receive an on-chain payout in roughly one round cadence. Unilateral exit exists for emergencies when the operator cannot or will not co-sign.
A **cooperative leave** is the normal way to move Ark balance to a plain on-chain Bitcoin address. You forfeit selected VTXOs in an operator **round**; the batch includes a **leave output** that pays your destination address on-chain. The operator co-signs, so the exit settles in roughly one round cadence instead of waiting for CSV timelocks.
In Wavelength this is the default when you send to an on-chain address (`prepareSend` / `send` with `onchainAddress`) or call `exit({ outpoint, destination })` with a destination set. Activity shows up as kind **`exit`** (or a **`send`** on the on-chain rail). The path is **`cooperative`** in the exit result.
Cooperative leave requires a **live, honest operator**. It is cheaper and faster than unilateral exit: one batch transaction (plus normal mining confirmation) rather than a chain of checkpoint and connector transactions you broadcast yourself. Round completion is asynchronous; the entry may stay pending through `settling` until the leave output confirms.
> **Tip**
>
> For everyday withdrawals, cooperative leave is what you want. Direct users to a normal Bitcoin address send, not the unilateral exit APIs.
## Unilateral exit
When the operator is unreachable or dishonest, you can broadcast pre-signed tree branches yourself. That path is slower, costs more in fees, and should never be the default “Send” action.
A **unilateral exit** (also called unilateral unroll) is the **trustless emergency** path. You broadcast the pre-signed Virtual Transaction Tree branch for your VTXO on-chain without operator cooperation, then walk checkpoint and connector transactions until your funds sit in plain UTXOs you control.
It works even if the operator disappears, but it is **slow and expensive**:
- CSV delays on VTXO scripts must mature before each step is valid.
- Multiple on-chain transactions may be required (preview with `getExitPlan({ outpoints })`).
- You may need to fund a backing wallet for fees before the exit can proceed unilaterally.
Wavelength exposes this through the acknowledged `exit()` branch:
```ts
import { FORCE_UNROLL_ACK } from '@lightninglabs/wavelength-core';
await client.exit({
outpoint,
forceUnrollAck: FORCE_UNROLL_ACK,
});
```
Import `FORCE_UNROLL_ACK` from any Wavelength package. It carries the exact acknowledgement string the daemon requires and cannot be combined with a cooperative `destination`. Cooperative leave failures reject; they never start unilateral unroll. Once a unilateral job exists, follow-up `exitStatus({ outpoint })` and `sweepWallet({ destinationAddress, broadcast: false })` calls drive the unroll to completion. Call the sweep with `broadcast: false` first to preview. This path is not the same API as sending to an address cooperatively.
Unilateral exit does not instantaneously credit a user-chosen address. It creates claimable on-chain outputs over time; `sweepWallet()` consolidates them once timelocks expire.
Track progress with `exitStatus({ outpoint })`, which returns an `ExitJobStatus` that advances through `pending` (job queued), `materializing` (checkpoint and connector transactions being broadcast), `csv_pending` (waiting on CSV timelocks to mature), `sweeping` (final consolidation broadcast), and `completed` (funds are plain UTXOs). A job can also land in `failed`, surfaced via `ExitStatusResult.lastError`; `exitStatus()` returns `found: false` rather than an error when no job exists yet for that outpoint.
Before the job can start, `getExitPlan({ outpoints })` reports whether it is ready to go: each `ExitPlanEntry` includes a per-outpoint `fundingAddress` and `fundingShortfallSat` for topping up the backing wallet’s fees, plus a `canStart` flag (and an overall `canStart` on the `GetExitPlanResult`). Fund the reported address until the shortfall clears and `canStart` becomes `true` before calling `exit()`.
## When to use which
| Situation | Prefer |
| ----------------------------------------------------------------- | ----------------------------------------------------------------- |
| User taps “Withdraw to my exchange address” | **Cooperative leave** (on-chain send / `exit` with `destination`) |
| Operator online, routine payout | **Cooperative leave** |
| Operator unresponsive or dishonest | **Unilateral exit** |
| User needs funds soon and network is healthy | **Cooperative leave** |
| User accepts higher fees and multi-block delays for trustlessness | **Unilateral exit** |
Use cooperative leave whenever the Ark service is reachable. Reserve unilateral exit for custody emergencies, disputed operator behavior, or infrastructure outages that block rounds.
> **Caution**
>
> Unilateral exit is irreversible once broadcast and can cost many times the fee of a cooperative leave. Preview `getExitPlan({ outpoints })`, explain delays, and never trigger it silently from a primary “Send” button.
If cooperative leave fails, Wavelength surfaces the error directly; it does not silently fall back to unilateral unroll. The unilateral path requires an explicit `forceUnrollAck` on `exit()`. Read `ExitResult.path` to see which branch ran so the UI can treat unilateral exit as a deliberate, informed event rather than a silent substitution.
> **Note**
>
> See [Unilateral exit](/guides/unilateral-exit/) for a walkthrough of building the flow end to end, or [Handle phases and errors](/guides/handle-phases-and-errors/) for surfacing `WavelengthErrorCode` failures along the way.
---
Source: https://wavelength.lightning.engineering/web/get-started/quickstart.md
# Quickstart (React)
## Install
Install the two packages you need: `wavelength-web` for the browser wallet runtime and `wavelength-react` for the React bindings.
```sh
npm install @lightninglabs/wavelength-web @lightninglabs/wavelength-react
```
Requires Node 20 or later to run locally. The runtime itself runs in Chrome 100+, Firefox 110+, and Safari 16+. No special build config is needed for Vite or Next.js.
## Wire the provider
Create a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) with [`createWebWalletEngine()`](/reference/wavelength-web/#createWebWalletEngine), and wrap your app with [`WavelengthProvider`](/reference/wavelength-react/#WavelengthProvider). Pass `config` and `autoStart: true` to boot the embedded daemon as soon as the runtime is ready, no boot effect needed. Every component below the provider gains access to the wallet through hooks.
```tsx
import { createWebWalletEngine, defaultConfig } from '@lightninglabs/wavelength-web';
import { WavelengthProvider, useWallet } from '@lightninglabs/wavelength-react';
const engine = createWebWalletEngine({
runtimeBaseUrl: '/wavewalletdk/',
config: defaultConfig('signet'),
autoStart: true,
});
export function App() {
return (
);
}
function Balance() {
const { phase } = useWallet();
return
Wallet phase: {phase}
;
}
```
`defaultConfig(network, overrides?)` returns a `RuntimeConfig` preloaded with public REST gateway endpoints for that network. Overrides are a shallow merge, so pass only top-level fields such as `dataDir`. Keep this quickstart on the default config path; see [Networks & config](/concepts/networks-and-config/) for advanced self-hosting fields.
mainnet has no preset: build its `RuntimeConfig` by hand with `allowMainnet: true`, and only after you have key-backup UX in place. Mainnet access is also gated to an approved allowlist; see [Mainnet access](/concepts/networks-and-config/#mainnet-access).
## Create a wallet
Call `create` from [`useWalletCreate`](/reference/wavelength-react/#useWalletCreate) to generate a new HD seed in the browser. The seed is encrypted locally and never transmitted.
```tsx
import { useWalletCreate } from '@lightninglabs/wavelength-react';
function Onboard() {
const { create, createPending } = useWalletCreate();
const handleCreate = async () => {
const { mnemonic } = await create({ password: 'a-strong-password' });
// Back up `mnemonic` securely before the user sends funds.
console.log('Wallet created');
};
return (
Create wallet
);
}
```
Once `create` resolves, `phase` from `useWallet` automatically advances (through `'syncing'` when the daemon needs to catch up) to `'ready'`, so any component watching `phase` updates without extra wiring.
## Try it
Call `send` from [`useWalletSend`](/reference/wavelength-react/#useWalletSend) with a Lightning invoice or on-chain address. Wavelength routes Lightning payments through the swap server and settles on-chain sends through a cooperative leave with the Ark operator.
```tsx
import { useWalletSend } from '@lightninglabs/wavelength-react';
function SendButton() {
const { send, sendPending } = useWalletSend();
const handleSend = async () => {
const result = await send({
invoice: 'lnbc1…', // or onchainAddress: 'bc1q…'
});
console.log('Sent!', result.paymentHash ?? result);
};
return (
Send
);
}
```
You are all set. Explore the full guides to go deeper on individual topics.
### [Try the live demo](https://wavelength.lightning.engineering/demo/)
[Run a signet wallet in your browser, no install required.](https://wavelength.lightning.engineering/demo/)
[Open the demo →](https://wavelength.lightning.engineering/demo/)
### [Create a wallet](/guides/create-a-wallet/)
[Full walkthrough of wallet creation, key backup, and passkey registration.](/guides/create-a-wallet/)
[Read the guide →](/guides/create-a-wallet/)
### [Send a payment](/guides/send-a-payment/)
[Fee estimation, prepare/confirm flow, and on-chain cooperative leave.](/guides/send-a-payment/)
[Read the guide →](/guides/send-a-payment/)
### [Show balance and activity](/guides/show-balance-and-activity/)
[Subscribe to balance changes and render a live activity feed.](/guides/show-balance-and-activity/)
[Read the guide →](/guides/show-balance-and-activity/)
---
Source: https://wavelength.lightning.engineering/web/get-started/run-the-demo-app.md
# Run the demo app
> **Try it live**
>
> A hosted signet demo runs in your browser at . No clone or local setup required.
## Clone & install
The reference integration lives in the Wavelength monorepo at `apps/web-wallet-demo`. Clone the repository, install dependencies with pnpm, and build the workspace packages:
```bash
git clone https://github.com/lightninglabs/wavelength-sdk.git
cd wavelength-sdk
pnpm install
pnpm build
```
The demo depends on the workspace packages `@lightninglabs/wavelength-web` and `@lightninglabs/wavelength-react`. The `apps/web-wallet-demo/public/` folder is gitignored and ephemeral, so before running the demo you need to populate it with the runtime WASM binaries:
```bash
pnpm --filter web-wallet-demo run wasm:local
```
See [Hosting runtime assets](/web/get-started/hosting-runtime-assets/) for details on what this stages and how to point the demo at a different asset source.
## Run
Start the Vite dev server from the monorepo root:
```bash
pnpm --filter web-wallet-demo dev
```
Open the URL Vite prints (typically `http://localhost:5173`). The demo boots a **signet** wallet out of the box: choose signet on the connect screen and the app boots with its own signet preset (`signetDefaults` in `src/lib/runtime-config.ts`), which mirrors the same public gateway URLs `defaultConfig('signet')` would produce. No local backend is required.
Cross-origin isolation headers are configured in the demo’s Vite config so OPFS persistence works in every supported browser. See [Cross-origin isolation](/web/get-started/cross-origin-isolation/) for why these headers are required and how to configure them outside of Vite:
```ts
const crossOriginIsolation = {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Resource-Policy': 'same-origin',
};
export default defineConfig({
server: { headers: crossOriginIsolation },
preview: { headers: crossOriginIsolation },
});
```
Use `pnpm --filter web-wallet-demo preview` to serve the production build with the same headers.
## Optional (wasm:local, regtest, testnet)
**Rebuild runtime assets locally.** When you are developing against a custom wavelength build, stage fresh WASM files into the demo’s `public/` folder:
```bash
pnpm --filter web-wallet-demo run wasm:local
```
This runs `make wasm-wallet` in wavelength and copies the full `RUNTIME_ASSET_FILES` set into `public/runtime//`, the versioned path the demo’s `runtimeBaseUrl` points at.
**Regtest with arktest.** To exercise deposits, swaps, and exits against a local chain, run [arktest](https://github.com/lightninglabs/wavelength) in a separate terminal:
```bash
make build
./arktest-debug start --swapserver
```
Keep arktest running. In another terminal, print the connection URLs:
```bash
./arktest-debug env
```
In the demo connect screen, switch the network to **regtest**. The defaults match arktest’s usual ports (`7071` for Ark, `8501` for Esplora, `10032` for the swap server). Adjust the gateway fields if your arktest instance binds different ports.
At a high level the full local workflow is:
1. Terminal 1: `./arktest-debug start --swapserver`
2. Terminal 2: `pnpm --filter web-wallet-demo dev` (optionally after `wasm:local` if you changed the daemon)
3. Browser: select regtest, start the runtime, create or unlock a wallet
**Testnet.** Select testnet on the connect screen to boot with its own testnet preset (`testnetDefaults` in `src/lib/runtime-config.ts`), which mirrors the same public gateway URLs `defaultConfig('testnet')` would produce. No arktest instance is needed.
**Troubleshooting OPFS.** If wallet persistence fails in the browser, confirm COOP/COEP headers are present:
```bash
curl -sI http://localhost:5173 | grep -i cross-origin
```
Both `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` must appear in the response. See [Troubleshooting / FAQ](/web/support/troubleshooting/) for more fixes, and the [web guides](/guides/create-a-wallet/) for end-to-end wallet flows built on top of this demo.
---
Source: https://wavelength.lightning.engineering/web/support/demo-app.md
# Demo app
> **Try it live**
>
> A hosted signet demo runs in your browser at . No clone or local setup required.
## The reference integration
The **`web-wallet-demo`** app in the Wavelength monorepo is a full React integration you can read, run, and copy patterns from. It is not a separate product; it exercises the same `@lightninglabs/wavelength-web` and `@lightninglabs/wavelength-react` packages your app would use.
What it demonstrates:
- **`WavelengthProvider`** with an injected `createWebWalletEngine()` engine (self-hosted runtime assets, worker mode, optional RPC debug logging).
- **Phase-based routing** from `useWallet()`: connect, create/unlock, backup, syncing, ready, stopped, and error screens driven by `phase` and `useWalletInfo()`.
- **Password and passkey onboarding** via `useWalletCreate` / `useWalletUnlock` and `useWalletPasskey(webPasskeyCeremony)`, including credential id persistence in localStorage for scoped unlock (see `src/lib/walletKind.ts`).
- **Core wallet flows**: deposit address, Lightning receive, send (prepare and confirm), balance and activity, settings, and runtime configuration forms.
- **Network presets** for signet, testnet, and regtest (`runtime-config.ts` mirrors public gateway URLs and local arktest ports).
- **Production-like hosting concerns**: COOP/COEP headers in `vite.config.ts`, self-hosted WASM binaries under `public/runtime//`, persistent storage request, and a local wipe path that clears OPFS and app markers (`src/lib/wipeLocalData.ts`).
Treat the demo as the source of truth for wiring details that docs summarize. Key entry points: `src/main.tsx` (engine bootstrap), `src/App.tsx` (orchestration), and `src/lib/runtime-config.ts` (gateway defaults).
See also: [`createWebWalletEngine`](/reference/wavelength-web/#createWebWalletEngine), [`useWallet`](/reference/wavelength-react/#useWallet), and [`useWalletPasskey`](/reference/wavelength-react/#useWalletPasskey) in the API reference for details behind each of these bullets.
## How to run it
Clone the monorepo, install dependencies, and start the demo dev server. Step-by-step commands (including `wasm:local` for a fresh WASM build and regtest/testnet overrides) live on the dedicated run page:
**[Run the demo app](/web/get-started/run-the-demo-app/)**
Out of the box the demo boots a **signet** wallet against Lightning Labs public gateways. No local backend is required for signet. For regtest, start [arktest](https://github.com/lightninglabs/wavelength) locally and point the runtime form at the ports it prints.
Runtime assets are staged (never committed) under `public/runtime//`. Before the first `pnpm dev`, and after upgrading the SDK or rebuilding WASM from `wavelength`, run `pnpm --filter web-wallet-demo run wasm:local` to stage fresh binaries.
React Native readers: see [Run the demo app](/react-native/get-started/run-the-demo-app/) for the mobile counterpart, which mirrors this demo screen for screen.
---
Source: https://wavelength.lightning.engineering/web/get-started/installation.md
# Installation
## The packages
The Wavelength SDK ships as three npm packages. You install one or two of them depending on your stack:
| Package | What it is |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `@lightninglabs/wavelength-core` | Types, the `WavelengthClient` interface, errors, and enums. Pulled in transitively; you rarely install it directly. |
| `@lightninglabs/wavelength-web` | The browser WASM transport. Framework-agnostic: use it from vanilla JS, Vue, Svelte, or React. Re-exports everything from `core`. |
| `@lightninglabs/wavelength-react` | React provider and hooks. Transport-agnostic: it takes an injected engine and depends only on `core`. |
**React apps** need the web transport and the React bindings:
```bash
npm install @lightninglabs/wavelength-web @lightninglabs/wavelength-react
```
**Vanilla JS, Vue, Svelte, or other non-React frameworks** need only the web transport:
```bash
npm install @lightninglabs/wavelength-web
```
Before wiring up the code samples below, make sure the browser environment you’re deploying to meets the [requirements](/web/get-started/requirements/) and serves your app with the headers described in [Cross-origin isolation](/web/get-started/cross-origin-isolation/). The Wavelength SDK’s WebAssembly runtime will not start without them.
## Peer deps
`@lightninglabs/wavelength-react` declares `react` as a peer dependency, with an accepted range of `^18.0.0 || ^19.0.0`. Your app must also provide `react-dom` (or the equivalent for your React renderer). Either major version, 18 or 19, works:
```bash
npm install react react-dom
```
Build the wallet engine once near the root of your app. `createWebWalletEngine` accepts `WebWalletEngineOptions` (for example `runtimeBaseUrl` pointing at hosted WASM assets, see [Hosting runtime assets](/web/get-started/hosting-runtime-assets/)), plus `config` and `autoStart` to boot the embedded daemon as soon as the runtime is ready, no boot effect needed.
```tsx
import { WavelengthProvider, useWallet, useWalletBalance } from '@lightninglabs/wavelength-react';
import { createWebWalletEngine, defaultConfig } from '@lightninglabs/wavelength-web';
// Configure transport options once. runtimeBaseUrl points at the hosted WASM
// asset folder.
const engine = createWebWalletEngine({
runtimeBaseUrl: 'https://your-host/wavewalletdk/',
config: defaultConfig('signet'),
autoStart: true,
});
export function App() {
return (
);
}
function Wallet() {
const { phase, error } = useWallet();
const balance = useWalletBalance();
if (phase !== 'ready') {
return Loading… ({phase})
;
}
return Spendable: {balance?.confirmedSat ?? 0} sats
;
}
```
`error` from `useWallet` holds the last fatal runtime-level error: set when the initial runtime load fails or when `start`/`stop` fails. Render it instead of dropping the failure on the floor.
For a non-React app, skip the provider and call `start` on the client directly after `ready()` resolves:
```ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
const client = createWebClient({
runtimeBaseUrl: 'https://your-host/wavewalletdk/',
});
await client.ready();
await client.start(defaultConfig('signet'));
```
## Next steps
- [Requirements](/web/get-started/requirements/): confirm the browser support matrix and backend services the Wavelength SDK needs.
- [Hosting runtime assets](/web/get-started/hosting-runtime-assets/): obtain the WebAssembly runtime asset set and serve it from your own host.
- [Quickstart](/web/get-started/quickstart/): wire up the provider, create a wallet, and send your first payment.
---
Source: https://wavelength.lightning.engineering/web/get-started/requirements.md
# Requirements
## Browser support
Wavelength runs the full wallet daemon in the browser as WebAssembly. Target browsers with mature WASM, Web Worker, and storage APIs:
| Browser | Minimum version |
| ------------------------------------ | --------------- |
| Chrome (and Chromium-based browsers) | 100+ |
| Firefox | 110+ |
| Safari (macOS and iOS) | 16+ |
Node.js 20 or later is recommended for local development (Vite, Next.js, and similar bundlers). No special build plugins are required beyond serving the WASM runtime assets and, for worker mode, setting cross-origin isolation headers.
Passkey support (WebAuthn with PRF) is optional and depends on the browser and OS. Password-based wallets work everywhere in the matrix above.
## Cross-origin isolation
The default transport runs the daemon in a dedicated Web Worker and persists wallet state with OPFS-backed SQLite. Both paths rely on `SharedArrayBuffer`, which browsers only expose on **cross-origin isolated** pages.
Your app must send `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` response headers on every route that loads the wallet. See [Cross-origin isolation](/web/get-started/cross-origin-isolation/) for the exact values and a quick verification command.
If you cannot isolate the page (for example, because third-party embeds block `require-corp`), you can run the runtime on the main thread with `createWebClient({ runtimeThread: 'main' })`, but OPFS persistence will not be available and the UI thread will block while the daemon is busy. See [Data & persistence](/web/runtime/data-and-persistence/) for how storage behavior differs between main-thread and worker mode.
## Backends
Wavelength is self-custodial: the seed never leaves the browser. The embedded daemon still needs reachable **Ark**, **Esplora**, and (for Lightning swaps) **swap server** endpoints at runtime.
**Signet (recommended for development)** works out of the box with the public preset. Pass `defaultConfig('signet')` to `start()` and the client connects to Lightning Labs hosted gateways:
```ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
const client = createWebClient();
await client.start(defaultConfig('signet'));
```
Presets also exist for `testnet`. No local backend is required for any hosted network.
**Regtest (local development)** targets services you run yourself, so there is no `defaultConfig` preset for it; local ports vary per machine, and the URLs are the whole point of a preset. Start [arktest](https://github.com/lightninglabs/wavelength), note the ports it prints, and build the config by hand, including the insecure-transport flags for plain-HTTP local gateways:
```ts
import type { RuntimeConfig } from '@lightninglabs/wavelength-web';
const config: RuntimeConfig = {
network: 'regtest',
arkServerAddress: 'http://127.0.0.1:7071',
walletEsploraUrl: 'http://127.0.0.1:8501',
swapServerAddress: 'http://127.0.0.1:10032',
arkServerInsecure: true,
swapServerInsecure: true,
};
await client.start(config);
```
**Mainnet** has no public preset yet, so like regtest there is no `defaultConfig` for it. Build the config by hand with your own gateway URLs and set `allowMainnet: true` before going live:
```ts
await client.start({
network: 'mainnet',
arkServerAddress: 'https://your-ark-server.example.com',
walletEsploraUrl: 'https://your-esplora.example.com/api',
swapServerAddress: 'https://your-swap-server.example.com',
allowMainnet: true,
});
```
On web, `arkServerAddress` and `swapServerAddress` must be REST URLs. `walletEsploraUrl` is an HTTP Esplora endpoint. The web transport always rejects `arkServerTlsCertPath`. It accepts `swapServerTlsCertPath` only when `disableSwaps: true`, which suppresses every swap field.
Mainnet access is gated to an approved allowlist. See [Mainnet access](/concepts/networks-and-config/#mainnet-access) for how to request it with your client’s `identity_pubkey`.
---
Source: https://wavelength.lightning.engineering/web/get-started/hosting-runtime-assets.md
# Hosting runtime assets
The web transport loads its WebAssembly runtime from a set of files that you host yourself and point `runtimeBaseUrl` at. You always provide the assets. The worker entry (`wavewalletdk-worker.js`) ships inside `@lightninglabs/wavelength-web` and is emitted by your bundler; only the daemon binaries below need to be hosted.
```ts
import { createWebClient } from '@lightninglabs/wavelength-web';
const client = createWebClient({
runtimeBaseUrl: 'https://your-host/wavewalletdk//',
});
```
If `runtimeBaseUrl` is unset, assets resolve relative to the page URL in both worker and main-thread mode, so you can also serve the runtime files alongside your app.
## The asset set
The runtime is a fixed set of files built from [wavelength](https://github.com/lightninglabs/wavelength). Import `RUNTIME_ASSET_FILES` to see every filename that must be served together at one base URL. `RUNTIME_MANIFEST_VERSION` identifies the daemon build the SDK is paired with:
```ts
import { RUNTIME_ASSET_FILES, RUNTIME_MANIFEST_VERSION } from '@lightninglabs/wavelength-web';
console.log(RUNTIME_ASSET_FILES);
// wavewalletdk.wasm, wavewalletdk.wasm.gz, wasm_exec.js, sqlite-bridge.js,
// sqlite-worker.js, sqlite3.js, sqlite3.wasm, sqlite3-opfs-async-proxy.js
```
## Get the asset set
There are two ways to obtain the files, both producing the set for the pinned `RUNTIME_MANIFEST_VERSION`:
**Download from the wavelength release.** The matching asset set is attached to the paired [wavelength release](https://github.com/lightninglabs/wavelength/releases). Download the files for the pinned version.
**Build from a wavelength checkout.** From a checkout of `wavelength`, build the WASM wallet target and copy the output into your app’s static folder:
```bash
make -C /path/to/wavelength wasm-wallet
mkdir -p "./public/wavewalletdk/"
for f in wavewalletdk.wasm wavewalletdk.wasm.gz wasm_exec.js sqlite-bridge.js \
sqlite-worker.js sqlite3.js sqlite3.wasm sqlite3-opfs-async-proxy.js; do
cp "/path/to/wavelength/bin/wasm/$f" "./public/wavewalletdk//"
done
```
The monorepo demo wraps the same build in `pnpm --filter web-wallet-demo run wasm:local`, which builds from a sibling `wavelength` checkout and stages files into `apps/web-wallet-demo/public/runtime//`, the versioned path the demo’s `runtimeBaseUrl` points at. After `vite build`, those files land in `dist/` and are served from the app origin.
## Host the assets
Host all of them under a single path and set `runtimeBaseUrl` to that directory. Prefer a path that includes `RUNTIME_MANIFEST_VERSION` (for example `/wavewalletdk//`): every asset set then gets a unique URL, so browsers pick up new assets on an SDK upgrade instead of serving stale cached copies, and you can cache the files aggressively. Trailing slashes are optional; the client normalizes the base URL.
**Version lock.** Runtime assets are version-locked to the embedded daemon inside `@lightninglabs/wavelength-web`. When you upgrade the SDK, obtain and redeploy the matching WASM bundle. Mismatched versions can fail at load time or produce subtle runtime errors.
**Compression.** The client prefers `wavewalletdk.wasm.gz` when the browser supports `DecompressionStream` and falls back to the uncompressed `wavewalletdk.wasm`. Host both files so every supported browser can load the runtime.
---
Source: https://wavelength.lightning.engineering/web/get-started/cross-origin-isolation.md
# Cross-origin isolation
## Why
The Wavelength SDK’s default transport runs the wallet daemon in a Web Worker and stores encrypted wallet data in OPFS-backed SQLite. Both the worker’s WASM module and the SQLite stack use `SharedArrayBuffer` for efficient memory sharing.
Browsers gate `SharedArrayBuffer` behind **cross-origin isolation**: the page must be served with headers that prevent other origins from reading its memory. Without isolation, `SharedArrayBuffer` is unavailable, OPFS persistence fails, and the worker transport cannot start.
Cross-origin isolation is a web-only concern. It applies to every route in your app that loads the Wavelength SDK, including embedded demo or playground pages on the docs site.
## COOP/COEP headers
These are response headers on your own HTML document, so only the server that serves your app can set them. The Wavelength SDK cannot set them for you: they are not something the SDK, the worker, or the runtime assets can emit on their own. Send them on wallet routes (and on your dev server while testing locally):
```http
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
```
`Cross-Origin-Opener-Policy: same-origin` keeps other browsing contexts from holding a reference to your page’s global object.
`Cross-Origin-Embedder-Policy: require-corp` requires every subresource (script, stylesheet, image, font, WASM) to either be same-origin or explicitly opt in with `Cross-Origin-Resource-Policy: cross-origin` (or be loaded with `crossorigin` where applicable). Same-origin assets need no extra attribute.
**Scope headers to embed routes.** If only part of your site runs the wallet (for example `/web/playground/*` on the docs site), apply COOP/COEP on those paths rather than the entire domain. Hosts like Netlify and Cloudflare Pages read a `_headers` file:
```text
/web/playground/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
```
For Vite during local development, set the same headers on the dev and preview servers:
```ts
const crossOriginIsolation = {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Resource-Policy': 'same-origin',
};
export default defineConfig({
server: { headers: crossOriginIsolation },
preview: { headers: crossOriginIsolation },
});
```
**Self-host third-party assets.** Cross-origin Google Fonts, analytics scripts, and similar embeds are blocked under `require-corp`. Bundle fonts locally (for example with `@fontsource`) or serve them from the same origin as your app.
> **Note**
>
> If you set `runtimeBaseUrl` to point the daemon runtime binaries (`wavewalletdk.wasm.gz`, `wasm_exec.js`, `sqlite-*.js`) at a different origin, that origin must also send `Cross-Origin-Resource-Policy: cross-origin` on those asset responses. Otherwise `require-corp` blocks them from loading. See [Hosting runtime assets](/web/get-started/hosting-runtime-assets/) for details.
**Optional: `credentialless` COEP.** Some teams use `Cross-Origin-Embedder-Policy: credentialless` as a lighter alternative. It is not supported uniformly across browsers (Safari in particular). The Wavelength SDK targets `require-corp` because every major engine supports it today. The key difference: `credentialless` drops credentials (cookies, client certificates) on cross-origin requests instead of requiring the target origin to opt in with `Cross-Origin-Resource-Policy`.
**Verify with curl.** After deploying, confirm the headers are present:
```bash
curl -sI https://your-app.example/web/playground/ | grep -i cross-origin
```
You should see both `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp` (or `credentialless` if you chose that mode).
In the browser, open DevTools and check that `crossOriginIsolated` is `true`:
```js
console.log(crossOriginIsolated); // true
```
If isolation is missing, the wallet will fail to initialize the OPFS database. See [Troubleshooting](/web/support/troubleshooting/) for common fixes.
## See also
- [Requirements](/web/get-started/requirements/)
- [Hosting runtime assets](/web/get-started/hosting-runtime-assets/)
---
Source: https://wavelength.lightning.engineering/web/runtime/data-and-persistence.md
# Data & persistence
## OPFS vs localStorage
The embedded daemon persists two kinds of browser-local data:
1. **Encrypted seed and wallet metadata** (the wallet database).
2. **SQLite state** for swap tracking and other daemon bookkeeping (via an OPFS-backed VFS when available). Set **`swapDatabaseFileName`** in `RuntimeConfig` to name this SQLite file explicitly; set **`disableSwaps`** to turn off the Lightning swap subsystem (and its storage) entirely. When swaps are disabled, Wavelength suppresses preset and override swap fields.
**OPFS (Origin Private File System)** is the production path. Files live under the page origin in a private directory the page cannot enumerate from outside the API. The shipped WASM daemon stores the encrypted seed and SQLite databases in OPFS. Set **`dataDir`** in `RuntimeConfig` to choose a subdirectory (for example `'/my-wallet'`); when unset the daemon uses its default.
**localStorage** is a window-only key/value store. It is **not available inside Web Workers**, so the default worker transport cannot use it for daemon storage. Main-thread mode can fall back to storage patterns that do not require OPFS, but you lose durable SQLite persistence and the UI thread blocks while the daemon runs.
Your app may still use **localStorage for app-level markers** (for example recording whether a wallet was created with a passkey or storing a passkey credential id for scoped unlock). The reference demo does this; daemon state remains in OPFS.
OPFS-backed SQLite requires **`SharedArrayBuffer`**, which browsers only expose on [cross-origin isolated](/web/get-started/cross-origin-isolation/) pages. Without isolation, OPFS persistence for the daemon will not come up cleanly.
Request **persistent storage** (`navigator.storage.persist()`) if you want the browser to deprioritize eviction of origin data. It is best-effort and does not replace backing up the recovery phrase.
```ts
// Ask the browser to treat this origin's storage as persistent.
const granted = await navigator.storage.persist();
```
Whether the browser grants the request depends on its own engagement heuristics (installed as a PWA, bookmarked, frequently visited, and similar signals). Do not rely on it being granted; treat it as best-effort.
## Main thread vs worker
`createWebClient()` accepts **`runtimeThread`**: `'worker'` (default) or `'main'`.
### 'worker' (default)
**Thread:** Dedicated Web Worker. **Storage:** OPFS.
Production web apps. Keeps the UI responsive while the daemon runs.
### 'main'
**Thread:** Page main thread. **Storage:** No OPFS SQLite path.
Escape hatch when Workers are unavailable or you cannot set COOP/COEP headers. Expect UI jank.
Worker mode spawns the bundled `wavewalletdk-worker.js` (override with `workerURL`). The worker receives **`runtimeBaseUrl`** on init so it can fetch `wavewalletdk.wasm.gz`, `wasm_exec.js`, and the SQLite bridge from your hosted asset folder. Main-thread mode loads those scripts on the page directly.
```ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
// Default: worker + OPFS (requires cross-origin isolation).
const workerClient = createWebClient({
runtimeBaseUrl: 'https://your-host/wavewalletdk/',
});
// Escape hatch: main thread, no OPFS persistence.
const mainClient = createWebClient({
runtimeThread: 'main',
runtimeBaseUrl: 'https://your-host/wavewalletdk/',
});
// Choose a dataDir subdirectory for the daemon's on-disk tree.
const config = defaultConfig('signet', { dataDir: '/my-wallet' });
await workerClient.ready();
await workerClient.start(config);
```
**Persistence requirements** for the default worker path:
1. Serve **COOP/COEP headers** so `crossOriginIsolated` is true and `SharedArrayBuffer` is available.
2. Host the **runtime asset set** (`RUNTIME_ASSET_FILES`) at `runtimeBaseUrl`.
3. Use a **secure context** (HTTPS or localhost) for WebAuthn passkeys.
4. Pick a stable **`dataDir`** per wallet profile; changing it starts a fresh on-disk tree.
Wiping local wallet data means clearing both OPFS entries under your origin and any app-level localStorage keys you wrote. See the demo’s wipe helper (`apps/web-wallet-demo/src/lib/wipeLocalData.ts`) for a reference pattern, and the [wavelength-web reference](/reference/wavelength-web/) for the client start/stop lifecycle.
---
Source: https://wavelength.lightning.engineering/web/support/browser-support.md
# Browser support
## Matrix
The Wavelength SDK targets evergreen desktop and mobile browsers with mature WebAssembly, Worker, and storage APIs. Minimum versions below are **floors for the WASM runtime**; passkeys and OPFS persistence have stricter requirements noted in their columns.
Chrome / Edge
- Min version
`100`
- WebAuthn passkeys (PRF)
`114+ (platform authenticator + PRF extension)`
- SharedArrayBuffer
`Yes, with COOP/COEP`
- OPFS persistence
`Yes, with COOP/COEP`
Firefox
- Min version
`110`
- WebAuthn passkeys (PRF)
`Limited PRF support; prefer password fallback`
- SharedArrayBuffer
`Yes, with COOP/COEP`
- OPFS persistence
`Yes, with COOP/COEP`
Safari (macOS)
- Min version
`16`
- WebAuthn passkeys (PRF)
`17.4+ for PRF`
- SharedArrayBuffer
`Yes, with COOP/COEP`
- OPFS persistence
`Yes, with COOP/COEP`
Safari (iOS / iPadOS)
- Min version
`16`
- WebAuthn passkeys (PRF)
`17.4+ for PRF`
- SharedArrayBuffer
`Yes, with COOP/COEP`
- OPFS persistence
`Yes, with COOP/COEP`
**WebAssembly runtime:** Chrome 100+, Firefox 110+, and Safari 16+ are the documented development targets. Older engines may fail to instantiate the daemon or lack required APIs. See the [error codes reference](/guides/handle-phases-and-errors/) for the `runtime_not_ready` and `asset_load_failed` codes to catch when instantiation fails.
**SharedArrayBuffer:** Available only when the page is [cross-origin isolated](/web/get-started/cross-origin-isolation/). Verify in DevTools: `crossOriginIsolated` should be `true`. Without it, worker mode with OPFS SQLite will not initialize.
**OPFS:** Requires a secure context and cross-origin isolation for the SQLite VFS the daemon uses. `runtimeThread: 'main'` only changes which thread runs the wasm runtime (main thread instead of a Web Worker); it does not change the storage backend, so it is not a fallback for missing OPFS. Passing `createWebClient({ runtimeThread: 'main' })` is useful as a debugging escape hatch in environments without Web Worker support, but do not ship that path for real wallets. If OPFS itself is unavailable, wallet persistence is not currently guaranteed; treat this as an open question and verify behavior for your target environment before relying on it.
**WebAuthn passkeys:** Wavelength derives wallet keys from the WebAuthn **PRF** extension. `webPasskeyCeremony.supportsPasskeyPrf()` checks for a user-verifying platform authenticator, but the authenticator must still return a PRF result at ceremony time. Gate passkey UI on the hook’s `supported` flag: it is `null` while the probe is in flight, so hold a loading state until it settles, then offer a password or recovery-phrase path when it is `false`. See the [passkey guide](/guides/use-a-passkey/) for a full example using `useWalletPasskey`.
Node.js **20+** is recommended for local development (Vite, Next.js, and similar bundlers). The runtime itself executes entirely in the user’s browser.
Password-based wallets work across the full matrix. Passkeys are optional and depend on the OS authenticator as well as the browser.
---
Source: https://wavelength.lightning.engineering/web/support/troubleshooting.md
# Troubleshooting / FAQ
## Common errors
### Runtime asset could not be loaded (`asset_load_failed`)
The client throws `WavelengthError` with code **`asset_load_failed`** when `wavewalletdk.wasm.gz`, `wasm_exec.js`, or the SQLite bridge cannot be fetched.
**Fix:** Host every file listed in **`RUNTIME_ASSET_FILES`** together at one base URL and set **`runtimeBaseUrl`** on `createWebClient()`. Open the failing URL in the browser network tab; a 404 almost always means the base path or version folder is wrong. See [Hosting runtime assets](/web/get-started/hosting-runtime-assets/).
### Page is not cross-origin isolated
Symptoms: `SharedArrayBuffer` is undefined, worker startup fails, or SQLite/OPFS never initializes. The SQLite worker reports an error like `Missing SharedArrayBuffer API ... the server must emit COOP/COEP`.
**Fix:** Send **`Cross-Origin-Opener-Policy: same-origin`** and **`Cross-Origin-Embedder-Policy: require-corp`** (plus **`Cross-Origin-Resource-Policy: same-origin`** for your own assets) on every HTML route that loads the wallet. Confirm with:
```bash
curl -sI https://your-app.example/ | grep -i cross-origin
```
In DevTools, `crossOriginIsolated` must be `true`. See [Cross-origin isolation](/web/get-started/cross-origin-isolation/). Third-party scripts or fonts that are not CORP-compatible block isolation; self-host assets when needed (the demo self-hosts its fonts for this reason).
### Passkey unsupported or PRF missing
`useWalletPasskey(...).supported` stays `false`, or ceremonies fail with *“passkey PRF extension result was not returned by this authenticator”*.
**Fix:** Passkeys require a **secure context** (HTTPS or localhost), a **user-verifying platform authenticator**, and browser support for the **PRF** extension (Safari 17.4+, Chrome 114+). There is no synchronous PRF probe; `supportsPasskeyPrf()` can return true and still fail on a given device. Offer password creation/unlock or recovery-phrase restore when passkeys are unavailable.
On the web, `useWalletPasskey` (from `wavelength-react`) takes a `PasskeyCeremony` implementation as its argument; pass `webPasskeyCeremony` from `@lightninglabs/wavelength-web`. See the [passkey guide](/guides/use-a-passkey/) for a full wiring example.
### Connection refused (wrong URLs or ports)
Daemon logs or network errors show `connection refused` or failed fetches to `arkServerAddress`, `walletEsploraUrl`, or `swapServerAddress`.
**Fix:** Verify each gateway is reachable from the browser (not just from your shell). For **regtest**, start arktest and match the URLs in your `RuntimeConfig` to the ports the environment prints (`./arktest env` or the `frontend-regtest` overlay). URLs must include the scheme (`http://` or `https://`). There is no `defaultConfig` preset for regtest, so every regtest URL comes from you; the demo app ships its own local defaults (Ark on `7071`, Esplora on `8501`, swap on `10032`).
Signet and testnet presets from `defaultConfig()` should work without a local backend; if they fail, check corporate proxies or ad blockers first. On web, Ark and swap values must be REST URLs. The transport always rejects `arkServerTlsCertPath`. It accepts `swapServerTlsCertPath` only when `disableSwaps: true`, which suppresses every swap field.
### 401 Unauthorized from a gateway
Occasional **401** responses from the Ark or swap REST gateways are retried with refreshed credentials inside the daemon. Persistent 401s usually mean the server is unhealthy or the URL points at the wrong service. These are daemon-log-only: the daemon does not currently map gateway-originated errors to a specific `WavelengthError` code, so a persistent 401 surfaces client-side (if at all) as a generic `wavelength_error`, not a dedicated code you can match on.
**Fix:** Restart the backend (arktest for regtest, or wait and retry for hosted signet). Confirm you are hitting the **REST** gateway hostname (`*-rest` pattern on public networks), not a raw gRPC port.
### OPFS not available
Wallet state does not survive reload, or startup logs mention missing OPFS / SQLite VFS support.
**Fix:** Serve the app with COOP/COEP headers as described in [Cross-origin isolation](/web/get-started/cross-origin-isolation/) (the demo’s Vite config does this on `dev` and `preview`). Plain static hosting without those headers will not get OPFS-backed SQLite in worker mode. Verify isolation as above. Private browsing modes may restrict storage; test in a normal profile.
### Metro or bundler cache serving stale SDK code
After upgrading `@lightninglabs/wavelength-web` or rebuilding WASM, the browser still loads an old worker or runtime manifest.
**Fix:** For Expo/Metro workflows, run `npx expo start --clear` or delete `node_modules/.cache`. For Vite (including `web-wallet-demo`), restart with `vite --force` or remove `node_modules/.vite`. Hard-refresh the browser and confirm `RUNTIME_MANIFEST_VERSION` matches the assets you host.
## Stuck sends
Lightning sends are **atomic swaps** through the swap server, not direct channel payments. A send can sit in **`pending`** while the swap server waits for Lightning liquidity or routing.
Watch the activity entry’s **`progress.phase`**. Typical outbound phases include `waiting_for_payment`, `settling`, and `confirmed`. If liquidity never arrives before the swap timeout, the daemon moves the entry to **`refunding`** and then **`refunded`**: funds return to your Ark balance automatically. You do not need to manually cancel; show the user that the payment failed and the balance will update when the refund completes.
If an entry stays pending far longer than expected:
1. Call **`refresh()`** (from `useWalletRefresh()` in `wavelength-react`) or rely on the engine’s activity subscription (the engine debounces activity-driven refreshes). Without React, call **`client.list({ pendingOnly: true })`** directly.
2. Inspect daemon **`log`** events (`useWalletLogs()` or `client.subscribe()`).
3. Confirm **`swapServerAddress`** is correct and swaps are enabled (`disableSwaps` is false).
4. Retry only after the prior attempt reaches `failed`/`refunded`; duplicate sends to the same invoice can race.
On-chain sends use cooperative leave and follow a different activity shape; see [Send a payment](/guides/send-a-payment/) for that path.
---
Source: https://wavelength.lightning.engineering/react-native/get-started/quickstart.md
# Quickstart (React Native)
## Install
Install the two packages you need: `wavelength-react-native` for the native wallet runtime and `wavelength-react` for the React bindings.
```sh
npm install @lightninglabs/wavelength-react-native @lightninglabs/wavelength-react
```
Requires React Native 0.76+ with the New Architecture enabled, iOS 15.1+, and Android minSdk 24. Expo apps need a development build, not Expo Go: the native wallet runtime is a compiled module that Expo Go cannot load. See [Requirements](/react-native/get-started/requirements/) for the full platform matrix.
Until hosted binary distribution ships, the native wallet runtime must be staged before the first build; see [Installation](/react-native/get-started/installation/) for the staging step. The native side is built by `npx expo run:android` / `npx expo run:ios` (which installs iOS pods automatically), so there is no separate pod step to run by hand.
## Wire the provider
Create a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) with [`createNativeWalletEngine()`](/reference/wavelength-react-native/#createNativeWalletEngine), and wrap your app with [`WavelengthProvider`](/reference/wavelength-react/#WavelengthProvider). Pass `config` and `autoStart: true` to boot the embedded daemon as soon as the runtime is ready, no boot effect needed. Every component below the provider gains access to the wallet through hooks.
```tsx
import { Text } from 'react-native';
import { createNativeWalletEngine, defaultConfig } from '@lightninglabs/wavelength-react-native';
import { WavelengthProvider, useWallet } from '@lightninglabs/wavelength-react';
const engine = createNativeWalletEngine({
config: defaultConfig('signet'),
autoStart: true,
});
export default function App() {
return (
{/* your app */}
);
}
function Status() {
const { phase } = useWallet();
return Wallet phase: {phase} ;
}
```
`defaultConfig(network, overrides?)` returns a `RuntimeConfig` preloaded with public gRPC `host:port` gateway addresses for that network. Overrides are a shallow merge, so pass only top-level fields such as `dataDir`. Keep this quickstart on the default config path; see [Networks & config](/concepts/networks-and-config/) for advanced self-hosting fields.
mainnet has no preset: build its `RuntimeConfig` by hand with `allowMainnet: true`, and only after you have key-backup UX in place. Mainnet access is also gated to an approved allowlist; see [Mainnet access](/concepts/networks-and-config/#mainnet-access).
## Create a wallet
Call `create` from [`useWalletCreate`](/reference/wavelength-react/#useWalletCreate) to generate a new HD seed on the device. The seed is encrypted locally and never transmitted.
```tsx
import { Button } from 'react-native';
import { useWalletCreate } from '@lightninglabs/wavelength-react';
function Onboard() {
const { create, createPending } = useWalletCreate();
const handleCreate = async () => {
const { mnemonic } = await create({ password: 'a-strong-password' });
// Back up `mnemonic` securely before the user sends funds.
console.log('Wallet created');
};
return (
);
}
```
Once `create` resolves, `phase` from `useWallet` automatically advances (through `'syncing'` when the daemon needs to catch up) to `'ready'`, so any component watching `phase` updates without extra wiring.
## Try it
Call `send` from [`useWalletSend`](/reference/wavelength-react/#useWalletSend) with a Lightning invoice or on-chain address. Wavelength routes Lightning payments through the swap server and settles on-chain sends through a cooperative leave with the Ark operator.
```tsx
import { Button } from 'react-native';
import { useWalletSend } from '@lightninglabs/wavelength-react';
function SendButton() {
const { send, sendPending } = useWalletSend();
const handleSend = async () => {
const result = await send({
invoice: 'lnbc1…', // or onchainAddress: 'bc1q…'
});
console.log('Sent!', result.paymentHash ?? result);
};
return ;
}
```
You are all set. Run the app on a device or simulator to see it end to end; see [Run the demo app](/react-native/get-started/run-the-demo-app/) for staging the native runtime binaries and getting a build running.
---
Source: https://wavelength.lightning.engineering/react-native/get-started/run-the-demo-app.md
# Run the demo app
## Clone and install
The reference integration lives in the Wavelength monorepo at `apps/rn-wallet-demo`. Clone the repository, install dependencies with pnpm, and build the workspace packages:
```sh
git clone https://github.com/lightninglabs/wavelength-sdk.git
cd wavelength-sdk
pnpm install
pnpm build
```
The demo needs the native wallet runtime binaries staged before it can build. Point `WAVELENGTH_DIR` at a daemon source checkout and run the package’s `fetch-bindings` script:
```sh
WAVELENGTH_DIR=/path/to/daemon-checkout \
pnpm --filter @lightninglabs/wavelength-react-native run fetch-bindings
```
See [Installation](/react-native/get-started/installation/) for why the binaries ship this way and what the script stages.
## Run
Start Metro in its own terminal, then build and launch per platform:
```sh
cd apps/rn-wallet-demo
npx expo start --dev-client --clear
```
This starts Metro, the dev server that serves the JS bundle to the app. Keep it running in its own terminal; `--clear` drops a possibly stale cache.
```sh
npx expo run:android
```
This generates the native `android` project if needed, compiles the dev build, installs it on the running emulator or a connected device, and launches it.
```sh
LANG=en_US.UTF-8 npx expo run:ios
```
This does the same for the iOS simulator: generates the native `ios` project if needed, compiles the dev build, installs it, and launches it. The `LANG` prefix satisfies `pod install`’s locale requirement.
The first `expo run:*` generates the native `android/` and `ios/` projects (gitignored) and takes a while; later runs are incremental.
## Networks
The start screen offers four presets:
- **regtest** targets a local stack. Host addressing is automatic per platform: the Android emulator reaches your machine as `10.0.2.2`, the iOS simulator as `127.0.0.1`.
- **testnet** and **signet** target the public test network deployments over TLS and also work on physical devices.
Every endpoint is editable under “Advanced endpoints” before starting, so a preset is a starting point, not a limit.
## What the demo shows
The app exercises the full wallet flow: create, restore, or unlock (password or passkey), recovery-phrase backup, on-chain boarding and Lightning receive with scannable QRs, send, live activity, local-data wipe, and light/dark themes. It mirrors the web demo screen for screen, so the two SDK surfaces can be compared directly; see [Demo app](/web/support/demo-app/) for the web counterpart.
---
Source: https://wavelength.lightning.engineering/react-native/get-started/installation.md
# Installation
## The packages
```sh
npm install @lightninglabs/wavelength-react-native @lightninglabs/wavelength-react
```
### @lightninglabs/wavelength-react-native
The native transport: a Turbo Module wrapping the wallet runtime compiled into your app binary. Re-exports the entire `core` surface, so it is the only wallet import an app needs besides the hooks package.
### @lightninglabs/wavelength-react
React provider and hooks. Transport-agnostic: it takes an injected client and depends only on `core`.
## New Architecture and Expo
This package is New Architecture only; it does not support the legacy architecture. Enable the New Architecture in your app before installing.
Expo apps need a **development build**, not Expo Go: the native wallet runtime is a compiled module that Expo Go cannot load. `npx expo run:android` and `npx expo run:ios` both produce development builds that include it.
## iOS pods
`pod install` runs as part of `expo run:ios` or a standard React Native build; you do not need to run it separately in the common case. If `pod install` fails with a locale-related error, set `LANG=en_US.UTF-8` before retrying.
## Native runtime binaries
> **Native binaries are not bundled**
>
> The native wallet runtime binaries (`Wavewalletdk.aar`, `Wavewalletdk.xcframework`) are not included in the npm package. Get them either from the paired [wavelength release](https://github.com/lightninglabs/wavelength/releases) or by building them from a `wavelength` checkout with the package’s `scripts/fetch-bindings.sh`, then stage them into the package before running `pod install` or a Gradle build.
## Next steps
- [Quickstart](/react-native/get-started/quickstart/): wire up the provider, create a wallet, and send your first payment.
- [Requirements](/react-native/get-started/requirements/): confirm the platform, OS, and passkey requirements.
---
Source: https://wavelength.lightning.engineering/react-native/get-started/requirements.md
# Requirements
## Platform requirements
- React Native 0.76 or newer, with the New Architecture enabled. This package is New Architecture only; it does not support the legacy architecture.
- iOS 15.1+.
- Android minSdk 24.
- Expo apps need a **development build**, not Expo Go: the native wallet runtime is a compiled module that Expo Go cannot load.
## Passkey requirements
Passkey wallets let users create and unlock a wallet with a platform passkey instead of a password. Support depends on the device:
- **Android:** Android 9+ (API 28), Google Play services with a signed-in Google account, and a device screen lock.
- **iOS:** iOS 18 or newer.
> **iOS passkeys are experimental**
>
> The iOS ceremony has not been verified end to end. Treat it as experimental until a verified release notes otherwise.
`supportsPasskeyPrf()` (surfaced as the `supported` flag returned by `useWalletPasskey`) reports whether the platform prerequisites are present; a supported device can still decline the ceremony, which surfaces as a normal error.
## Networks
Signet and testnet work out of the box over TLS: pass `defaultConfig('signet')` (imported from `@lightninglabs/wavelength-react-native`, which presets gRPC `host:port` addresses) to `start()` and the client connects to Lightning Labs hosted infrastructure. `walletEsploraUrl` remains an HTTP Esplora endpoint. Regtest needs a local stack running on your development machine, reachable from the device or simulator; see [Run the demo app](/react-native/get-started/run-the-demo-app/) for how host addressing works on the emulator and simulator.
---
Source: https://wavelength.lightning.engineering/react-native/get-started/passkey-setup.md
# Passkey setup
Passkeys bind to a relying-party domain (`rpId`) that must vouch for your app. Without the association in place, `createNativePasskeyCeremony({ rpId })` fails the ceremony with “RP ID cannot be validated”.
## What is an rpId?
The `rpId` is the relying-party identifier: a domain you control, for example `wallet.example.com`. Passkeys created with a given `rpId` are bound to it, so the association files described in this guide must be served from exactly that domain over HTTPS. Using the same `rpId` across your web app and your mobile app means the same passkey, and therefore the same wallet, works on both.
## Android
Serve `https:///.well-known/assetlinks.json` listing your app’s package name and signing-certificate SHA-256 fingerprint with both relations the platform checks: `delegate_permission/common.handle_all_urls` and `delegate_permission/common.get_login_creds`.
Declare the association app-side too: an `asset_statements` string resource that references that assetlinks URL, wired up through a manifest `meta-data` entry. The demo’s Expo config plugin, `apps/rn-wallet-demo/plugins/withAssetStatements.js`, is a working example of both pieces for an Expo app.
At runtime the device needs Android 9+ (API 28), Google Play services with a signed-in Google account, and a device screen lock.
## iOS (experimental)
Add the Associated Domains capability (`webcredentials:`) to your app and serve `https:///.well-known/apple-app-site-association` listing your Team ID and bundle id. Requires iOS 18 or newer at runtime.
> **iOS passkeys are experimental**
>
> The iOS ceremony has not been verified end to end. Treat it as experimental until a verified release notes otherwise.
## Verify it works
`supportsPasskeyPrf()` surfaces as the `supported` flag returned by `useWalletPasskey`, reporting whether the platform prerequisites above are present. A supported device can still decline the ceremony, which surfaces as a normal error rather than an unsupported state.
See [Use a passkey](/guides/use-a-passkey/) for the wiring pattern once the association is live.
---
Source: https://wavelength.lightning.engineering/react-native/troubleshooting.md
# Troubleshooting
## The app runs old code after a native change
Metro’s dev-server cache can serve a stale bundle after native code or dependency changes. Restart Metro with `npx expo start --dev-client --clear` and relaunch the app.
## The module fails to load in Expo Go
The wallet runtime is a compiled native module; Expo Go cannot load it. Build and run a development build instead (`npx expo run:android` or `npx expo run:ios`), which both produce a build that includes the compiled runtime.
## Where are the wallet runtime logs?
Wallet runtime logs go to the platform log, not to JS-visible events: Android logcat, or iOS `os_log`. Use your platform’s native log viewer while debugging rather than looking for them in the Metro console.
## pod install fails with a locale error
Run the iOS build with a UTF-8 locale:
```sh
LANG=en_US.UTF-8 npx expo run:ios
```
## Passkey creation fails with “RP ID cannot be validated”
The relying-party domain association is missing or incomplete on one of the platforms. See [Passkey setup](/react-native/get-started/passkey-setup/) for what each platform needs to serve and declare.
---
Source: https://wavelength.lightning.engineering/native-ios-android/overview.md
# Native iOS & Android overview
## Who this section is for
This section is for apps written directly in Kotlin or Swift, with no React Native layer. If your app is built with React Native, you want the [React Native section](/react-native/get-started/quickstart/) instead: it ships the same embedded wallet as an npm package with TypeScript types, and pairs with the [`wavelength-react`](/reference/wavelength-react/) hooks.
## What the native SDK is
The native SDK lives in the [wavelength-mobile](https://github.com/lightninglabs/wavelength-mobile) repository. It provides idiomatic wrappers over the same gomobile-compiled wallet daemon that powers the React Native package:
- **`android/walletkit`**: a Kotlin library exposing `suspend` functions, a `Flow` for wallet activity, and typed models.
- **`ios/WalletKit`**: a Swift library built as an `actor` with `async` functions, an `AsyncThrowingStream` for activity, and `Codable` models.
Both wrap the `Wavewalletdk` bindings (`Wavewalletdk.aar` on Android, `Wavewalletdk.xcframework` on iOS) that the [wavelength](https://github.com/lightninglabs/wavelength) repo builds and publishes with each release. The whole wallet daemon runs inside your app’s process: no separate daemon, no open network port, and your users keep sole custody of their keys. See [Architecture](/native-ios-android/architecture/) for how that works.
The repo also contains a sample app per platform (Jetpack Compose on Android, SwiftUI on iOS) that drives the wrappers end to end.
## Native or React Native?
Choose the **native SDK** when:
- You have an existing Kotlin or Swift app and do not want to introduce a React Native dependency.
- You want platform-native concurrency idioms: Kotlin coroutines and `Flow`, Swift `async`/`await` and `AsyncThrowingStream`.
Choose **React Native** when:
- You share a TypeScript codebase across platforms, or your team lives in the React ecosystem.
- You want the [`wavelength-react`](/reference/wavelength-react/) hooks and the shared `WalletEngine` state layer.
Both paths embed the same daemon and speak the same [wallet API](/api/), so the concepts, guides, and API documentation on this site apply to either.
## What lives where
This site owns the stable material: this overview, the [architecture](/native-ios-android/architecture/) of the embedded daemon, and a [quickstart](/native-ios-android/quickstart/) that gets a wallet created and synced. Everything that moves with upstream releases stays canonical in the wavelength-mobile repo:
- [API guide](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/api-guide.md): every wallet operation with Kotlin and Swift side by side.
- [Android workflow](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/android.md): toolchain setup, building the bindings, running the sample app.
- [Signet setup](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/signet.md): endpoints and network notes for running against signet.
- [Full method list](https://github.com/lightninglabs/wavelength/blob/main/docs/wavewalletdk_mobile.md): the complete gomobile facade surface, documented in the wavelength repo.
---
Source: https://wavelength.lightning.engineering/native-ios-android/architecture.md
# Native SDK architecture
This page explains what runs where when a native app uses the `Wavewalletdk` bindings, and why the API looks the way it does. It condenses the [upstream architecture doc](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/architecture.md), which remains the canonical version.
## The wallet runs inside your app
A normal `waved` deployment is a daemon process that clients reach over a gRPC socket. On mobile that model is awkward: an app cannot reliably keep a sidecar process alive, and an open port is an attack surface.
Instead, the binding compiles the whole daemon into a native library and starts it inside the app’s own process. The Go SDK boots the daemon on a background goroutine and connects to it over an in-memory gRPC channel backed by a memory buffer rather than a TCP socket. The app’s calls travel that buffer. Nothing listens on the network, and there is no second process to manage.
This is the same embedding the [React Native transport](/react-native/get-started/quickstart/) uses; the native SDK simply removes the JavaScript layer above it.
## A thin facade and the JSON boundary
`gomobile bind` carries only a narrow set of types between Go and Kotlin/Swift. The facade therefore presents a flat, gomobile-safe surface: RPC verbs pass JSON bytes in and JSON bytes out, and each wrapper decodes results with the platform’s own tools (`kotlinx.serialization` on Android, `Codable` on iOS). Neither platform needs a protobuf runtime. A few hot paths skip JSON and return a plain scalar, such as a `Long` confirmed balance.
## No callbacks
The bindings are callback-free. `start(config)` blocks until the daemon’s gRPC channel is serving, then returns; the wrappers run it off the main thread for you. Unary verbs are ordinary throwing calls: a Go error becomes a Kotlin exception or a Swift `throws`. Streaming uses a pull handle that the wrappers turn into a Kotlin `Flow` or a Swift `AsyncThrowingStream`, so the threading lives where the platform’s concurrency tools already are, and the host implements no interfaces.
## Lifecycle
One daemon runs per process. Calling `start` a second time before `stop` throws rather than booting a second daemon. `stop` tears the daemon down, unblocks any in-flight stream reads, and resets the guard so the app can start again after the operating system suspends and resumes it.
Panics do not cross the language boundary, so an unrecovered one takes the whole app down. The facade installs recovery at two entry points: `start` and the subscription’s `next`. A panic there becomes an ordinary error you can catch. A panic anywhere else is not recovered, so treat that guarantee as covering startup and stream reads rather than every call.
---
Source: https://wavelength.lightning.engineering/native-ios-android/quickstart.md
# Quickstart (native iOS & Android)
This quickstart goes from nothing to a created, syncing wallet. It condenses the first-run portion of the upstream [API guide](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/api-guide.md); everything past first-run (activity streaming, sending, receiving, exit, error handling) is covered there.
The wrappers are not published as artifacts yet, so you work from a checkout of the wavelength-mobile repository rather than adding a dependency. Step 1 clones it; the last step covers wiring it into an app of your own.
## Get the repo and stage the bindings
The Kotlin and Swift wrappers, the sample apps, and the fetch scripts all live in [wavelength-mobile](https://github.com/lightninglabs/wavelength-mobile), and the scripts run from its root. Start by cloning it:
```sh
git clone https://github.com/lightninglabs/wavelength-mobile.git
cd wavelength-mobile
```
The wrappers link the `Wavewalletdk` native bindings, which are not checked in. The fetch scripts download them from the wavelength GitHub release and stage them where the build expects:
```sh
./scripts/fetch-aar.sh
./scripts/fetch-xcframework.sh
```
Both scripts pull the release with the [`gh` CLI](https://cli.github.com/), so sign in with `gh auth login` first. Toolchain requirements and pinned versions live in the upstream [Android workflow](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/android.md) doc.
## Create a client
Hold one `WalletClient` for the app’s lifetime (an Android `ViewModel` field, a Swift `@State` value or a stored property). One daemon runs per process, so a single client serves the whole app. `WalletClient` is a Swift `actor` and does not conform to `ObservableObject`, so `@StateObject` does not apply; the upstream sample holds it in `@State`.
```kotlin
val client = WalletClient()
```
```swift
let client = WalletClient()
```
## Boot the wallet
`start` boots the embedded daemon and returns once it is serving. It blocks while booting, so both wrappers run it off the main thread for you. `WalletConfig.signet(...)` fills in public signet endpoints; override `dataDir` per install. Endpoint details are in the upstream [signet doc](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/signet.md).
```kotlin
// Inside a coroutine.
client.start(WalletConfig.signet(dataDir = "${context.filesDir}/wavewalletdk"))
```
```swift
// Inside a Task.
let dir = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
.appendingPathComponent("wavewalletdk").path
try await client.start(.signet(dataDir: dir))
```
Starting twice before `stop` throws rather than booting a second daemon.
## Create or unlock a wallet
`createWallet` with an empty mnemonic generates a fresh seed and returns the words to back up; pass an existing mnemonic to restore. The password encrypts the seed on disk. On later launches, `unlockWallet` with the same password.
```kotlin
// First launch: new wallet.
val res = client.createWallet(walletPassword = "my-password".toByteArray())
saveMnemonic(res.mnemonic)
// Later launches.
client.unlockWallet("my-password".toByteArray())
```
```swift
// First launch: new wallet.
let res = try await client.createWallet(
walletPassword: Data("my-password".utf8)
)
saveMnemonic(res.mnemonic)
// Later launches.
_ = try await client.unlockWallet(
walletPassword: Data("my-password".utf8)
)
```
## Watch sync and read the balance
`getInfo` returns a snapshot: the synced block height, operator connectivity, and whether the wallet is ready for signing. Poll it to show sync progress, then read the balance in satoshis.
```kotlin
val info = client.getInfo()
if (info.walletReady) {
val b = client.balance()
println("synced to ${info.blockHeight}: ${b.confirmedSat} sat confirmed")
}
```
```swift
let info = try await client.getInfo()
if info.walletReady {
let b = try await client.balance()
print("synced to \(info.blockHeight): \(b.confirmedSat) sat confirmed")
}
```
### Wiring it into your own app
The steps above run against the checkout, which is where the sample apps live and the fastest way to see the wallet working. There is no published Maven artifact or remote Swift package yet, so an app of your own consumes the wrapper as a local module:
- **Android**: include `android/walletkit` as a Gradle module and depend on it with `implementation(project(":walletkit"))`, the way `android/app` does.
- **iOS**: add `ios/WalletKit` as a local Swift package. Its `Package.swift` declares a binary target at `Frameworks/Wavewalletdk.xcframework`, which is not committed, so the fetch script above has to have staged it first.
Either way the bindings are large native artifacts fetched per checkout rather than resolved by a package manager, so treat staging them as part of your build setup. The upstream [Android workflow](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/android.md) doc covers the toolchain in full.
That is the whole first-run path. Explore these to go further.
### [Full API guide](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/api-guide.md)
[Activity streaming, receiving, sending, exit to chain, and error handling.](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/api-guide.md)
[Read the guide →](https://github.com/lightninglabs/wavelength-mobile/blob/main/docs/api-guide.md)
### [Sample apps](https://github.com/lightninglabs/wavelength-mobile)
[A Jetpack Compose app and a SwiftUI app driving the wrappers end to end.](https://github.com/lightninglabs/wavelength-mobile)
[Browse the repo →](https://github.com/lightninglabs/wavelength-mobile)
### [How it works](/native-ios-android/architecture/)
[The in-process daemon, the JSON boundary, and the callback-free API.](/native-ios-android/architecture/)
[Read the guide →](/native-ios-android/architecture/)
### [Balances and VTXOs](/concepts/balances-and-vtxos/)
[The wallet model this SDK exposes, the same on every platform.](/concepts/balances-and-vtxos/)
[Read the guide →](/concepts/balances-and-vtxos/)
---
Source: https://wavelength.lightning.engineering/integrations/react.md
# React
## Provider (injected engine)
`@lightninglabs/wavelength-react` is **transport-agnostic**: it depends only on `@lightninglabs/wavelength-core` and never imports a specific transport. The provider takes a prebuilt **`WalletEngine`**; each transport ships its own factory to build one (`createWebWalletEngine` from [`@lightninglabs/wavelength-web`](/reference/wavelength-web/) for the web transport, `createNativeWalletEngine` from [`@lightninglabs/wavelength-react-native`](/reference/wavelength-react-native/) for React Native).
Build the engine once, typically at module scope outside any component, and pass it to `WavelengthProvider`. Set `autoStart: true` (with `config`) to boot the runtime as soon as it is ready, instead of wiring a boot `useEffect` yourself:
```tsx
import { createWebWalletEngine, defaultConfig } from '@lightninglabs/wavelength-web';
import { WavelengthProvider } from '@lightninglabs/wavelength-react';
const engine = createWebWalletEngine({
runtimeBaseUrl: 'https://your-host/wavewalletdk/',
config: defaultConfig('signet'),
autoStart: true,
});
export function App() {
return {/* your app */} ;
}
```
```tsx
import { createNativeWalletEngine, defaultConfig } from '@lightninglabs/wavelength-react-native';
import { WavelengthProvider } from '@lightninglabs/wavelength-react';
const engine = createNativeWalletEngine({
config: defaultConfig('signet'),
autoStart: true,
});
export default function App() {
return (
{/* your app */}
);
}
```
The engine is built once outside the component tree, so `WavelengthProvider` owns nothing beyond publishing it to `useSyncExternalStore` subscribers: it never disposes the engine on unmount. Every lifecycle behavior (the sync-poll while `phase === 'syncing'`, activity-driven balance refresh, the restore readiness poll, and an unsolicited `'stopped'` transition on a runtime crash) is owned by the `WalletEngine` itself and works the same whether or not React is in the picture. If you would rather not use `autoStart`, call `start()` from [`useWallet()`](/reference/wavelength-react/#useWallet) yourself once `phase === 'runtimeReady'`.
`phase` is a `RuntimePhase`, one of: `'loading'`, `'runtimeReady'`, `'starting'`, `'needsWallet'`, `'locked'`, `'syncing'`, `'restoring'`, `'ready'`, `'stopping'`, `'stopped'`, or `'error'`. The `loading`/ `runtimeReady`/`starting`/`stopping`/`stopped`/`error` phases are owned by the start/stop flow; `needsWallet`/`locked`/`syncing`/`ready` are derived from wallet info; `restoring` is owned by the engine while a background restore (recovery scan) is bringing a freshly restored wallet up. See [Handle phases and errors](/guides/handle-phases-and-errors/) for the full state machine and how to react to each phase.
The package **re-exports every type and enum from `core`**, including `WalletEngine` and `createWalletEngine`, so a React app can import contracts from one place. It does **not** re-export any transport’s engine factory; keep the transport import separate so the same binding runs over any transport, web or React Native.
## Hooks
**`useWallet()`** is the application-shell hook: `phase`, `error`, `start`, `stop`. It throws outside a provider. Read the rest of the state through the granular hooks below.
**State-reading hooks** are plain selectors; each re-renders only when the slice it reads changes:
| Hook | Returns |
| --------------------- | --------------------------- |
| `useWalletInfo()` | `WalletInfo \| null` |
| `useWalletBalance()` | `Balance \| null` |
| `useWalletActivity()` | `Entry[]` |
| `useWalletRecovery()` | `{ recovery, acknowledge }` |
| `useWalletLogs()` | `{ logs, clear }` |
**Mutation hooks** each expose an action plus verb-prefixed `{ Pending, Error, Data, reset }` fields built on one shared convention, throw-and-capture: the action both settles its own promise (resolve with the result, or reject with the failure) and mirrors that outcome into the pending/error/data fields, so you can `await`/`catch` it imperatively or read the hook’s fields declaratively, whichever fits. Error fields are always an `Error | null`, never a string:
| Hook | Returns |
| ------------------------ | ------------------------------------------------------------------------------------------------------ |
| `useWalletCreate()` | `{ create, createPending, createError, createData, resetCreate }` |
| `useWalletRestore()` | `{ restore, restorePending, restoreError, restoreData, resetRestore }` |
| `useWalletUnlock()` | `{ unlock, unlockPending, unlockError, unlockData, resetUnlock }` |
| `useWalletDeposit()` | `{ deposit, depositPending, depositError, depositData, resetDeposit }` |
| `useWalletReceive()` | `{ receive, receivePending, receiveError, receiveData, resetReceive }` |
| `useWalletPrepareSend()` | `{ prepare, preparePending, prepareError, prepareData, resetPrepare }` |
| `useWalletSend()` | `{ send, sendPrepared, sendPending, sendError, sendData, resetSend }` |
| `useWalletRefresh()` | `{ refresh, refreshPending, refreshError, resetRefresh }` (no data field: `refresh()` resolves `void`) |
**Exit hooks** cover cooperative and unilateral exits. `useWalletExit`, `useWalletExitPlan`, `useWalletSweep`, and `useWalletList` are 1:1 wrappers around one daemon verb and follow the same throw-and-capture convention as the mutation hooks above. `useWalletExitBatch` is the orchestrator: it drives a multi-outpoint exit through funding-contention guards and additionally reports progress events. `useWalletExitStatus` and `useWalletExits` are read hooks instead of mutations: they fetch on mount (and on relevant changes) and expose a `refresh*` action rather than a `reset*` one:
| Hook | Returns |
| -------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `useWalletExit()` | `{ exit, exitPending, exitError, exitData, resetExit }` |
| `useWalletExitPlan()` | `{ plan, planPending, planError, planData, resetPlan }` |
| `useWalletSweep()` | `{ sweep, sweepPending, sweepError, sweepData, resetSweep }` |
| `useWalletExitBatch()` | `{ exitBatch, exitBatchPending, exitBatchError, exitBatchData, exitBatchEvents, resetExitBatch }` |
| `useWalletExitStatus(outpoint, opts?)` | `{ status, statusPending, statusError, refreshStatus }` |
| `useWalletExits()` | `{ summary, summaryPending, summaryError, refreshSummary }` |
| `useWalletList()` | `{ list, listPending, listError, listData, resetList }` |
`useWalletExitStatus` defaults to a cheap, coarse phase-only fetch; pass `{ detailed: true }` for recovery-tree progress, the CSV countdown, and fees. Pass `{ pollMs }` to poll while the hook is mounted: polling is opt-in and runs with no visibility/focus gating, stopping only on unmount. See [Unilateral exit & status](/guides/unilateral-exit/) for end-to-end usage, and the [wavelength-react reference](/reference/wavelength-react/#exiting-ark) for the full hazard notes on `exitBatch`.
For example, `useWalletSend()` returns a `send(req)` action alongside its own `sendPending`/`sendError` state:
```tsx
import { useWalletSend } from '@lightninglabs/wavelength-react';
function SendButton({ invoice }: { invoice: string }) {
const { send, sendPending, sendError } = useWalletSend();
return (
<>
send({ invoice })}>
Send payment
{sendError && {sendError.message}
}
>
);
}
```
See [Send a payment](/guides/send-a-payment/), [Receive a Lightning payment](/guides/receive-a-lightning-payment/), [Get a deposit address](/guides/get-a-deposit-address/), and [Show balance and activity](/guides/show-balance-and-activity/) for end-to-end usage of these hooks.
`useWalletCreate()` and `useWalletRestore()` are two separate hooks (mirroring `WalletEngine.createWallet` and `WalletEngine.restoreWallet`) rather than one combined bootstrap hook, so a component only subscribes to the flow it drives:
```tsx
import { useWalletCreate } from '@lightninglabs/wavelength-react';
function Onboard() {
const { create, createPending, createError } = useWalletCreate();
const handleCreate = async () => {
const { mnemonic } = await create({ password: 'a-strong-password' });
// Back up `mnemonic` securely before the user sends funds.
console.log('Wallet created');
};
return (
<>
Create wallet
{createError && {createError.message}
}
>
);
}
```
Once `create` resolves, `phase` from `useWallet()` automatically advances (through `'syncing'` when the daemon needs to catch up) to `'ready'`, so any component watching `phase` updates without extra wiring.
## Passkey
Passkeys are driven by **`useWalletPasskey(ceremony)`**, where `ceremony` is a `PasskeyCeremony` implementation injected from your transport. On web, pass **`webPasskeyCeremony`** from `@lightninglabs/wavelength-web`; on React Native, pass **`createNativePasskeyCeremony({ rpId })`** from `@lightninglabs/wavelength-react-native` (see [Passkey setup](/react-native/get-started/passkey-setup/) for the relying-party domain association it requires). `create` and `open` track separately, each with its own `pending`/`error`/`reset`:
```tsx
import { useWalletPasskey } from '@lightninglabs/wavelength-react';
import { webPasskeyCeremony } from '@lightninglabs/wavelength-web';
function PasskeyUnlock() {
const {
supported,
create,
createPending,
createError,
open,
openPending,
openError,
} = useWalletPasskey(webPasskeyCeremony);
if (!supported) {
return Passkeys are not available on this device.
;
}
return (
<>
create('My App')}>
Create with passkey
open(/* optional credential id */)}>
Unlock with passkey
{createError && {createError.message}
}
{openError && {openError.message}
}
>
);
}
```
```tsx
import { Button, Text } from 'react-native';
import { useWalletPasskey } from '@lightninglabs/wavelength-react';
import { createNativePasskeyCeremony } from '@lightninglabs/wavelength-react-native';
const ceremony = createNativePasskeyCeremony({ rpId: 'your-app-domain.example' });
function PasskeyUnlock() {
const {
supported,
create,
createPending,
createError,
open,
openPending,
openError,
} = useWalletPasskey(ceremony);
if (!supported) {
return Passkeys are not available on this device. ;
}
return (
<>
create('My App')}
title="Create with passkey"
/>
open(/* optional credential id */)}
title="Unlock with passkey"
/>
{createError && {createError.message} }
{openError && {openError.message} }
>
);
}
```
The hook checks **`ceremony.supportsPasskeyPrf()`** on mount (platform authenticator availability). **`create(appName)`** runs `registerPasskeyWallet`, then opens the wallet from the derived PRF output, then refreshes engine state. **`open(credentialId?)`** asserts an existing passkey (scoped when an id is passed, discoverable otherwise) and opens the wallet the same way. On success the engine’s phase advances automatically; persist the returned **`credentialId`** if you want scoped unlocks later.
Unlike the rest of the hooks on this page, `create`/`open` are the one place where a failure can be something other than a real error: dismissing the OS passkey prompt rejects with a `PasskeyCancelledError`, which `useWalletPasskey` rethrows without recording into `createError`/`openError` (a cancelled prompt is not a failure worth displaying). Every other rejection behaves like the mutation hooks above: `await`/`catch` the call, or read `createError`/ `openError`.
Both `create` and `open` resolve to a `PasskeyWalletOutcome`: `{ result, credentialId }`. Capture `credentialId` from a successful result and store it so a later `open(credentialId)` call can perform a scoped unlock instead of a discoverable one:
```tsx
const outcome = await create('My App');
localStorage.setItem('passkeyCredentialId', outcome.credentialId);
```
See [Use a passkey](/guides/use-a-passkey/) for end-to-end onboarding UX.
---
Source: https://wavelength.lightning.engineering/guides/create-a-wallet.md
[Wavelength](/)›Guides›Create a wallet (password)
# Create a wallet (password)
2 min read
Guides
Web SDK
## Install the packages
The Wavelength SDK ships as a transport package, one per platform, plus `@lightninglabs/wavelength-react`, the React bindings. All are ES modules with full TypeScript declarations.
The web transport, `@lightninglabs/wavelength-web`, bundles a WASM module for cryptography and uses OPFS for storage. There are no native dependencies and no server-side component, so a modern browser is all you need at runtime.
```bash
npm install @lightninglabs/wavelength-web @lightninglabs/wavelength-react
```
The React Native transport, `@lightninglabs/wavelength-react-native`, wraps a wallet runtime that is compiled into your app binary rather than fetched at runtime.
```bash
npm install @lightninglabs/wavelength-react-native @lightninglabs/wavelength-react
```
See [Installation](/react-native/get-started/installation/) for staging the native runtime binaries before your first build.
## Add the provider
Build a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) outside your component tree and wrap your app in [`WavelengthProvider`](/reference/wavelength-react/#WavelengthProvider) so every component below it can reach the wallet through hooks. Pass `config` and `autoStart: true` so the engine boots the embedded daemon on its own as soon as the runtime is ready; no boot effect needed.
Start with [`defaultConfig('signet')`](/reference/wavelength-web/#defaultConfig). Move to a hand-built mainnet config (with `allowMainnet: true`) only once you have key backup and recovery in place.
[`createWebWalletEngine()`](/reference/wavelength-web/#createWebWalletEngine) builds the engine for the browser transport:
```tsx
import { createWebWalletEngine } from '@lightninglabs/wavelength-web';
import { WavelengthProvider } from '@lightninglabs/wavelength-react';
import { defaultConfig } from '@lightninglabs/wavelength-web';
const engine = createWebWalletEngine({
runtimeBaseUrl: '/wavewalletdk/',
config: defaultConfig('signet'),
autoStart: true,
});
export function App() {
return (
{/* your app */}
);
}
```
[`createNativeWalletEngine()`](/reference/wavelength-react-native/#createNativeWalletEngine) builds the engine for the native transport:
```tsx
import { createNativeWalletEngine } from '@lightninglabs/wavelength-react-native';
import { WavelengthProvider } from '@lightninglabs/wavelength-react';
import { defaultConfig } from '@lightninglabs/wavelength-react-native';
const engine = createNativeWalletEngine({
config: defaultConfig('signet'),
autoStart: true,
});
export function App() {
return (
{/* your app */}
);
}
```
The engine itself owns starting the runtime, so `App` needs no start effect. If you would rather call `start()` yourself (for example to prompt the user first), omit `autoStart` and drive it from [`useWallet()`](/reference/wavelength-react/#useWallet); see [Handle phases & errors](/guides/handle-phases-and-errors/).
## Create the wallet
Call [`useWalletCreate()`](/reference/wavelength-react/#useWalletCreate) inside the provider to create a wallet. When `phase` from [`useWallet()`](/reference/wavelength-react/#useWallet) is `'needsWallet'`, no wallet exists yet.
`create({ password })` generates a fresh HD seed, encrypts it with the password, and stores it locally. The password is whatever you pass in; the SDK does not enforce a minimum length or complexity, so validate it yourself before calling `create`. The whole operation runs on-device and the seed never leaves it. After it resolves, `phase` becomes `'ready'`.
The resolved result includes `mnemonic`, the wallet’s recovery phrase. This is the only time the SDK ever returns it, so show it to the user immediately and prompt them to write it down before continuing. To rebuild a wallet from an existing recovery phrase instead of generating a new one, see [Restore a wallet](/guides/restore-a-wallet/).
```tsx
import { useState } from 'react';
import { useWallet, useWalletCreate } from '@lightninglabs/wavelength-react';
function Onboard() {
const { phase } = useWallet();
const { create, createPending, createError } = useWalletCreate();
const [mnemonic, setMnemonic] = useState(null);
const handleCreate = async () => {
// Generate the HD seed on-device, encrypted with the password.
const result = await create({ password: 'a-strong-password' });
// Show the mnemonic once, right after creation: it is not retrievable later.
setMnemonic(result.mnemonic);
};
if (phase !== 'needsWallet') {
return null;
}
return (
<>
Create wallet
{createError && {createError.message}
}
{mnemonic && (
Write down your recovery phrase: {mnemonic.join(' ')}
)}
>
);
}
```
---
Source: https://wavelength.lightning.engineering/guides/restore-a-wallet.md
[Wavelength](/)›Guides›Restore a wallet
# Restore a wallet
3 min read
Guides
Web SDK
A restore rebuilds a wallet on-device from a recovery phrase the user already has, rather than generating a fresh seed. Restores are always local password wallets: the user supplies the recovery phrase plus a new password to encrypt the seed on this device. This guide covers restoring the seed, opting into server-assisted recovery to bring back funds and history, and observing that recovery run in the background so the user is not stuck on a loading screen.
## Restore from a recovery phrase
Pass an existing `mnemonic` to [`useWalletRestore()`](/reference/wavelength-react/#useWalletRestore)’s `restore` (with `seedPassphrase` too, if the original wallet used a BIP-39 passphrase). This re-derives the same HD seed and encrypts it with the new `password`.
On its own, that rebuilds the keys but leaves the wallet empty: it does not know about any funds or past activity yet. To bring those back, opt into recovery.
## Bring back balances and history
Set `recoverState: true` to turn on server-assisted recovery. The daemon walks the seed’s addresses and queries the operator’s indexer to rebuild wallet state: boarding outputs, VTXOs, and Lightning receive history. Without it, a restored wallet starts at a zero balance even if the seed has funds.
`recoveryWindow` is an optional per-key-family address look-ahead: how many unused addresses past the last used one the scan probes before giving up. Omit it to let the daemon pick its own default, and only raise it if a wallet skipped a large gap of addresses.
## Restore in the background
The recovery scan can take minutes and reports no progress while it runs. Rather than blocking on the whole scan, `restore` from [`useWalletRestore()`](/reference/wavelength-react/#useWalletRestore) resolves (and flips `restorePending` back to `false`) as soon as the restored wallet is usable, which happens well before the scan completes; `phase` from [`useWallet()`](/reference/wavelength-react/#useWallet) advances to `'restoring'` while the wallet comes up and then to `'ready'` at that point. The scan itself keeps running in the background, tracked separately through [`useWalletRecovery()`](/reference/wavelength-react/#useWalletRecovery). The user lands in a working wallet immediately while balances and history fill in behind them.
```tsx
import { useWallet, useWalletRestore } from '@lightninglabs/wavelength-react';
function Restore({ mnemonic }: { mnemonic: string[] }) {
const { phase } = useWallet();
const { restore, restorePending, restoreError } = useWalletRestore();
const handleRestore = async () => {
// Resolves as soon as the wallet is usable; the recovery scan (if any)
// keeps rebuilding balances and history in the background, tracked
// through useWalletRecovery.
await restore({
password: 'a-strong-password',
mnemonic,
recoverState: true,
});
};
if (phase !== 'needsWallet') {
return null;
}
return (
<>
Restore wallet
{restoreError && {restoreError.message}
}
>
);
}
```
## Show recovery progress
[`useWalletRecovery()`](/reference/wavelength-react/#useWalletRecovery) returns `recovery`, a discriminated union on `status`, and `acknowledge`, which resets it to `idle`. Drive a banner off `recovery` so the user knows the scan is still running, and report the outcome when it settles.
```tsx
import { useWalletRecovery } from '@lightninglabs/wavelength-react';
function RecoveryBanner() {
const { recovery, acknowledge } = useWalletRecovery();
switch (recovery.status) {
case 'idle':
return null;
case 'restoring':
return (
Restoring your balance and history. This can take a few minutes; your
balance will fill in as it is found.
);
case 'done':
return (
Wallet restored. Recovered {recovery.result.recoveredVTXOs} VTXOs.
Dismiss
);
case 'failed':
return recovery.walletUsable ? (
Could not finish restoring your history, so your balance may be
incomplete. The wallet is still usable. {recovery.error.message}
Dismiss
) : (
Restore failed: {recovery.error.message}
Dismiss
);
}
}
```
`recovery.status === 'failed'` covers two distinct situations: a background scan that errored on an already-usable wallet, or a restore that failed before the wallet came up at all (for example the recovery phrase was rejected). `recovery.walletUsable` disambiguates the two directly: `true` means the background scan errored on an already-usable wallet, which works with whatever the scan found before it failed; `false` means the restore itself never got that far, so there is no wallet to fall back to and the failure should be shown on the onboarding screen instead of a post-usability banner. This applies even to an untracked restore (one whose returned promise you did not await, or whose component unmounted while `restorePending`): the failure still lands in `recovery`, so a screen that reads it after remounting picks it back up.
The wallet is fully usable throughout the first case. Recovery only reads state, so sending and receiving during the scan is safe. Until it finishes, the balance is understated (a send may briefly report insufficient funds), while receiving is unaffected. If the scan fails, the wallet still works with whatever it found; the phrase is the source of truth, so the user can restore again later.
---
Source: https://wavelength.lightning.engineering/guides/get-a-deposit-address.md
[Wavelength](/)›Guides›Get a deposit address
# Get a deposit address
2 min read
Guides
Web SDK
## Get a boarding address
This guide assumes your app already renders inside a [`WavelengthProvider`](/reference/wavelength-react/#WavelengthProvider) with a wallet unlocked and ready. See [Create a wallet](/guides/create-a-wallet/).
`deposit()` from [`useWalletDeposit()`](/reference/wavelength-react/#useWalletDeposit) creates a tracked boarding address from your wallet key. Send any amount of on-chain bitcoin to this address and the Ark server will sweep it into your VTXO balance in the next round.
The returned [`DepositResult`](/reference/wavelength-core/#DepositResult) includes both the address string and an initial activity entry. Display the address as text, or render it as a QR code with a library of your choice for easy scanning.
```tsx
import { useState } from 'react';
import { useWalletDeposit } from '@lightninglabs/wavelength-react';
function DepositAddress() {
const { deposit, depositPending, depositError, resetDeposit } = useWalletDeposit();
const [address, setAddress] = useState(null);
const getAddress = async () => {
const result = await deposit();
setAddress(result.address);
};
return (
Get deposit address
{address &&
{address}
}
{depositError && (
{depositError.message} Dismiss
)}
);
}
```
`deposit()` also accepts an optional `{ amountSatHint }` if you want to record the amount the user intends to deposit:
```tsx
const result = await deposit({ amountSatHint: 100000 });
```
## Watch for confirmation
Use [`useWalletActivity()`](/reference/wavelength-react/#useWalletActivity) to watch for your deposit to land. The engine subscribes to the activity stream and refreshes automatically, so pending entries appear as soon as the server sees the boarding transaction and flip to `'complete'` once the round settles. An entry can also flip to `'failed'`; in that case show `entry.failureReason` so the user knows what went wrong.
Filter on `entry.kind === 'deposit'` to surface only deposit events, or display the full feed with status badges so the user can see the progress.
```tsx
import { useWalletActivity } from '@lightninglabs/wavelength-react';
function WatchDeposit() {
const activity = useWalletActivity();
const deposits = activity.filter(entry => entry.kind === 'deposit');
return (
{deposits.map(entry => (
{entry.kind} {entry.amountSat} sats - {entry.status}
))}
);
}
```
---
Source: https://wavelength.lightning.engineering/guides/show-balance-and-activity.md
[Wavelength](/)›Guides›Show balance & activity
# Show balance & activity
2 min read
Guides
Web SDK
This guide assumes your app already renders inside a [`WavelengthProvider`](/reference/wavelength-react/#WavelengthProvider) with a wallet unlocked and ready. See [Create a wallet](/guides/create-a-wallet/).
## Read the balance
[`useWalletBalance()`](/reference/wavelength-react/#useWalletBalance) is a plain selector: it returns the current [`Balance`](/reference/wavelength-core/#Balance) directly (`null` before it is known), and re-renders your component only when the balance actually changes, for example after a payment settles. There is no `pending`/`busy` flag on it; a `null` balance is your loading signal.
Display `confirmedSat` as spendable balance. Add `pendingInSat` and `pendingOutSat` when you want to show in-flight funds.
```tsx
import { useWalletBalance } from '@lightninglabs/wavelength-react';
function Balance() {
const balance = useWalletBalance();
if (!balance) return Loading...
;
return (
{balance.confirmedSat.toLocaleString()} sats available
);
}
```
## Subscribe to activity
[`useWalletActivity()`](/reference/wavelength-react/#useWalletActivity) is the same kind of selector: it returns the full transaction history array directly. Each [`Entry`](/reference/wavelength-core/#Entry) has `id`, `kind` (`send`, `receive`, `deposit`, or `exit`), `amountSat`, `status` (`pending`, `complete`, or `failed`), and timestamps. It also carries `feeSat`, `counterparty`, `note`, and `failureReason` (a human-readable message set when `status` is `failed`).
The engine stays live: new entries arrive through the activity stream and pending entries update in place when they settle. This live-update behavior comes from the [`WalletEngine`](/reference/wavelength-core/#WalletEngine) your [`WavelengthProvider`](/reference/wavelength-react/#WavelengthProvider) wraps; see [Create a wallet](/guides/create-a-wallet/) for how it is set up. No polling or manual refresh is needed.
```tsx
import { useWalletActivity } from '@lightninglabs/wavelength-react';
function ActivityFeed() {
const activity = useWalletActivity();
if (activity.length === 0) return No activity yet.
;
return (
{activity.map(entry => (
{entry.kind}
{entry.kind === 'receive' || entry.kind === 'deposit' ? '+' : ''}
{entry.kind === 'send' || entry.kind === 'exit' ? '-' : ''}
{entry.amountSat} sats
{entry.status}
{entry.status === 'failed' && entry.failureReason
? `: ${entry.failureReason}`
: ''}
))}
);
}
```
## Pull-to-refresh
[`useWalletBalance()`](/reference/wavelength-react/#useWalletBalance) and [`useWalletActivity()`](/reference/wavelength-react/#useWalletActivity) update on their own as the engine’s background processes (the activity stream, the settle reconcile, the sync poll) keep the snapshot fresh, so most UIs need nothing more. For an explicit pull-to-refresh control, call `refresh()` from [`useWalletRefresh()`](/reference/wavelength-react/#useWalletRefresh) and show its `refreshPending` flag while the call is in flight. That `refreshPending` is scoped to this hook instance’s own calls; the engine’s own background refreshes never flip it, so it only lights up when the user actually asked for a refresh.
```tsx
import { useWalletRefresh } from '@lightninglabs/wavelength-react';
function RefreshButton() {
const { refresh, refreshPending } = useWalletRefresh();
return (
refresh()} disabled={refreshPending}>
{refreshPending ? 'Refreshing...' : 'Refresh'}
);
}
```
## Filter and group
Filter `activity` with standard array methods to build tab views or summary cards. Use each entry’s `kind` to determine direction (`receive` and `deposit` are inbound; `send` and `exit` are outbound). `amountSat` is a plain magnitude in satoshis. Filter on `status === 'pending'` to surface in-flight payments that need user attention.
All filtering happens in memory, so you can derive multiple views from the same hook call without additional network requests.
```tsx
import { useWalletActivity } from '@lightninglabs/wavelength-react';
function FilteredFeed() {
const activity = useWalletActivity();
const sends = activity.filter(entry => entry.kind === 'send');
const receives = activity.filter(entry => entry.kind === 'receive');
const pending = activity.filter(entry => entry.status === 'pending');
return (
Pending: {pending.length}
Received: {receives.length} txs
Sent: {sends.length} txs
);
}
```
## What’s next
Now that your UI can show balance and activity, try [Send a payment](/guides/send-a-payment/), [Receive a Lightning payment](/guides/receive-a-lightning-payment/), or [Get a deposit address](/guides/get-a-deposit-address/).
---
Source: https://wavelength.lightning.engineering/guides/send-a-payment.md
[Wavelength](/)›Guides›Send a payment
# Send a payment
2 min read
Guides
Web SDK
## Send over Lightning
Wavelength uses a send swap to route a Lightning payment from your Ark balance. Call `prepare({ invoice })` from [`useWalletPrepareSend()`](/reference/wavelength-react/#useWalletPrepareSend) to get a fee quote and a single-use `sendIntentId`. Pass the result to `sendPrepared()` from [`useWalletSend()`](/reference/wavelength-react/#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()`](/reference/wavelength-react/#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.
```tsx
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 (
Fee: {quote.expectedFeeSat} sats
{!quote.feeKnown && ' (estimate)'}
{quote.warning &&
{quote.warning}
}
Confirm payment
Cancel
);
}
return doPrepare('lnbc...')}>Pay invoice ;
}
```
## 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()`](/reference/wavelength-react/#useWalletSend). The returned [`SendResult`](/reference/wavelength-core/#SendResult) includes the activity entry and `actualAmountSat`.
```tsx
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 (
handleSend('bc1q...', 50_000)} disabled={sendPending}>
Send on-chain
);
}
```
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.
```tsx
await send({ onchainAddress: address, sweepAll: true });
```
## Handle errors
`send` and `sendPrepared` from [`useWalletSend()`](/reference/wavelength-react/#useWalletSend) throw a [`WavelengthError`](/reference/wavelength-core/#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()`](/reference/wavelength-react/#useWalletPrepareSend)) are safe to retry. Errors thrown during `sendPrepared` may indicate the payment was already sent; check the activity feed before retrying.
```tsx
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 (
<>
pay('lnbc...')}>Pay
{sendError && {sendError.message}
}
>
);
}
```
---
Source: https://wavelength.lightning.engineering/guides/receive-a-lightning-payment.md
[Wavelength](/)›Guides›Receive a Lightning payment
# Receive a Lightning payment
2 min read
Guides
Web SDK
## Create a Lightning invoice
`receive()` from [`useWalletReceive()`](/reference/wavelength-react/#useWalletReceive) triggers a receive swap: the Ark server sets up a Lightning payment path that deposits into your VTXO once the payer settles. Pass `amountSat` and an optional `memo` for the invoice.
The returned `ReceiveResult.invoice` is a standard BOLT11 string that any compatible wallet can pay. Display it as a QR code alongside the raw string so the payer has both options.
```tsx
import { useState } from 'react';
import { useWalletReceive } from '@lightninglabs/wavelength-react';
function CreateInvoice() {
const { receive, receivePending } = useWalletReceive();
const [invoice, setInvoice] = useState(null);
const generate = async () => {
const result = await receive({
amountSat: 10_000,
memo: 'Coffee',
});
setInvoice(result.invoice);
};
return (
Generate invoice
{invoice &&
{invoice} }
);
}
```
## Wait for settlement
[`useWalletActivity()`](/reference/wavelength-react/#useWalletActivity) returns the live activity array and streams updates in place. Match the entry by invoice string in `entry.request?.lightningInvoice` and watch its `status` field: the entry appears as `'pending'` when the payer sends and switches to `'complete'` once the receive swap round completes. Like any entry, a receive swap can also terminate as `'failed'`, so handle that branch too.
Render a loading state until the entry appears, then show a pending indicator while it settles, a failure message if it fails, and a success message once `status === 'complete'`.
```tsx
import { useWalletActivity } from '@lightninglabs/wavelength-react';
function InvoiceStatus({ bolt11 }: { bolt11: string }) {
const activity = useWalletActivity();
const payment = activity.find(
entry =>
entry.kind === 'receive' &&
entry.request?.lightningInvoice === bolt11,
);
if (!payment) return Waiting for payment...
;
if (payment.status === 'pending') return Payment received, settling...
;
if (payment.status === 'failed') {
return Payment failed: {payment.failureReason}
;
}
return Settled: +{payment.amountSat} sats
;
}
```
For finer-grained progress than the top-level `status` field offers (for example, distinguishing “waiting for payment” from “settling” while a receive swap is still `'pending'`), see `entry.progress?.phase` in [Handle phases and errors](/guides/handle-phases-and-errors/). This guide sticks to the simpler `status` field for clarity.
---
Source: https://wavelength.lightning.engineering/guides/use-a-passkey.md
[Wavelength](/)›Guides›Use a passkey
# Use a passkey
4 min read
Guides
Web SDK
## Create a passkey-protected wallet
[`useWalletPasskey(ceremony)`](/reference/wavelength-react/#useWalletPasskey) drives the passkey ceremony and opens the wallet through the engine, refreshing engine state on success so `phase` advances automatically. The `ceremony` argument is injected from your transport:
`supported` is `boolean | null`, not a plain boolean: it starts `null` while the platform capability probe is in flight and settles to `true` or `false` once it resolves. Treat `null` as “still checking” and hold the UI on a loading state rather than falling into the unsupported branch, or a capable device will flash a password-only form before flipping to the passkey option a moment later:
Pass [`webPasskeyCeremony`](/reference/wavelength-web/#webPasskeyCeremony) from `@lightninglabs/wavelength-web` as the ceremony implementation.
```tsx
import { webPasskeyCeremony } from '@lightninglabs/wavelength-web';
import { useWalletPasskey } from '@lightninglabs/wavelength-react';
function PasskeyOnboard() {
const { create, supported, createPending } =
useWalletPasskey(webPasskeyCeremony);
const handleCreate = async () => {
const outcome = await create('My Wallet App');
// Persist outcome.credentialId to scope future unlocks.
console.log('Wallet ready:', outcome.result.identityPubKey);
};
if (supported === null) return Checking device capabilities…
;
if (!supported) return Passkeys are not supported on this device.
;
return (
Create wallet with passkey
);
}
```
Pass [`createNativePasskeyCeremony({ rpId })`](/reference/wavelength-react-native/#createNativePasskeyCeremony) from `@lightninglabs/wavelength-react-native` as the ceremony implementation. The `rpId` domain must be associated with your app first; see [Passkey setup](/react-native/get-started/passkey-setup/).
```tsx
import { Button, Text } from 'react-native';
import { createNativePasskeyCeremony } from '@lightninglabs/wavelength-react-native';
import { useWalletPasskey } from '@lightninglabs/wavelength-react';
const ceremony = createNativePasskeyCeremony({ rpId: 'wallet.example.com' });
function PasskeyOnboard() {
const { create, supported, createPending } = useWalletPasskey(ceremony);
const handleCreate = async () => {
const outcome = await create('My Wallet App');
// Persist outcome.credentialId to scope future unlocks.
console.log('Wallet ready:', outcome.result.identityPubKey);
};
if (supported === null) return Checking device capabilities… ;
if (!supported) return Passkeys are not supported on this device. ;
return (
);
}
```
`create(appName)` registers a device-bound credential (Face ID, Touch ID, Windows Hello, or a hardware security key) and derives the wallet seed from the PRF output. No password is stored or transmitted. Creation and opening track separately (`create`/`createPending`/`createError` versus `open`/`openPending`/`openError`) because apps typically render them on different screens.
The underlying capability probe ([`ceremony.supportsPasskeyPrf()`](/reference/wavelength-web/#supportsPasskeyPrf)) is memoized: only the first call per ceremony instance actually runs it, and every [`useWalletPasskey`](/reference/wavelength-react/#useWalletPasskey) call sharing that instance resolves from the same promise. Because of this, it is worth warming the probe once at app boot, before onboarding ever mounts (`void webPasskeyCeremony.supportsPasskeyPrf();` alongside your engine setup). By the time the first screen reads `supported`, the probe has usually already resolved, so the loading branch above rarely paints in practice.
## Unlock with a passkey
The samples below use the web ceremony for brevity; on React Native, substitute the `ceremony` you created in the tabbed section above.
When `phase === 'locked'`, call `open(credentialId?)`. The platform presents the passkey dialog. On success, the engine refreshes and `phase` advances through `'syncing'` to `'ready'` on its own. If the ceremony fails, `open` rejects and `openError` is set; see [Handle phases & errors](/guides/handle-phases-and-errors/) for how errors and phases interact more broadly.
Pass the stored `credentialId` to scope the assertion to the same passkey. Omit it for a discoverable credential flow, useful when the app has not persisted a `credentialId` yet or the user is unlocking from a new device where a synced passkey is already available:
```tsx
// No credentialId: the platform prompts with any discoverable passkey
// registered for this origin.
await open();
```
```tsx
import { webPasskeyCeremony } from '@lightninglabs/wavelength-web';
import { useWallet, useWalletPasskey } from '@lightninglabs/wavelength-react';
function PasskeyUnlock({ credentialId }: { credentialId: string }) {
const { phase } = useWallet();
const { open, openPending, openError } = useWalletPasskey(webPasskeyCeremony);
const unlock = async () => {
await open(credentialId);
};
return (
{phase === 'locked' && (
Unlock with passkey
)}
{openError &&
{openError.message}
}
);
}
```
## Handle a cancelled ceremony
If the user dismisses the OS passkey prompt, `create`/`open` reject with a [`PasskeyCancelledError`](/reference/wavelength-core/#PasskeyCancelledError) instead of a regular failure, and that rejection is never recorded into `createError`/`openError`: a dismissed prompt is not a failure to display. Check for it with `instanceof` and treat it as a no-op rather than showing an error:
```tsx
import { webPasskeyCeremony } from '@lightninglabs/wavelength-web';
import { PasskeyCancelledError, useWalletPasskey } from '@lightninglabs/wavelength-react';
function PasskeyUnlock({ credentialId }: { credentialId: string }) {
const { open } = useWalletPasskey(webPasskeyCeremony);
const unlock = async () => {
try {
await open(credentialId);
} catch (err) {
if (err instanceof PasskeyCancelledError) {
// The user dismissed the prompt; nothing to show.
return;
}
// A genuine failure: openError is already set for a declarative render,
// or handle err here for imperative flow.
}
};
return Unlock with passkey ;
}
```
### Troubleshooting: a ceremony that always “cancels”
Browsers collapse most WebAuthn failures into the same `NotAllowedError` signal used for a genuine user dismissal: a permissions-policy block, an iframe restriction, or an `rpId` that does not match the page’s association file all surface as [`PasskeyCancelledError`](/reference/wavelength-core/#PasskeyCancelledError) here, indistinguishable from the user tapping away. If a ceremony instantly “cancels” on every attempt, before assuming users are backing out, check the configuration angle first: confirm `rpId` matches the domain hosting the app, and that the `.well-known/assetlinks.json` (Android) or `apple-app-site-association` (iOS) association file for that `rpId` is reachable and lists the app.
## Provide a recovery-phrase fallback
If the passkey credential is unavailable (for example, the user is on a new device or has lost access to the registered authenticator), `open` rejects with a genuine error (not a [`PasskeyCancelledError`](/reference/wavelength-core/#PasskeyCancelledError)). Catch it and offer a recovery-phrase fallback instead of leaving the user stuck.
Every successful passkey outcome includes `outcome.result.imported`, a boolean that is `true` when the ceremony created a new local wallet from the derived seed (a fresh device) and `false` when it unlocked an existing local wallet. `outcome.result.mnemonic` is populated only when `imported` is `true`, so check `imported` before reading `mnemonic` for backup display. To restore on a new device without a passkey, call `create({ password, mnemonic })` from [`useWalletCreate()`](/reference/wavelength-react/#useWalletCreate) with a new wallet password and the saved recovery phrase; `password` is required by [`CreateWalletRequest`](/reference/wavelength-core/#createWallet). This is why users should record their recovery phrase when they first create the wallet.
```tsx
import { useState } from 'react';
import { webPasskeyCeremony } from '@lightninglabs/wavelength-web';
import {
PasskeyCancelledError,
useWalletCreate,
useWalletPasskey,
} from '@lightninglabs/wavelength-react';
function UnlockWithFallback({ credentialId }: { credentialId: string }) {
const { open } = useWalletPasskey(webPasskeyCeremony);
const { create } = useWalletCreate();
const [recovering, setRecovering] = useState(false);
const [password, setPassword] = useState('');
const [mnemonic, setMnemonic] = useState('');
const tryPasskey = async () => {
try {
await open(credentialId);
} catch (err) {
if (err instanceof PasskeyCancelledError) return;
setRecovering(true);
}
};
if (recovering) {
return (
);
}
return Unlock with passkey ;
}
```
---
Source: https://wavelength.lightning.engineering/guides/handle-phases-and-errors.md
[Wavelength](/)›Guides›Handle phases & errors
# Handle phases & errors
3 min read
Guides
Web SDK
## React to wallet phases
`phase` from [`useWallet()`](/reference/wavelength-react/#useWallet) describes where the wallet is in its lifecycle. Use a switch or a series of conditionals at the root of your app to render the right screen for each phase. The value updates reactively, so the UI transitions automatically as the wallet moves through phases.
| Phase | Meaning |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `loading` | The runtime assets have not finished loading. |
| `runtimeReady` | The runtime is usable, but the daemon has not started. |
| `starting` | `start()` has been called and has not yet resolved. |
| `needsWallet` | No wallet exists yet; create or restore one. |
| `locked` | A wallet exists but is locked; unlock it. |
| `syncing` | The wallet is unlocked and catching up with the chain. |
| `restoring` | A background restore ([`useWalletRestore()`](/reference/wavelength-react/#useWalletRestore)) is bringing a freshly restored wallet up; setting `recoverState: true` on the request additionally tracks the recovery scan through the recovery state. |
| `ready` | The wallet is unlocked and ready to use. |
| `stopping` | `stop()` has been called and has not yet resolved. |
| `stopped` | The daemon has stopped. |
| `error` | The runtime failed to start, `start()`/`stop()` failed, or a background process (sync poll, activity stream, refresh) exhausted its failure budget. |
`info.walletState` (from [`useWalletInfo()`](/reference/wavelength-react/#useWalletInfo)) carries the underlying [`WalletState`](/reference/wavelength-core/#WalletState) string (`none`, `locked`, `ready`, or `syncing`) when you need the daemon-level detail instead of the broader [`RuntimePhase`](/reference/wavelength-core/#RuntimePhase).
```tsx
import { useWallet } from '@lightninglabs/wavelength-react';
function WalletGate({ children }: { children: React.ReactNode }) {
const { phase } = useWallet();
switch (phase) {
case 'loading':
case 'runtimeReady':
case 'starting':
return Starting wallet runtime...
;
case 'needsWallet':
return ;
case 'locked':
return ;
case 'syncing':
case 'restoring':
return Syncing with the wallet server...
;
case 'ready':
return <>{children}>;
case 'stopping':
case 'stopped':
return Wallet stopped.
;
case 'error':
return Runtime failed to start.
;
default:
return null;
}
}
```
A failed `start()` or `stop()` moves `phase` straight to `'error'`; there is no separate `'startFailed'`/`'stopFailed'` phase to branch on. Read `error` from [`useWallet()`](/reference/wavelength-react/#useWallet) for the reason.
## The Error | null convention
Every mutation hook ([`useWalletCreate`](/reference/wavelength-react/#useWalletCreate), [`useWalletRestore`](/reference/wavelength-react/#useWalletRestore), [`useWalletUnlock`](/reference/wavelength-react/#useWalletUnlock), [`useWalletDeposit`](/reference/wavelength-react/#useWalletDeposit), [`useWalletReceive`](/reference/wavelength-react/#useWalletReceive), [`useWalletPrepareSend`](/reference/wavelength-react/#useWalletPrepareSend), [`useWalletSend`](/reference/wavelength-react/#useWalletSend), [`useWalletRefresh`](/reference/wavelength-react/#useWalletRefresh), and the `create`/`open` pair on [`useWalletPasskey`](/reference/wavelength-react/#useWalletPasskey)) exposes the same throw-and-capture shape: an action function, a verb-prefixed `Pending: boolean`, a verb-prefixed `Error: Error | null`, and (except [`useWalletRefresh`](/reference/wavelength-react/#useWalletRefresh) and [`useWalletPasskey`](/reference/wavelength-react/#useWalletPasskey), neither of which exposes a `Data` field) a verb-prefixed `Data`, the last successful result. The error field is always `Error | null`, never a string, so `.message` is always safe to read. Calling the action clears the previous error/data, awaits the underlying engine call, and on completion both updates the hook’s state and settles the returned promise the normal way: resolves with the result, or rejects with the same `Error` instance that lands in the error field. So you can read the error field for a declarative render, or `await`/`try`/`catch` the action for imperative flow, whichever fits the call site. `reset()` clears the pending/error/data trio back to idle without calling anything.
`error` at the top level, from [`useWallet()`](/reference/wavelength-react/#useWallet), is the same `Error | null` shape but tracks fatal runtime-level failures instead of a single mutation: it is set when the initial runtime load fails, when `start()`/`stop()` fails, or after a background process exhausts its failure budget, and it clears on the next successful `start()`.
```tsx
import { useWalletSend } from '@lightninglabs/wavelength-react';
function PayButton({ bolt11 }: { bolt11: string }) {
const { send, sendError, sendPending, resetSend } = useWalletSend();
const pay = async () => {
resetSend();
try {
await send({ invoice: bolt11 });
} catch (err) {
// sendError is already set for a declarative render; err is the same
// Error instance for imperative handling right here.
console.error('Payment failed:', err);
}
};
return (
<>
Pay
{sendError && {sendError.message}
}
>
);
}
```
## Interpret error codes
Every error the Wavelength SDK originates extends [`WavelengthError`](/reference/wavelength-core/#WavelengthError) and carries a `code` string that identifies the failure reason at the machine level. Match on `err.code` to show a specific message rather than a generic fallback. Named SDK codes include `runtime_not_ready`, `asset_load_failed`, and `worker_error`; daemon-originated errors currently use `wavelength_error`.
```tsx
import { useWalletSend, WavelengthError } from '@lightninglabs/wavelength-react';
function PayButton({ bolt11 }: { bolt11: string }) {
const { send, sendError, sendPending, resetSend } = useWalletSend();
const pay = async () => {
resetSend();
try {
await send({ invoice: bolt11 });
} catch (err) {
if (err instanceof WavelengthError) {
switch (err.code) {
case 'runtime_not_ready':
console.error('Runtime still loading.');
break;
case 'asset_load_failed':
console.error('Check runtimeBaseUrl and hosted assets.');
break;
default:
console.error('Payment failed:', err.message);
}
}
}
};
return (
<>
Pay
{sendError && {sendError.message}
}
>
);
}
```
---
Source: https://wavelength.lightning.engineering/guides/unilateral-exit.md
[Wavelength](/)›Guides›Unilateral exit & status
# Unilateral exit & status
5 min read
Guides
Web SDK
Every VTXO leaves Ark one of two ways: a cooperative leave the operator settles in the next round, or a unilateral exit you force on-chain yourself. [`useWalletExit`](/reference/wavelength-react/#useWalletExit) starts one outpoint; [`useWalletExitBatch`](/reference/wavelength-react/#useWalletExitBatch) starts several at once. [`useWalletExitPlan`](/reference/wavelength-react/#useWalletExitPlan), [`useWalletExitStatus`](/reference/wavelength-react/#useWalletExitStatus), and [`useWalletExits`](/reference/wavelength-react/#useWalletExits) preview and track them. `sweepWallet` has no dedicated hook yet, so reach it through [`useWalletEngine()`](/reference/wavelength-react/#useWalletEngine).client, the escape hatch every granular hook is built on.
## Cooperative or unilateral?
Try cooperative first. It settles in roughly one round instead of waiting out a CSV timelock, and it costs nothing beyond the round’s normal fees. It’s also the default: calling `exit({ outpoint })` with no `forceUnrollAck` is a cooperative leave.
Reach for unilateral only when the operator is unreachable or unresponsive, or a cooperative attempt already failed and you need a way out regardless of that. It’s trustless, since it needs no cooperation from the operator, but it is slower and costs real sats: it materializes a recovery transaction tree on-chain, then waits out the VTXO’s CSV timelock before you can sweep the funds. Preview the cost with `getExitPlan` before committing (see below). See [Leaving Ark](/concepts/leaving-ark/) for the full decision writeup, including what each path looks like in the activity feed.
Unilateral is a separate, explicit branch, not something the SDK falls back to on your behalf: a cooperative failure rejects the call and never silently starts a unilateral exit underneath you. Starting one requires the exported `FORCE_UNROLL_ACK` constant:
```ts
import { FORCE_UNROLL_ACK } from '@lightninglabs/wavelength-react';
await client.exit({ outpoint, forceUnrollAck: FORCE_UNROLL_ACK });
```
`FORCE_UNROLL_ACK` exists so the unilateral path is something you opt into on purpose, not something a typo or a copy-pasted request can trigger by accident. `destination` (cooperative) and `forceUnrollAck` (unilateral) are mutually exclusive on `ExitRequest`; you pick the branch up front.
## Preview with getExitPlan
Before starting a unilateral exit, call `client.getExitPlan({ outpoints })` (or the equivalent [`useWalletExitPlan`](/reference/wavelength-react/#useWalletExitPlan) hook) to preview funding requirements for each VTXO. Show the `plans` summary to the user so they can make an informed decision before broadcasting.
Pass `confTarget` (in blocks) to influence the fee rate used for the recommended-funding estimate, the same way you would for other fee-sensitive calls.
```tsx
import { useState } from 'react';
import { useWalletEngine } from '@lightninglabs/wavelength-react';
import type { GetExitPlanResult } from '@lightninglabs/wavelength-react';
function ExitPlan({ outpoints }: { outpoints: string[] }) {
const { client } = useWalletEngine();
const [plan, setPlan] = useState(null);
const preview = async () => {
// confTarget is optional; omit it to use the client's default fee estimate.
const exitPlan = await client.getExitPlan({ outpoints, confTarget: 6 });
setPlan(exitPlan);
};
return (
Preview exit
{plan && (
Can start: {String(plan.canStart)}. Recommended funding:{' '}
{plan.totalRecommendedFundingSat} sats.
)}
);
}
```
If `canStart` is `false`, check each `ExitPlanEntry.infeasibilityReason`: a plain funding shortfall is fixable by sending sats to `fundingAddress`, but `sweep_below_dust` and `uneconomical` are structural and no amount of funding helps. [`isExitInfeasibilityFundable`](/reference/wavelength-core/#isExitInfeasibilityFundable) makes that split for you, so you can show a “fund your wallet” affordance for the fixable case and a plain “can’t exit this VTXO” message for the rest.
## Start a single exit
`useWalletExit` follows the same throw-and-capture convention as the other mutation hooks: an `exit` action plus `exitPending`/`exitError`/`exitData`. Do not call it twice for the same outpoint; check [`useWalletExitStatus`](#track-status-and-summary) first if the user may have already started an exit in a previous session.
```tsx
import { FORCE_UNROLL_ACK, useWalletExit } from '@lightninglabs/wavelength-react';
function StartExit({ outpoint }: { outpoint: string }) {
const { exit, exitPending, exitError } = useWalletExit();
const startExit = () =>
exit({ outpoint, forceUnrollAck: FORCE_UNROLL_ACK });
return (
<>
Start exit
{exitError && {exitError.message}
}
>
);
}
```
`exitData` resolves with an [`ExitResult`](/reference/wavelength-core/#ExitResult) whose `path` discriminates the outcome. Which fields are populated depends on `path`:
| Field | Populated when |
| ------------------ | ------------------------------------------------------------------- |
| `queuedOutpoints` | `path === 'cooperative'` |
| `created` | `path` is `'unilateral'` or `'unilateral_fallback'` |
| `actorID` | `path` is `'unilateral'` or `'unilateral_fallback'` |
| `cooperativeError` | retained for source compatibility; not populated by the current RPC |
`unilateral_fallback` is also retained for source compatibility, but the current RPC never returns it: cooperative failures reject instead of silently entering that legacy branch.
## Batch exits with useWalletExitBatch
Exiting more than one outpoint by hand means calling `exit()` in a loop and handling funding contention yourself, since exit funding is never reserved: an earlier start can consume the fee inputs a later one needs. `useWalletExitBatch` does that work for you. It resolves once every outpoint has **started**, not completed; a unilateral exit keeps running for hours or days after that. On the unilateral path it re-plans between starts, so it notices a funding conflict before attempting the next exit rather than after, and it stops cleanly (rather than retrying) if the daemon rejects a start mid-batch.
```tsx
import { useWalletExitBatch } from '@lightninglabs/wavelength-react';
function ExitAll({ outpoints }: { outpoints: string[] }) {
const { exitBatch, exitBatchPending, exitBatchError, exitBatchEvents } =
useWalletExitBatch();
const startAll = () => exitBatch({ mode: 'unilateral', outpoints, confTarget: 6 });
return (
Exit all
{exitBatchEvents.map((event, i) => (
{event.type}
{'outpoint' in event ? `: ${event.outpoint}` : ''}
))}
{exitBatchError &&
{exitBatchError.message}
}
);
}
```
Pass `{ mode: 'cooperative', outpoints, destination? }` instead to queue every outpoint into the next cooperative round; the cooperative path has no funding-contention re-plan step, since it never touches the backing wallet. Once the call settles, read `exitBatchData.started` for what ran, `.skipped` for outpoints that already had a job running, `.remaining` for what never started, and `.stoppedBy` for why, if the batch stopped early.
## Track status and summary
[`useWalletExitStatus(outpoint, opts?)`](/reference/wavelength-react/#useWalletExitStatus) fetches on mount and whenever `outpoint` changes, and defaults to the cheap, phase-only call. Pass `{ detailed: true }` when you need the recovery-tree progress, the CSV maturity countdown, and a fee breakdown; that’s a live round-trip, so leave it off for a plain status label. Pass `{ pollMs }` to poll while the component stays mounted: polling is opt-in and runs with no visibility/focus gating, stopping only on unmount, so there’s nothing to tear down yourself.
```tsx
import { useWalletExitStatus } from '@lightninglabs/wavelength-react';
function ExitStatus({ outpoint }: { outpoint: string }) {
const { status, statusPending, statusError } = useWalletExitStatus(outpoint, {
detailed: true,
pollMs: 5000,
});
if (statusPending && !status) return Loading...
;
if (statusError) return {statusError.message}
;
if (!status?.found) return No exit job for this outpoint.
;
if (status.status === 'failed') return {status.lastError}
;
return (
{status.status}
{status.cSV && !status.cSV.mature && ` - ${status.cSV.blocksRemaining} blocks to maturity`}
);
}
```
[`useWalletExits()`](/reference/wavelength-react/#useWalletExits) reads the wallet-wide portfolio instead of one outpoint: every in-progress exit plus the combined amount, fees, and net recoverable. It fetches on mount and refetches whenever wallet activity changes, so a completed exit drops out of the summary without a manual refresh.
```tsx
import { useWalletExits } from '@lightninglabs/wavelength-react';
function ExitSummary() {
const { summary, summaryPending } = useWalletExits();
if (summaryPending && !summary) return Loading...
;
if (!summary || summary.totalExits === 0) return null;
return (
{summary.totalExits} exit(s) in progress, ~{summary.totalEstNetRecoveredSat} sats recoverable.
);
}
```
## Sweep the backing wallet
Once claimable outputs are ready, call `client.sweepWallet()` with `broadcast: false` first to preview, then `broadcast: true` with a `destinationAddress` to consolidate funds on-chain. `broadcast` defaults to `false` when omitted, so a call without it is always a preview. `sweepWallet` also accepts optional `feeRateSatPerVByte` and `confTarget` overrides for the sweep transaction’s fee; both have sensible defaults when omitted, so most callers can leave them unset. The preview result’s `estimatedFeeSat` and `netAmountSat` are useful to show the user what a broadcast would cost and net before they commit to it. If `preview.canBroadcast` is `false`, do not attempt the broadcast call; show `preview.failureReason` to explain why instead.
```tsx
import { useWalletEngine } from '@lightninglabs/wavelength-react';
function Sweep() {
const { client } = useWalletEngine();
const sweep = async () => {
const preview = await client.sweepWallet({
destinationAddress: 'bc1q...',
broadcast: false,
});
if (!preview.canBroadcast) {
console.error(preview.failureReason);
return;
}
await client.sweepWallet({
destinationAddress: 'bc1q...',
broadcast: true,
});
};
return Sweep to on-chain wallet ;
}
```
---
Source: https://wavelength.lightning.engineering/reference/wavelength-core.md
# wavelength-core
Cross-platform interfaces, shared types, and error codes that every Wavelength SDK platform target builds on.
`@lightninglabs/wavelength-core` defines the portable wallet contract every SDK target builds on. It provides the typed client surface, shared wallet primitives, and error model without taking a dependency on the browser, React, or React Native.
At a glance
Typed contract
WavelengthClient, WalletEngine primitives, request and result shapes, errors, and events share one interface.
At a glance
Cross-platform core
wavelength-web implements this contract in the browser, while React and React Native bindings build on the same surface.
At a glance
Predictable responses
Daemon responses arrive as camelCase JavaScript values, with known optional values and arrays normalized for app code.
## Configuration
## networkDefaults
function
Returns the canonical public endpoint preset for a network in one transport’s flavor: REST gateway URLs for `'rest'` (the web transport), host:port gRPC addresses for `'grpc'` (native transports). This is the building block the transport packages’ `defaultConfig` helpers compose over; app code normally calls [wavelength-web’s](/reference/wavelength-web/#defaultConfig) or [wavelength-react-native’s](/reference/wavelength-react-native/#defaultConfig) `defaultConfig` instead.
Only the preset networks are accepted. `mainnet` and `regtest` have no preset: a `RuntimeConfig` for either is built by hand.
```ts
function networkDefaults(
network: PresetNetwork,
transport: ServerTransport,
): Partial
```
| Parameter | Type | Description |
| ----------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `network` | `PresetNetwork` | The Bitcoin network to look up; mainnet and regtest are excluded because they have no preset. |
| `transport` | [`ServerTransport`](/reference/wavelength-core/#ServerTransport) | The endpoint flavor the caller's transport dials: 'rest' or 'grpc'. |
Returns`Partial`
The preset config fields for that network and transport.
## DEBUG\_LEVELS
const
The standard daemon log verbosity levels, from most to least verbose. Exported for UIs that render a level picker; `debugLevel` itself stays a plain string because the daemon also accepts a per-subsystem list such as `'ROND=debug,info'`.
```ts
const DEBUG_LEVELS = [
'trace', 'debug', 'info', 'warn', 'error', 'critical', 'off',
] as const;
type DebugLevel = (typeof DEBUG_LEVELS)[number];
```
## RuntimeConfig
type
Configuration passed to `WavelengthClient.start()`. For the common case, start from your transport package’s `defaultConfig(network)` and override only the fields you need.
```ts
type RuntimeConfig = {
network?: Network;
allowMainnet?: boolean;
dataDir?: string;
debugLevel?: string;
arkServerAddress?: string;
arkServerTlsCertPath?: string;
arkServerInsecure?: boolean;
walletType?: 'lwwallet' | 'btcwallet';
walletEsploraUrl?: string;
walletPasswordFile?: string;
walletPollIntervalSeconds?: number;
walletRecoveryWindow?: number;
walletFeeUrl?: string;
walletBlockHeadersSource?: string;
walletFilterHeadersSource?: string;
swapServerAddress?: string;
swapServerTlsCertPath?: string;
swapServerInsecure?: boolean;
swapDatabaseFileName?: string;
disableSwaps?: boolean;
maxOperatorFeeSat?: number;
signingWorkers?: number;
bufferSize?: number;
};
```
The fields stay flat even when they apply to one embedded backend. For example, this `btcwallet` configuration uses only top-level `RuntimeConfig` fields:
```ts
const config: RuntimeConfig = {
network: 'signet',
walletType: 'btcwallet',
walletFeeUrl: 'https://fees.example.com',
walletBlockHeadersSource: 'neutrino',
walletFilterHeadersSource: 'neutrino',
walletRecoveryWindow: 250,
maxOperatorFeeSat: 100,
signingWorkers: 4,
bufferSize: 64,
};
```
| Parameter | Type | Description |
| --------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `network` | [`Network`](/reference/wavelength-core/#Network) | The Bitcoin network to run against. Use mainnet only together with allowMainnet. |
| `allowMainnet` | `boolean` | Must be true to run on mainnet. The SDK rejects a mainnet config without it before startup. |
| `dataDir` | `string` | Storage root for daemon and wallet state (an OPFS path in the browser). A daemon default is used when unset. |
| `debugLevel` | `string` | Daemon log verbosity. Accepts a standard DebugLevel or a per-subsystem expression such as 'ROND=debug,info'. Distinct from the web client's RPC-payload debug option. |
| `arkServerAddress` | `string` | Ark operator and mailbox endpoint. Use a REST URL on web and a host:port gRPC address on React Native. |
| `arkServerTlsCertPath` | `string` | Optional filesystem TLS certificate path for native transports. The web transport rejects it. |
| `arkServerInsecure` | `boolean` | Disables TLS for the Ark endpoint. Use only for local development. |
| `walletType` | `'lwwallet' \| 'btcwallet'` | Embedded wallet backend. Default: lwwallet. |
| `walletEsploraUrl` | `string` | HTTP Esplora endpoint for lwwallet only, on both platforms. |
| `walletPasswordFile` | `string` | Password file for lwwallet only. |
| `walletPollIntervalSeconds` | `number` | Chain polling interval for lwwallet only. Must be a nonnegative safe integer. |
| `walletRecoveryWindow` | `number` | Wallet address look-ahead window for either supported embedded backend. Must fit in uint32. |
| `walletFeeUrl` | `string` | Fee estimator endpoint for btcwallet only. |
| `walletBlockHeadersSource` | `string` | Block-header source for btcwallet only. |
| `walletFilterHeadersSource` | `string` | Compact-filter-header source for btcwallet only. |
| `swapServerAddress` | `string` | Swap endpoint. Use a REST URL on web and a host:port gRPC address on React Native. |
| `swapServerTlsCertPath` | `string` | Optional filesystem TLS certificate path for native transports. The web transport rejects it unless swaps are disabled. |
| `swapServerInsecure` | `boolean` | Disables TLS for the swap endpoint. Use only for local development. |
| `swapDatabaseFileName` | `string` | Daemon-owned SQLite file for swap state. |
| `disableSwaps` | `boolean` | Disables Lightning swaps and suppresses preset and override swap fields. |
| `maxOperatorFeeSat` | `number` | Maximum operator fee in satoshis. Must be a nonnegative safe integer. |
| `signingWorkers` | `number` | Signing worker count. Must be a nonnegative safe integer. |
| `bufferSize` | `number` | Runtime buffer size. Must be a nonnegative safe integer. |
The SDK validates `RuntimeConfig` before it starts the daemon. It rejects backend-only fields used with the wrong `walletType`, invalid numeric values, mainnet without `allowMainnet: true`, and `arkServerTlsCertPath` on web. Web accepts `swapServerTlsCertPath` only when `disableSwaps: true`, which suppresses every swap field. `lwwallet` fields are `walletEsploraUrl`, `walletPasswordFile`, and `walletPollIntervalSeconds`. `btcwallet` fields are `walletFeeUrl`, `walletBlockHeadersSource`, and `walletFilterHeadersSource`.
LND and eager-round-join disable are intentionally unavailable through this mobile start contract. Most apps should use `defaultConfig()` from their transport package and override only the fields they need.
## Network
type
Selects the Bitcoin network the embedded daemon runs against. `PresetNetwork` narrows the union to the networks that carry a public endpoint preset (the domain of `networkDefaults` and the transports’ `defaultConfig`). mainnet and regtest are excluded: mainnet has no public deployment yet, and regtest’s local ports vary per development environment.
```ts
type Network = 'mainnet' | 'testnet' | 'testnet4' | 'signet' | 'regtest';
```
The transport packages’ `defaultConfig()` helpers draw from one canonical endpoint table per network, so the common case needs no URLs looked up. Toggle between the REST gateway URLs (web transport) and the gRPC host:port addresses (React Native transport):
RESTgRPC
signet
- arkServerAddress
`https://signet.wavelength-rest.lightning.finance``signet.wavelength.lightning.finance:443`
- walletEsploraUrl
`https://mempool-signet.testnet.lightningcluster.com/api`
- swapServerAddress
`https://signet.swapd-rest.lightning.finance``swap.signet.wavelength.lightning.finance:443`
testnet
- arkServerAddress
`https://test.wavelength-rest.lightning.finance``test.wavelength.lightning.finance:443`
- walletEsploraUrl
`https://mempool-testnet3.testnet.lightningcluster.com/api`
- swapServerAddress
`https://test.swapd-rest.lightning.finance``swap.test.wavelength.lightning.finance:443`
mainnet
No public preset - build the RuntimeConfig by hand with your own arkServerAddress, walletEsploraUrl, swapServerAddress, and allowMainnet.
> **mainnet has no public preset**
>
> mainnet is not a `PresetNetwork`, so there is no `defaultConfig` for it. There is no canonical public gateway for mainnet yet, so build the `RuntimeConfig` by hand: supply your own `arkServerAddress`, `walletEsploraUrl`, and `swapServerAddress` (or `disableSwaps`) and pass `allowMainnet: true`, or `start()` will refuse to run.
## ServerTransport
type
The wire protocol used by a transport to connect to the Ark and swap servers. Browser transports use `rest`; native transports use `grpc`.
```ts
type ServerTransport = 'rest' | 'grpc';
```
## MobileConfig
type
The flat configuration shape passed to the embedded mobile facade. Application code normally supplies [`RuntimeConfig`](#RuntimeConfig) instead. This internal shape is shown for transport debugging and is not exported from the core package.
```ts
type MobileConfig = {
data_dir?: string;
network?: string;
allow_mainnet?: boolean;
debug_level?: string;
wallet_type?: 'lwwallet' | 'btcwallet';
wallet_esplora_url?: string;
wallet_password_file?: string;
wallet_poll_interval_seconds?: number;
wallet_recovery_window?: number;
wallet_fee_url?: string;
wallet_block_headers_source?: string;
wallet_filter_headers_source?: string;
server_address?: string;
server_tls_cert_path?: string;
server_transport?: ServerTransport;
server_insecure?: boolean;
swap_server_address?: string;
swap_server_tls_cert_path?: string;
swap_server_transport?: ServerTransport;
swap_server_insecure?: boolean;
swap_database_file_name?: string;
max_operator_fee_sat?: number;
signing_workers?: number;
buffer_size?: number;
};
```
Calling `client.callFacade('start', mobileConfig)` sends this lower-level shape directly to the daemon and bypasses public `RuntimeConfig` validation. App code should call `client.start(config)` instead.
## Client lifecycle
## WavelengthClient
type
The typed RPC surface every transport implements. Create a client with [`createWebClient()`](/reference/wavelength-web/) from wavelength-web or `createNativeClient()` from [wavelength-react-native](/reference/wavelength-react-native/), call `start(config)`, then use the wallet methods documented in the sections below.
```ts
interface WavelengthClient {
ready(): Promise;
start(config: RuntimeConfig): Promise;
stop(): Promise;
getInfo(): Promise;
status(): Promise;
balance(): Promise;
createWallet(req: CreateWalletRequest): Promise;
unlockWallet(req: UnlockWalletRequest): Promise;
openWalletFromPasskey(req: OpenWalletFromPasskeyRequest): Promise;
deposit(req?: DepositRequest): Promise;
receive(req: ReceiveRequest): Promise;
prepareSend(req: SendRequest): Promise;
sendPrepared(prepared: PrepareSendResult): Promise;
send(req: SendRequest): Promise;
list(req?: ListRequest): Promise;
exit(req: ExitRequest): Promise;
exitStatus(req: ExitStatusRequest): Promise;
exitSummary(req?: ExitSummaryRequest): Promise;
getExitPlan(req: GetExitPlanRequest): Promise;
sweepWallet(req: SweepWalletRequest): Promise;
callFacade(method: FacadeMethod, params?: unknown): Promise;
isRunning(): Promise;
subscribe(listener: WavelengthListener): () => void;
startActivity(opts?: ActivityStreamOptions): Promise;
stopActivity(): void;
dispose(): void;
}
```
**Call order.** A client’s methods are only meaningful in this order: await `ready()` once, then `start(config)` once, then any of the wallet operations (any number of times, in any order appropriate to your app), then `stop()` when you are done running the daemon, then `dispose()` to release the client’s own resources. `isRunning()` is the exception: it can be called before startup or after shutdown to inspect the daemon process state. `dispose()` is terminal: build a new client to start again.
## ready
function
Resolves once the runtime assets (the wasm daemon and its supporting files) are loaded and the client is otherwise usable. Always await this before `start()`.
```ts
ready(): Promise
```
Returns`Promise`
Resolves when the client is ready to `start()`. Rejects with a `WavelengthError` (code `asset_load_failed`) if the runtime assets fail to load.
## start
function
Starts the embedded daemon with the given config and resolves with its initial [`WalletInfo`](#WalletInfo).
```ts
start(config: RuntimeConfig): Promise
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `config` | [`RuntimeConfig`](/reference/wavelength-core/#RuntimeConfig) | The runtime configuration to start the daemon with. Build it with your transport package's defaultConfig(network) plus any overrides. |
Returns`Promise`
The daemon’s info immediately after startup, reflecting whatever wallet state already exists on disk (none, locked, or ready).
## stop
function
Stops the embedded daemon. The client instance itself remains usable afterward: call `start()` again to restart the daemon without constructing a new client. Use `dispose()` instead when you are done with the client entirely, since it also releases the client’s own resources (subscriptions, the activity stream, and, for the worker transport, the underlying Worker).
```ts
stop(): Promise
```
Returns`Promise`
Resolves once the daemon has stopped.
## dispose
function
Releases the client’s resources and unsubscribes all listeners: it closes the activity stream opened by [`startActivity()`](#startActivity) and, for the worker transport, terminates the underlying Worker. The client is unusable afterward; construct a new one to start again.
```ts
dispose(): void
```
Returns`void`
Nothing; the client is torn down synchronously.
## Wallet engine
`WalletEngine` is the headless wallet orchestrator every framework binding is built on: it owns the lifecycle phase machine, the state snapshot (`phase`, `info`, `balance`, `activity`, `recovery`, `logs`), and the background processes that keep them fresh. Build one over any transport with [`createWalletEngine()`](#createWalletEngine), or reach for a transport’s own factory (`createWebWalletEngine()` from [wavelength-web](/reference/wavelength-web/), `createNativeWalletEngine()` from [wavelength-react-native](/reference/wavelength-react-native/)), which builds the client for you. Pass the engine to `WavelengthProvider` from [wavelength-react](/reference/wavelength-react/), or drive it directly: `getSnapshot()`/`subscribe()` work the same for a vanilla consumer as for a framework binding.
## createWalletEngine
function
Creates a `WalletEngine` over any transport client.
```ts
function createWalletEngine(options: WalletEngineOptions): WalletEngine
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| `options` | [`WalletEngineOptions`](/reference/wavelength-core/#WalletEngineOptions) | The client to drive, plus optional default config and autoStart. |
Returns[`WalletEngine`](/reference/wavelength-core/#WalletEngine)
A live engine. It calls `client.ready()` immediately; `getSnapshot().phase` moves from `'loading'` to `'runtimeReady'` once that resolves (and, when `autoStart` is set, `start()` follows automatically).
**`WalletEngineOptions`**
A discriminated union: the types require a config when autoStart is enabled, so `autoStart: true` without `config` is a compile error.
```ts
type WalletEngineOptions =
| { client: WavelengthClient; config: RuntimeConfig; autoStart: true }
| { client: WavelengthClient; config?: RuntimeConfig; autoStart?: false };
```
| Parameter | Type | Description |
| ----------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client` | [`WavelengthClient`](/reference/wavelength-core/#WavelengthClient) | The transport client the engine drives. |
| `config` | [`RuntimeConfig`](/reference/wavelength-core/#RuntimeConfig) | Required when autoStart is true. Default runtime config used by autoStart and by start() when called with no argument. |
| `autoStart` | `true \| false` | Optional. Start the runtime automatically once it is ready. Setting it to true requires config; start() still throws if later called with no argument and no config. |
**`DistributiveOmit`**
An advanced utility type used to omit a property from each member of a discriminated union independently. It is exported for transport and binding implementers; application code rarely needs it directly.
```ts
type DistributiveOmit =
T extends unknown ? Omit : never;
```
## WalletEngine
type
The engine interface. Every method mirrors the matching `WavelengthClient` method plus engine bookkeeping: refetching info, kicking a background refresh, or advancing the phase machine.
```ts
interface WalletEngine {
readonly client: WavelengthClient;
getSnapshot(): WalletSnapshot;
subscribe(listener: () => void): () => void;
start(config?: RuntimeConfig): Promise;
stop(): Promise;
refresh(): Promise;
createWallet(req: CreateWalletRequest): Promise;
restoreWallet(req: RestoreWalletRequest): Promise;
acknowledgeRecovery(): void;
unlockWallet(req: UnlockWalletRequest): Promise;
openWalletFromPasskey(req: OpenWalletFromPasskeyRequest): Promise;
deposit(req?: DepositRequest): Promise;
receive(req: ReceiveRequest): Promise;
prepareSend(req: SendRequest): Promise;
sendPrepared(prepared: PrepareSendResult): Promise;
send(req: SendRequest): Promise;
exit(req: ExitRequest): Promise;
exitStatus(req: ExitStatusRequest): Promise;
exitSummary(req?: ExitSummaryRequest): Promise;
getExitPlan(req: GetExitPlanRequest): Promise;
sweepWallet(req: SweepWalletRequest): Promise;
exitBatch(opts: ExitBatchOptions & {
signal?: AbortSignal;
onEvent?: (event: ExitBatchEvent) => void;
}): Promise;
list(req: ListRequest): Promise;
clearLogs(): void;
dispose(): void;
}
```
| Parameter | Type | Description |
| ---------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client` | [`WavelengthClient`](/reference/wavelength-core/#WavelengthClient) | The underlying transport client, as an escape hatch. |
| `getSnapshot()` | `() => WalletSnapshot` | The current immutable state snapshot; see WalletSnapshot below. |
| `subscribe(listener)` | `(listener: () => void) => () => void` | Subscribes to snapshot changes; returns the unsubscribe function. Framework bindings call this via useSyncExternalStore. |
| `start(config?)` | `(config?: RuntimeConfig) => Promise` | Starts the runtime. Falls back to the engine's configured config when called without an argument; throws if neither exists. A failure moves phase to 'error' and rejects. On success it best-effort calls refresh() (a locked or empty wallet can fail balance/list until bootstrap; that failure is swallowed). |
| `stop()` | `() => Promise` | Stops the runtime and clears the in-memory snapshot (info/balance/activity reset to null/null/\[]); persisted wallet data is untouched. A failure moves phase to 'error'. |
| `refresh()` | `() => Promise` | Re-fetches info, balance, and activity concurrently. |
| `createWallet(req)` | `(req: CreateWalletRequest) => Promise` | Creates a new wallet, refetches info, and kicks a background refresh. |
| `restoreWallet(req)` | `(req: RestoreWalletRequest) => Promise` | Restores a wallet from req.mnemonic. Resolves as soon as the restored wallet is usable; the optional server-assisted recovery scan (req.recoverState) continues in the background, observed through snapshot.recovery. Rejects only when the restore fails before the wallet came up; if the scan itself fails after the wallet is usable, the promise still resolves and the failure surfaces as recovery.status === 'failed' instead. See RestoreWalletRequest below. |
| `acknowledgeRecovery()` | `() => void` | Resets snapshot.recovery to { status: 'idle' } (e.g. after dismissing a banner). |
| `unlockWallet(req)` | `(req: UnlockWalletRequest) => Promise` | Unlocks an existing wallet, refetches info, and kicks a background refresh. |
| `openWalletFromPasskey(req)` | `(req: OpenWalletFromPasskeyRequest) => Promise` | Opens the wallet from a passkey PRF output, refetches info, and kicks a background refresh. |
| `deposit(req?)` | `(req?: DepositRequest) => Promise` | Requests an on-chain deposit address and kicks a background refresh. |
| `receive(req)` | `(req: ReceiveRequest) => Promise` | Requests a Lightning receive and kicks a background refresh. |
| `prepareSend(req)` | `(req: SendRequest) => Promise` | Quotes a payment without dispatching it. No refresh: a quote moves no money. |
| `sendPrepared(prepared)` | `(prepared: PrepareSendResult) => Promise` | Dispatches a payment quoted by prepareSend and kicks a background refresh. |
| `send(req)` | `(req: SendRequest) => Promise` | Sends a payment and kicks a background refresh. |
| `exit(req)` | `(req: ExitRequest) => Promise` | Starts a single exit (cooperative or unilateral) for one outpoint and kicks a background refresh. See exit below. |
| `exitStatus(req)` | `(req: ExitStatusRequest) => Promise` | Queries the status of an exit job. Read-only: it moves no money, so it does not refresh. See exitStatus below. |
| `exitSummary(req?)` | `(req?: ExitSummaryRequest) => Promise` | Summarizes all in-progress exits. Read-only; does not refresh. See exitSummary below. |
| `getExitPlan(req)` | `(req: GetExitPlanRequest) => Promise` | Previews unilateral-exit readiness and funding without moving funds. Read-only; does not refresh. See getExitPlan below. |
| `sweepWallet(req)` | `(req: SweepWalletRequest) => Promise` | Previews (req.broadcast: false) or broadcasts (req.broadcast: true) a sweep of the backing on-chain wallet. Refreshes only when broadcasting, since a preview moves no money. See sweepWallet below. |
| `exitBatch(opts)` | `(opts: ExitBatchOptions & { signal?, onEvent? }) => Promise` | Starts a batch of exits, kicking a background refresh after each one starts. Resolves once every exit is started, not completed. Takes the same opts as the standalone exitBatch() below, minus client, since the engine already owns one. See exitBatch below. |
| `list(req)` | `(req: ListRequest) => Promise` | Lists wallet activity, VTXOs, or on-chain outputs. Read-only; does not refresh. See list below. |
| `clearLogs()` | `() => void` | Clears the buffered log tail. |
| `dispose()` | `() => void` | Tears down subscriptions, polls, and streams. The engine is done after this; build a new one to start again. |
> **Background processes the engine owns**
>
> The engine runs several processes automatically, keyed off `phase`, so hosts never reimplement them:
>
> - **Activity-stream auto-refresh**: while `phase` is `'ready'`, the engine opens the transport’s activity stream and, on each event, runs a bounded settle-reconcile: an immediate `refresh()` followed by up to three follow-up re-reads (at 750ms, 1500ms, and 3000ms) that stop early once the balance has moved off its pre-event baseline and then held steady across two reads. This covers the daemon reporting an entry settled a beat before `balance()` reflects the new funds.
> - **Activity-stream recovery**: terminal `activityStream` events trigger a `list()` reconciliation, then the engine retries from the last safe numeric activity cursor with bounded exponential backoff. The cursor resets when the runtime stops or restarts. Replayed events trigger the same idempotent snapshot refresh instead of being appended directly, so the engine provides eventual snapshot consistency after a disconnect. The current web and native bridges do not expose structured gap cursor or reason details.
> - **Sync auto-poll**: while `phase` is `'syncing'`, the engine calls `refresh()` every 2000ms until the phase leaves `'syncing'`. After 5 consecutive failures it stops polling and escalates to `phase: 'error'` instead of polling forever.
> - **Restore readiness poll**: while `phase` is `'restoring'` (any `restoreWallet()` in flight; when the request sets `recoverState: true` the recovery scan is additionally tracked through `snapshot.recovery`), the engine polls `getInfo()` every 1500ms until the wallet reports ready, then settles the `restoreWallet()` promise and kicks a refresh.
> - **Background-refresh failure budget**: any refresh kicked after a mutation (create/unlock/restore/deposit/receive/send) or by a poll above shares one consecutive-failure counter. At 5 consecutive failures the engine sets `snapshot.error` and escalates `phase` to `'error'`, so a dead daemon cannot hide behind a healthy-looking ready wallet.
> - **Unsolicited `stopped` transition**: a `runtimeStopped` client event, whether from a clean `stop()` or a worker crash/fatal surfaced by the transport, moves `phase` to `'stopped'` and wipes `info`/`balance`/`activity` to `null`/`null`/`[]`, even if the host never called `stop()` itself.
## WalletSnapshot
type
The engine’s immutable state snapshot, returned by [`getSnapshot()`](#WalletEngine) and delivered to [`subscribe()`](#WalletEngine) listeners. A new object per change; refresh fetches that changed nothing keep the previous field references, so consumers can cheaply compare slices with `Object.is`.
```ts
type WalletSnapshot = {
phase: RuntimePhase;
error: Error | null;
info: WalletInfo | null;
balance: Balance | null;
activity: Entry[];
recovery: RecoveryState;
logs: WavelengthLogPayload[];
};
```
| Parameter | Type | Description |
| ---------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| `phase` | [`RuntimePhase`](/reference/wavelength-core/#RuntimePhase) | The current runtime/wallet lifecycle phase. |
| `error` | `Error \| null` | The last fatal runtime-level error, or null. |
| `info` | `WalletInfo \| null` | The most recent complete wallet info, or null before the runtime reports it. |
| `balance` | `Balance \| null` | The most recent wallet balance, or null before it is known. |
| `activity` | `Entry[]` | The most recent activity entries, newest-first as returned by the daemon. |
| `recovery` | [`RecoveryState`](/reference/wavelength-core/#RecoveryState) | The background recovery status set by restoreWallet(); see RecoveryState below. |
| `logs` | `WavelengthLogPayload[]` | A bounded tail (up to 200 entries) of 'log' events from the runtime, newest last. |
## RecoveryState
type
The state of a background wallet recovery started by `restoreWallet()`, a discriminated union keyed on `status`.
```ts
type RecoveryState =
| { status: 'idle' }
| { status: 'restoring' }
| { status: 'done'; result: CreateWalletResult }
| { status: 'failed'; error: Error; walletUsable: boolean };
```
| Parameter | Type | Description |
| ------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `{ status: 'idle' }` | `variant` | No tracked restore in flight, and none has run since the last acknowledgeRecovery() (or ever). |
| `{ status: 'restoring' }` | `variant` | While the daemon's server-assisted scan runs. The wallet is already usable at this point; restoreWallet()'s promise has typically already resolved. |
| `{ status: 'done', result }` | `variant` | Once the scan completes successfully. result carries the same CreateWalletResult recovery counters (recoveredVTXOs, recoveredBoardingUTXOs, and so on) documented under CreateWalletResult above. |
| `{ status: 'failed', error, walletUsable }` | `variant` | walletUsable is true when the background scan errored on an already-usable wallet (its state may just be incomplete), and false when the restore itself failed before the wallet came up at all. This also covers an untracked restore (a restoreWallet() call whose promise was not awaited, or whose caller unmounted): the failure still lands here, surviving the unmount. |
Read it to drive a “restoring your balance and history” banner while the wallet is already usable (`walletUsable: true`), or a restore-failed message on the onboarding screen when the restore never got that far (`walletUsable: false`), and call `acknowledgeRecovery()` to dismiss it back to `'idle'`.
## RestoreWalletRequest
type
Parameters for [`WalletEngine.restoreWallet()`](#WalletEngine): everything [`CreateWalletRequest`](#createWallet) accepts, with `mnemonic` promoted from optional to required, since a restore is meaningless without one.
```ts
type RestoreWalletRequest = CreateWalletRequest & {
mnemonic: string[];
};
```
| Parameter | Type | Description |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mnemonic` | `string[]` | The recovery phrase to restore from. Required (unlike CreateWalletRequest.mnemonic, which is optional). |
| `password` | `string` | The plain password the client base64-encodes into the daemon's byte field. |
| `seedPassphrase` | `string` | Optional. A BIP-39 passphrase applied on top of the mnemonic. |
| `recoverState` | `boolean` | Optional. Opt into server-assisted recovery, tracked through snapshot.recovery. Set this for a restore so funds and activity come back; see CreateWalletRequest.recoverState above for the full description. Defaults to false. |
| `recoveryWindow` | `number` | Optional. The per-key-family address look-ahead used during recovery. Only meaningful when recoverState is true. |
## Wallet setup and authentication
## createWallet
function
Creates a new wallet from the given request: generates (or imports) a mnemonic, enciphers it with `password`, and returns the seed words for backup display.
```ts
createWallet(req: CreateWalletRequest): Promise
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------------ | -------------------------------------------------- |
| `req` | [`CreateWalletRequest`](/reference/wavelength-core/#CreateWalletRequest) | Parameters for creating (or importing) the wallet. |
Returns`Promise`
The generated (or imported) mnemonic, the daemon identity, and recovery counters.
**`CreateWalletRequest`**
```ts
type CreateWalletRequest = {
password: string;
mnemonic?: string[];
seedPassphrase?: string;
recoverState?: boolean;
recoveryWindow?: number;
};
```
| Parameter | Type | Description |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `password` | `string` | The plain password the client base64-encodes into the daemon's byte field. |
| `mnemonic` | `string[]` | Optional. An existing mnemonic to restore from; a fresh one is generated when omitted. |
| `seedPassphrase` | `string` | Optional. A BIP-39 passphrase applied on top of the mnemonic. |
| `recoverState` | `boolean` | Optional. Opt into server-assisted recovery: rebuild wallet state (boarding UTXOs, VTXOs, receive history) from the seed by querying the operator's indexer. Set it when restoring from a mnemonic so funds and activity come back. Defaults to false. |
| `recoveryWindow` | `number` | Optional. The per-key-family address look-ahead used during recovery. Only meaningful when recoverState is true; omit to let the daemon resolve its own default. |
**`CreateWalletResult`**
```ts
type CreateWalletResult = {
mnemonic: string[];
encipheredSeed: string;
identityPubKey: string;
recoveryRan: boolean;
recoveredBoardingAddresses: number;
recoveredBoardingUTXOs: number;
recoveredVTXOs: number;
recoveredOORReceiveScripts: number;
recoveredOORRecipientEvents: number;
};
```
| Parameter | Type | Description |
| ----------------------------- | ---------- | ----------------------------------------------------------------------------------------------------- |
| `mnemonic` | `string[]` | The seed words. Display this to the user for backup; it is not recoverable from the daemon afterward. |
| `encipheredSeed` | `string` | The password-enciphered seed, as stored by the daemon. |
| `identityPubKey` | `string` | The wallet's identity public key. |
| `recoveryRan` | `boolean` | True when mnemonic was supplied and the daemon ran chain recovery to rediscover funds. |
| `recoveredBoardingAddresses` | `number` | Count of boarding addresses recovered. Only meaningful when recoveryRan is true. |
| `recoveredBoardingUTXOs` | `number` | Count of boarding UTXOs recovered. |
| `recoveredVTXOs` | `number` | Count of VTXOs recovered. |
| `recoveredOORReceiveScripts` | `number` | Count of out-of-round receive scripts recovered. |
| `recoveredOORRecipientEvents` | `number` | Count of out-of-round recipient events recovered. |
## unlockWallet
function
Unlocks an existing wallet with the given password.
```ts
unlockWallet(req: UnlockWalletRequest): Promise
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------------ | ---------------------------- |
| `req` | [`UnlockWalletRequest`](/reference/wavelength-core/#UnlockWalletRequest) | The password to unlock with. |
Returns`Promise`
The daemon identity after unlock.
**`UnlockWalletRequest`**
```ts
type UnlockWalletRequest = {
password: string;
};
```
| Parameter | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------- |
| `password` | `string` | The plain password the client base64-encodes into the daemon's byte field. |
**`UnlockWalletResult`**
```ts
type UnlockWalletResult = {
identityPubKey: string;
};
```
| Parameter | Type | Description |
| ---------------- | -------- | --------------------------------- |
| `identityPubKey` | `string` | The wallet's identity public key. |
## openWalletFromPasskey
function
Opens a wallet from a passkey assertion: derives a wallet from the PRF output of a [`PasskeyCeremony`](#PasskeyCeremony) ceremony, creating one on first use per device or unlocking the existing one thereafter.
```ts
openWalletFromPasskey(
req: OpenWalletFromPasskeyRequest,
): Promise
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------- |
| `req` | [`OpenWalletFromPasskeyRequest`](/reference/wavelength-core/#OpenWalletFromPasskeyRequest) | The passkey PRF output to derive the wallet from. |
Returns`Promise`
Whether a wallet was freshly created from the derived seed, plus the mnemonic (on import only) and the daemon identity.
**`OpenWalletFromPasskeyRequest`**
```ts
type OpenWalletFromPasskeyRequest = {
prfOutput: string;
};
```
| Parameter | Type | Description |
| ----------- | -------- | ----------------------------------------------------------------------------- |
| `prfOutput` | `string` | The PRF output (hex) derived from the passkey ceremony. See PasskeyAssertion. |
**`OpenWalletFromPasskeyResult`**
```ts
type OpenWalletFromPasskeyResult = {
imported: boolean;
mnemonic: string[];
identityPubKey: string;
};
```
| Parameter | Type | Description |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `imported` | `boolean` | true when a new local wallet was created from the derived seed (a fresh device that has never seen this passkey); false when an existing local wallet was unlocked. |
| `mnemonic` | `string[]` | The seed words. Populated only when imported is true, for backup display. |
| `identityPubKey` | `string` | The wallet's identity public key. |
## Reading wallet state
**When to use each.** `getInfo()` is the richest snapshot (daemon version, network, block height, and wallet lifecycle state) and is what `start()` also returns; reach for it when you need `walletState` or build metadata. `status()` is a lighter operational check (ready/unlocked/pending count) that also embeds the balance, useful for a quick health probe. `balance()` returns only the `Balance` you would find nested in `status().balance`; call it directly when that is all you need. `isRunning()` reports only whether the embedded daemon process is running.
The typed methods are the preferred way to read facade-level scalar values:
```ts
const confirmedBalanceSat = (await client.balance()).confirmedSat;
const pendingInboundSat = (await client.balance()).pendingInSat;
const walletReady = (await client.getInfo()).walletReady;
const running = await client.isRunning();
```
The `confirmedBalanceSat`, `pendingInboundSat`, `walletReady`, and `isRunning` facade verbs also remain available through [`callFacade()`](#callFacade) for portable low-level integrations.
## getInfo
function
Returns the current normalized wallet info.
```ts
getInfo(): Promise
```
Returns`Promise`
The current wallet and daemon info.
## status
function
Returns the daemon’s runtime status snapshot.
```ts
status(): Promise
```
Returns`Promise`
The current readiness, lock, network, balance, and pending-activity snapshot.
## balance
function
Returns the current wallet balance.
```ts
balance(): Promise
```
Returns`Promise`
The current confirmed and pending balances, in satoshis.
## isRunning
function
Reports whether the embedded daemon is running. This does not report wallet readiness; use [`getInfo()`](#getInfo) when you need `walletReady`.
```ts
isRunning(): Promise
```
Returns`Promise`
`true` while the embedded daemon process is running.
## WalletInfo
type
Wallet and daemon info returned by [`getInfo()`](#getInfo) and [`start()`](#start).
```ts
type WalletInfo = {
version: string;
commit: string;
network: string;
blockHeight: number;
serverConnected: boolean;
walletType: string;
identityPubKey: string;
walletState: WalletState;
walletReady: boolean;
serverInfo?: ServerInfo;
};
```
| Parameter | Type | Description |
| ----------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `version` | `string` | The daemon version string. |
| `commit` | `string` | The daemon build commit hash. |
| `network` | `string` | The Bitcoin network the daemon is running against. |
| `blockHeight` | `number` | The current chain tip height the wallet has synced to. |
| `serverConnected` | `boolean` | Whether the daemon's connection to the Ark server is currently up. |
| `walletType` | `string` | The backing wallet implementation in use. |
| `identityPubKey` | `string` | The wallet's identity public key. |
| `walletState` | [`WalletState`](/reference/wavelength-core/#WalletState) | The wallet lifecycle state, normalized to the SDK string union. |
| `walletReady` | `boolean` | True iff the wallet is unlocked and ready (mirrors the daemon's Info.WalletReady() method); backfilled by normalizeInfo() since the daemon JSON does not carry it directly. |
| `serverInfo` | [`ServerInfo`](/reference/wavelength-core/#ServerInfo) | Operator policy hints, when the daemon has learned them from the Ark server; see ServerInfo below. Optional: absent until the daemon has cached the operator terms. |
## ServerInfo
type
Operator policy hints surfaced on [`WalletInfo.serverInfo`](#WalletInfo). The daemon learns these from its cached operator terms at bootstrap and on later terms refreshes.
```ts
type ServerInfo = {
freeRefreshWindowBlocks: number;
};
```
| Parameter | Type | Description |
| ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `freeRefreshWindowBlocks` | `number` | The late-lifetime window, in blocks before VTXO expiry, in which a pure refresh receives an operator fee waiver. 0 means the operator advertises no waiver window. |
## WalletStatus
type
Runtime status snapshot returned by [`status()`](#status).
```ts
type WalletStatus = {
ready: boolean;
unlocked: boolean;
network: string;
balance: Balance;
pendingCount: number;
};
```
| Parameter | Type | Description |
| -------------- | ------------------------------------------------ | ---------------------------------------------------------------- |
| `ready` | `boolean` | Whether the wallet is unlocked and fully usable. |
| `unlocked` | `boolean` | Whether the wallet is currently unlocked (may still be syncing). |
| `network` | `string` | The Bitcoin network the daemon is running against. |
| `balance` | [`Balance`](/reference/wavelength-core/#Balance) | The current wallet balance; see Balance below. |
| `pendingCount` | `number` | The number of activity entries currently pending. |
## Balance
type
Wallet-level balance returned by [`balance()`](#balance) and embedded in [`WalletStatus.balance`](#WalletStatus). All amounts are in satoshis.
```ts
type Balance = {
confirmedSat: number;
pendingInSat: number;
pendingOutSat: number;
};
```
| Parameter | Type | Description |
| --------------- | -------- | --------------------------------- |
| `confirmedSat` | `number` | Spendable VTXO balance. |
| `pendingInSat` | `number` | Inbound amount not yet confirmed. |
| `pendingOutSat` | `number` | Outbound amount in flight. |
## Deposits and receiving
## deposit
function
Generates an on-chain deposit (boarding) address.
```ts
deposit(req?: DepositRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------- | -------------------------------------- |
| `req` | [`DepositRequest`](/reference/wavelength-core/#DepositRequest) | Optional. Defaults to {} when omitted. |
Returns`Promise`
The boarding address and its initial [`Entry`](#Entry) in the activity feed.
**`DepositRequest`**
```ts
type DepositRequest = {
amountSatHint?: number;
};
```
| Parameter | Type | Description |
| --------------- | -------- | ------------------------------------------------------------------------ |
| `amountSatHint` | `number` | Optional. A hint for the amount (in sats) the caller intends to deposit. |
**`DepositResult`**
```ts
type DepositResult = {
address: string;
entry: Entry;
};
```
| Parameter | Type | Description |
| --------- | -------------------------------------------- | ---------------------------------------------------- |
| `address` | `string` | The boarding (on-chain) address to deposit to. |
| `entry` | [`Entry`](/reference/wavelength-core/#Entry) | The activity entry tracking this deposit; see Entry. |
## receive
function
Generates a receive invoice for the requested amount.
```ts
receive(req: ReceiveRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------- | ------------------------------------------ |
| `req` | [`ReceiveRequest`](/reference/wavelength-core/#ReceiveRequest) | The amount (and optional memo) to request. |
Returns`Promise`
The invoice and its initial [`Entry`](#Entry) in the activity feed.
**`ReceiveRequest`**
```ts
type ReceiveRequest = {
amountSat: number;
memo?: string;
};
```
| Parameter | Type | Description |
| ----------- | -------- | ----------------------------------------- |
| `amountSat` | `number` | The amount to request, in sats. |
| `memo` | `string` | Optional. A memo attached to the invoice. |
**`ReceiveResult`**
```ts
type ReceiveResult = {
invoice: string;
entry: Entry;
};
```
| Parameter | Type | Description |
| --------- | -------------------------------------------- | ---------------------------------------------------- |
| `invoice` | `string` | The BOLT-11 Lightning invoice to be paid. |
| `entry` | [`Entry`](/reference/wavelength-core/#Entry) | The activity entry tracking this receive; see Entry. |
## Sending payments
Sending a payment follows a quote → confirm → pay flow. [`prepareSend()`](#prepareSend) validates the request and returns a quote (the expected fee, rail, and a single-use `sendIntentId`) without moving funds; [`sendPrepared()`](#sendPrepared) then dispatches that exact quote. [`send()`](#send) folds both steps into one call for the common case where you do not need to show the user a confirmation screen between quoting and paying.
## prepareSend
function
Quotes a payment without dispatching it: validates the request and returns the expected fee, settlement rail, and a single-use `sendIntentId`. Pair it with [`sendPrepared()`](#sendPrepared) for a quote → confirm → pay flow.
```ts
prepareSend(req: SendRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------- | -------------------------------------------------------------- |
| `req` | [`SendRequest`](/reference/wavelength-core/#SendRequest) | The Lightning invoice or on-chain address to quote a send for. |
Returns`Promise`
The quote: fee, rail, and the `sendIntentId` to pass to `sendPrepared()`.
## sendPrepared
function
Dispatches a payment previously quoted by [`prepareSend()`](#prepareSend).
```ts
sendPrepared(prepared: PrepareSendResult): Promise
```
| Parameter | Type | Description |
| ---------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `prepared` | [`PrepareSendResult`](/reference/wavelength-core/#PrepareSendResult) | The quote returned by prepareSend(). Its sendIntentId is consumed on dispatch; if dispatch fails, prepare a fresh send before retrying. |
Returns`Promise`
The dispatched send’s activity entry and actual amounts.
## send
function
Quotes and dispatches a payment in a single call, folding [`prepareSend()`](#prepareSend) and [`sendPrepared()`](#sendPrepared) together.
```ts
send(req: SendRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------- | ------------------------------------------------- |
| `req` | [`SendRequest`](/reference/wavelength-core/#SendRequest) | The Lightning invoice or on-chain address to pay. |
Returns`Promise`
The dispatched send’s activity entry and actual amounts.
## SendRequest
type
Parameters accepted by [`prepareSend()`](#prepareSend) and [`send()`](#send), as a discriminated union: supply `invoice` for a Lightning send or `onchainAddress` for an on-chain send. The two arms are mutually exclusive, and `sweepAll` applies only to the on-chain arm.
```ts
type SendRequest =
| {
invoice: string;
amountSat?: number;
note?: string;
maxFeeSat?: number;
}
| {
onchainAddress: string;
amountSat?: number;
sweepAll?: boolean;
note?: string;
maxFeeSat?: number;
};
```
**Lightning arm**
| Parameter | Type | Description |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `invoice` | `string` | A Lightning invoice to pay. |
| `amountSat` | `number` | Optional. The amount to send, in sats. Ignored on the invoice path: v1 requires an amount-bearing BOLT-11 invoice. |
| `note` | `string` | Optional. A note recorded with the activity. |
| `maxFeeSat` | `number` | Optional. A cap on the fee, in sats. |
**On-chain arm**
| Parameter | Type | Description |
| ---------------- | --------- | ---------------------------------------------------------------------------- |
| `onchainAddress` | `string` | An on-chain address to pay. |
| `amountSat` | `number` | Optional. The amount to send, in sats. Provide this or set sweepAll. |
| `sweepAll` | `boolean` | Optional. When true, sweeps the entire spendable balance to the destination. |
| `note` | `string` | Optional. A note recorded with the activity. |
| `maxFeeSat` | `number` | Optional. A cap on the fee, in sats. |
## PrepareSendResult
type
The quote and intent id returned by [`prepareSend()`](#prepareSend).
```ts
type PrepareSendResult = {
sendIntentId: string;
amountSat: number;
expectedFeeSat: number;
feeKnown: boolean;
expectedTotalOutflowSat: number;
totalOutflowKnown: boolean;
rail: SendRail;
quoteStatus: 'unspecified' | 'complete' | 'local_only';
destinationSummary: string;
invoiceDescription: string;
paymentHash: string;
expiresAtUnix: number;
selectedOutpoints: string[];
warning: string;
};
```
| Parameter | Type | Description |
| ------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `sendIntentId` | `string` | Single-use id identifying this quote; pass the whole result object to sendPrepared() to dispatch it. |
| `amountSat` | `number` | The amount that will be sent, in sats. |
| `expectedFeeSat` | `number` | The expected fee, in sats. |
| `feeKnown` | `boolean` | Whether expectedFeeSat is a firm quote rather than an estimate. |
| `expectedTotalOutflowSat` | `number` | The expected total amount leaving the wallet (amount plus fee), in sats. |
| `totalOutflowKnown` | `boolean` | Whether expectedTotalOutflowSat is firm rather than an estimate. |
| `rail` | [`SendRail`](/reference/wavelength-core/#SendRail) | The settlement rail this send is expected to use. |
| `quoteStatus` | `'unspecified' \| 'complete' \| 'local_only'` | How complete the prepare-time quote is: complete means the fee is fully known; local\_only means only a local estimate was possible. |
| `destinationSummary` | `string` | A human-readable summary of the destination. |
| `invoiceDescription` | `string` | The invoice's embedded description, for Lightning sends. |
| `paymentHash` | `string` | The Lightning payment hash, when applicable. |
| `expiresAtUnix` | `number` | Unix timestamp after which this quote (and its sendIntentId) is no longer valid. |
| `selectedOutpoints` | `string[]` | The VTXO outpoints selected to fund this send; sendPrepared() spends exactly this set. |
| `warning` | `string` | An optional human-readable warning about this quote (e.g. a high fee). |
## SendResult
type
The result of a dispatched send, returned by [`sendPrepared()`](#sendPrepared) and [`send()`](#send).
```ts
type SendResult = {
entry: Entry;
actualAmountSat: number;
paymentHash?: string;
};
```
| Parameter | Type | Description |
| ----------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entry` | [`Entry`](/reference/wavelength-core/#Entry) | The activity entry tracking this send; see Entry. |
| `actualAmountSat` | `number` | The real amount that left the wallet. For invoice sends this matches the invoice principal; for a bounded on-chain send it matches the requested amount; for a sweep-all send it reflects the swept VTXO total, so echo it back to the user before treating the send as confirmed. |
| `paymentHash` | `string` | Optional. The Lightning payment hash, when the send produced one. The daemon returns this from prepareSend (not sendPrepared), so the client folds it into send()'s result here; prefer this field over digging into entry.progress or entry.request, and note that entry.id is an activity id, not a payment hash. |
## SendRail
type
Identifies the expected settlement rail for a prepared send.
```ts
type SendRail =
| 'unspecified'
| 'offchain_unknown'
| 'in_ark'
| 'lightning'
| 'onchain'
| 'credit'
| 'mixed';
```
| Value | Meaning |
| ------------------ | ------------------------------------------------------------- |
| `unspecified` | No rail could be determined. |
| `offchain_unknown` | An off-chain rail was used but the specific one is not known. |
| `in_ark` | Settled directly within Ark, without leaving the protocol. |
| `lightning` | Settled over Lightning (via a swap). |
| `onchain` | Settled with an on-chain transaction. |
| `credit` | Settled from the wallet’s server-side credit balance. |
| `mixed` | Settled by combining credit with the normal vHTLC path. |
## classifyDestination
function
Classifies a pasted destination string so a send UI can render only the fields that apply to it. Pure, synchronous, and offline: it reads a BOLT-11 amount from the invoice’s human-readable part without decoding the bech32 payload, so an amountless invoice is detected before any network call.
```ts
function classifyDestination(raw: string): Destination
```
> **Note**
>
> `classifyDestination` deliberately does **not** name a settlement rail. A string it reports as `invoice` may still quote as `in_ark`, `credit`, or `mixed` (an address always settles `onchain`). Read [`rail`](#SendRail) from the [`PrepareSendResult`](#PrepareSendResult) for the authoritative answer, and treat any rail shown before that as provisional.
## Destination
type
The result of [`classifyDestination()`](#classifyDestination).
```ts
type Destination =
| { kind: 'empty' }
| { kind: 'invoice'; amount: InvoiceAmount }
| { kind: 'address' };
```
| Parameter | Type | Description |
| ----------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{ kind: 'empty' }` | `variant` | The input is blank. Render no conditional fields. |
| `{ kind: 'invoice', amount }` | `variant` | A BOLT-11 invoice. amount is the InvoiceAmount read from the human-readable part; see below. The daemon ignores a caller-supplied amountSat on the invoice path, and the current daemon rejects an amountless invoice outright, so a UI should never collect an amount for an invoice. |
| `{ kind: 'address' }` | `variant` | Anything that is not a BOLT-11 invoice. Treat it as a payable address; the rail is still unknown. |
## InvoiceAmount
type
The amount an invoice carries, when it can be read from the human-readable part. A discriminated union keyed on `status`.
```ts
type InvoiceAmount =
| { status: 'known'; sat: number }
| { status: 'amountless' }
| { status: 'unrepresentable' };
```
| Parameter | Type | Description |
| ------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{ status: 'known', sat }` | `variant` | A whole number of satoshis, read directly from the invoice. |
| `{ status: 'amountless' }` | `variant` | The invoice carries no amount at all. The payer must supply one; the current daemon rejects such an invoice outright. |
| `{ status: 'unrepresentable' }` | `variant` | The invoice carries an amount that cannot be shown as a whole number of satoshis: a sub-satoshi figure, or one too large to represent exactly. The invoice is still amount-bearing and the daemon pays it (a sub-satoshi amount is rounded up to the next satoshi), so a UI must not ask the payer for an amount. It simply cannot display one. |
## Activity and history
## list
function
Lists wallet activity or UTXOs per the request.
```ts
list(req?: ListRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------- | ---------------------------------------------------------- |
| `req` | [`ListRequest`](/reference/wavelength-core/#ListRequest) | Optional. Defaults to {} (the activity view) when omitted. |
Returns`Promise`
A tagged union with only the field matching the requested view populated.
Filter the activity view by one or more entry kinds:
```ts
await client.list({
view: 'activity',
kinds: ['receive', 'deposit'],
});
```
**`ListRequest`**
```ts
type ListRequest = {
view?: ListView;
pendingOnly?: boolean;
kinds?: EntryKind[];
limit?: number;
offset?: number;
cursor?: string;
};
```
| Parameter | Type | Description |
| ------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `view` | [`ListView`](/reference/wavelength-core/#ListView) | Optional. Which view to list: activity entries, VTXOs, or on-chain outputs. Default: 'activity'. |
| `pendingOnly` | `boolean` | Optional. When true, returns only pending items. Applies to the activity view only. |
| `kinds` | `EntryKind[]` | Optional. Returns only the selected entry kinds. Applies to the activity view only. |
| `limit` | `number` | Optional. The maximum number of items to return; zero uses the daemon default. |
| `offset` | `number` | Optional. The number of items to skip for pagination. Applies to the vtxos and onchain views only; the activity view paginates by cursor and ignores offset. |
| `cursor` | `string` | Optional. The opaque pagination token for the activity view. Empty starts from the newest entry; otherwise pass the nextCursor from the previous ActivityList page. Ignored by the vtxos and onchain views. |
**`ListResult`**
A tagged union on `view`: exactly one of `activity`, `vtxos`, or `onchain` is populated, matching the requested (or default) view. Switch on `view` to read the right field.
```ts
type ListResult = {
view: ListView;
activity?: ActivityList;
vtxos?: VTXOInventory;
onchain?: OnchainHistory;
};
```
| Parameter | Type | Description |
| ---------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `view` | [`ListView`](/reference/wavelength-core/#ListView) | The populated variant. Mirrors the request’s view; an empty request view is reported as 'activity'. |
| `activity` | [`ActivityList`](/reference/wavelength-core/#ActivityList) | Populated when view === 'activity'. |
| `vtxos` | [`VTXOInventory`](/reference/wavelength-core/#VTXOInventory) | Populated when view === 'vtxos'. See the balances-and-vtxos guide for the VTXO shape. |
| `onchain` | [`OnchainHistory`](/reference/wavelength-core/#OnchainHistory) | Populated when view === 'onchain'. |
**`ListView`**
```ts
type ListView = 'activity' | 'vtxos' | 'onchain';
```
| Value | Meaning |
| ---------- | ------------------------------------------------------------------- |
| `activity` | The merged send/receive/deposit/exit activity feed. Default. |
| `vtxos` | The live VTXO inventory. |
| `onchain` | The on-chain transaction history (boarding, sweeps, leave outputs). |
**`ActivityList`**
```ts
type ActivityList = {
entries: Entry[];
total: number;
hasMore: boolean;
nextCursor: string;
};
```
| Parameter | Type | Description |
| ------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `entries` | `Entry[]` | The matching activity entries; see Entry. |
| `total` | `number` | The number of entries on this page, not a full-feed count. The feed is cursor-paged, so use hasMore to decide whether to fetch again. |
| `hasMore` | `boolean` | Whether more entries exist after this page. |
| `nextCursor` | `string` | The token to pass as ListRequest.cursor to fetch the next page. Empty when hasMore is false. |
## VTXOInventory
type
The paginated inventory returned for the `vtxos` list view.
```ts
type VTXOInventory = {
vtxos: WalletVTXO[];
total: number;
};
```
| Parameter | Type | Description |
| --------- | -------------- | ----------------------------------- |
| `vtxos` | `WalletVTXO[]` | The VTXOs on the current page. |
| `total` | `number` | The total number of matching VTXOs. |
## WalletVTXO
type
One spendable or spent VTXO in a wallet inventory.
```ts
type WalletVTXO = {
outpoint: string;
amountSat: number;
status: string;
batchExpiry: number;
relativeExpiry: number;
commitmentTxid: string;
};
```
## OnchainHistory
type
The paginated history returned for the `onchain` list view.
```ts
type OnchainHistory = {
txs: OnchainTx[];
total: number;
hasMore: boolean;
};
```
| Parameter | Type | Description |
| --------- | ------------- | ---------------------------------------------- |
| `txs` | `OnchainTx[]` | The on-chain transactions on the current page. |
| `total` | `number` | The total number of matching transactions. |
| `hasMore` | `boolean` | Whether another page is available. |
## OnchainTx
type
One transaction in the on-chain history returned by the daemon.
```ts
type OnchainTx = {
txid: string;
kind: string;
amountSat: number;
feeSat: number;
status: string;
confirmationHeight: number;
createdAt: string;
description: string;
};
```
## subscribe
function
Subscribes a listener to runtime events.
```ts
subscribe(listener: WavelengthListener): () => void
```
| Parameter | Type | Description |
| ---------- | ---------------------------------------------------------------------- | ------------------------------------------- |
| `listener` | [`WavelengthListener`](/reference/wavelength-core/#WavelengthListener) | Callback invoked with each WavelengthEvent. |
Returns`() => void`
An unsubscribe function; call it to stop receiving events on this listener.
**`WavelengthListener`**
```ts
type WavelengthListener = (event: WavelengthEvent) => void;
```
See [`WavelengthEvent`](#WavelengthEvent) in the events section below for the event shapes delivered to a listener.
## startActivity
function
Opens the wallet activity stream and forwards each entry to subscribers as an `'activity'` event until [`stopActivity()`](#stopActivity) is called.
```ts
startActivity(opts?: ActivityStreamOptions): Promise
```
```ts
// Replay entries that already exist, then continue with new activity.
await client.startActivity({
includeExisting: true,
kinds: ['send', 'receive'],
});
// Resume after the last entry this consumer processed.
await client.startActivity({
kinds: ['send', 'receive'],
cursor: lastCursor,
});
```
| Parameter | Type | Description |
| ---------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `opts.includeExisting` | `boolean` | Optional. When true, immediately emits an activity event for every entry that already exists, in addition to future changes. |
| `opts.kinds` | `EntryKind[]` | Optional. Streams only the selected activity kinds. |
| `opts.cursor` | `number` | Optional. Resumes after the last monotonic Entry.cursor the consumer processed. Must be a nonnegative safe integer. |
**`ActivityStreamOptions`**
```ts
type ActivityStreamOptions = {
includeExisting?: boolean;
kinds?: EntryKind[];
cursor?: number;
};
```
Returns`Promise`
Resolves once the activity stream is open.
A direct `WavelengthClient` receives an `activityStream` event when the stream ends or fails and chooses its own retry policy. [`WalletEngine`](#WalletEngine) adds reconciliation and bounded cursor-based recovery for apps that want that managed behavior.
## stopActivity
function
Closes the activity stream opened by [`startActivity()`](#startActivity).
```ts
stopActivity(): void
```
Returns`void`
Nothing; stops emitting `'activity'` events synchronously.
## Entry
type
A single row in the wallet activity feed, returned by [`list()`](#list) and streamed via [`startActivity()`](#startActivity) as `activity` events.
```ts
type Entry = {
id: string;
kind: EntryKind;
status: EntryStatus;
amountSat: number;
feeSat: number;
counterparty: string;
createdAt: string;
updatedAt: string;
note: string;
failureReason: string;
failureCode: EntryFailureCode;
cursor: number;
progress?: EntryProgress;
request?: EntryRequest;
};
```
| Parameter | Type | Description |
| --------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id` | `string` | The activity id. Not a payment hash; see SendResult.paymentHash for that. |
| `kind` | [`EntryKind`](/reference/wavelength-core/#EntryKind) | The user-visible activity category. |
| `status` | [`EntryStatus`](/reference/wavelength-core/#EntryStatus) | The collapsed pending/complete/failed state. See the Phase vs Status note below. |
| `amountSat` | `number` | The activity amount, in sats. |
| `feeSat` | `number` | The fee associated with this activity, in sats. |
| `counterparty` | `string` | A human-readable counterparty label, when known. |
| `createdAt` | `string` | When the entry was created. |
| `updatedAt` | `string` | When the entry was last updated. |
| `note` | `string` | An optional note recorded with the activity. |
| `failureReason` | `string` | Human-readable supplement to failureCode; populated only when status is 'failed'. |
| `failureCode` | [`EntryFailureCode`](/reference/wavelength-core/#EntryFailureCode) | Stable, machine-readable classification of why the entry failed. Empty unless status is 'failed'. |
| `cursor` | `number` | The monotonic position of this update on the activity stream. Persist it to resume with ActivityStreamOptions.cursor. It is zero on entries returned outside the stream. |
| `progress` | [`EntryProgress`](/reference/wavelength-core/#EntryProgress) | Optional. Lifecycle metadata the daemon normalized for this entry. Absent when the backing subsystem supplied no progress hint. |
| `request` | [`EntryRequest`](/reference/wavelength-core/#EntryRequest) | Optional. The user-recognizable request that created the entry (an invoice, address, or Ark address). Absent when the backing subsystem did not persist one. |
## EntryProgress
type
The wrapper-owned view of the lifecycle metadata the daemon computes for an [`Entry`](#Entry). Fields are populated on a best-effort basis by the backing subsystem; an empty field means not applicable or not yet known.
```ts
type EntryProgress = {
phase: EntryPhase;
phaseLabel: string;
paymentHash: string;
txid: string;
confirmationHeight: number;
vTXOOutpoint: string;
preimage: string;
};
```
| Parameter | Type | Description |
| -------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`EntryPhase`](/reference/wavelength-core/#EntryPhase) | The coarse lifecycle phase for the entry. |
| `phaseLabel` | `string` | The short lowercase label the daemon emitted; render this directly instead of switching on phase if you prefer. |
| `paymentHash` | `string` | Populated for Lightning-backed send/receive entries. |
| `txid` | `string` | Populated when the backing ledger row has an on-chain txid. |
| `confirmationHeight` | `number` | Populated once the source records it. |
| `vTXOOutpoint` | `string` | Populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | The hex-encoded Lightning payment preimage when known. For a completed Lightning-backed send, it is proof of payment for the invoice. |
## EntryRequest
type
The wrapper-owned, flattened view of the request that created an [`Entry`](#Entry). Exactly one variant’s fields are populated, named by `type`; read `type` first and treat the other fields as zero.
```ts
type EntryRequest = {
type: EntryRequestType;
lightningInvoice: string;
paymentHash: string;
onchainAddress: string;
arkAddress: string;
};
```
| Parameter | Type | Description |
| ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | [`EntryRequestType`](/reference/wavelength-core/#EntryRequestType) | Names the populated variant. |
| `lightningInvoice` | `string` | The BOLT-11 payment request. Populated when type is 'lightning'. |
| `paymentHash` | `string` | Identifies the Lightning invoice and stays stable after the invoice is no longer convenient to display. Populated when type is 'lightning'. |
| `onchainAddress` | `string` | The bech32 on-chain address originally issued or targeted. Populated when type is 'onchain'. |
| `arkAddress` | `string` | The Ark address originally issued or targeted. Populated when type is 'ark'. |
## EntryKind
type
The user-visible wallet activity category.
```ts
type EntryKind = 'send' | 'receive' | 'deposit' | 'exit';
```
| Value | Meaning |
| --------- | --------------------------------------- |
| `send` | An outbound wallet payment. |
| `receive` | An inbound Lightning-to-wallet receive. |
| `deposit` | A boarding on-chain deposit. |
| `exit` | A cooperative wallet-to-on-chain exit. |
## EntryStatus
type
The collapsed wallet activity state.
```ts
type EntryStatus = 'pending' | 'complete' | 'failed';
```
| Value | Meaning |
| ---------- | ---------------------------------------- |
| `pending` | The activity is still in flight. |
| `complete` | The activity finished successfully. |
| `failed` | The activity reached a terminal failure. |
## EntryPhase
type
A coarse, wrapper-owned lifecycle phase for an [`Entry`](#Entry).
```ts
type EntryPhase =
| 'unspecified'
| 'request_created'
| 'waiting_for_payment'
| 'payment_detected'
| 'settling'
| 'confirmed'
| 'refunding'
| 'refunded'
| 'failed'
| 'waiting_for_confirmation';
```
| Value | Meaning |
| -------------------------- | ------------------------------------------------------------------------ |
| `unspecified` | The backing subsystem provided no lifecycle hint. |
| `request_created` | The request was created but no payment has been observed yet. |
| `waiting_for_payment` | The wallet is waiting for an inbound payment or swap funding. |
| `payment_detected` | A payment was detected but is not yet settled. |
| `settling` | The operation is settling through Ark, Lightning, or on-chain machinery. |
| `confirmed` | The backing operation is confirmed or otherwise durably complete. |
| `refunding` | The operation is currently refunding. |
| `refunded` | The refund path completed. |
| `failed` | The backing operation reached a terminal failed state. |
| `waiting_for_confirmation` | An on-chain payment was detected and is waiting for block confirmation. |
> **Phase vs. Status**
>
> `EntryStatus` answers only pending/complete/failed; `EntryPhase` explains the current backing-system step within that status (for example, a `pending` entry might be `waiting_for_payment` or `settling`). Use `status` for coarse UI state and `progress.phase` when you want to show the reader more detail about what is currently happening.
## EntryFailureCode
type
A wrapper-owned, stable classification of why a failed [`Entry`](#Entry) failed.
```ts
type EntryFailureCode =
| 'timed_out'
| 'expired'
| 'refunded'
| 'needs_intervention'
| 'failed';
```
| Value | Meaning |
| -------------------- | ---------------------------------------------------------------------------- |
| `timed_out` | The operation exceeded the wallet deadline before reaching a terminal state. |
| `expired` | The swap expired before it was funded. |
| `refunded` | An outbound payment was refunded back to the wallet. |
| `needs_intervention` | The swap reached an anomalous state requiring manual recovery. |
| `failed` | A generic terminal failure with no more specific classification. |
## EntryRequestType
type
Discriminates which request shape an [`EntryRequest`](#EntryRequest) carries.
```ts
type EntryRequestType = 'lightning' | 'onchain' | 'ark';
```
| Value | Meaning |
| ----------- | ------------------------------------------------------------------------------------- |
| `lightning` | A Lightning send/receive request; `lightningInvoice` and `paymentHash` are populated. |
| `onchain` | A deposit/exit request; `onchainAddress` is populated. |
| `ark` | A direct Ark send/receive request; `arkAddress` is populated. |
## Exiting Ark
> **Preview before you broadcast**
>
> `getExitPlan()` and `sweepWallet({ broadcast: false })` (the default) let you preview an exit or sweep, including required funding and fees, without moving any funds. Always show the preview to the user and get explicit confirmation before calling `sweepWallet({ broadcast: true })`, since that call moves funds and cannot be undone.
## exit
function
Exits a single outpoint through one explicit branch: a cooperative leave, optionally to `destination`, or an acknowledged unilateral unroll. When `destination` is omitted, the daemon generates a fresh backing-wallet address.
```ts
exit(req: ExitRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `req` | [`ExitRequest`](/reference/wavelength-core/#ExitRequest) | The outpoint and either an optional cooperative destination or the exact unilateral acknowledgement. |
Returns`Promise`
The outcome, discriminated by `path`.
**`ExitRequest`**
`destination` and `forceUnrollAck` are mutually exclusive. Omit both to make a cooperative exit to a fresh backing-wallet address:
```ts
await client.exit({ outpoint });
```
A cooperative error rejects the call and never falls back to unilateral unroll. Starting unilateral unroll requires the exported `FORCE_UNROLL_ACK` constant:
```ts
await client.exit({
outpoint,
forceUnrollAck: FORCE_UNROLL_ACK,
});
```
```ts
type ExitRequest =
| {
outpoint: string;
destination?: string;
forceUnrollAck?: never;
}
| {
outpoint: string;
destination?: never;
forceUnrollAck: typeof FORCE_UNROLL_ACK;
};
const FORCE_UNROLL_ACK = 'I_KNOW_WHAT_I_AM_DOING' as const;
```
| Parameter | Type | Description |
| ---------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | The VTXO outpoint to exit, in "txid:index" format. |
| `destination` | `string` | Optional. Cooperative branch only. The on-chain address that receives the leave output. When omitted, the daemon generates a fresh backing-wallet address. Mutually exclusive with forceUnrollAck. |
| `forceUnrollAck` | `typeof FORCE_UNROLL_ACK` | Unilateral branch only. Must be the exact exported acknowledgement constant and cannot be combined with destination. |
**`ExitResult`**
A tagged union over the three exit paths. Read `path` first and only inspect the variant fields associated with that path; the remaining fields are zero-valued.
```ts
type ExitResult = {
path: ExitPath;
cooperative: boolean;
queuedOutpoints: string[];
created: boolean;
actorID: string;
cooperativeError: string;
};
```
| Parameter | Type | Description |
| ------------------ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `path` | [`ExitPath`](/reference/wavelength-core/#ExitPath) | Discriminates between the three legal outcomes; switch on this before reading the variant fields below. |
| `cooperative` | `boolean` | true iff path === 'cooperative'. Retained for backwards compatibility; prefer path. |
| `queuedOutpoints` | `string[]` | The outpoints the cooperative leave admitted into a round. Populated only when path === 'cooperative'. |
| `created` | `boolean` | Whether the unilateral-unroll path spawned a fresh job. Populated when path is 'unilateral' or 'unilateral\_fallback'. |
| `actorID` | `string` | Identifies the durable unroll job that owns the unilateral path. Populated when path is 'unilateral' or 'unilateral\_fallback'. |
| `cooperativeError` | `string` | Retained for source compatibility with a prior SDK fallback shape; current daemon behavior returns cooperative failures directly rather than populating this. |
**`ExitPath`**
```ts
type ExitPath = 'cooperative' | 'unilateral' | 'unilateral_fallback';
```
| Value | Meaning |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cooperative` | The cooperative leave was admitted by the operator; `queuedOutpoints` carries the round’s selection echo. Round completion is asynchronous; subscribe to confirm terminal state. |
| `unilateral` | The caller supplied the exact force acknowledgement and the daemon started unilateral unroll; `created` and `actorID` describe the job. |
| `unilateral_fallback` | Retained for source compatibility with a prior SDK result shape. Current wallet RPC behavior never returns this path, since cooperative failures are surfaced directly. |
## exitStatus
function
Queries the status of an exit.
```ts
exitStatus(req: ExitStatusRequest): Promise
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------- | ------------------------------------------ |
| `req` | [`ExitStatusRequest`](/reference/wavelength-core/#ExitStatusRequest) | The outpoint whose exit status is queried. |
Returns`Promise`
Whether a job exists for the outpoint, and its current status.
**`ExitStatusRequest`**
```ts
type ExitStatusRequest = {
outpoint: string;
detailed?: boolean;
};
```
| Parameter | Type | Description |
| ---------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | The VTXO outpoint whose exit status is queried. |
| `detailed` | `boolean` | Optional. Request recovery-tree progress, a CSV maturity countdown, a fee breakdown, and a best-case block countdown. It costs one live actor round-trip plus a fee estimate, so leave it false for a coarse, cheaper phase-only status. |
**`ExitStatusResult`**
`found` is false when no job exists for the requested outpoint; that is not an error condition. The detailed fields (`phaseDetail`, `progress`, `cSV`, `fees`, `bestCaseBlocksRemaining`, `currentHeight`) are populated only when the request set `detailed`; otherwise they are empty, nil, or zero.
```ts
type ExitStatusResult = {
found: boolean;
status: ExitJobStatus;
sweepTxid: string;
lastError: string;
phaseDetail: string;
progress?: ExitProgress;
cSV?: ExitCSV;
fees?: ExitFees;
bestCaseBlocksRemaining: number;
currentHeight: number;
};
```
| Parameter | Type | Description |
| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `found` | `boolean` | Whether an exit job exists for the requested outpoint. |
| `status` | [`ExitJobStatus`](/reference/wavelength-core/#ExitJobStatus) | The job’s current status. Meaningful only when found is true. |
| `sweepTxid` | `string` | The sweep transaction id, once known. |
| `lastError` | `string` | The last error the job recorded, if any. |
| `phaseDetail` | `string` | A one-line human description of the current phase. Empty on a coarse (non-detailed) query. |
| `progress` | [`ExitProgress`](/reference/wavelength-core/#ExitProgress) | Recovery-tree materialization progress. Nil on a coarse query, or when no live actor backs the job. |
| `cSV` | [`ExitCSV`](/reference/wavelength-core/#ExitCSV) | The target’s CSV maturity countdown. Nil until the target confirms. |
| `fees` | [`ExitFees`](/reference/wavelength-core/#ExitFees) | The on-chain cost breakdown for the exit. Nil on a coarse query. |
| `bestCaseBlocksRemaining` | `number` | The optimistic block count until a confirmed sweep. Zero on a coarse query. |
| `currentHeight` | `number` | The best block height the exit job has observed. Zero on a coarse query, or when no live actor backs the job. |
**`ExitProgress`**
Materialization progress through the recovery tree. Populated only for a detailed query against a live exit job.
```ts
type ExitProgress = {
confirmedTxs: number;
inFlightTxs: number;
readyTxs: number;
blockedTxs: number;
totalTxs: number;
currentLayer: number;
totalLayers: number;
targetConfirmed: boolean;
allProofConfirmed: boolean;
};
```
| Parameter | Type | Description |
| ------------------- | --------- | --------------------------------------------------------------------------------------------------------------------- |
| `confirmedTxs` | `number` | Proof transactions confirmed on-chain. |
| `inFlightTxs` | `number` | Proof transactions broadcast but not yet observed confirmed. |
| `readyTxs` | `number` | Proof transactions ready to broadcast now. |
| `blockedTxs` | `number` | Proof transactions still waiting on an unconfirmed in-proof parent. |
| `totalTxs` | `number` | Total transactions in the exit proof tree. |
| `currentLayer` | `number` | The frontier layer index: the shallowest topological layer (roots first) that still holds an unconfirmed transaction. |
| `totalLayers` | `number` | The depth of the exit proof tree. |
| `targetConfirmed` | `boolean` | True once the target VTXO transaction has confirmed. |
| `allProofConfirmed` | `boolean` | True once every proof-tree transaction has confirmed, so only the CSV wait and final sweep remain. |
**`ExitCSV`**
The target’s CSV maturity countdown. Populated only once the target transaction has confirmed.
```ts
type ExitCSV = {
targetConfirmHeight: number;
maturityHeight: number;
blocksRemaining: number;
mature: boolean;
};
```
| Parameter | Type | Description |
| --------------------- | --------- | ----------------------------------------------------- |
| `targetConfirmHeight` | `number` | The height at which the target transaction confirmed. |
| `maturityHeight` | `number` | The height at which the CSV timelock matures. |
| `blocksRemaining` | `number` | Blocks remaining until maturity. |
| `mature` | `boolean` | Whether the CSV timelock has matured. |
**`ExitFees`**
The on-chain cost breakdown for the exit. The CPFP total is estimated; `sweepFeeActual` reports whether `sweepFeeSat` is the real built-sweep fee rather than an estimate.
```ts
type ExitFees = {
cPFPFeeSat: number;
sweepFeeSat: number;
totalCostSat: number;
spentSoFarSat: number;
vTXOAmountSat: number;
netRecoveredSat: number;
feeRateSatVByte: number;
sweepFeeActual: boolean;
};
```
| Parameter | Type | Description |
| ----------------- | --------- | ------------------------------------------------------------------------------- |
| `cPFPFeeSat` | `number` | The estimated total CPFP fee, in sats. |
| `sweepFeeSat` | `number` | The sweep fee, in sats. Real rather than estimated when sweepFeeActual is true. |
| `totalCostSat` | `number` | The projected on-chain cost of the whole exit, in sats. |
| `spentSoFarSat` | `number` | The estimated fee committed so far, in sats. |
| `vTXOAmountSat` | `number` | The VTXO value being recovered, in sats. |
| `netRecoveredSat` | `number` | The estimated net recoverable after fees, in sats. |
| `feeRateSatVByte` | `number` | The fee rate used, in sat/vByte. |
| `sweepFeeActual` | `boolean` | Whether sweepFeeSat is the real built-sweep fee rather than an estimate. |
**`ExitJobStatus`**
Collapses the underlying unroll job phases to a short wallet-facing string set.
```ts
type ExitJobStatus =
| 'unspecified'
| 'pending'
| 'materializing'
| 'csv_pending'
| 'sweeping'
| 'completed'
| 'failed';
```
| Value | Meaning |
| --------------- | ----------------------------------------------------------- |
| `unspecified` | No status hint available. |
| `pending` | The job has been created but has not started materializing. |
| `materializing` | The unilateral exit transaction is being built. |
| `csv_pending` | Waiting on the CSV timelock before the sweep can broadcast. |
| `sweeping` | The sweep transaction has been broadcast. |
| `completed` | The exit finished successfully. |
| `failed` | The job reached a terminal failure. |
## exitSummary
function
Summarizes all in-progress exits: one entry per active exit plus wallet-wide totals for the amount being recovered, the estimated fees, and the estimated net recoverable. Completed and failed exits are omitted; they have no amount left to recover.
```ts
exitSummary(req?: ExitSummaryRequest): Promise
```
| Parameter | Type | Description |
| --------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `req` | [`ExitSummaryRequest`](/reference/wavelength-core/#ExitSummaryRequest) | Takes no arguments; the daemon reports the wallet-wide portfolio. |
Returns`Promise`
The in-progress exits plus aggregate totals.
**`ExitSummaryRequest`**
Takes no arguments.
```ts
type ExitSummaryRequest = Record;
```
**`ExitSummaryResult`**
```ts
type ExitSummaryResult = {
exits: ExitSummaryEntry[];
totalExits: number;
totalVTXOAmountSat: number;
totalEstFeeSat: number;
totalEstNetRecoveredSat: number;
};
```
| Parameter | Type | Description |
| ------------------------- | -------------------- | ----------------------------------------------------------- |
| `exits` | `ExitSummaryEntry[]` | One entry per in-progress exit. |
| `totalExits` | `number` | The number of in-progress exits. |
| `totalVTXOAmountSat` | `number` | The combined VTXO value being recovered, in sats. |
| `totalEstFeeSat` | `number` | The combined estimated on-chain fees, in sats. |
| `totalEstNetRecoveredSat` | `number` | The combined estimated net recoverable after fees, in sats. |
**`ExitSummaryEntry`**
One in-progress exit’s coarse contribution to the portfolio.
```ts
type ExitSummaryEntry = {
outpoint: string;
status: ExitJobStatus;
vTXOAmountSat: number;
estTotalFeeSat: number;
estNetRecoveredSat: number;
};
```
| Parameter | Type | Description |
| -------------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
| `outpoint` | `string` | The VTXO outpoint being exited. |
| `status` | [`ExitJobStatus`](/reference/wavelength-core/#ExitJobStatus) | The exit job’s current status. |
| `vTXOAmountSat` | `number` | The VTXO value being recovered, in sats. |
| `estTotalFeeSat` | `number` | The estimated total on-chain fee for this exit, in sats. |
| `estNetRecoveredSat` | `number` | The estimated net recoverable after fees, in sats. |
## getExitPlan
function
Previews unilateral-exit readiness (and the backing-wallet funding required) for a set of VTXO outpoints, without moving funds.
```ts
getExitPlan(req: GetExitPlanRequest): Promise
```
| Parameter | Type | Description |
| --------- | ---------------------------------------------------------------------- | ---------------------------------- |
| `req` | [`GetExitPlanRequest`](/reference/wavelength-core/#GetExitPlanRequest) | The outpoints to plan an exit for. |
Returns`Promise`
Per-outpoint funding plans plus aggregate totals.
**`GetExitPlanRequest`**
```ts
type GetExitPlanRequest = {
outpoints: string[];
confTarget?: number;
};
```
| Parameter | Type | Description |
| ------------ | ---------- | ------------------------------------------------------------------ |
| `outpoints` | `string[]` | The VTXO outpoints to plan an exit for. |
| `confTarget` | `number` | Optional. A confirmation target (in blocks) used to estimate fees. |
**`GetExitPlanResult`**
Describes the combined backing-wallet funding plan for every previewed outpoint plus aggregate totals.
```ts
type GetExitPlanResult = {
plans: ExitPlanEntry[];
feeRateSatPerVByte: number;
canStart: boolean;
totalFundingShortfallSat: number;
totalRecommendedFundingSat: number;
};
```
| Parameter | Type | Description |
| ---------------------------- | ----------------- | ---------------------------------------------------------------------- |
| `plans` | `ExitPlanEntry[]` | One entry per requested outpoint. |
| `feeRateSatPerVByte` | `number` | The fee rate used to compute the plan. |
| `canStart` | `boolean` | Whether every outpoint in the plan is ready to exit right now. |
| `totalFundingShortfallSat` | `number` | The combined backing-wallet funding still needed across all outpoints. |
| `totalRecommendedFundingSat` | `number` | The combined recommended backing-wallet funding across all outpoints. |
**`ExitPlanEntry`**
Describes how to fund the backing wallet before `exit()` for a single previewed VTXO outpoint.
```ts
type ExitPlanEntry = {
outpoint: string;
fundingAddress: string;
requiredConfirmations: number;
requiredFeeUTXOCount: number;
usableFeeUTXOCount: number;
recommendedUTXOAmountSat: number;
recommendedTotalFundingSat: number;
fundingShortfallSat: number;
canStart: boolean;
infeasibilityReason: ExitInfeasibilityReason;
exitJobFound: boolean;
exitStatus: ExitJobStatus;
sweepTxid: string;
lastError: string;
err: string;
};
```
| Parameter | Type | Description |
| ---------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | The VTXO outpoint this entry describes. |
| `fundingAddress` | `string` | The backing-wallet address to fund for this exit. |
| `requiredConfirmations` | `number` | Confirmations the funding UTXO(s) need before the exit can start. |
| `requiredFeeUTXOCount` | `number` | The number of fee UTXOs required. |
| `usableFeeUTXOCount` | `number` | The number of fee UTXOs currently usable. |
| `recommendedUTXOAmountSat` | `number` | The recommended amount per funding UTXO, in sats. |
| `recommendedTotalFundingSat` | `number` | The recommended total funding amount, in sats. |
| `fundingShortfallSat` | `number` | The funding still needed before this outpoint can exit, in sats. |
| `canStart` | `boolean` | Whether this outpoint is ready to exit right now. |
| `infeasibilityReason` | [`ExitInfeasibilityReason`](/reference/wavelength-core/#ExitInfeasibilityReason) | Why canStart is false: a structural block (a dust or uneconomical VTXO) or a funding shortfall the wallet could cover. It is "unspecified" when canStart is true. |
| `exitJobFound` | `boolean` | Whether an exit job already exists for this outpoint. |
| `exitStatus` | [`ExitJobStatus`](/reference/wavelength-core/#ExitJobStatus) | The existing job’s status, when exitJobFound is true. |
| `sweepTxid` | `string` | The sweep transaction id, once known. |
| `lastError` | `string` | The last error the existing job recorded, if any. |
| `err` | `string` | A per-outpoint failure encountered while building this plan entry (empty on success). |
**`ExitInfeasibilityReason`**
Explains why a previewed exit cannot start. A structural block (dust or uneconomical) is one no amount of wallet funding fixes; the others are funding shortfalls the wallet could cover.
```ts
type ExitInfeasibilityReason =
| 'unspecified'
| 'sweep_below_dust'
| 'uneconomical'
| 'wallet_underfunded'
| 'wallet_too_few_inputs';
```
| Value | Meaning |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `unspecified` | The exit is feasible, or the block is a plain funding shortfall reported via `fundingShortfallSat`. |
| `sweep_below_dust` | The swept output, after the sweep fee, would fall at or below the dust limit, so the sweep could never relay. Impossible regardless of funding. |
| `uneconomical` | The total on-chain cost to recover the VTXO exceeds the configured fraction of its value. |
| `wallet_underfunded` | The confirmed on-chain wallet balance is too small to cover the CPFP fees. Funding the wallet resolves it. |
| `wallet_too_few_inputs` | The wallet has fewer usable confirmed UTXOs than the VTXO has independent ancestry paths, so it lacks distinct CPFP fee inputs. |
> **exitBatch starts exits and moves money**
>
> `exitBatch()` walks the given outpoints and calls [`exit()`](#exit) for each one in turn: it moves funds. It resolves once every exit has been **started**, not completed. A unilateral exit keeps running for hours or days after the promise settles; use [`exitStatus()`](#exitStatus) or [`exitSummary()`](#exitSummary) to follow it to completion. Funding is never reserved ahead of time, so on the unilateral path `exitBatch()` re-plans with [`getExitPlan()`](#getExitPlan) between starts: an earlier start can consume the fee inputs a later one needs, and re-planning is the only way to catch that before the later exit is attempted instead of after.
## exitBatch
function
Starts a batch of exits, one outpoint per [`exit()`](#exit) call, and reports which started, which were skipped, and which never started.
```ts
function exitBatch(
opts: ExitBatchOptions & {
client: WavelengthClient;
signal?: AbortSignal;
onEvent?: (event: ExitBatchEvent) => void;
},
): Promise
```
On the unilateral path it previews funding with [`getExitPlan()`](#getExitPlan) before each start, skips outpoints that already have a running exit job, refuses to start anything if the wallet cannot fund the batch, and re-plans between starts, as the callout above explains. Because fee inputs are leased only at broadcast time, a mid-batch `exit()` rejection is treated as a clean stop rather than retried. On the cooperative path it queues each outpoint into the next round, one `exit()` call at a time, with no re-planning step.
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `opts` | [`ExitBatchOptions`](/reference/wavelength-core/#ExitBatchOptions) | The batch mode and outpoints, plus client (the WavelengthClient to drive), an optional AbortSignal, and an optional onEvent progress callback. |
Returns`Promise`
Which outpoints started, which were skipped because they already had a running exit, which never started, and why the batch stopped early, if it did.
**`ExitBatchOptions`**
A discriminated union on `mode`. A cooperative batch queues each outpoint into the next round to an optional on-chain `destination`; a unilateral batch forces each outpoint on-chain, funding the recovery from the backing wallet.
```ts
type ExitBatchOptions =
| { mode: 'cooperative'; outpoints: string[]; destination?: string }
| { mode: 'unilateral'; outpoints: string[]; confTarget?: number };
```
| Parameter | Type | Description |
| -------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{ mode: 'cooperative', outpoints, destination? }` | `variant` | Queues every outpoint into the next cooperative round. destination is optional; when omitted the daemon generates a fresh backing-wallet address per outpoint. |
| `{ mode: 'unilateral', outpoints, confTarget? }` | `variant` | Forces every outpoint on-chain, funding the recovery from the backing wallet. confTarget is an optional confirmation target (in blocks) used to estimate fees for the funding plan. |
**`ExitBatchEvent`**
Progress events delivered to `exitBatch()`’s `onEvent` callback, in order, as it works through a batch.
```ts
type ExitBatchEvent =
| { type: 'planned'; plan: GetExitPlanResult }
| { type: 'starting'; outpoint: string }
| { type: 'started'; outpoint: string; result: ExitResult }
| { type: 'stopped'; stoppedBy: ExitBatchStop; remaining: string[] };
```
| Parameter | Type | Description |
| ------------------------------------------- | --------- | ---------------------------------------------------------------------------------- |
| `{ type: 'planned', plan }` | `variant` | The latest unilateral funding plan from a re-plan round. Unilateral batches only. |
| `{ type: 'starting', outpoint }` | `variant` | Fires immediately before the exit() call for this outpoint. |
| `{ type: 'started', outpoint, result }` | `variant` | Fires after exit() succeeds for this outpoint, carrying its ExitResult. |
| `{ type: 'stopped', stoppedBy, remaining }` | `variant` | Fires once, when the batch halts before every outpoint started; see ExitBatchStop. |
**`ExitBatchStop`**
Why an `exitBatch()` run stopped before finishing every outpoint.
```ts
type ExitBatchStop =
| { reason: 'infeasible'; plan: GetExitPlanResult }
| { reason: 'rejected'; outpoint: string; error: Error };
```
| Parameter | Type | Description |
| ----------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{ reason: 'infeasible', plan }` | `variant` | A re-plan reported the backing wallet can no longer fund the remaining exits. Unilateral batches only; plan is the GetExitPlanResult that triggered the stop. |
| `{ reason: 'rejected', outpoint, error }` | `variant` | An individual exit() call was rejected by the daemon for this outpoint. |
**`ExitBatchResult`**
The outcome of `exitBatch()`.
```ts
type ExitBatchResult = {
started: { outpoint: string; result: ExitResult }[];
skipped: string[];
remaining: string[];
stoppedBy?: ExitBatchStop;
};
```
| Parameter | Type | Description |
| ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `started` | `{ outpoint: string, result: ExitResult }[]` | The exits successfully started. Each unilateral entry still runs for hours or days afterward; appearing here means started, not completed. |
| `skipped` | `string[]` | Outpoints that already had a running exit job and were left alone rather than double-started. |
| `remaining` | `string[]` | Outpoints never started, because the batch stopped early. |
| `stoppedBy` | [`ExitBatchStop`](/reference/wavelength-core/#ExitBatchStop) | Optional. Present when the batch halted before every outpoint started; see ExitBatchStop. |
## isExitInfeasibilityFundable
function
Distinguishes a fixable exit-infeasibility (the backing wallet needs more confirmed funds or inputs) from a structural one (the VTXO cannot be exited economically at all). Use it to decide whether to show a “fund your wallet” affordance or a terminal “cannot exit this VTXO” message. Mirrors the daemon’s own infeasibility split.
```ts
function isExitInfeasibilityFundable(
reason: ExitInfeasibilityReason,
): boolean
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `reason` | [`ExitInfeasibilityReason`](/reference/wavelength-core/#ExitInfeasibilityReason) | The infeasibility reason from an ExitPlanEntry, typically read from a getExitPlan() response. |
Returns`boolean`
`true` for `wallet_underfunded` and `wallet_too_few_inputs` (funding the wallet resolves it); `false` for every other reason, including the structural `sweep_below_dust` and `uneconomical` blocks that no amount of funding fixes.
## sweepWallet
function
Previews or broadcasts a sweep of the backing wallet. Call it with `broadcast: false` first to preview; `broadcast: true` moves funds.
```ts
sweepWallet(req: SweepWalletRequest): Promise
```
| Parameter | Type | Description |
| --------- | ---------------------------------------------------------------------- | ------------------------------------------------- |
| `req` | [`SweepWalletRequest`](/reference/wavelength-core/#SweepWalletRequest) | The destination and broadcast flag for the sweep. |
Returns`Promise`
The selected inputs, fees, and (on broadcast) the resulting txid.
**`SweepWalletRequest`**
```ts
type SweepWalletRequest = {
destinationAddress: string;
broadcast?: boolean;
feeRateSatPerVByte?: number;
confTarget?: number;
};
```
| Parameter | Type | Description |
| -------------------- | --------- | -------------------------------------------------------------------------------------- |
| `destinationAddress` | `string` | The on-chain address to sweep the backing wallet to. |
| `broadcast` | `boolean` | Optional. When true, broadcasts the sweep; when false (the default), only previews it. |
| `feeRateSatPerVByte` | `number` | Optional. An explicit fee rate, in sat/vByte. |
| `confTarget` | `number` | Optional. A confirmation target (in blocks) used to estimate fees. |
**`SweepWalletResult`**
```ts
type SweepWalletResult = {
inputs: WalletSweepInput[];
totalInputSat: number;
estimatedFeeSat: number;
netAmountSat: number;
feeRateSatPerVByte: number;
canBroadcast: boolean;
txid: string;
failureReason: string;
};
```
| Parameter | Type | Description |
| -------------------- | -------------------- | --------------------------------------------------------------------------------------------- |
| `inputs` | `WalletSweepInput[]` | The backing-wallet UTXOs selected for this sweep. |
| `totalInputSat` | `number` | The combined amount of the selected inputs, in sats. |
| `estimatedFeeSat` | `number` | The estimated fee, in sats. |
| `netAmountSat` | `number` | The amount that will reach destinationAddress after fees, in sats. |
| `feeRateSatPerVByte` | `number` | The fee rate used, in sat/vByte. |
| `canBroadcast` | `boolean` | Whether the sweep is currently valid to broadcast. |
| `txid` | `string` | The broadcast transaction id. Populated only when broadcast was true and the sweep succeeded. |
| `failureReason` | `string` | A human-readable reason the sweep cannot broadcast, when canBroadcast is false. |
**`WalletSweepInput`**
Describes one backing-wallet UTXO selected by `sweepWallet()`.
```ts
type WalletSweepInput = {
outpoint: string;
amountSat: number;
};
```
| Parameter | Type | Description |
| ----------- | -------- | ------------------------- |
| `outpoint` | `string` | The UTXO outpoint. |
| `amountSat` | `number` | The UTXO amount, in sats. |
See the [leaving Ark guide](/concepts/leaving-ark/) for a walkthrough of the exit and sweep flow end to end.
## Portable facade calls
## callFacade
function
Invokes one portable mobile/wasm facade method. This is the low-level escape hatch for transport integrations; application code should prefer the typed methods documented above.
```ts
callFacade(
method: FacadeMethod,
params?: unknown,
): Promise
```
| Parameter | Type | Description |
| --------- | -------------- | ---------------------------------------------------------------------------------------------- |
| `method` | `FacadeMethod` | One of the portable facade method names listed below. |
| `params` | `unknown` | Optional. The raw mobile/wasm request shape for that method, not the typed SDK request mapper. |
Returns`Promise`
The response with normal SDK casing and method-aware null normalization, typed as `T` (unchecked; the caller asserts the shape).
**`FacadeMethod`**
```ts
type FacadeMethod =
| 'start' | 'stop' | 'getInfo' | 'status' | 'balance'
| 'createWallet' | 'unlockWallet' | 'openWalletFromPasskey'
| 'deposit' | 'receive' | 'prepareSend' | 'sendPrepared'
| 'list' | 'exit' | 'exitStatus' | 'exitSummary'
| 'getExitPlan' | 'sweepWallet'
| 'confirmedBalanceSat' | 'pendingInboundSat'
| 'walletReady' | 'isRunning';
```
The whitelist excludes the streaming `subscribe` operation and private worker control methods such as `$ready` and `$init`. Requests use the raw mobile/wasm shapes, while responses use the same camelCase keys and schema-aware nil normalization as typed methods. The four scalar verbs `confirmedBalanceSat`, `pendingInboundSat`, `walletReady`, and `isRunning` are included for portable bridge callers.
> **Prefer the typed methods**
>
> Typed methods map SDK request shapes, validate relevant inputs, and return named results. Use `callFacade()` only when you specifically need the portable facade contract.
## camelizeKeys
function
Maps a daemon PascalCase JSON response onto the SDK’s camelCase shapes. The shared client applies this after transport invocation so typed methods and `callFacade()` return camelCase keys consistently across transports.
```ts
function camelizeKeys(value: unknown): T
```
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------------------------------- |
| `value` | `unknown` | The raw daemon response (or any nested value) to camelize. |
Returns`T`
The same structure with every object key rewritten to camelCase. Arrays are walked recursively; primitives pass through unchanged.
## Runtime state model
## WalletState
const
Wallet lifecycle state returned in `WalletInfo.walletState`. The daemon sends a numeric enum; the SDK maps it to these lowercase strings at the response boundary via [`walletStateFromProto()`](#walletStateFromProto).
```ts
const WalletState = {
None: 'none',
Locked: 'locked',
Ready: 'ready',
Syncing: 'syncing',
} as const;
type WalletState = typeof WalletState[keyof typeof WalletState];
```
| Value | Meaning |
| --------- | ------------------------------------------------------ |
| `none` | No wallet exists yet; one must be created. |
| `locked` | A wallet exists but is locked and must be unlocked. |
| `ready` | The wallet is unlocked and ready to use. |
| `syncing` | The wallet is unlocked and catching up with the chain. |
## RuntimePhase
type
Lifecycle phase a UI renders. Runtime phases (`loading`, `runtimeReady`, `starting`, `stopping`, `stopped`, `error`) are owned by the host start/stop flow. Wallet phases (`needsWallet`, `locked`, `syncing`, `ready`) are derived from `WalletInfo` via [`phaseFromInfo()`](#phaseFromInfo). `restoring` is owned by the [`WalletEngine`](#WalletEngine) itself (like `starting`): it is never returned by `phaseFromInfo()`, and appears whenever an engine `restoreWallet()` call is in flight. When the call sets `recoverState: true`, the server-assisted recovery scan is additionally tracked through `snapshot.recovery`.
```ts
type RuntimePhase =
| 'loading'
| 'runtimeReady'
| 'starting'
| 'needsWallet'
| 'locked'
| 'syncing'
| 'restoring'
| 'ready'
| 'stopping'
| 'stopped'
| 'error';
```
| Value | Owner | Meaning |
| -------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `loading` | Host | The runtime assets have not finished loading. |
| `runtimeReady` | Host | `ready()` resolved; the client is usable but the daemon has not started. |
| `starting` | Host | `start()` has been called and has not yet resolved. |
| `needsWallet` | Wallet | No wallet exists yet (`WalletState.None`). |
| `locked` | Wallet | A wallet exists but is locked. |
| `syncing` | Wallet | The wallet is unlocked and catching up with the chain. |
| `restoring` | Engine | A background restore (`WalletEngine.restoreWallet()`) is bringing a freshly restored wallet up; `recoverState: true` additionally tracks the recovery scan through the recovery state. |
| `ready` | Wallet | The wallet is unlocked and ready (`walletReady` or `WalletState.Ready`). |
| `stopping` | Host | `stop()` has been called and has not yet resolved. |
| `stopped` | Host | The daemon has stopped. |
| `error` | Host | The host encountered an unrecoverable error starting or running the client. |
## WalletPhase
type
The subset of lifecycle phases derived from wallet information by [`phaseFromInfo()`](#phaseFromInfo). Runtime-owned phases such as `starting` and `stopped` are represented by `RuntimePhase`, not this type.
```ts
type WalletPhase = 'needsWallet' | 'locked' | 'syncing' | 'ready';
```
## phaseFromInfo
function
Derives the wallet-state phase from a `WalletInfo`-shaped value. Runtime phases are not represented here; the caller owns those.
```ts
function phaseFromInfo(info: {
walletState?: WalletState;
walletReady?: boolean;
}): RuntimePhase
```
| Parameter | Type | Description |
| ------------------ | -------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `info.walletState` | [`WalletState`](/reference/wavelength-core/#WalletState) | Optional. The wallet lifecycle state to derive a phase from. |
| `info.walletReady` | `boolean` | Optional. When true, short-circuits directly to 'ready' regardless of walletState. |
Returns[`RuntimePhase`](/reference/wavelength-core/#RuntimePhase)
`'ready'` when `walletReady` is true or `walletState === 'ready'`; otherwise `'locked'` or `'syncing'` matching `walletState`; otherwise `'needsWallet'` (the fallback for `'none'` or any unrecognized value).
## normalizeInfo
function
Maps a raw daemon `Info` payload (numeric `walletState`, no `walletReady`) onto the public `WalletInfo`: converts `walletState` to the string union via [`walletStateFromProto()`](#walletStateFromProto) and backfills `walletReady` (ready iff `walletState === 'ready'`), mirroring the daemon’s `Info.WalletReady()` method. `BaseWavelengthClient` applies this through shared facade normalization after the transport returns the raw `getInfo()` result.
```ts
function normalizeInfo(raw: unknown): WalletInfo
```
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------------------- |
| `raw` | `unknown` | The raw daemon Info payload (untrusted shape). |
Returns[`WalletInfo`](/reference/wavelength-core/#WalletInfo)
The normalized `WalletInfo`, with `walletState` as the SDK string union and `walletReady` backfilled if the raw payload did not already carry it.
## walletStateFromProto
function
Normalizes a raw daemon `walletState` (a proto number) to the SDK string union. Already-string values pass through unchanged.
```ts
function walletStateFromProto(
value: number | WalletState | undefined,
): WalletState
```
| Parameter | Type | Description |
| --------- | ------------------------------------ | ------------------------------------------------------------------- |
| `value` | `number \| WalletState \| undefined` | The raw walletState as a proto number, an SDK string, or undefined. |
Returns[`WalletState`](/reference/wavelength-core/#WalletState)
`'none'` when `value` is `undefined` or the proto zero value; the matching string for a recognized proto number (`1` → `'none'`, `2` → `'locked'`, `3` → `'ready'`, `4` → `'syncing'`); otherwise the conservative fallback `'locked'` for an unrecognized non-zero value (a future or garbled daemon state), so it never drives the UI into offering wallet creation over an existing wallet.
## Events and logging
## WavelengthEvent
type
Discriminated union of runtime events delivered to subscribers. Narrow on `type` to read the payload: `'activity'` carries the changed [`Entry`](#Entry), `'log'` carries a level and message, and the lifecycle events (`'runtimeReady'`, `'runtimeStopped'`) carry none. There are five event types: `runtimeReady`, `runtimeStopped`, `activity`, `activityStream`, and `log`.
```ts
type WavelengthEvent =
| { type: 'runtimeReady' }
| { type: 'runtimeStopped' }
| { type: 'activity'; payload: Entry }
| { type: 'activityStream'; payload: ActivityStreamPayload }
| { type: 'log'; payload: WavelengthLogPayload };
```
| `type` | Payload | Meaning |
| ---------------- | ----------------------- | ----------------------------------------------------------------------- |
| `runtimeReady` | (none) | The runtime finished loading. |
| `runtimeStopped` | (none) | The daemon stopped, including an unsolicited stop from a worker crash. |
| `activity` | `Entry` | An activity entry changed; delivered while `startActivity()` is active. |
| `activityStream` | `ActivityStreamPayload` | The activity stream ended or failed without a consumer-initiated close. |
| `log` | `WavelengthLogPayload` | A daemon log line. |
Direct clients receive `activityStream` terminal events and choose their own retry policy. The event carries only `state` and, for failures, `message`. Structured gap cursor and reason details are not available through the current web and native bridges.
## ActivityStreamState
type
Whether an activity stream ended normally or failed unexpectedly. A consumer-initiated close emits no stream-status event.
```ts
type ActivityStreamState = 'ended' | 'failed';
```
## ActivityStreamPayload
type
The payload carried by an `activityStream` event. Narrow on `state` to access the failure message.
```ts
type ActivityStreamPayload =
| { state: 'ended' }
| { state: 'failed'; message: string };
```
## WavelengthEventType
type
The set of `WavelengthEvent` discriminants.
```ts
type WavelengthEventType = WavelengthEvent['type'];
```
## WavelengthListener
type
A subscriber callback invoked with each `WavelengthEvent`, passed to [`subscribe()`](#subscribe).
```ts
type WavelengthListener = (event: WavelengthEvent) => void;
```
## WavelengthLogPayload
type
The payload carried by a `'log'` event.
```ts
type WavelengthLogPayload = {
level: WavelengthLogLevel;
message: string;
};
```
| Parameter | Type | Description |
| --------- | ---------------------------------------------------------------------- | ------------------------------- |
| `level` | [`WavelengthLogLevel`](/reference/wavelength-core/#WavelengthLogLevel) | The severity of the log line. |
| `message` | `string` | The human-readable log message. |
## WavelengthLogLevel
type
The severity of a `'log'` event emitted by the runtime.
```ts
type WavelengthLogLevel = 'debug' | 'info' | 'warn' | 'error';
```
| Value | Meaning |
| ------- | ---------------------------------------- |
| `debug` | Verbose diagnostic detail. |
| `info` | Normal operational messages. |
| `warn` | Recoverable but noteworthy conditions. |
| `error` | Failures worth surfacing to a developer. |
## Errors
## WavelengthError
class
Base error class thrown by all Wavelength SDK packages. Carries a machine-readable `code` alongside the human-readable message.
```ts
class WavelengthError extends Error {
readonly code: WavelengthErrorCode;
constructor(message: string, code?: WavelengthErrorCode, options?: { cause?: unknown });
}
```
| Parameter | Type | Description |
| --------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message` | `string` | Human-readable description of what went wrong. |
| `code` | [`WavelengthErrorCode`](/reference/wavelength-core/#WavelengthErrorCode) | Machine-readable error code. Defaults to wavelength\_error. |
| `options.cause` | `unknown` | Optional. The underlying error or value that caused this one, forwarded to the native Error cause chain (options.cause) so it survives in stack traces and error inspection tools. |
## WavelengthErrorCode
type
Stable, machine-readable error classification on `WavelengthError`. Named SDK codes are listed below; daemon-originated errors currently fall back to `wavelength_error`. The `(string & {})` arm keeps the union open for forward compatibility while still offering autocomplete on the known codes.
```ts
type WavelengthErrorCode =
| 'wavelength_error'
| 'runtime_not_ready'
| 'asset_load_failed'
| 'worker_error'
| (string & {});
```
| Code | Thrown when |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| `wavelength_error` | The generic default: any SDK error without a more specific code, and all daemon-originated failures today. |
| `runtime_not_ready` | A method is called before `ready()` has resolved, or after `dispose()`. |
| `asset_load_failed` | `ready()` fails to load the runtime assets (wasm binary or its supporting files). |
| `worker_error` | The worker transport’s underlying Worker fails or crashes. |
## PasskeyCancelledError
class
Thrown by passkey ceremonies when the user dismisses the OS prompt, so hosts can suppress cancellation copy without string-matching the message. `wavelength-react`’s `useWalletPasskey` rethrows it without recording it into `createError`/`openError`.
```ts
class PasskeyCancelledError extends Error {
constructor(message?: string);
}
```
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------------- |
| `message` | `string` | Optional. Defaults to 'passkey ceremony was cancelled'. |
## toError
function
Normalizes an unknown thrown value to an `Error`, preserving an existing `Error` instance (and therefore its stack and `cause`) unchanged. Used throughout the SDK, including by every mutation hook’s throw-and-capture convention, to guarantee `error` fields are always `Error | null`, never a string or other thrown value.
```ts
function toError(value: unknown): Error
```
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------------------------- |
| `value` | `unknown` | The thrown value to normalize; may already be an Error. |
Returns`Error`
`value` unchanged if it is already an `Error`; otherwise a new `Error` built from it via `errorMessage()`.
## Passkeys
## WalletKind
type
Labels how a local wallet is unlocked.
```ts
type WalletKind = 'passkey' | 'password';
```
| Value | Meaning |
| ---------- | ----------------------------------------------------------------------------- |
| `passkey` | The wallet is unlocked by deriving key material from a passkey PRF assertion. |
| `password` | The wallet is unlocked with a plain password via `unlockWallet()`. |
## PasskeyAssertion
type
A passkey ceremony result: the PRF output (hex) shared with the Go SDK, plus the credential id that produced it so callers can scope later assertions to the same passkey.
```ts
type PasskeyAssertion = {
prfOutput: string;
credentialId: string;
};
```
| Parameter | Type | Description |
| -------------- | -------- | --------------------------------------------------------------------------------------------------- |
| `prfOutput` | `string` | The PRF output (hex) shared with the Go SDK. Pass this into OpenWalletFromPasskeyRequest.prfOutput. |
| `credentialId` | `string` | The credential id that produced the PRF output. |
## PasskeyCeremony
type
The per-platform passkey ceremony contract a host injects to drive [`openWalletFromPasskey()`](#openWalletFromPasskey). Injecting it keeps consumers of this contract free of any transport dependency: [`wavelength-web`](/reference/wavelength-web/) supplies the browser (WebAuthn/PRF) implementation as `webPasskeyCeremony`, and `wavelength-react`’s `useWalletPasskey` hook drives whichever implementation you pass it. The React Native transport supplies its own implementation, `createNativePasskeyCeremony` ([wavelength-react-native](/reference/wavelength-react-native/)).
```ts
type PasskeyCeremony = {
supportsPasskeyPrf(): Promise;
registerPasskeyWallet(appName: string): Promise;
assertPasskeyPrf(allowCredentialId?: string): Promise;
};
```
| Parameter | Type | Description |
| ----------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------ |
| `supportsPasskeyPrf` | `() => Promise` | Resolves true when the platform supports passkey PRF. |
| `registerPasskeyWallet` | `(appName: string) => Promise` | Registers a new passkey wallet and returns its assertion. |
| `assertPasskeyPrf` | `(allowCredentialId?: string) => Promise` | Asserts an existing passkey, optionally scoped to a credential id. |
---
Source: https://wavelength.lightning.engineering/reference/wavelength-react.md
# wavelength-react
React bindings for the Wavelength SDK - the WavelengthProvider component and hooks for balance, activity, and wallet operations.
`@lightninglabs/wavelength-react` is the React binding for the Wavelength SDK: a context provider that hands a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) down to a component subtree, plus hooks for reading its state and calling wallet operations.
> **Note**
>
> This package re-exports the complete `wavelength-core` contract (`export * from '@lightninglabs/wavelength-core'`), including `WavelengthClient`, `WalletEngine`, `FacadeMethod`, `ActivityStreamOptions`, every request/result and nested response type, and every core-exported generated string-enum constant. You can import the core contract directly from `@lightninglabs/wavelength-react` instead of adding `@lightninglabs/wavelength-core` as a separate dependency. The engine itself is not built here: create one with `createWebWalletEngine` from [wavelength-web](/reference/wavelength-web/) or `createNativeWalletEngine` from [wavelength-react-native](/reference/wavelength-react-native/). Every hook on this page, including `useWalletPasskey`, must be called within a `WavelengthProvider` subtree; each throws if it is not.
## Provider
## WavelengthProvider
class
Context provider that hands a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) down to descendants. Wrap your application root (or any subtree) with this component so child hooks can access wallet state and operations.
```ts
function WavelengthProvider(props: WavelengthProviderProps): JSX.Element
```
| Parameter | Type | Description |
| ---------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `engine` | [`WalletEngine`](/reference/wavelength-core/#WalletEngine) | A WalletEngine from any transport, e.g. createWebWalletEngine() from wavelength-web or createNativeWalletEngine() from wavelength-react-native. Required; the provider throws synchronously if it is missing. |
| `children` | `ReactNode` | The component subtree that can call Wavelength SDK hooks. |
> **Note**
>
> **The provider owns nothing.** `WavelengthProvider` does not build, start, poll, or dispose anything: it publishes the engine you hand it to `useSyncExternalStore` subscribers. Every lifecycle behavior described below (sync polling, activity-driven refresh, `autoStart`, the `stopped` transition on an unsolicited runtime stop) is owned by the `WalletEngine` itself, so it also applies to a vanilla (non-React) consumer that drives the same engine directly. See [`WalletEngine`](/reference/wavelength-core/#WalletEngine) on the wavelength-core reference for the full behavior list.
>
> The provider never calls `engine.dispose()` on unmount. The code that constructed the engine (typically module scope, outside any component) owns its lifetime and is responsible for disposing it when the app is truly done with it.
## WavelengthProviderProps
type
Props for the `WavelengthProvider` component. `WavelengthProviderProps` is not itself an exported name; the props type is declared inline on the component’s destructured parameter. It is named and documented separately here only for readability.
```ts
type WavelengthProviderProps = {
children: ReactNode;
engine: WalletEngine;
};
```
## useWalletEngine
function
Returns the [`WalletEngine`](/reference/wavelength-core/#WalletEngine) from the nearest `WavelengthProvider`: the escape hatch for anything the granular hooks below do not cover (for example calling `engine.client` directly, or a method with no dedicated hook). Throws outside a provider.
```ts
function useWalletEngine(): WalletEngine
```
Returns[`WalletEngine`](/reference/wavelength-core/#WalletEngine)
The provider’s engine instance.
## useWallet
## useWallet
function
The application-shell hook: the lifecycle phase to route your top-level UI on, the last fatal runtime error, and the start/stop actions. There are no `pending` flags here: `phase === 'starting'` / `'stopping'` already encode them.
```ts
function useWallet(): {
phase: RuntimePhase;
error: Error | null;
start(config?: RuntimeConfig): Promise;
stop(): Promise;
};
```
| Parameter | Type | Description |
| ---------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`RuntimePhase`](/reference/wavelength-core/#RuntimePhase) | Current runtime/wallet lifecycle phase. See RuntimePhase on the wavelength-core reference; note it now includes 'restoring'. |
| `error` | `Error \| null` | The last fatal runtime-level error, or null. Set when the initial runtime load fails, when start()/stop() fails, or after the engine's background processes exhaust their failure budget (e.g. repeated sync-poll or background-refresh failures). |
| `start(config?)` | `(config?: RuntimeConfig) => Promise` | Starts the runtime. Falls back to the engine's configured config (the one passed to createWebWalletEngine/createNativeWalletEngine) when called with no argument; throws if neither exists. Resolves with the WalletInfo from startup. |
| `stop()` | `() => Promise` | Stops the runtime and clears the in-memory snapshot (info/balance/activity reset to null/null/\[]); persisted wallet data is untouched. |
Returns`{ phase, error, start, stop }`
The runtime lifecycle phase, last fatal error, and start/stop actions.
## Reading state
The five hooks below each subscribe to one slice of the engine’s snapshot via `useSyncExternalStore`, so a component re-renders only when the slice it reads actually changes. `useWalletInfo`, `useWalletBalance`, and `useWalletActivity` are plain selectors; `useWalletRecovery` and `useWalletLogs` also return an action (`acknowledge()` and `clear()` respectively) alongside their slice.
## useWalletInfo
function
The most recent complete wallet info, or `null` before the runtime reports it.
```ts
function useWalletInfo(): WalletInfo | null
```
Returns`WalletInfo | null`
The current `WalletInfo`, or `null` before the runtime reports it. Always a complete object: the engine refetches full info whenever the wallet comes up (create, unlock, passkey open, restore).
## useWalletBalance
function
The most recent wallet balance, or `null` before it is known.
```ts
function useWalletBalance(): Balance | null
```
Returns`Balance | null`
The current Balance, or null before known.
## useWalletActivity
function
The most recent activity entries, newest-first as returned by the daemon.
```ts
function useWalletActivity(): Entry[]
```
Returns`Entry[]`
Always the result of `list({ view: 'activity', pendingOnly: false })`; it does not reflect the vtxos or onchain views.
## useWalletRecovery
function
The background recovery status set by [`useWalletRestore`](#useWalletRestore) and its acknowledge action.
```ts
function useWalletRecovery(): {
recovery: RecoveryState;
acknowledge(): void;
};
```
| Parameter | Type | Description |
| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `recovery` | [`RecoveryState`](/reference/wavelength-core/#RecoveryState) | The background recovery status, a discriminated union on status: { status: 'idle' } \| { status: 'restoring' } \| { status: 'done', result: CreateWalletResult } \| { status: 'failed', error: Error }. failed covers both a background scan that errored on an already-usable wallet and a restore that failed before the wallet came up at all, including an untracked restore; read phase from useWallet() alongside it to tell the two apart (ready vs. needsWallet). See RecoveryState on the wavelength-core reference. Drive a "restoring your balance and history" banner off the ready case, or a restore-failed message on the onboarding screen off the needsWallet case. |
| `acknowledge()` | `() => void` | Resets recovery to { status: 'idle' }, e.g. after the user dismisses the banner. |
## useWalletLogs
function
The buffered runtime log tail and a clear action.
```ts
function useWalletLogs(): {
logs: WavelengthLogPayload[];
clear(): void;
};
```
| Parameter | Type | Description |
| --------- | ------------------------ | ------------------------------------------------------------------------------- |
| `logs` | `WavelengthLogPayload[]` | Bounded tail (up to 200 entries) of 'log' events from the runtime, newest last. |
| `clear()` | `() => void` | Clears the buffered log tail. |
## Mutations
The eight hooks below drive a wallet operation and carry hook-local, verb-prefixed `*Pending`/`*Error`/`*Data` state, all built on one shared convention:
> **Mutation convention: throw-and-capture**
>
> Every mutation hook exposes the same shape, with its fields prefixed by the verb it performs: an action function (e.g. `create`), `Pending: boolean`, `Error: Error | null`, `Data` (the last successful result, or `null`), and `reset(): void`. Calling the action clears the previous error/data, sets pending to `true`, awaits the underlying engine call, and on completion **both** updates the hook’s state **and** settles the returned promise the normal way: it resolves with the result (and sets the data field) or rejects with the failure (and sets the error field to that same `Error` instance). This is throw-and-capture: read the `*Error`/`*Data` fields for a declarative render, or `await`/`try`/`catch` the action’s return value for imperative flow, whichever fits the call site. Error fields are always `Error | null`, never a string. `reset()` clears the pending/error/data trio back to idle without calling anything.
>
> The verb prefix is deliberate: a component composing several of these hooks (e.g. `create` and `deposit` side by side) never needs destructure aliases to avoid name collisions. [`useWalletPasskey`](#useWalletPasskey)’s `create`/`open` fields follow the same prefixing pattern, but this is one place the prefix does not save you: `useWalletCreate` and `useWalletPasskey` both expose a `create` verb, so destructuring both in the same component collides on all four of `create`, `createPending`, `createError`, and `resetCreate`. Keep one as a namespaced object (e.g. `const passkey = useWalletPasskey(...)`) instead of destructuring both. Each hook’s return type is also exported as `UseWalletResult` (`UseWalletCreateResult`, `UseWalletRestoreResult`, `UseWalletUnlockResult`, `UseWalletDepositResult`, `UseWalletReceiveResult`, `UseWalletPrepareSendResult`, `UseWalletSendResult`, `UseWalletRefreshResult`, and the exit-hook result types documented in [Exiting Ark](#exiting-ark) below), for typing wrapper components around a hook without re-deriving its shape.
>
> A `PasskeyCancelledError` (the user dismissed the OS prompt) is the one exception: it rethrows without being recorded into the error field, since a dismissed prompt is not a failure to display.
## useWalletCreate
function
Creates a new wallet, with hook-local createPending/createError/createData state.
```ts
function useWalletCreate(): {
create(req: CreateWalletRequest): Promise;
createPending: boolean;
createError: Error | null;
createData: CreateWalletResult | null;
resetCreate(): void;
};
```
| Parameter | Type | Description |
| ------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `create(req)` | `(req: CreateWalletRequest) => Promise` | Creates a new wallet, refetches info, and kicks a background refresh. See createWallet on the wavelength-core reference. |
Returns`{ create, createPending, createError, createData, resetCreate }`
See the mutation convention above.
## useWalletRestore
function
Restores a wallet from a mnemonic, with hook-local restorePending/restoreError/restoreData state.
```ts
function useWalletRestore(): {
restore(req: RestoreWalletRequest): Promise;
restorePending: boolean;
restoreError: Error | null;
restoreData: WalletInfo | null;
resetRestore(): void;
};
```
| Parameter | Type | Description |
| -------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `restore(req)` | `(req: RestoreWalletRequest) => Promise` | Restores a wallet from req.mnemonic. See restoreWallet on the wavelength-core reference. |
Returns`{ restore, restorePending, restoreError, restoreData, resetRestore }`
See the mutation convention above, with one twist: `restore`’s promise (and therefore `restorePending`/`restoreError`) only covers getting the wallet up; it resolves as soon as the restored wallet is usable, not when the optional server-assisted recovery scan finishes, and it never rejects for a scan failure. Observe the scan, and a restore that fails before the wallet came up, separately through [`useWalletRecovery`](#useWalletRecovery): even a fire-and-forget `restore()` call whose promise is not awaited still surfaces a pre-usability failure there once `phase` falls back to `'needsWallet'`.
## useWalletUnlock
function
Unlocks an existing wallet, with hook-local unlockPending/unlockError/unlockData state.
```ts
function useWalletUnlock(): {
unlock(req: UnlockWalletRequest): Promise;
unlockPending: boolean;
unlockError: Error | null;
unlockData: UnlockWalletResult | null;
resetUnlock(): void;
};
```
| Parameter | Type | Description |
| ------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `unlock(req)` | `(req: UnlockWalletRequest) => Promise` | Unlocks an existing wallet, refetches info, and kicks a background refresh. See unlockWallet on the wavelength-core reference. |
Returns`{ unlock, unlockPending, unlockError, unlockData, resetUnlock }`
See the mutation convention above.
## useWalletDeposit
function
Requests an on-chain deposit address, with hook-local depositPending/depositError/depositData state.
```ts
function useWalletDeposit(): {
deposit(req?: DepositRequest): Promise;
depositPending: boolean;
depositError: Error | null;
depositData: DepositResult | null;
resetDeposit(): void;
};
```
| Parameter | Type | Description |
| --------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `deposit(req?)` | `(req?: DepositRequest) => Promise` | Requests an on-chain deposit address and kicks a background refresh. req is optional and defaults to {} when omitted. See deposit on the wavelength-core reference. |
Returns`{ deposit, depositPending, depositError, depositData, resetDeposit }`
See the mutation convention above.
## useWalletReceive
function
Requests a Lightning receive invoice, with hook-local receivePending/receiveError/receiveData state.
```ts
function useWalletReceive(): {
receive(req: ReceiveRequest): Promise;
receivePending: boolean;
receiveError: Error | null;
receiveData: ReceiveResult | null;
resetReceive(): void;
};
```
| Parameter | Type | Description |
| -------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `receive(req)` | `(req: ReceiveRequest) => Promise` | Requests a Lightning receive invoice and kicks a background refresh. See receive on the wavelength-core reference. |
Returns`{ receive, receivePending, receiveError, receiveData, resetReceive }`
See the mutation convention above.
## useWalletPrepareSend
function
Quotes a payment without dispatching it, with hook-local preparePending/prepareError/prepareData state.
```ts
function useWalletPrepareSend(): {
prepare(req: SendRequest): Promise;
preparePending: boolean;
prepareError: Error | null;
prepareData: PrepareSendResult | null;
resetPrepare(): void;
};
```
| Parameter | Type | Description |
| -------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `prepare(req)` | `(req: SendRequest) => Promise` | Quotes a payment without dispatching it; no refresh, since a quote moves no money. See prepareSend on the wavelength-core reference. |
Returns`{ prepare, preparePending, prepareError, prepareData, resetPrepare }`
See the mutation convention above. `prepareData` holds the latest quote for a review screen; pair it with [`useWalletSend`](#useWalletSend)’s `sendPrepared` to dispatch it.
## useWalletSend
function
Dispatches a payment: `send` is the one-shot path, `sendPrepared` confirms a quote from [`useWalletPrepareSend`](#useWalletPrepareSend). The two verbs are alternative dispatch paths for the same payment and share one sendPending/sendError/sendData slot.
```ts
function useWalletSend(): {
send(req: SendRequest): Promise;
sendPrepared(prepared: PrepareSendResult): Promise;
sendPending: boolean;
sendError: Error | null;
sendData: SendResult | null;
resetSend(): void;
};
```
| Parameter | Type | Description |
| ------------------------ | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `send(req)` | `(req: SendRequest) => Promise` | Quotes and dispatches a payment in one call, then kicks a background refresh. See send on the wavelength-core reference. |
| `sendPrepared(prepared)` | `(prepared: PrepareSendResult) => Promise` | Dispatches a quote returned by useWalletPrepareSend, then kicks a background refresh. See sendPrepared on the wavelength-core reference. |
Returns`{ send, sendPrepared, sendPending, sendError, sendData, resetSend }`
See the mutation convention above.
## useWalletRefresh
function
Re-fetches info, balance, and activity.
```ts
function useWalletRefresh(): {
refresh(): Promise;
refreshPending: boolean;
refreshError: Error | null;
resetRefresh(): void;
};
```
| Parameter | Type | Description |
| ----------- | --------------------- | -------------------------------------------------------------------------------------------------- |
| `refresh()` | `() => Promise` | Re-fetches info, balance, and activity concurrently. See refresh on the wavelength-core reference. |
Returns`{ refresh, refreshPending, refreshError, resetRefresh }`
The one mutation hook with no data field: `refresh()` resolves `void`, so there is nothing to hold. `refreshPending` tracks only this hook instance’s own calls, not the engine’s own background refreshes (the sync poll, the activity-driven settle reconcile, or the refresh kicked automatically after another mutation): those never flip it. Use it as what a pull-to-refresh spinner should show.
## Exiting Ark
Seven hooks cover the exit surface. `useWalletExit`, `useWalletExitPlan`, `useWalletSweep`, and [`useWalletList`](#useWalletList) are 1:1 wrappers around one [`WalletEngine`](/reference/wavelength-core/#WalletEngine) verb each, following the same mutation convention as the hooks above. [`useWalletExitBatch`](#useWalletExitBatch) is the orchestrator: it drives a full multi-outpoint exit through the funding-contention guards documented on [`exitBatch`](/reference/wavelength-core/#exitBatch) in the wavelength-core reference, and additionally exposes the batch’s progress events. [`useWalletExitStatus`](#useWalletExitStatus) and [`useWalletExits`](#useWalletExits) are read hooks instead of mutations: they fetch on mount (and on relevant changes) and expose a `refresh*` action rather than a `reset*` one, since there is no pending/error/data trio to clear back to idle, only a cached read to force fresh.
## useWalletExit
function
Starts a single exit for one outpoint, with hook-local exitPending/exitError/exitData state. A 1:1 wrapper around [`WalletEngine.exit()`](/reference/wavelength-core/#WalletEngine).
```ts
function useWalletExit(): {
exit(req: ExitRequest): Promise;
exitPending: boolean;
exitError: Error | null;
exitData: ExitResult | null;
resetExit(): void;
};
```
| Parameter | Type | Description |
| ----------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `exit(req)` | `(req: ExitRequest) => Promise` | Starts a single exit, cooperative or unilateral depending on req. Takes the full ExitRequest union; see exit on the wavelength-core reference. |
Returns`{ exit, exitPending, exitError, exitData, resetExit }`
See the mutation convention above.
## useWalletExitPlan
function
Previews unilateral-exit readiness and backing-wallet funding for a set of outpoints, with hook-local planPending/planError/planData state. A 1:1 wrapper around [`WalletEngine.getExitPlan()`](/reference/wavelength-core/#WalletEngine). Pair it with [`useWalletExitBatch`](#useWalletExitBatch) to act on a plan: `exitBatch` previews and re-plans internally, so this hook is for a standalone preview screen rather than a required prerequisite step before calling `exitBatch`.
```ts
function useWalletExitPlan(): {
plan(req: GetExitPlanRequest): Promise;
planPending: boolean;
planError: Error | null;
planData: GetExitPlanResult | null;
resetPlan(): void;
};
```
| Parameter | Type | Description |
| ----------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `plan(req)` | `(req: GetExitPlanRequest) => Promise` | Previews funding for the given outpoints without moving funds. See getExitPlan on the wavelength-core reference. |
Returns`{ plan, planPending, planError, planData, resetPlan }`
See the mutation convention above.
## useWalletSweep
function
Previews (`broadcast: false`) or broadcasts (`broadcast: true`) a sweep of the backing on-chain wallet, with hook-local sweepPending/sweepError/sweepData state. A 1:1 wrapper around [`WalletEngine.sweepWallet()`](/reference/wavelength-core/#WalletEngine). Call it with `broadcast: false` first to preview the fee and net amount, then again with `broadcast: true` once the user confirms; only the broadcasting call moves funds and kicks a background refresh.
```ts
function useWalletSweep(): {
sweep(req: SweepWalletRequest): Promise;
sweepPending: boolean;
sweepError: Error | null;
sweepData: SweepWalletResult | null;
resetSweep(): void;
};
```
| Parameter | Type | Description |
| ------------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `sweep(req)` | `(req: SweepWalletRequest) => Promise` | Previews or broadcasts the sweep, depending on req.broadcast. See sweepWallet on the wavelength-core reference. |
Returns`{ sweep, sweepPending, sweepError, sweepData, resetSweep }`
See the mutation convention above.
## useWalletExitBatch
function
The orchestrator: starts a batch of exits across multiple outpoints, handling the funding-contention guards internally. Unlike the other mutation hooks, it also exposes `exitBatchEvents`, the batch’s progress events in order for the current or last run, so a UI can render a live per-outpoint timeline instead of waiting for the whole batch to settle.
> **exitBatch starts exits and moves money**
>
> Read the hazard callout on [`exitBatch`](/reference/wavelength-core/#exitBatch) in the wavelength-core reference: this hook’s `exitBatch` action resolves once every exit is **started**, not completed, and a unilateral exit keeps running for hours or days afterward.
```ts
function useWalletExitBatch(): {
exitBatch(opts: ExitBatchOptions): Promise;
exitBatchPending: boolean;
exitBatchError: Error | null;
exitBatchData: ExitBatchResult | null;
exitBatchEvents: ExitBatchEvent[];
resetExitBatch(): void;
};
```
| Parameter | Type | Description |
| ----------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| `exitBatch(opts)` | `(opts: ExitBatchOptions) => Promise` | Starts the batch. See ExitBatchOptions on the wavelength-core reference for the cooperative/unilateral discriminated union. |
Returns`{ exitBatch, exitBatchPending, exitBatchError, exitBatchData, exitBatchEvents, resetExitBatch }`
The mutation convention above, plus exitBatchEvents: the ExitBatchEvent list for the current or last run, reset to empty each time exitBatch() is called or resetExitBatch() runs.
## useWalletExitStatus
function
Reads one exit’s status. Defaults to the cheap, coarse phase-only call; pass `{ detailed: true }` for recovery-tree progress, the CSV maturity countdown, and a fee breakdown, at the cost of a live round-trip. Fetches on mount and whenever `outpoint` or the options change.
```ts
function useWalletExitStatus(
outpoint: string,
opts?: { detailed?: boolean; pollMs?: number },
): {
status: ExitStatusResult | null;
statusPending: boolean;
statusError: Error | null;
refreshStatus(): Promise;
};
```
| Parameter | Type | Description |
| --------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | The VTXO outpoint whose exit status is read. |
| `opts.detailed` | `boolean` | Optional. Requests the detailed fields; leave it false for a coarse, cheaper status. |
| `opts.pollMs` | `number` | Optional. Polls at this interval, in milliseconds, while the component stays mounted. Polling is opt-in (omit it for a one-shot fetch); it keeps running with no visibility/focus gating (including in a backgrounded tab) and stops only on unmount, so there is no timer left running once the user navigates away. |
Returns`{ status, statusPending, statusError, refreshStatus }`
`status` is null before the first load resolves. Unlike the mutation hooks above, there is no reset action: refetch on demand with refreshStatus(), or change outpoint/opts to refetch automatically.
## useWalletExits
function
Reads the wallet-wide in-progress exit portfolio. Fetches on mount and whenever wallet activity changes, so a completed exit clears from the summary without a manual refresh.
```ts
function useWalletExits(): {
summary: ExitSummaryResult | null;
summaryPending: boolean;
summaryError: Error | null;
refreshSummary(): Promise;
};
```
Returns`{ summary, summaryPending, summaryError, refreshSummary }`
`summary` is null before the first load resolves. Like useWalletExitStatus, there is no reset action; call refreshSummary() to refetch on demand.
## useWalletList
function
Lists wallet activity, VTXOs, or on-chain outputs on demand, with hook-local listPending/listError/listData state. A 1:1 wrapper around [`WalletEngine.list()`](/reference/wavelength-core/#WalletEngine). Reach for it for an on-demand fetch, such as populating a VTXO picker before an exit; the always-fresh activity feed is already available from [`useWalletActivity()`](#useWalletActivity) without a manual fetch.
```ts
function useWalletList(): {
list(req: ListRequest): Promise;
listPending: boolean;
listError: Error | null;
listData: ListResult | null;
resetList(): void;
};
```
| Parameter | Type | Description |
| ----------- | ------------------------------------------- | -------------------------------------------------------------------- |
| `list(req)` | `(req: ListRequest) => Promise` | Lists the requested view. See list on the wavelength-core reference. |
Returns`{ list, listPending, listError, listData, resetList }`
See the mutation convention above.
## Passkeys
## useWalletPasskey
function
Drives a passkey ceremony and opens the wallet through the engine, refreshing engine state on success so `phase` advances automatically. The ceremony is injected, which keeps `wavelength-react` free of any transport dependency: in the browser, pass [`webPasskeyCeremony`](/reference/wavelength-web/#webPasskeyCeremony) from `wavelength-web`; the React Native transport supplies `createNativePasskeyCeremony` ([wavelength-react-native](/reference/wavelength-react-native/)). Creation and opening track separately (`create*`/`open*` fields below) because apps render them on different screens. Must be called within a `WavelengthProvider` subtree; throws otherwise.
```ts
function useWalletPasskey(ceremony: PasskeyCeremony): {
supported: boolean | null;
create(appName: string): Promise;
createPending: boolean;
createError: Error | null;
resetCreate(): void;
open(credentialId?: string): Promise;
openPending: boolean;
openError: Error | null;
resetOpen(): void;
};
```
| Parameter | Type | Description |
| ---------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `ceremony` | [`PasskeyCeremony`](/reference/wavelength-core/#PasskeyCeremony) | Platform ceremony implementation. In the browser, pass webPasskeyCeremony from wavelength-web. |
| Parameter | Type | Description |
| --------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `supported` | `boolean \| null` | Whether the environment supports passkey PRF. Starts null while ceremony.supportsPasskeyPrf() is in flight, then settles to true or false once it resolves; if that probe rejects, supported settles to false rather than throwing. Treat null as still-checking and gate passkey UI on it accordingly, not as a falsy unsupported state. The probe is memoized per ceremony instance, so warming it once at app boot means supported is usually already settled by the time a screen reads it. |
| `create(appName)` | `(appName: string) => Promise` | Registers a passkey and creates the wallet from it. Follows the same throw-and-capture convention as the mutation hooks above: it both resolves/rejects and updates createPending/createError/reset via resetCreate. |
| `createPending` | `boolean` | True while create() is in flight. |
| `createError` | `Error \| null` | The last error from create(), or null. A PasskeyCancelledError is never recorded here. |
| `resetCreate()` | `() => void` | Clears createPending/createError back to idle. |
| `open(credentialId?)` | `(credentialId?: string) => Promise` | Asserts a passkey (scoped to credentialId when provided, discoverable otherwise) and imports/unlocks the wallet. |
| `openPending` | `boolean` | True while open() is in flight. |
| `openError` | `Error \| null` | The last error from open(), or null. A PasskeyCancelledError is never recorded here. |
| `resetOpen()` | `() => void` | Clears openPending/openError back to idle. |
> **A cancelled ceremony still rejects**
>
> `create()` and `open()` reject with errors when they fail; a `PasskeyCancelledError` (user dismissed the OS prompt) is rethrown but never recorded into `createError`/`openError`. Always `await`/`catch` these calls, or read `createError`/`openError` for a declarative render.
## PasskeyWalletOutcome
type
Pairs the daemon-side open result with the credential id used in the ceremony, so the app can persist it and scope future unlocks to the same passkey. Returned by both `create` and `open`.
```ts
type PasskeyWalletOutcome = {
result: OpenWalletFromPasskeyResult;
credentialId: string;
};
```
| Parameter | Type | Description |
| -------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `result` | [`OpenWalletFromPasskeyResult`](/reference/wavelength-core/#OpenWalletFromPasskeyResult) | The result returned by opening the wallet from the passkey. See OpenWalletFromPasskeyResult on the wavelength-core reference. |
| `credentialId` | `string` | The credential id that produced the PRF output, for persistence and scoping future assertions. |
## PasskeyCeremony
type
The per-platform contract `useWalletPasskey` drives. It is defined in `wavelength-core` but documented here since this package is its primary consumer; the browser implementation, `webPasskeyCeremony`, lives in [`wavelength-web`](/reference/wavelength-web/#webPasskeyCeremony). The React Native transport supplies `createNativePasskeyCeremony` ([wavelength-react-native](/reference/wavelength-react-native/)) as its implementation of the same contract.
```ts
type WalletKind = 'passkey' | 'password';
type PasskeyAssertion = {
prfOutput: string;
credentialId: string;
};
type PasskeyCeremony = {
supportsPasskeyPrf(): Promise;
registerPasskeyWallet(appName: string): Promise;
assertPasskeyPrf(allowCredentialId?: string): Promise;
};
```
| Parameter | Type | Description |
| -------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `WalletKind` | `'passkey' \| 'password'` | Labels how a local wallet is unlocked. |
| `supportsPasskeyPrf()` | `() => Promise` | Resolves true when the platform supports passkey PRF. |
| `registerPasskeyWallet(appName)` | `(appName: string) => Promise` | Registers a new passkey and returns its assertion. appName becomes the WebAuthn relying-party name in the web implementation. |
| `assertPasskeyPrf(allowCredentialId?)` | `(allowCredentialId?: string) => Promise` | Asserts an existing passkey, optionally scoped to a credential id (discoverable when omitted). |
| `PasskeyAssertion.prfOutput` | `string` | The PRF output (hex) shared with the Go SDK. |
| `PasskeyAssertion.credentialId` | `string` | The credential id that produced the PRF output. |
## Re-exported from wavelength-core
`@lightninglabs/wavelength-react` re-exports the complete `@lightninglabs/wavelength-core` surface. That includes `WavelengthClient`, `WalletEngine`, `createWalletEngine`, `FacadeMethod`, `ActivityStreamOptions`, `RuntimeConfig`, `WalletInfo`, `WalletStatus`, `Balance`, `Entry`, the complete activity/exit/send/list type families, all named nested response types, and all generated string-enum constants. `RuntimePhase`, `WalletState`, `RecoveryState`, `PasskeyCancelledError`, and `toError` are included too. `defaultConfig` comes from the transport packages (`@lightninglabs/wavelength-web` and `@lightninglabs/wavelength-react-native`). The core contract and enum constant families are listed on the [wavelength-core reference](/reference/wavelength-core/).
## See also
- [wavelength-core reference](/reference/wavelength-core/) for `WalletEngine`, `WavelengthClient`, `RuntimeConfig`, `WalletInfo`, `Balance`, `Entry`, `RuntimePhase`, and `WalletState`.
- [wavelength-web reference](/reference/wavelength-web/) for `createWebWalletEngine()` and `webPasskeyCeremony`, the browser transport and passkey ceremony used with this package.
- [wavelength-react-native reference](/reference/wavelength-react-native/) for `createNativeWalletEngine()` and `createNativePasskeyCeremony()`, the React Native transport and passkey ceremony used with this package.
---
Source: https://wavelength.lightning.engineering/reference/wavelength-web.md
# wavelength-web
Browser SDK factory, configuration, and passkey ceremony API for Wavelength web apps.
`@lightninglabs/wavelength-web` is the browser/wasm transport for the Wavelength SDK: it runs the embedded daemon as WebAssembly, either in a dedicated Web Worker (the default) or on the page’s main thread, and exposes it as a standard [`WavelengthClient`](/reference/wavelength-core/#WavelengthClient). `createWebWalletEngine()` wraps that client in a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) in one call, which is the entry point most apps want. It also ships the browser (WebAuthn/PRF) passkey ceremony.
> **One import for the client and every type**
>
> This package re-exports the entire [`@lightninglabs/wavelength-core`](/reference/wavelength-core/) public surface (`export * from '@lightninglabs/wavelength-core'`). You only need to import from `@lightninglabs/wavelength-web` to get the client factory, every request/result and nested response type, every core-exported generated string-enum constant, and `defaultConfig()`. This includes `FacadeMethod`, `ActivityStreamOptions`, `EntryProgress`, `CreditPreview`, and the complete activity, list, send, and exit type families. There is no need to also depend on `@lightninglabs/wavelength-core` directly.
## Configuration
## defaultConfig
function
Returns a ready-to-use `RuntimeConfig` for a network, preloaded with the canonical public REST gateway endpoints the web transport dials, merged with any overrides. This is a shallow merge of top-level `RuntimeConfig` fields. Pass overrides to set `dataDir` or point at your own infrastructure.
```ts
function defaultConfig(
network: PresetNetwork,
overrides?: Partial,
): RuntimeConfig
```
| Parameter | Type | Description |
| ----------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `network` | `PresetNetwork` | The Bitcoin network to run against; mainnet and regtest are excluded because they have no preset. Use signet for development and testing. |
| `overrides` | `Partial` | Optional. Fields that override the network preset's defaults. Default: {}. |
Returns[`RuntimeConfig`](/reference/wavelength-core/#RuntimeConfig)
A complete configuration object for `client.start()` or the engine factory’s `config`.
```ts
import { defaultConfig } from '@lightninglabs/wavelength-web';
const config = defaultConfig('signet', { dataDir: 'my-wallet' });
```
`'mainnet'` and `'regtest'` are not accepted: mainnet has no public deployment yet, and regtest’s local ports vary per development environment. Build their `RuntimeConfig` by hand, mainnet with your own gateway URLs and `allowMainnet`, regtest with local URLs and the insecure-transport flags. On web, `arkServerAddress` and `swapServerAddress` are REST URLs, and `walletEsploraUrl` is an HTTP Esplora endpoint. The web transport rejects `arkServerTlsCertPath`. It accepts `swapServerTlsCertPath` only when `disableSwaps: true`, which suppresses every swap field. The per-network endpoint values are on the [wavelength-core reference](/reference/wavelength-core/#Network).
## Creating a client
## createWebClient
function
Creates and returns a `WavelengthClient` that runs the embedded daemon in the browser. Call once at application startup, then wrap it with [`createWalletEngine`](/reference/wavelength-core/#createWalletEngine) from `@lightninglabs/wavelength-core` (or use [`createWebWalletEngine`](#creating-an-engine) below to do both in one call) or call `start(defaultConfig(network))` directly. Defaults to the Web Worker transport ([`WorkerWavelengthClient`](#transports)); pass `runtimeThread: 'main'` to run on the page’s main thread instead.
```ts
function createWebClient(
options?: WebClientOptions,
): WavelengthClient
```
| Parameter | Type | Description |
| --------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `options` | [`WebClientOptions`](/reference/wavelength-web/#WebClientOptions) | Optional browser-specific settings. Defaults to worker-thread mode with page-relative runtime assets. |
Returns[`WavelengthClient`](/reference/wavelength-core/#WavelengthClient)
A live client instance. Call `ready()` to wait for the WASM runtime to load, then `start(config)` to boot the daemon.
## WebClientOptions
type
Browser-specific options for `createWebClient`. The config passed to `client.start()` is separate: use `defaultConfig(network)` from `@lightninglabs/wavelength-web`.
```ts
type WebClientOptions = {
workerURL?: string;
runtimeBaseUrl?: string;
runtimeThread?: RuntimeThread;
debug?: boolean;
};
```
| Parameter | Type | Description |
| ---------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `workerURL` | `string` | Overrides the worker entry point. By default the worker client spawns the worker its bundler emits from new URL("./wavewalletdk-worker.js", import.meta.url); pass this to point at a custom-hosted copy instead. runtimeBaseUrl is still forwarded to the worker even when this is set. Default: bundler-emitted worker URL. |
| `runtimeBaseUrl` | `string` | Base URL the daemon runtime binaries (wavewalletdk.wasm.gz, wasm\_exec.js, sqlite-\*.js) are resolved against. Unset resolves relative to the document: worker mode computes the document base URL and forwards it to the worker so it resolves page-relative just like main-thread mode. Default: unset (page-relative). |
| `runtimeThread` | [`RuntimeThread`](/reference/wavelength-web/#RuntimeThread) | Which thread runs the WASM runtime. 'main' produces a MainThreadWavelengthClient that blocks rendering while busy; use it only when Worker support is unavailable. Default: 'worker'. |
| `debug` | `boolean` | Logs every RPC request and response payload to the console. Payloads can include addresses and amounts, so treat this as a development-only aid and never enable it in a shipped app. Main-thread mode logs facade calls directly; worker mode forwards the flag into the worker so it can log there too. Default: false. |
## RuntimeThread
type
Selects which thread the wasm runtime runs on: `'worker'` (default) in a dedicated Web Worker that keeps the UI thread free, or `'main'` on the page’s main thread as an escape hatch (for example, environments without Worker support).
```ts
type RuntimeThread = 'main' | 'worker';
```
## Creating an engine
## createWebWalletEngine
function
Creates a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) over the browser/wasm transport: the one-call setup for a web app. It builds the client with `createWebClient()` internally, so you only need this one factory. Pass the result to `WavelengthProvider` from [wavelength-react](/reference/wavelength-react/), or drive it directly without React.
```ts
function createWebWalletEngine(
options?: WebWalletEngineOptions,
): WalletEngine
```
| Parameter | Type | Description |
| --------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `options` | [`WebWalletEngineOptions`](/reference/wavelength-web/#WebWalletEngineOptions) | Optional. The same browser-specific settings as createWebClient, plus the engine's config and autoStart. |
Returns[`WalletEngine`](/reference/wavelength-core/#WalletEngine)
A live engine driving a freshly built web client. See [`WalletEngine`](/reference/wavelength-core/#WalletEngine) for its full behavior, including the background processes it runs automatically.
**`WebWalletEngineOptions`**
The browser-specific settings from `WebClientOptions` combined with [`WalletEngineOptions`](/reference/wavelength-core/#createWalletEngine)’s config/autoStart union (minus `client`, which this factory builds for you): the types require a config when autoStart is enabled, so `autoStart: true` without `config` is a compile error.
```ts
type WebWalletEngineOptions = WebClientOptions &
(
| { config: RuntimeConfig; autoStart: true }
| { config?: RuntimeConfig; autoStart?: false }
);
```
| Parameter | Type | Description |
| ---------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `workerURL` | `string` | Same as WebClientOptions.workerURL. |
| `runtimeBaseUrl` | `string` | Same as WebClientOptions.runtimeBaseUrl. |
| `runtimeThread` | [`RuntimeThread`](/reference/wavelength-web/#RuntimeThread) | Same as WebClientOptions.runtimeThread. |
| `debug` | `boolean` | Same as WebClientOptions.debug. |
| `config` | [`RuntimeConfig`](/reference/wavelength-core/#RuntimeConfig) | Required when autoStart is true. Default runtime config used by autoStart and by the engine's start() when called with no argument. |
| `autoStart` | `true \| false` | Optional. Start the runtime automatically once it is ready. Setting it to true requires config. |
```tsx
import { createWebWalletEngine, defaultConfig } from '@lightninglabs/wavelength-web';
import { WavelengthProvider } from '@lightninglabs/wavelength-react';
const engine = createWebWalletEngine({
runtimeBaseUrl: 'https://your-host/wavewalletdk/',
config: defaultConfig('signet'),
autoStart: true,
});
export function Root() {
return {/* your app */} ;
}
```
## Transports
## MainThreadWavelengthClient
class
Runs the wasm runtime on the page’s main thread. It implements [`WavelengthClient`](/reference/wavelength-core/#WavelengthClient) directly and is the escape hatch for environments without Web Worker support (or where main-thread execution is preferred for another reason). Unlike worker mode it blocks rendering while the runtime is busy.
```ts
class MainThreadWavelengthClient implements WavelengthClient {
constructor(options?: WebClientOptions);
}
```
| Parameter | Type | Description |
| --------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `options` | [`WebClientOptions`](/reference/wavelength-web/#WebClientOptions) | Same options accepted by createWebClient. runtimeThread is ignored here since the constructor already picks the main thread. |
Prefer `createWebClient({ runtimeThread: 'main' })` over constructing this class directly; only reach for the class import when you need to branch construction logic yourself (for example, feature-detecting Worker support at runtime).
`WorkerWavelengthClient` is the default transport returned by `createWebClient()` (when `runtimeThread` is unset or `'worker'`). It is not exported directly, select it via `createWebClient()`. Worker mode requires a daemon that stores the encrypted seed in OPFS rather than `localStorage` (which is window-only and unavailable inside a Worker); the daemon shipped with this package already does this. See the [browser support guide](/web/support/browser-support/) for which browsers provide OPFS and the other APIs worker mode depends on.
## Runtime assets and hosting
## RUNTIME\_ASSETS
const
The canonical set of daemon runtime binaries the wasm client loads at runtime, resolved against `runtimeBaseUrl`. `RUNTIME_ASSET_FILES` is just `Object.values(RUNTIME_ASSETS)`. This set is built by wavelength (`make wasm-wallet`) and version-locked to the embedded wavelength daemon, so the files must be hosted side by side at one base URL.
```ts
const RUNTIME_ASSETS: {
wasm: string;
wasmGz: string;
wasmExec: string;
sqliteBridge: string;
sqliteWorker: string;
sqlite: string;
sqliteWasm: string;
sqliteOpfsProxy: string;
};
```
The values are fixed filenames, not just string-typed placeholders:
| Key | Filename |
| ----------------- | ----------------------------- |
| `wasm` | `wavewalletdk.wasm` |
| `wasmGz` | `wavewalletdk.wasm.gz` |
| `wasmExec` | `wasm_exec.js` |
| `sqliteBridge` | `sqlite-bridge.js` |
| `sqliteWorker` | `sqlite-worker.js` |
| `sqlite` | `sqlite3.js` |
| `sqliteWasm` | `sqlite3.wasm` |
| `sqliteOpfsProxy` | `sqlite3-opfs-async-proxy.js` |
## RUNTIME\_ASSET\_FILES
const
Flat list of every runtime binary that must be hosted together at `runtimeBaseUrl`: `wavewalletdk.wasm`, `wavewalletdk.wasm.gz`, `wasm_exec.js`, `sqlite-bridge.js`, `sqlite-worker.js`, `sqlite3.js`, `sqlite3.wasm`, `sqlite3-opfs-async-proxy.js` (that is, `Object.values(RUNTIME_ASSETS)`). Host these files and point `runtimeBaseUrl` at them before calling `client.ready()`.
```ts
const RUNTIME_ASSET_FILES: readonly string[];
```
## RUNTIME\_MANIFEST\_VERSION
const
The daemon build this SDK release is paired with: the generated types, the client methods, and the runtime asset set all come from this one daemon revision. Hosted asset sets live in a directory named after this value, so a version bump changes every asset URL and browsers can never serve a stale cached runtime. Defined in (and re-exported from) `@lightninglabs/wavelength-core`.
```ts
const RUNTIME_MANIFEST_VERSION: string;
```
### Asset resolution and loading
`runtimeBaseUrl` controls where each file in `RUNTIME_ASSETS` is fetched from:
- **Trailing slash:** a base URL is normalized by appending a trailing slash if missing before resolving each asset name against it, so `https://assets.example.com/wavewalletdk` and `https://assets.example.com/wavewalletdk/` behave the same.
- **Unset base:** with no `runtimeBaseUrl`, the bare filename is used and resolves relative to the document. In worker mode the worker cannot resolve page-relative paths itself, so the client computes the document’s base URL (`document.baseURI`) on the main thread and forwards it to the worker as part of its `$init` message, keeping worker mode and main-thread mode page-relative in the same way.
- **Compressed-first wasm loading:** the client prefers the gzip-compressed `wavewalletdk.wasm.gz` binary, inflating it through a `DecompressionStream` and instantiating the resulting bytes, when the browser supports `DecompressionStream`. If that path fails (or the API is unavailable), it falls back to fetching the uncompressed `wavewalletdk.wasm` and instantiating it via `WebAssembly.instantiateStreaming`.
- **Streaming-to-ArrayBuffer fallback:** `instantiateStreaming` requires the server to serve the wasm response with an `application/wasm` content type. If the host is misconfigured and omits it, streaming instantiation throws and the client retries by reading the same response into an `ArrayBuffer` and calling `WebAssembly.instantiate` on the bytes directly, so a misconfigured MIME type does not break self-hosted runtimes.
- **Worker entry resolution:** the worker script itself (unlike the daemon binaries above) ships inside `@lightninglabs/wavelength-web` and is emitted by your bundler via `new URL('../wavewalletdk-worker.js', import.meta.url)`, so it versions with the SDK and needs no separate hosting. `runtimeBaseUrl` is still forwarded to the worker (via its `$init` message) even when you override the worker entry point with `workerURL`, since the two settings are independent: `workerURL` only changes where the worker script itself loads from.
See the [hosting runtime assets guide](/web/get-started/hosting-runtime-assets/) for a complete self-hosting walkthrough.
## Passkeys (browser)
## webPasskeyCeremony
const
Browser (WebAuthn/PRF) implementation of the [`PasskeyCeremony`](/reference/wavelength-core/#PasskeyCeremony) contract. It is literally the three standalone functions below grouped into one object; pass it to `useWalletPasskey` from wavelength-react, or call the functions directly.
```ts
const webPasskeyCeremony: PasskeyCeremony;
```
```ts
const webPasskeyCeremony: PasskeyCeremony = {
supportsPasskeyPrf,
registerPasskeyWallet,
assertPasskeyPrf,
};
```
## supportsPasskeyPrf
function
Reports whether a user-verifying platform authenticator is available, which is a prerequisite for WebAuthn PRF.
```ts
function supportsPasskeyPrf(): Promise;
```
Returns`Promise`
`true` when a platform authenticator that can perform user verification is available.
> **Not a guarantee**
>
> `supportsPasskeyPrf() === true` does not guarantee the PRF ceremony will succeed. There is no synchronous PRF-detection API, so a browser can report `true` here yet still fail to return a PRF result during `registerPasskeyWallet` or `assertPasskeyPrf`. Always handle rejection from those calls even after a `true` check.
## registerPasskeyWallet
function
Creates a new platform passkey and returns its PRF output and credential id. Some browsers do not surface the PRF result from `create()`, so this falls back to an assertion scoped to the just-created credential to read it reliably without prompting a chooser.
```ts
function registerPasskeyWallet(
appName: string,
): Promise;
```
| Parameter | Type | Description |
| --------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `appName` | `string` | Used as both the WebAuthn relying-party name (rp.name) and the account display name shown by the platform passkey UI. |
Returns`Promise`
The new credential’s PRF output and credential id. Rejects if registration is cancelled.
## assertPasskeyPrf
function
Authenticates with a passkey and returns its PRF output plus the credential id used.
```ts
function assertPasskeyPrf(
allowCredentialId?: string,
): Promise;
```
| Parameter | Type | Description |
| ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowCredentialId` | `string` | Optional. When set, the assertion is scoped to that one credential (base64url id from a prior PasskeyAssertion.credentialId) so the OS unlocks it directly without a chooser. When omitted, the assertion is discoverable (an empty allowCredentials list) so a synced passkey can be offered even on a device that has never seen this wallet. |
Returns`Promise`
The asserted credential’s PRF output and credential id. Rejects if authentication is cancelled.
## PasskeyAssertion
type
Result of a passkey registration or assertion ceremony. Defined in [`@lightninglabs/wavelength-core`](/reference/wavelength-core/#PasskeyAssertion) and surfaced here through the re-export (see the callout at the top of this page); shown in full because it is the return type of the two functions above.
```ts
type PasskeyAssertion = {
prfOutput: string;
credentialId: string;
};
```
| Parameter | Type | Description |
| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prfOutput` | `string` | The WebAuthn PRF extension output, as a lower-case hex string. |
| `credentialId` | `string` | The base64url-encoded WebAuthn credential id. Safe to persist and pass back into assertPasskeyPrf(allowCredentialId) to scope a later assertion to this credential. |
**Reproducibility.** Both functions evaluate the PRF extension against a fixed salt, `SHA-256("wavewalletdk-passkey:v1")`, a fixed namespace chosen client-side. Using the same fixed salt on every device/platform is what makes the derived wallet reproducible: asserting the same passkey anywhere yields the same `prfOutput`, and therefore the same derived wallet.
**Discoverable vs. scoped assertions.** `assertPasskeyPrf()` called with no argument performs a *discoverable* assertion (empty `allowCredentials`), letting the platform’s passkey chooser surface any matching synced passkey, including on a device that has never seen this wallet before. Passing `allowCredentialId` instead performs a *scoped* assertion limited to that one credential, which lets the OS unlock it directly without showing a chooser.
## Re-exported from wavelength-core
Types like `SendRequest` and `ReceiveRequest` referenced by [`WavelengthClient`](/reference/wavelength-core/#WavelengthClient) methods (`send`, `prepareSend`, `receive`, and so on) originate in [`@lightninglabs/wavelength-core`](/reference/wavelength-core/) and are available here only because `wavelength-web` re-exports the entire core package (see the callout at the top of this page). The `.d.ts` filename shown in each signature below is illustrative of the shape, not of which package physically declares the type.
## SendRequest
type
Parameters accepted by `prepareSend()` and `send()`. `SendRequest` is a discriminated union: provide `invoice` for a Lightning send, or `onchainAddress` for an on-chain send. The two branches are mutually exclusive; `sweepAll` exists only on the on-chain branch.
```ts
type SendRequest =
| {
invoice: string;
amountSat?: number;
note?: string;
maxFeeSat?: number;
}
| {
onchainAddress: string;
amountSat?: number;
sweepAll?: boolean;
note?: string;
maxFeeSat?: number;
};
```
| Parameter | Type | Description |
| ---------------- | --------- | ---------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | BOLT11 lightning invoice to pay. Mutually exclusive with onchainAddress. |
| `onchainAddress` | `string` | Bech32 Bitcoin address for an on-chain send. Mutually exclusive with invoice. |
| `amountSat` | `number` | Required at runtime for on-chain sends unless sweepAll is true; the TypeScript type does not enforce this. |
| `note` | `string` | Optional memo stored with the activity entry. |
| `maxFeeSat` | `number` | Optional fee ceiling for the send. |
| `sweepAll` | `boolean` | Drain every live VTXO to onchainAddress. Only present on the on-chain branch. |
## ReceiveRequest
type
Parameters accepted by `receive()`.
```ts
type ReceiveRequest = {
amountSat: number;
memo?: string;
};
```
| Parameter | Type | Description |
| ----------- | -------- | --------------------------------------------- |
| `amountSat` | `number` | Invoice amount in satoshis. |
| `memo` | `string` | Optional description embedded in the invoice. |
## See also
- [wavelength-core reference](/reference/wavelength-core/) for `WavelengthClient`, `WalletEngine`, `RuntimeConfig`, `WalletInfo`, `Balance`, `Entry`, and every other re-exported type.
- [wavelength-react reference](/reference/wavelength-react/) for `WavelengthProvider` and the hooks that consume an engine created here.
- [Hosting runtime assets](/web/get-started/hosting-runtime-assets/) for a full self-hosting walkthrough of the files listed above.
---
Source: https://wavelength.lightning.engineering/reference/wavelength-react-native.md
# wavelength-react-native
React Native transport factory, native passkey ceremony, and data-directory API.
`@lightninglabs/wavelength-react-native` is the React Native transport for the Wavelength SDK: the daemon compiled into the app binary via gomobile bindings, exposed as a standard [`WavelengthClient`](/reference/wavelength-core/#WavelengthClient). [`createNativeWalletEngine()`](#createNativeWalletEngine) wraps that client in a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) in one call, which is the entry point most apps want. It also ships the native (Android Credential Manager / iOS AuthenticationServices) passkey ceremony.
> **One import for the client and every type**
>
> This package re-exports the entire [`@lightninglabs/wavelength-core`](/reference/wavelength-core/) public surface (`export * from '@lightninglabs/wavelength-core'`). You only need to import from `@lightninglabs/wavelength-react-native` to get the client factory, every request/result and nested response type, every core-exported generated string-enum constant, and `defaultConfig()`. This includes `FacadeMethod`, `ActivityStreamOptions`, `EntryProgress`, `CreditPreview`, and the complete activity, list, send, and exit type families. There is no need to also depend on `@lightninglabs/wavelength-core` directly.
## Configuration
## defaultConfig
function
Returns a ready-to-use `RuntimeConfig` for a network, preloaded with the canonical public gRPC host:port addresses the native transport dials, merged with any overrides. This is a shallow merge of top-level `RuntimeConfig` fields. Pass overrides to set `dataDir` or point at your own infrastructure.
```ts
function defaultConfig(
network: PresetNetwork,
overrides?: Partial,
): RuntimeConfig
```
| Parameter | Type | Description |
| ----------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `network` | `PresetNetwork` | The Bitcoin network to run against; mainnet and regtest are excluded because they have no preset. Use signet for development and testing. |
| `overrides` | `Partial` | Optional. Fields that override the network preset's defaults. Default: {}. |
Returns[`RuntimeConfig`](/reference/wavelength-core/#RuntimeConfig)
A complete configuration object for `client.start()` or the engine factory’s `config`.
```ts
import { defaultConfig } from '@lightninglabs/wavelength-react-native';
const config = defaultConfig('signet', { dataDir: '/wallet' });
```
`'mainnet'` and `'regtest'` are not accepted: mainnet has no public deployment yet, and regtest’s local ports vary per development environment. Build their `RuntimeConfig` by hand, mainnet with your own gRPC addresses and `allowMainnet`, regtest with local addresses and the insecure-transport flags. `arkServerAddress` and `swapServerAddress` are gRPC `host:port` values on native, while `walletEsploraUrl` remains an HTTP Esplora endpoint. The per-network endpoint values are on the [wavelength-core reference](/reference/wavelength-core/#Network).
## Creating a client
## createNativeClient
function
Creates and returns a `WavelengthClient` backed by the React Native transport: the daemon compiled into the app via the gomobile bindings. Takes no options in this release; the signature reserves an options object for future use. Activity arrives as typed `activity` events, re-emitted here from native `wavelengthActivity` device events.
```ts
function createNativeClient(): WavelengthClient
```
Returns[`WavelengthClient`](/reference/wavelength-core/#WavelengthClient)
A live client instance. Call `ready()` (resolves immediately, since the runtime is already compiled in) then `start(config)` to boot the daemon.
## createNativeWalletEngine
function
Creates a [`WalletEngine`](/reference/wavelength-core/#WalletEngine) over the React Native transport: the one-call setup for an RN app. It builds the client with `createNativeClient()` internally, so you only need this one factory. Pass the result to `WavelengthProvider` from [wavelength-react](/reference/wavelength-react/).
```ts
function createNativeWalletEngine(
options?: NativeWalletEngineOptions,
): WalletEngine
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `options` | [`NativeWalletEngineOptions`](/reference/wavelength-react-native/#NativeWalletEngineOptions) | Optional. The engine's config and autoStart; createNativeClient() itself takes no options today. |
Returns[`WalletEngine`](/reference/wavelength-core/#WalletEngine)
A live engine driving a freshly built native client. See [`WalletEngine`](/reference/wavelength-core/#WalletEngine) for its full behavior, including the background processes it runs automatically.
**`NativeWalletEngineOptions`**
[`WalletEngineOptions`](/reference/wavelength-core/#createWalletEngine)’s config/autoStart union minus `client`, which this factory builds for you: the types require a config when autoStart is enabled, so `autoStart: true` without `config` is a compile error.
```ts
type NativeWalletEngineOptions =
| { config: RuntimeConfig; autoStart: true }
| { config?: RuntimeConfig; autoStart?: false };
```
| Parameter | Type | Description |
| ----------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `config` | [`RuntimeConfig`](/reference/wavelength-core/#RuntimeConfig) | Required when autoStart is true. Default runtime config used by autoStart and by the engine's start() when called with no argument. |
| `autoStart` | `true \| false` | Optional. Start the runtime automatically once it is ready. Setting it to true requires config. |
```tsx
import { createNativeWalletEngine, defaultConfig } from '@lightninglabs/wavelength-react-native';
import { WavelengthProvider } from '@lightninglabs/wavelength-react';
const engine = createNativeWalletEngine({
config: defaultConfig('signet'),
autoStart: true,
});
export default function Root() {
return {/* your app */} ;
}
```
## Passkeys (native)
## createNativePasskeyCeremony
function
Creates the native implementation of the [`PasskeyCeremony`](/reference/wavelength-core/#PasskeyCeremony) contract (Android Credential Manager, iOS AuthenticationServices). Pass it to `useWalletPasskey` from wavelength-react, or call its methods directly.
```ts
function createNativePasskeyCeremony(
options: NativePasskeyCeremonyOptions,
): PasskeyCeremony
```
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `options` | [`NativePasskeyCeremonyOptions`](/reference/wavelength-react-native/#NativePasskeyCeremonyOptions) | Native-specific ceremony configuration. |
Returns[`PasskeyCeremony`](/reference/wavelength-core/#PasskeyCeremony)
The native passkey ceremony. Its `supportsPasskeyPrf()` method is what backs `useWalletPasskey`’s `supported` flag.
Requires the relying-party domain to be associated with your app: `assetlinks.json` on Android, an Associated Domains entitlement plus `apple-app-site-association` on iOS. See the [passkey setup guide](/react-native/get-started/passkey-setup/) for the full walkthrough.
> **iOS support is experimental**
>
> iOS passkey support needs iOS 18 or newer at runtime and is experimental in this release.
## NativePasskeyCeremonyOptions
type
Options for `createNativePasskeyCeremony`.
```ts
type NativePasskeyCeremonyOptions = {
rpId: string;
};
```
| Parameter | Type | Description |
| --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `rpId` | `string` | The WebAuthn relying-party id: the domain whose assetlinks.json (Android) and apple-app-site-association (iOS) vouch for this app. Native apps have no window\.location, so the rpId is explicit configuration. |
## Data directory
## getDefaultDataDir
function
Resolves the platform default wallet data directory: the same directory `createNativeClient`’s client uses when `RuntimeConfig.dataDir` is unset. Exposed for app-level data management (show the storage location, back it up, or wipe it).
```ts
function getDefaultDataDir(): Promise;
```
Returns`Promise`
A plain absolute filesystem path, with no URI scheme. Not guaranteed to exist until the runtime has started.
## See also
- [wavelength-core reference](/reference/wavelength-core/) for `WavelengthClient`, `WalletEngine`, `RuntimeConfig`, `WalletInfo`, `Balance`, `Entry`, and every other re-exported type.
- [wavelength-react reference](/reference/wavelength-react/) for `WavelengthProvider`, `useWalletPasskey`, and the other hooks that consume an engine created here.
- [Passkey setup](/react-native/get-started/passkey-setup/) for the Android and iOS domain-association walkthrough required by `createNativePasskeyCeremony`.
---
Source: https://wavelength.lightning.engineering/glossary.md
# Glossary
### Ark
Ark is the shared off-chain protocol layer Wavelength builds on. Users hold VTXOs (virtual UTXOs) that settle on-chain in batched rounds, so payments are instant and cheap while remaining self-custodial, and users can always exit to on-chain Bitcoin unilaterally. This underpins boarding, rounds, VTXOs, and leaving Ark, which have their own entries.
### Batch swap
A batch swap refreshes VTXOs that are nearing expiry by forfeiting them in the current round in exchange for new VTXOs in a fresh batch. The new outputs reset the batch lifetime clock, so you can keep funds off-chain without returning to the Bitcoin base layer. Wavelength may perform batch swaps automatically as part of routine wallet maintenance. See [Balances & VTXOs](/concepts/balances-and-vtxos/) for how VTXO expiry fits into the lifecycle.
### Boarding
Boarding is how you enter Ark from the Bitcoin base layer. You spend an on-chain UTXO as an input to the operator’s next batch transaction and receive one or more VTXOs in return. Once boarding completes, your funds live off-chain and can be sent, swapped, or withdrawn through Ark operations. See [Balances & VTXOs](/concepts/balances-and-vtxos/) for the full boarding-to-VTXO flow.
### Boarding address
A boarding address is an on-chain Bitcoin address your wallet derives for deposits that will board into Ark. Send base-layer bitcoin to this address; Wavelength tracks the deposit and boards it at the next round tick (typically on the order of \~90 seconds). Use `wallet.deposit()` as described in [Get a deposit address](/guides/get-a-deposit-address/).
### BOLT 11
BOLT 11 is the standard Lightning invoice format. Every Lightning payment Wavelength sends or receives is a BOLT 11 invoice, so the wallet interoperates with any Lightning wallet, exchange, or app. See the [Lightning receive](#lightning-receive) and [Lightning send](#lightning-send) entries for how Lightning sends and receives are handled as swaps.
### Cooperative leave
Cooperative leave is the normal way to withdraw Ark funds to a standard on-chain Bitcoin address. You forfeit your VTXOs in an upcoming round and the operator includes a direct on-chain output paying your chosen destination in the batch transaction. It is faster and cheaper than unilateral exit because both you and the operator sign cooperatively. See [Leaving Ark](/concepts/leaving-ark/).
### Esplora
Esplora is a REST API for querying Bitcoin chain data and UTXOs. Wavelength’s `lwwallet` backend uses an Esplora-compatible HTTP endpoint (configured as `walletEsploraUrl`) to watch boarding deposits, fee estimates, and on-chain activity. The endpoint must implement the Esplora `/address/:addr/utxo` API (electrs or Blockstream-compatible) and is set via the `walletEsploraUrl` field on `RuntimeConfig`. See [Networks & config](/concepts/networks-and-config/) and [System architecture](/introduction/system-architecture/).
### Lightning receive
A Lightning receive brings value into your Ark wallet over Lightning through an atomic swap. When you create a Lightning invoice, the swap server and operator set up a vHTLC; once a payer settles the invoice, you claim the locked funds as Ark balance. See [Lightning payments = swaps](/concepts/lightning-payments-are-swaps/) and [Receive a Lightning payment](/guides/receive-a-lightning-payment/).
### Lightning send
A Lightning send moves value out of your Ark wallet over Lightning through an atomic swap. You fund a vHTLC from your Ark balance; the swap server pays the destination invoice and claims the vHTLC once Lightning settlement reveals the preimage. See [Lightning payments = swaps](/concepts/lightning-payments-are-swaps/) and [Send a payment](/guides/send-a-payment/).
### Mailbox
The mailbox is the operator’s asynchronous message channel for events that the wallet cannot pull synchronously over a simple RPC. Swap notifications, round updates, and similar push-style events arrive through the mailbox edge, which shares the Ark operator address (`arkServerAddress`). Wavelength polls and acknowledges mailbox messages as part of its background sync. See [Activity & events](/concepts/activity-and-events/) and [System architecture](/introduction/system-architecture/).
### OOR (out-of-round)
OOR (out-of-round) transfers move VTXOs between owners without waiting for the next batch round. They are the mechanism behind instant off-chain Ark payments and vHTLC funding or claiming. OOR is an advanced topic; Wavelength handles OOR transfers internally and detailed protocol coverage is deferred to a later docs pass. See [Lightning payments = swaps](/concepts/lightning-payments-are-swaps/) for how OOR funds and claims a vHTLC.
### Operator (ASP)
The operator, also called the Ark Service Provider (ASP), runs the Ark coordinator that constructs batch transactions, co-signs off-chain spends, and maintains the VTXO index. You trust the operator for availability and honest signing, but not for custody: you can always reclaim funds on-chain via unilateral exit if the operator stops cooperating. Configure the operator endpoint as `arkServerAddress`. See [System architecture](/introduction/system-architecture/).
### Passkey
A passkey is a phishing-resistant credential (WebAuthn on web, the platform authenticator on mobile) that can protect a Wavelength wallet instead of a password. Wavelength derives the wallet’s encryption from the passkey’s PRF (pseudo-random function) output, so the same passkey deterministically unlocks the same wallet. Passkey support requires platform support; see the requirements pages.
### Round
A round is one cycle in which the operator collects participant requests, constructs a batch transaction, co-signs it with clients, and broadcasts it on-chain. Rounds run on a fixed periodic cadence, aggregating boarding inputs, leave requests, batch swaps, and new VTXO allocations into a single on-chain transaction. Your boarded deposit becomes spendable Ark balance once the round that includes it confirms.
### Transport
A transport is the layer that runs the embedded wallet daemon for a given platform. The Wavelength SDK ships two: the web transport (`wavelength-web`, daemon compiled to WebAssembly, reaches gateways over REST) and the React Native transport (`wavelength-react-native`, daemon compiled into the app binary, uses gRPC). Both implement the same `WavelengthClient` contract, so app code is transport-agnostic.
### Unilateral exit
Unilateral exit is the emergency fallback for withdrawing Ark funds without operator cooperation. You broadcast the pre-signed virtual transaction tree path for your VTXO on-chain and claim it after a CSV timelock, without needing the operator’s signature. It works even if the operator goes offline, but it is slower and more expensive than cooperative leave. See [Leaving Ark](/concepts/leaving-ark/).
### vHTLC
A vHTLC (virtual HTLC) is an Ark output whose taproot script encodes hashlock-and-timelock logic similar to a Lightning HTLC, but settled inside Ark rather than on the Bitcoin base layer. Lightning receives and sends both pivot on a vHTLC: the payer and receiver keys, operator key, payment hash, and timelocks determine who can claim or refund the output cooperatively off-chain or, as a last resort, unilaterally on-chain. See [Lightning payments = swaps](/concepts/lightning-payments-are-swaps/).
### VTXO
A VTXO (Virtual Transaction Output) is an off-chain output that represents spendable bitcoin inside Ark. You hold VTXOs after boarding or receiving a payment; you spend them through cooperative off-chain transfers, batch swaps, cooperative leave, or unilateral exit. Each VTXO is backed by an on-chain batch output and carries a CSV-based expiry, after which you must refresh or exit. See [Balances & VTXOs](/concepts/balances-and-vtxos/).
---
Source: https://wavelength.lightning.engineering/api.md
# API
## What this is
The wallet daemon (`waved`) exposes a compact wallet API, `WalletService`, over both gRPC and REST. It is 14 methods covering the full wallet lifecycle: create and unlock a wallet, send and receive Lightning and on-chain payments, read balance and activity, and exit to a fully self-custodial on-chain position. A companion service, `WalletInspectionService`, adds one read-only method for inspecting activity entries in detail, for 15 methods total.
This slice is for **remote integrations**: a backend service that manages a wallet on behalf of your product, an internal dashboard, a monitoring script, or any process that talks to a `waved` instance you run yourself, rather than embedding a wallet in a browser tab.
## API vs SDK
Wavelength ships two different ways to reach the same daemon, and they solve different problems.
- **The SDK** ([What is the Wavelength SDK?](/introduction/what-is-wavelength-sdk/)) compiles `waved` to run **inside your app**: to WebAssembly in the browser on web, and into the app binary on React Native. Each of your users gets their own wallet instance, with keys held on their device. There is no server-side daemon to run or operate.
- **The API** documented here talks to a **daemon you run**, over the network, using gRPC or REST. Use this when the wallet needs to live server-side: a treasury or operations service, a CI job that needs test funds, or any integration that does not run inside an end user’s browser tab.
If you are building a product where each user holds their own keys on their own device, start with the SDK. If you are automating or operating a wallet from a backend you control, this API slice is for you.
## How this reference is organized
Every RPC gets its own page, grouped into five sidebar sections by what it does in the wallet lifecycle; **Overview** holds this page plus the get-started and REST-conventions pages:
- **Wallet lifecycle**: [Create](/api/wallet/create/), [Unlock](/api/wallet/unlock/), [Status](/api/wallet/status/)
- **Sending**: [PrepareSend](/api/wallet/prepare-send/), [Send](/api/wallet/send/)
- **Receiving**: [Recv](/api/wallet/recv/), [Deposit](/api/wallet/deposit/)
- **Balance and activity**: [Balance](/api/wallet/balance/), [List](/api/wallet/list/), [SubscribeWallet](/api/wallet/subscribe-wallet/), [InspectActivity](/api/wallet/inspect-activity/)
- **Exit and sweep**: [GetExitPlan](/api/wallet/get-exit-plan/), [Exit](/api/wallet/exit/), [ExitStatus](/api/wallet/exit-status/), [SweepWallet](/api/wallet/sweep-wallet/)
Start with [Get started](/api/get-started/) to build `waved` with the wallet API enabled and make your first call, then [REST conventions](/api/rest/) for the shared request, error, and streaming shape across every method.
If you would rather drive the daemon interactively or from a shell script, see the [CLI](/cli/) slice: `wavecli` wraps this same `WalletService` behind a command-line interface.
## Versioning
The API surface is versioned together with the daemon, not independently. Pin a specific daemon version for production integrations.
---
Source: https://wavelength.lightning.engineering/api/get-started.md
# Get started
## Build waved with the wallet API
`WalletService` and `WalletInspectionService` are compiled into `waved` only when the daemon is built with the `wavewalletrpc` build tag. That tag requires `swapruntime` transitively: building with `wavewalletrpc` but without `swapruntime` is a deliberate compile error, and a default `make build` includes neither, so the wallet API is off unless you opt in.
Build both `waved` and `wavecli` with the tag using the provided make target:
```sh
make build-wavewalletrpc
```
This produces `bin/waved` and `bin/wavecli` with the wallet RPC surface registered. `make install-wavewalletrpc` does the same via `go install`, landing the binaries in `$GOPATH/bin`.
> **Default builds omit the API**
>
> Without the `wavewalletrpc` tag, `WalletService` and `WalletInspectionService` are compiled out entirely and never registered. A call against a default build fails with gRPC’s standard unknown-service error, which surfaces as the `Unimplemented` status code. If a call fails that way, rebuild with `make build-wavewalletrpc`.
## Endpoints
`waved` starts two listeners by default:
| Protocol | Default address | Notes |
| -------- | ----------------- | ------------------------------------------------------------------- |
| gRPC | `localhost:10029` | The daemon’s own RPC server. |
| REST | `localhost:10031` | An HTTP/JSON gateway in front of the same RPCs, enabled by default. |
Every `WalletService` and `WalletInspectionService` method is reachable on both listeners with the same request and response shape, modulo JSON encoding. See [REST conventions](/api/rest/) for the route and body shape used by the gateway.
> **The gRPC listener is secured by default**
>
> The daemon’s gRPC server wires up TLS and macaroon authentication by default: it serves TLS credentials and enforces a macaroon on every call. For loopback development and regtest you can disable both with `--no-tls` and `--no-macaroons`; on a mainnet TCP listener the daemon refuses both by default, so a stray flag can’t quietly expose an unauthenticated plaintext RPC surface. A deployment that terminates TLS and enforces authentication at an external proxy can set `allow-insecure-mainnet` to override that refusal, which acknowledges that the daemon’s own listener then runs without transport security. The default macaroon is written to `admin.macaroon` under the daemon’s network directory. The REST gateway (`localhost:10031`) is a local plaintext HTTP proxy: it forwards the `macaroon` HTTP header to the enforcing gRPC backend, so a call needs that header whenever macaroons are enabled.
## Your first call
With `waved` running a `wavewalletrpc` build, call `Status` to confirm the daemon and wallet API are up. `StatusRequest` is empty, so an empty JSON object is a complete request body.
Over REST (a local daemon started with `--no-macaroons`; otherwise add a `-H "macaroon: "` header):
```sh
curl -X POST http://localhost:10031/v1/wallet/status -d '{}'
```
Over the CLI, the equivalent is:
```sh
wavecli getinfo --no-tls --no-macaroons
```
`getinfo` talks to the daemon’s own `DaemonService` rather than `WalletService.Status` directly, but it is the fastest way to confirm `wavecli` can reach `waved` at all before you start scripting `WalletService` calls. `--no-tls --no-macaroons` matches a daemon run with authentication disabled; it is the right pairing for local and regtest use.
## Where to go next
- Walk the method reference starting with [Create](/api/wallet/create/) and [Unlock](/api/wallet/unlock/) to bring up a wallet, then [PrepareSend](/api/wallet/prepare-send/) and [Send](/api/wallet/send/) to move funds.
- Read [REST conventions](/api/rest/) for the full request, error, and streaming shape shared by every route.
- Explore the same surface from a terminal in the [CLI](/cli/) slice: `wavecli` wraps every `WalletService` method as a subcommand.
---
Source: https://wavelength.lightning.engineering/api/rest.md
# REST conventions
The REST gateway is a grpc-gateway HTTP/JSON proxy in front of the same `WalletService` and `WalletInspectionService` gRPC methods. Every route follows the same conventions described here; the individual method pages only document what differs (route, request fields, response fields).
## Route shape
Every method is a `POST` under `/v1/wallet/...`, regardless of whether the underlying RPC is a read or a write:
```plaintext
POST /v1/wallet/create
POST /v1/wallet/status
POST /v1/wallet/prepare-send
POST /v1/wallet/subscribe
POST /v1/wallet/inspect/activity
```
Request bodies are JSON objects whose field names match the proto field names exactly, in `snake_case` (not the `camelCase` you would see in a hand-written REST API). Methods whose request message has no fields, such as `Status`, still take a request body: an empty JSON object.
```sh
curl -X POST http://localhost:10031/v1/wallet/status -d '{}'
```
## Error shape
On failure, the gateway returns a JSON body shaped like a `google.rpc.Status` message, with an HTTP status code derived from the underlying gRPC status:
```json
{
"code": 3,
"message": "amt_sat must be positive",
"details": []
}
```
- `code` is the numeric **gRPC** status code (for example `3` for `INVALID_ARGUMENT`), not the HTTP status code. The gateway also sets the HTTP status header using its own mapping from gRPC code to HTTP status (`INVALID_ARGUMENT` becomes `400`, `NOT_FOUND` becomes `404`, and so on), so check both if you branch on error type.
- `message` is the human-readable error string from the RPC.
- `details` carries any structured error detail messages attached to the gRPC status; it is usually empty.
This is the grpc-gateway default error handler: the daemon does not install a custom error mapper, so every route shares this exact shape.
## Streaming
`SubscribeWallet` is the one server-streaming method (`POST /v1/wallet/subscribe`). Unlike the other 14 routes, the response is not a single JSON object. The gateway keeps the HTTP connection open and emits one newline-delimited envelope per streamed message, with the entry under a `result` field:
```json
{"result":{"id":"activity-id","kind":"ENTRY_KIND_SEND"}}
```
This is the daemon’s grpc-gateway REST envelope. It is separate from Wavelength’s camelCase facade stream, which delivers an `Entry` as the payload of an `activity` event.
> **No write timeout on this connection**
>
> The gateway’s HTTP server disables its write timeout entirely so long-lived streaming responses are not cut off mid-stream. This applies to the gateway server as a whole, and matters most for `/v1/wallet/subscribe`: expect the connection to stay open until you close it client-side.
See [SubscribeWallet](/api/wallet/subscribe-wallet/) for the full `WalletEntry` shape.
## Field mapping example
Proto messages map to JSON bodies field-for-field, using the proto field name rather than a camelCased variant. `PrepareSendRequest` is a representative example: a `oneof` for the destination, plus scalar fields.
```protobuf
message PrepareSendRequest {
oneof destination {
string invoice = 1;
string onchain_address = 2;
}
uint64 amt_sat = 3;
string note = 4;
uint64 max_fee_sat = 5;
bool sweep_all = 6;
}
```
```json
{
"invoice": "lnbc1500n1p...",
"amt_sat": "0",
"note": "coffee",
"max_fee_sat": "50",
"sweep_all": false
}
```
Only one of `invoice` or `onchain_address` is set per call, matching the proto `oneof`. `amt_sat` and `max_fee_sat` are `uint64` fields, so the JSON gateway encodes them as strings to avoid precision loss in JSON’s number type; fields you omit take their proto zero value. See [PrepareSend](/api/wallet/prepare-send/) for the full request and response reference.
---
Source: https://wavelength.lightning.engineering/api/wallet/create.md
WalletService[CLI`wavecli create`](/cli/create/)
# Create
Create initializes a new wallet from a freshly generated aezeed mnemonic. The daemon generates the seed, encrypts it with the supplied password, and returns the mnemonic so the caller can record it. For recovery flows the caller MAY supply an existing mnemonic in the request; in that case the same mnemonic is echoed back. Proxies waverpc.GenSeed + waverpc.InitWallet server-side.
## Endpoints
gRPC`rpc Create(CreateRequest) returns (CreateResponse)`
REST`POST/v1/wallet/create`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli create
```
## Request`CreateRequest`
CreateRequest carries the parameters for WalletService.Create: the encryption password, optional seed material, and recovery options.
| Field | Type | Description |
| ----------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `wallet_password` | `bytes` | wallet\_password is the password used to encrypt the on-disk seed. Must be at least 8 bytes. The password is consumed and zeroed by the daemon after wallet initialization. |
| `seed_passphrase` | `bytes` | seed\_passphrase is the optional aezeed passphrase (BIP39-style 25th-word) protecting the mnemonic itself. Distinct from wallet\_password. |
| `mnemonic` | `repeated string` | mnemonic is the 24-word aezeed mnemonic to import. Empty means "generate a fresh seed"; non-empty means "recover from this mnemonic". When non-empty the response echoes the same mnemonic back unchanged. |
| `recover_state` | `bool` | recover\_state asks the daemon to scan deterministic Ark wallet keys after importing mnemonic and rebuild local Ark state from chain and indexer data. |
| `recovery_window` | `uint32` | recovery\_window is the number of key indexes to scan per recovery key family. Zero uses the daemon wallet.recoverywindow config value. |
## Response`CreateResponse`
CreateResponse returns the wallet mnemonic and identity, plus a summary of any Ark-state recovery performed during Create.
| Field | Type | Description |
| ------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mnemonic` | `repeated string` | mnemonic is the 24-word aezeed mnemonic for the wallet. For fresh wallets (request mnemonic empty) this is the newly generated seed and the caller MUST persist it offline before any unlock sequence - losing it makes the wallet unrecoverable. For recovery flows (request mnemonic supplied) this is the same mnemonic echoed back for confirmation. |
| `identity_pubkey` | `string` | identity\_pubkey is the hex-encoded daemon wallet identity public key derived from the newly created wallet. |
| `recovery_ran` | `bool` | recovery\_ran is true when Create attempted Ark-state recovery. |
| `recovered_boarding_addresses` | `uint32` | recovered\_boarding\_addresses is the number of boarding addresses rebuilt and persisted from deterministic keys. |
| `recovered_boarding_utxos` | `uint32` | recovered\_boarding\_utxos is the number of confirmed wallet UTXOs found at recovered boarding addresses during the recovery scan. |
| `recovered_vtxos` | `uint32` | recovered\_vtxos is the number of indexed VTXOs restored into local wallet state. |
| `recovered_oor_receive_scripts` | `uint32` | recovered\_oor\_receive\_scripts is the number of registered OOR receive scripts matched and restored into local ownership metadata. |
| `recovered_oor_events` | `uint32` | recovered\_oor\_events is the number of OOR recipient events processed during recovery. |
---
Source: https://wavelength.lightning.engineering/api/wallet/unlock.md
WalletService[CLI`wavecli unlock`](/cli/unlock/)
# Unlock
Unlock decrypts the on-disk wallet seed using the supplied password and starts the wallet subsystem. Proxies waverpc.UnlockWallet.
## Endpoints
gRPC`rpc Unlock(UnlockRequest) returns (UnlockResponse)`
REST`POST/v1/wallet/unlock`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli unlock
```
## Request`UnlockRequest`
UnlockRequest carries the password used to decrypt the on-disk seed for WalletService.Unlock.
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------------------------ |
| `wallet_password` | `bytes` | wallet\_password is the password used to decrypt the on-disk seed. |
## Response`UnlockResponse`
UnlockResponse returns the identity of the wallet unlocked by WalletService.Unlock.
| Field | Type | Description |
| ----------------- | -------- | --------------------------------------------------------------------------------------------- |
| `identity_pubkey` | `string` | identity\_pubkey is the hex-encoded daemon wallet identity public key of the unlocked wallet. |
---
Source: https://wavelength.lightning.engineering/api/wallet/status.md
WalletService
# Status
Status returns a wallet-level readiness summary: daemon readiness, wallet-unlocked state, balance summary, pending-entry count. Kept in the proto for programmatic callers; not surfaced as a CLI verb (the \`getinfo\` CLI verb covers the human-facing readiness view).
## Endpoints
gRPC`rpc Status(StatusRequest) returns (StatusResponse)`
REST`POST/v1/wallet/status`
## Examples
GoPythonJavaScriptcurlfetch
```
conn, err := grpc.NewClient("localhost:10029",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := wavewalletrpc.NewWalletServiceClient(conn)
ctx := context.Background()
req := &wavewalletrpc.StatusRequest{}
resp, err := client.Status(ctx, req)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp)
```
## Request`StatusRequest`
StatusRequest is the empty request for WalletService.Status.
This message has no fields.
## Response`StatusResponse`
StatusResponse is the wallet-level readiness summary returned by WalletService.Status.
| Field | Type | Description |
| --------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ready` | `bool` | ready is true when the daemon and its dependencies are up. |
| `unlocked` | `bool` | unlocked is a legacy wallet-exists signal: true once a wallet seed exists on disk or is loaded in memory. Use ready to check whether wallet RPCs are currently usable. |
| `network` | `string` | network is the bitcoin network the daemon is configured for, for example "mainnet", "testnet", "testnet4", "signet", or "regtest". |
| `balance` | [`BalanceResponse`](#BalanceResponse) | balance is the unified balance summary at the time of the call. |
| `pending_count` | `uint32` | pending\_count is the number of WalletEntry rows in PENDING status. |
## `BalanceResponse`message
BalanceResponse is the unified balance summary returned by WalletService.Balance across confirmed, in-flight, and credit amounts.
| Field | Type | Description |
| ---------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `confirmed_sat` | `int64` | confirmed\_sat is the total spendable VTXO amount in satoshis. |
| `pending_in_sat` | `int64` | pending\_in\_sat is the total in-flight inbound amount (boarding plus receive operations). |
| `pending_out_sat` | `int64` | pending\_out\_sat is the total in-flight outbound amount (send plus exit operations). |
| `credit_available_sat` | `uint64` | credit\_available\_sat is the server-authoritative available credit balance for the wallet identity. |
| `credit_reserved_sat` | `uint64` | credit\_reserved\_sat is the server-authoritative in-flight credit reservation amount. |
---
Source: https://wavelength.lightning.engineering/api/wallet/prepare-send.md
WalletService[CLI`wavecli send`](/cli/send/)
# PrepareSend
PrepareSend validates and previews an outbound payment without moving funds. The response carries a short-lived send\_intent\_id that must be consumed by Send.
## Endpoints
gRPC`rpc PrepareSend(PrepareSendRequest) returns (PrepareSendResponse)`
REST`POST/v1/wallet/prepare-send`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli send lnbcrt250u1p3xyz...truncated --note "coffee run" --force
```
## Request`PrepareSendRequest`
PrepareSendRequest describes the outbound payment to validate and preview via WalletService.PrepareSend, before any funds move.
| Field | Type | Description |
| ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `invoice`oneof destination | `string` | invoice is a BOLT-11 Lightning invoice. The daemon pays it via its owned swap subsystem; the swap server picks same-Ark p2p vs real Lightning transparently. |
| `onchain_address`oneof destination | `string` | onchain\_address is a bech32 onchain destination. The daemon submits a LeaveVTXOs request covering amt\_sat plus fees. |
| `amt_sat` | `uint64` | amt\_sat is required for onchain sends. For onchain sends amt\_sat must be strictly positive unless sweep\_all is set, in which case it must be zero. For invoice sends amt\_sat is ignored: v1 requires an amount-bearing BOLT-11 invoice; amountless invoices are rejected at the wallet layer until plumbing for caller-supplied amounts lands in the swap subserver. |
| `note` | `string` | note is an optional caller-supplied label persisted alongside the entry. It is never interpreted by the daemon. |
| `max_fee_sat` | `uint64` | max\_fee\_sat is the optional caller cap on routing or sweep fees. Zero means use daemon defaults. |
| `sweep_all` | `bool` | sweep\_all signals an explicit wallet-emptying onchain send: every live VTXO is swept to the destination, less fees. The caller must set amt\_sat = 0 when this flag is true and a strictly positive amt\_sat otherwise. This makes "drain the wallet" structurally distinct from "amt\_sat defaulted to zero" so a typo cannot empty the wallet by accident. Ignored on the invoice path. |
## Response`PrepareSendResponse`
PrepareSendResponse is the preview of a prepared send: the single-use intent token plus the amount, fee, rail, and quote detail a caller should confirm before calling Send.
| Field | Type | Description |
| ---------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `send_intent_id` | `string` | send\_intent\_id is a short-lived, single-use token consumed by Send. |
| `amount_sat` | `int64` | amount\_sat is the destination principal amount. |
| `expected_fee_sat` | `int64` | expected\_fee\_sat is meaningful only when fee\_known is true. |
| `fee_known` | `bool` | fee\_known is false when a required remote quote API is missing. |
| `expected_total_outflow_sat` | `int64` | expected\_total\_outflow\_sat is meaningful only when total\_outflow\_known is true. |
| `total_outflow_known` | `bool` | total\_outflow\_known is false when the final outflow depends on a remote quote that is not available yet. |
| `rail` | [`SendRail`](#SendRail) | rail is the expected settlement rail for this prepared send. Onchain prepares always return SEND\_RAIL\_ONCHAIN. Invoice prepares map the swap quote settlement type when a remote quote is available; when no quote is available (LOCAL\_ONLY) or the quote does not distinguish same-Ark from Lightning, the value is SEND\_RAIL\_OFFCHAIN\_UNKNOWN until Send starts. |
| `quote_status` | [`SendQuoteStatus`](#SendQuoteStatus) | quote\_status describes how complete the prepare-time quote is. Invoice sends: COMPLETE when every amount and fee is backed by a swap remote quote; LOCAL\_ONLY when the invoice parsed locally but the swap quote API was unavailable. Onchain sends: COMPLETE when waverpc.EstimateFee returned a binding operator quote; LOCAL\_ONLY when only a local fee floor could be computed. |
| `destination_summary` | `string` | destination\_summary is a short display string suitable for CLI and UI confirmation prompts. |
| `invoice_description` | `string` | invoice\_description is the BOLT-11 description when present. |
| `payment_hash` | `string` | payment\_hash is the invoice payment hash when available. |
| `expires_at_unix` | `int64` | expires\_at\_unix is the unix timestamp after which Send rejects the prepared intent. |
| `selected_outpoints` | `repeated string` | selected\_outpoints is populated for onchain sends so Send can spend exactly the VTXO set previewed to the user. |
| `warning` | `string` | warning carries a concise human-facing caveat for local-only previews. |
| `credit_preview` | [`CreditPreview`](#CreditPreview) | credit\_preview is populated when the invoice send will or can use sat-native server credits. |
## `SendRail`enum
SendRail identifies the expected settlement rail for a prepared send.
| Name | Number | Description |
| ---------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SEND_RAIL_UNSPECIFIED` | `0` | SEND\_RAIL\_UNSPECIFIED is the proto zero value used when no rail has been determined. |
| `SEND_RAIL_OFFCHAIN_UNKNOWN` | `1` | SEND\_RAIL\_OFFCHAIN\_UNKNOWN is used when the wallet can parse the invoice locally but the swapserver has not supplied a quote that distinguishes same-Ark settlement from Lightning settlement. |
| `SEND_RAIL_IN_ARK` | `2` | SEND\_RAIL\_IN\_ARK is same-Ark peer-to-peer settlement, where the payment never touches Lightning. |
| `SEND_RAIL_LIGHTNING` | `3` | SEND\_RAIL\_LIGHTNING is settlement over the real Lightning network. |
| `SEND_RAIL_ONCHAIN` | `4` | SEND\_RAIL\_ONCHAIN is an on-chain send via a cooperative leave to the destination address. |
| `SEND_RAIL_CREDIT` | `5` | SEND\_RAIL\_CREDIT is settlement drawn from the wallet's server-side credit balance. |
| `SEND_RAIL_MIXED` | `6` | SEND\_RAIL\_MIXED is settlement that combines credit with the normal vHTLC path. |
## `SendQuoteStatus`enum
SendQuoteStatus describes how complete the prepare-time quote is.
| Name | Number | Description |
| ------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SEND_QUOTE_STATUS_UNSPECIFIED` | `0` | SEND\_QUOTE\_STATUS\_UNSPECIFIED is the proto zero value; quote completeness was not reported. |
| `SEND_QUOTE_STATUS_COMPLETE` | `1` | SEND\_QUOTE\_STATUS\_COMPLETE means every user-visible amount and fee field is backed by a remote quote. |
| `SEND_QUOTE_STATUS_LOCAL_ONLY` | `2` | SEND\_QUOTE\_STATUS\_LOCAL\_ONLY means the wallet performed local validation and selection but one or more remote quote APIs are not available yet. |
## `CreditPreview`message
CreditPreview describes how server-side sat-native credits factor into a prepared send. It is populated on PrepareSendResponse when the invoice send will or can draw on credits.
| Field | Type | Description |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `must_use_credit` | `bool` | must\_use\_credit is true when the invoice amount cannot be represented by the normal vHTLC path and the send must be settled from the credit balance. |
| `credit_applied_sat` | `uint64` | credit\_applied\_sat is the credit balance in satoshis the quote expects to reserve for this send. |
| `credit_shortfall_sat` | `uint64` | credit\_shortfall\_sat is the additional credit in satoshis needed before this payment can be admitted. |
| `credit_topup_sat` | `uint64` | credit\_topup\_sat is the Ark top-up amount in satoshis required to cover the shortfall. It is rounded up and dust-limited by the server. |
| `ark_funding_sat` | `uint64` | ark\_funding\_sat is the amount in satoshis the client must still fund through the normal vHTLC path. |
---
Source: https://wavelength.lightning.engineering/api/wallet/send.md
WalletService[CLI`wavecli send`](/cli/send/)
# Send
Send dispatches a previously prepared outbound payment. The request is intentionally intent-only: callers must use PrepareSend first so they can present amount, rail, fee, and total-outflow details before funds move.
## Endpoints
gRPC`rpc Send(SendRequest) returns (SendResponse)`
REST`POST/v1/wallet/send`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli send lnbcrt250u1p3xyz...truncated --note "coffee run" --force
```
## Request`SendRequest`
SendRequest dispatches a previously prepared send by consuming the single-use intent token returned by PrepareSend.
| Field | Type | Description |
| ---------------- | -------- | --------------------------------------------------------------------- |
| `send_intent_id` | `string` | send\_intent\_id is returned by PrepareSend and may be consumed once. |
## Response`SendResponse`
SendResponse returns the initial WalletEntry for a dispatched send and the actual amount that will leave the wallet.
| Field | Type | Description |
| ------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entry` | [`WalletEntry`](#WalletEntry) | entry is the initial WalletEntry persisted for this send. The caller can poll List or SubscribeWallet for subsequent status transitions. |
| `actual_amount_sat` | `int64` | actual\_amount\_sat is the real amount that will leave the wallet for this operation. For invoice sends it matches the caller's amt\_sat (or the invoice amount for amount-bearing invoices). For a bounded onchain send it matches the requested amt\_sat: the seal-time fee handshake returns a change VTXO rather than sweeping whole VTXOs. For a sweep\_all onchain send it reflects the SUM of the swept VTXOs (the whole-wallet drain). CLI and UI surfaces SHOULD echo this back to the user before treating the send as confirmed. |
## `WalletEntry`message
WalletEntry is the single flat row type returned by the ACTIVITY view of List and by SubscribeWallet. The shape is deliberately minimal: display clients should be able to render kind/status/amount/fee/phase directly without parsing daemon-specific swap or ledger internals. Power-user detail belongs in WalletInspectionService.InspectActivity.
| Field | Type | Description |
| ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the stable identifier for this entry. Swap-backed SEND and RECV rows use the Lightning payment\_hash. Credit-backed RECV rows use the credit operation id (see CreditReceive.operation\_id); the payment\_hash lives on recv progress/detail instead. EXIT rows use leave\_job\_id (or sweep txid); DEPOSIT rows use the boarding outpoint or txid. |
| `kind` | [`EntryKind`](#EntryKind) | kind is the user-visible category. |
| `status` | [`EntryStatus`](#EntryStatus) | status is the coarse user-facing outcome: PENDING, COMPLETE, or FAILED. It mirrors the backing source's durable state and should not be reinterpreted by display clients. |
| `amount_sat` | `int64` | amount\_sat is signed: positive values are incoming to the wallet, negative values are outgoing. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this operation. |
| `counterparty` | `string` | counterparty is a short, display-friendly identifier of the other side: a truncated invoice for Lightning, an ark peer address for OOR, a bech32 onchain address for exits, or "boarding" for deposits. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last persisted update timestamp in unix seconds. List ACTIVITY and SubscribeWallet sort by this field, descending. |
| `note` | `string` | note is the caller-supplied label captured at submit time, if any. |
| `failure_reason` | `string` | failure\_reason is populated only when status is FAILED. It is a single human-readable string, never a coded enum tree. |
| `request` | [`WalletEntryRequest`](#WalletEntryRequest) | request is the original payment request or destination associated with the entry, when the backing subsystem persists it. This is useful for expanded views and inspection; compact clients should not need to parse it to explain status or lifecycle. |
| `progress` | [`WalletEntryProgress`](#WalletEntryProgress) | progress carries lifecycle-specific metadata already normalized by the wallet service. Clients can render phase\_label directly or switch on phase without inferring meaning from swap/ledger internals. |
| `failure_code`optional | [`EntryFailureCode`](#EntryFailureCode) | failure\_code is a stable, machine-readable classification of why a FAILED entry failed. It is set ONLY on FAILED entries; a non-failed entry omits the field entirely (presence-tracked), so its absence is the canonical "no failure" signal and ENTRY\_FAILURE\_CODE\_UNSPECIFIED is never sent on the wire. failure\_reason remains the human-readable supplement. |
## `EntryKind`enum
EntryKind tags each WalletEntry with the user-visible category of the underlying operation. Internal subtypes (same-Ark p2p vs real Lightning, OOR session correlators, round IDs) are deliberately not surfaced.
| Name | Number | Description |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------- |
| `ENTRY_KIND_UNSPECIFIED` | `0` | ENTRY\_KIND\_UNSPECIFIED is the proto zero value used when the operation category is unknown. |
| `ENTRY_KIND_SEND` | `1` | ENTRY\_KIND\_SEND is an outbound payment over any rail (Lightning, same-Ark, onchain, or credit). |
| `ENTRY_KIND_RECV` | `2` | ENTRY\_KIND\_RECV is an inbound receive. |
| `ENTRY_KIND_DEPOSIT` | `3` | ENTRY\_KIND\_DEPOSIT is a boarding deposit rolled into a VTXO. |
| `ENTRY_KIND_EXIT` | `4` | ENTRY\_KIND\_EXIT is a cooperative leave or unilateral exit to on-chain funds. |
## `EntryStatus`enum
EntryStatus collapses every backing FSM into the three states user-facing surfaces actually need.
| Name | Number | Description |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `ENTRY_STATUS_UNSPECIFIED` | `0` | ENTRY\_STATUS\_UNSPECIFIED is the proto zero value used when the backing state has not been mapped yet. |
| `ENTRY_STATUS_PENDING` | `1` | ENTRY\_STATUS\_PENDING means the operation is still in flight. |
| `ENTRY_STATUS_COMPLETE` | `2` | ENTRY\_STATUS\_COMPLETE means the operation reached a durable success state. |
| `ENTRY_STATUS_FAILED` | `3` | ENTRY\_STATUS\_FAILED means the operation reached a terminal failure state. |
## `WalletEntryRequest`message
WalletEntryRequest describes the user-recognizable request that created a WalletEntry. It is a oneof because each operation is backed by exactly one request shape.
| Field | Type | Description |
| -------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `lightning_invoice`oneof request | [`LightningInvoiceRequest`](#LightningInvoiceRequest) | lightning\_invoice is populated for Lightning send and receive entries. |
| `onchain_address`oneof request | [`OnchainAddressRequest`](#OnchainAddressRequest) | onchain\_address is populated for deposit and exit entries. |
| `ark_address`oneof request | [`ArkAddressRequest`](#ArkAddressRequest) | ark\_address is populated for direct Ark send or receive entries. |
## `LightningInvoiceRequest`message
LightningInvoiceRequest captures the Lightning request associated with an activity entry.
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request. |
| `payment_hash` | `string` | payment\_hash identifies the invoice and remains stable after the invoice itself is no longer convenient to display. |
## `OnchainAddressRequest`message
OnchainAddressRequest captures the onchain address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | -------------------------------------------------------------------- |
| `address` | `string` | address is the bech32 onchain address originally issued or targeted. |
## `ArkAddressRequest`message
ArkAddressRequest captures the Ark address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | --------------------------------------------------------- |
| `address` | `string` | address is the Ark address originally issued or targeted. |
## `WalletEntryProgress`message
WalletEntryProgress carries lifecycle metadata normalized by the wallet service.
| Field | Type | Description |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`WalletEntryPhase`](#WalletEntryPhase) | phase is the coarse lifecycle phase for this entry. |
| `phase_label` | `string` | phase\_label is a short lowercase label that clients can render as-is while they migrate to enum-based presentation. |
| `payment_hash` | `string` | payment\_hash is populated for Lightning-backed send/recv entries. |
| `txid` | `string` | txid is populated when the backing ledger row has an onchain txid. |
| `confirmation_height` | `int32` | confirmation\_height is populated once the source records it. |
| `vtxo_outpoint` | `string` | vtxo\_outpoint is populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed Lightning-backed send this is the proof of payment for the paid invoice (sha256(preimage) == payment\_hash); it is empty until the preimage is durably known and for non-Lightning entries. |
## `WalletEntryPhase`enum
WalletEntryPhase is a coarse, wallet-facing lifecycle phase. It is not a replacement for status; status answers whether the operation is pending, complete, or failed, while phase explains the current backing-system step.
| Name | Number | Description |
| --------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_ENTRY_PHASE_UNSPECIFIED` | `0` | WALLET\_ENTRY\_PHASE\_UNSPECIFIED is the proto zero value used when the backing subsystem has no lifecycle hint. |
| `WALLET_ENTRY_PHASE_REQUEST_CREATED` | `1` | WALLET\_ENTRY\_PHASE\_REQUEST\_CREATED means the request was created but no payment has been observed. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_PAYMENT` | `2` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_PAYMENT means the wallet is waiting for an inbound payment or swap funding. |
| `WALLET_ENTRY_PHASE_PAYMENT_DETECTED` | `3` | WALLET\_ENTRY\_PHASE\_PAYMENT\_DETECTED means the backing subsystem has detected a payment but it is not yet settled. |
| `WALLET_ENTRY_PHASE_SETTLING` | `4` | WALLET\_ENTRY\_PHASE\_SETTLING means the operation is being settled through Ark, Lightning, or onchain machinery. |
| `WALLET_ENTRY_PHASE_CONFIRMED` | `5` | WALLET\_ENTRY\_PHASE\_CONFIRMED means the backing operation is confirmed or otherwise durably complete. |
| `WALLET_ENTRY_PHASE_REFUNDING` | `6` | WALLET\_ENTRY\_PHASE\_REFUNDING means the operation is currently refunding. |
| `WALLET_ENTRY_PHASE_REFUNDED` | `7` | WALLET\_ENTRY\_PHASE\_REFUNDED means the refund path completed. |
| `WALLET_ENTRY_PHASE_FAILED` | `8` | WALLET\_ENTRY\_PHASE\_FAILED means the backing operation reached a terminal failed state. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_CONFIRMATION` | `9` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_CONFIRMATION means an onchain payment has been detected and is waiting for block confirmation. |
## `EntryFailureCode`enum
EntryFailureCode is a stable, machine-readable classification of why a FAILED WalletEntry failed. It complements the free-text failure\_reason so clients can branch on failure cause without string matching.
| Name | Number | Description |
| --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `ENTRY_FAILURE_CODE_UNSPECIFIED` | `0` | ENTRY\_FAILURE\_CODE\_UNSPECIFIED is the zero value used for non-failed entries or when the cause is unknown. |
| `ENTRY_FAILURE_CODE_TIMED_OUT` | `1` | ENTRY\_FAILURE\_CODE\_TIMED\_OUT means the operation exceeded the wallet deadline before reaching a terminal state. |
| `ENTRY_FAILURE_CODE_EXPIRED` | `2` | ENTRY\_FAILURE\_CODE\_EXPIRED means the swap expired before it was funded. |
| `ENTRY_FAILURE_CODE_REFUNDED` | `3` | ENTRY\_FAILURE\_CODE\_REFUNDED means an outbound payment was refunded back to the wallet. |
| `ENTRY_FAILURE_CODE_NEEDS_INTERVENTION` | `4` | ENTRY\_FAILURE\_CODE\_NEEDS\_INTERVENTION means the swap reached an anomalous state requiring manual recovery. |
| `ENTRY_FAILURE_CODE_FAILED` | `5` | ENTRY\_FAILURE\_CODE\_FAILED is a generic terminal failure with no more specific classification. |
---
Source: https://wavelength.lightning.engineering/api/wallet/recv.md
WalletService[CLI`wavecli recv`](/cli/recv/)
# Recv
Recv asks the daemon for a Lightning invoice the caller can hand out. Internally the daemon sets up the inbound receive via its owned swap subsystem; the invoice is signed with a daemon-managed key, not an ephemeral process-local key.
## Endpoints
gRPC`rpc Recv(RecvRequest) returns (RecvResponse)`
REST`POST/v1/wallet/recv`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli recv --offchain --amt 25000 --memo "invoice for coffee run"
```
## Request`RecvRequest`
RecvRequest describes the inbound receive to open via WalletService.Recv.
| Field | Type | Description |
| --------- | -------- | ----------------------------------------------------------------- |
| `amt_sat` | `uint64` | amt\_sat is the amount the caller wants to receive in satoshis. |
| `memo` | `string` | memo is the optional human-readable memo to embed in the invoice. |
## Response`RecvResponse`
RecvResponse returns the BOLT-11 invoice to hand out and the initial WalletEntry tracking the pending receive.
| Field | Type | Description |
| ---------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request the caller hands out to the payer. |
| `entry` | [`WalletEntry`](#WalletEntry) | entry is the initial WalletEntry persisted for this receive. |
| `credit_receive` | [`CreditReceive`](#CreditReceive) | credit\_receive is populated when the receive is backed by server credits rather than a client-claimable vHTLC. |
## `WalletEntry`message
WalletEntry is the single flat row type returned by the ACTIVITY view of List and by SubscribeWallet. The shape is deliberately minimal: display clients should be able to render kind/status/amount/fee/phase directly without parsing daemon-specific swap or ledger internals. Power-user detail belongs in WalletInspectionService.InspectActivity.
| Field | Type | Description |
| ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the stable identifier for this entry. Swap-backed SEND and RECV rows use the Lightning payment\_hash. Credit-backed RECV rows use the credit operation id (see CreditReceive.operation\_id); the payment\_hash lives on recv progress/detail instead. EXIT rows use leave\_job\_id (or sweep txid); DEPOSIT rows use the boarding outpoint or txid. |
| `kind` | [`EntryKind`](#EntryKind) | kind is the user-visible category. |
| `status` | [`EntryStatus`](#EntryStatus) | status is the coarse user-facing outcome: PENDING, COMPLETE, or FAILED. It mirrors the backing source's durable state and should not be reinterpreted by display clients. |
| `amount_sat` | `int64` | amount\_sat is signed: positive values are incoming to the wallet, negative values are outgoing. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this operation. |
| `counterparty` | `string` | counterparty is a short, display-friendly identifier of the other side: a truncated invoice for Lightning, an ark peer address for OOR, a bech32 onchain address for exits, or "boarding" for deposits. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last persisted update timestamp in unix seconds. List ACTIVITY and SubscribeWallet sort by this field, descending. |
| `note` | `string` | note is the caller-supplied label captured at submit time, if any. |
| `failure_reason` | `string` | failure\_reason is populated only when status is FAILED. It is a single human-readable string, never a coded enum tree. |
| `request` | [`WalletEntryRequest`](#WalletEntryRequest) | request is the original payment request or destination associated with the entry, when the backing subsystem persists it. This is useful for expanded views and inspection; compact clients should not need to parse it to explain status or lifecycle. |
| `progress` | [`WalletEntryProgress`](#WalletEntryProgress) | progress carries lifecycle-specific metadata already normalized by the wallet service. Clients can render phase\_label directly or switch on phase without inferring meaning from swap/ledger internals. |
| `failure_code`optional | [`EntryFailureCode`](#EntryFailureCode) | failure\_code is a stable, machine-readable classification of why a FAILED entry failed. It is set ONLY on FAILED entries; a non-failed entry omits the field entirely (presence-tracked), so its absence is the canonical "no failure" signal and ENTRY\_FAILURE\_CODE\_UNSPECIFIED is never sent on the wire. failure\_reason remains the human-readable supplement. |
## `EntryKind`enum
EntryKind tags each WalletEntry with the user-visible category of the underlying operation. Internal subtypes (same-Ark p2p vs real Lightning, OOR session correlators, round IDs) are deliberately not surfaced.
| Name | Number | Description |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------- |
| `ENTRY_KIND_UNSPECIFIED` | `0` | ENTRY\_KIND\_UNSPECIFIED is the proto zero value used when the operation category is unknown. |
| `ENTRY_KIND_SEND` | `1` | ENTRY\_KIND\_SEND is an outbound payment over any rail (Lightning, same-Ark, onchain, or credit). |
| `ENTRY_KIND_RECV` | `2` | ENTRY\_KIND\_RECV is an inbound receive. |
| `ENTRY_KIND_DEPOSIT` | `3` | ENTRY\_KIND\_DEPOSIT is a boarding deposit rolled into a VTXO. |
| `ENTRY_KIND_EXIT` | `4` | ENTRY\_KIND\_EXIT is a cooperative leave or unilateral exit to on-chain funds. |
## `EntryStatus`enum
EntryStatus collapses every backing FSM into the three states user-facing surfaces actually need.
| Name | Number | Description |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `ENTRY_STATUS_UNSPECIFIED` | `0` | ENTRY\_STATUS\_UNSPECIFIED is the proto zero value used when the backing state has not been mapped yet. |
| `ENTRY_STATUS_PENDING` | `1` | ENTRY\_STATUS\_PENDING means the operation is still in flight. |
| `ENTRY_STATUS_COMPLETE` | `2` | ENTRY\_STATUS\_COMPLETE means the operation reached a durable success state. |
| `ENTRY_STATUS_FAILED` | `3` | ENTRY\_STATUS\_FAILED means the operation reached a terminal failure state. |
## `WalletEntryRequest`message
WalletEntryRequest describes the user-recognizable request that created a WalletEntry. It is a oneof because each operation is backed by exactly one request shape.
| Field | Type | Description |
| -------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `lightning_invoice`oneof request | [`LightningInvoiceRequest`](#LightningInvoiceRequest) | lightning\_invoice is populated for Lightning send and receive entries. |
| `onchain_address`oneof request | [`OnchainAddressRequest`](#OnchainAddressRequest) | onchain\_address is populated for deposit and exit entries. |
| `ark_address`oneof request | [`ArkAddressRequest`](#ArkAddressRequest) | ark\_address is populated for direct Ark send or receive entries. |
## `LightningInvoiceRequest`message
LightningInvoiceRequest captures the Lightning request associated with an activity entry.
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request. |
| `payment_hash` | `string` | payment\_hash identifies the invoice and remains stable after the invoice itself is no longer convenient to display. |
## `OnchainAddressRequest`message
OnchainAddressRequest captures the onchain address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | -------------------------------------------------------------------- |
| `address` | `string` | address is the bech32 onchain address originally issued or targeted. |
## `ArkAddressRequest`message
ArkAddressRequest captures the Ark address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | --------------------------------------------------------- |
| `address` | `string` | address is the Ark address originally issued or targeted. |
## `WalletEntryProgress`message
WalletEntryProgress carries lifecycle metadata normalized by the wallet service.
| Field | Type | Description |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`WalletEntryPhase`](#WalletEntryPhase) | phase is the coarse lifecycle phase for this entry. |
| `phase_label` | `string` | phase\_label is a short lowercase label that clients can render as-is while they migrate to enum-based presentation. |
| `payment_hash` | `string` | payment\_hash is populated for Lightning-backed send/recv entries. |
| `txid` | `string` | txid is populated when the backing ledger row has an onchain txid. |
| `confirmation_height` | `int32` | confirmation\_height is populated once the source records it. |
| `vtxo_outpoint` | `string` | vtxo\_outpoint is populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed Lightning-backed send this is the proof of payment for the paid invoice (sha256(preimage) == payment\_hash); it is empty until the preimage is durably known and for non-Lightning entries. |
## `WalletEntryPhase`enum
WalletEntryPhase is a coarse, wallet-facing lifecycle phase. It is not a replacement for status; status answers whether the operation is pending, complete, or failed, while phase explains the current backing-system step.
| Name | Number | Description |
| --------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_ENTRY_PHASE_UNSPECIFIED` | `0` | WALLET\_ENTRY\_PHASE\_UNSPECIFIED is the proto zero value used when the backing subsystem has no lifecycle hint. |
| `WALLET_ENTRY_PHASE_REQUEST_CREATED` | `1` | WALLET\_ENTRY\_PHASE\_REQUEST\_CREATED means the request was created but no payment has been observed. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_PAYMENT` | `2` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_PAYMENT means the wallet is waiting for an inbound payment or swap funding. |
| `WALLET_ENTRY_PHASE_PAYMENT_DETECTED` | `3` | WALLET\_ENTRY\_PHASE\_PAYMENT\_DETECTED means the backing subsystem has detected a payment but it is not yet settled. |
| `WALLET_ENTRY_PHASE_SETTLING` | `4` | WALLET\_ENTRY\_PHASE\_SETTLING means the operation is being settled through Ark, Lightning, or onchain machinery. |
| `WALLET_ENTRY_PHASE_CONFIRMED` | `5` | WALLET\_ENTRY\_PHASE\_CONFIRMED means the backing operation is confirmed or otherwise durably complete. |
| `WALLET_ENTRY_PHASE_REFUNDING` | `6` | WALLET\_ENTRY\_PHASE\_REFUNDING means the operation is currently refunding. |
| `WALLET_ENTRY_PHASE_REFUNDED` | `7` | WALLET\_ENTRY\_PHASE\_REFUNDED means the refund path completed. |
| `WALLET_ENTRY_PHASE_FAILED` | `8` | WALLET\_ENTRY\_PHASE\_FAILED means the backing operation reached a terminal failed state. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_CONFIRMATION` | `9` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_CONFIRMATION means an onchain payment has been detected and is waiting for block confirmation. |
## `EntryFailureCode`enum
EntryFailureCode is a stable, machine-readable classification of why a FAILED WalletEntry failed. It complements the free-text failure\_reason so clients can branch on failure cause without string matching.
| Name | Number | Description |
| --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `ENTRY_FAILURE_CODE_UNSPECIFIED` | `0` | ENTRY\_FAILURE\_CODE\_UNSPECIFIED is the zero value used for non-failed entries or when the cause is unknown. |
| `ENTRY_FAILURE_CODE_TIMED_OUT` | `1` | ENTRY\_FAILURE\_CODE\_TIMED\_OUT means the operation exceeded the wallet deadline before reaching a terminal state. |
| `ENTRY_FAILURE_CODE_EXPIRED` | `2` | ENTRY\_FAILURE\_CODE\_EXPIRED means the swap expired before it was funded. |
| `ENTRY_FAILURE_CODE_REFUNDED` | `3` | ENTRY\_FAILURE\_CODE\_REFUNDED means an outbound payment was refunded back to the wallet. |
| `ENTRY_FAILURE_CODE_NEEDS_INTERVENTION` | `4` | ENTRY\_FAILURE\_CODE\_NEEDS\_INTERVENTION means the swap reached an anomalous state requiring manual recovery. |
| `ENTRY_FAILURE_CODE_FAILED` | `5` | ENTRY\_FAILURE\_CODE\_FAILED is a generic terminal failure with no more specific classification. |
## `CreditReceive`message
CreditReceive describes a receive backed by server credits rather than a client-claimable vHTLC. It is populated on RecvResponse for the credit path.
| Field | Type | Description |
| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `operation_id` | `string` | operation\_id is the durable credit-subsystem operation id tracking this receive. It is the WalletEntry.id for credit RECV rows; the server-owned payment\_hash is in payment\_hash below. |
| `amount_sat` | `uint64` | amount\_sat is the amount in satoshis the caller asked to receive, credited to the wallet account once the server-owned invoice settles. |
| `payment_hash` | `string` | payment\_hash is the hex-encoded payment hash of the server-owned receive invoice. |
---
Source: https://wavelength.lightning.engineering/api/wallet/deposit.md
WalletService[CLI`wavecli recv`](/cli/recv/)
# Deposit
Deposit returns a boarding onchain address the caller can fund. The daemon rolls the boarding output into a VTXO during the next round. Surfaced internally and via \`recv --onchain\`; not a top-level CLI verb.
## Endpoints
gRPC`rpc Deposit(DepositRequest) returns (DepositResponse)`
REST`POST/v1/wallet/deposit`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli recv --onchain
```
## Request`DepositRequest`
DepositRequest asks WalletService.Deposit for a fresh boarding address.
| Field | Type | Description |
| -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `amt_sat_hint` | `uint64` | amt\_sat\_hint is an optional caller hint about the expected deposit amount. The daemon may use it to size internal accounting; the returned boarding address is not amount-bound. |
## Response`DepositResponse`
DepositResponse returns a fresh boarding address and the initial DEPOSIT-kind WalletEntry tracking the pending boarding operation.
| Field | Type | Description |
| ----------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `onchain_address` | `string` | onchain\_address is a fresh boarding address the caller should fund. The daemon monitors it and rolls the boarding output into the next round. |
| `entry` | [`WalletEntry`](#WalletEntry) | entry is the initial DEPOSIT-kind WalletEntry persisted to track the pending boarding operation. |
## `WalletEntry`message
WalletEntry is the single flat row type returned by the ACTIVITY view of List and by SubscribeWallet. The shape is deliberately minimal: display clients should be able to render kind/status/amount/fee/phase directly without parsing daemon-specific swap or ledger internals. Power-user detail belongs in WalletInspectionService.InspectActivity.
| Field | Type | Description |
| ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the stable identifier for this entry. Swap-backed SEND and RECV rows use the Lightning payment\_hash. Credit-backed RECV rows use the credit operation id (see CreditReceive.operation\_id); the payment\_hash lives on recv progress/detail instead. EXIT rows use leave\_job\_id (or sweep txid); DEPOSIT rows use the boarding outpoint or txid. |
| `kind` | [`EntryKind`](#EntryKind) | kind is the user-visible category. |
| `status` | [`EntryStatus`](#EntryStatus) | status is the coarse user-facing outcome: PENDING, COMPLETE, or FAILED. It mirrors the backing source's durable state and should not be reinterpreted by display clients. |
| `amount_sat` | `int64` | amount\_sat is signed: positive values are incoming to the wallet, negative values are outgoing. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this operation. |
| `counterparty` | `string` | counterparty is a short, display-friendly identifier of the other side: a truncated invoice for Lightning, an ark peer address for OOR, a bech32 onchain address for exits, or "boarding" for deposits. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last persisted update timestamp in unix seconds. List ACTIVITY and SubscribeWallet sort by this field, descending. |
| `note` | `string` | note is the caller-supplied label captured at submit time, if any. |
| `failure_reason` | `string` | failure\_reason is populated only when status is FAILED. It is a single human-readable string, never a coded enum tree. |
| `request` | [`WalletEntryRequest`](#WalletEntryRequest) | request is the original payment request or destination associated with the entry, when the backing subsystem persists it. This is useful for expanded views and inspection; compact clients should not need to parse it to explain status or lifecycle. |
| `progress` | [`WalletEntryProgress`](#WalletEntryProgress) | progress carries lifecycle-specific metadata already normalized by the wallet service. Clients can render phase\_label directly or switch on phase without inferring meaning from swap/ledger internals. |
| `failure_code`optional | [`EntryFailureCode`](#EntryFailureCode) | failure\_code is a stable, machine-readable classification of why a FAILED entry failed. It is set ONLY on FAILED entries; a non-failed entry omits the field entirely (presence-tracked), so its absence is the canonical "no failure" signal and ENTRY\_FAILURE\_CODE\_UNSPECIFIED is never sent on the wire. failure\_reason remains the human-readable supplement. |
## `EntryKind`enum
EntryKind tags each WalletEntry with the user-visible category of the underlying operation. Internal subtypes (same-Ark p2p vs real Lightning, OOR session correlators, round IDs) are deliberately not surfaced.
| Name | Number | Description |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------- |
| `ENTRY_KIND_UNSPECIFIED` | `0` | ENTRY\_KIND\_UNSPECIFIED is the proto zero value used when the operation category is unknown. |
| `ENTRY_KIND_SEND` | `1` | ENTRY\_KIND\_SEND is an outbound payment over any rail (Lightning, same-Ark, onchain, or credit). |
| `ENTRY_KIND_RECV` | `2` | ENTRY\_KIND\_RECV is an inbound receive. |
| `ENTRY_KIND_DEPOSIT` | `3` | ENTRY\_KIND\_DEPOSIT is a boarding deposit rolled into a VTXO. |
| `ENTRY_KIND_EXIT` | `4` | ENTRY\_KIND\_EXIT is a cooperative leave or unilateral exit to on-chain funds. |
## `EntryStatus`enum
EntryStatus collapses every backing FSM into the three states user-facing surfaces actually need.
| Name | Number | Description |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `ENTRY_STATUS_UNSPECIFIED` | `0` | ENTRY\_STATUS\_UNSPECIFIED is the proto zero value used when the backing state has not been mapped yet. |
| `ENTRY_STATUS_PENDING` | `1` | ENTRY\_STATUS\_PENDING means the operation is still in flight. |
| `ENTRY_STATUS_COMPLETE` | `2` | ENTRY\_STATUS\_COMPLETE means the operation reached a durable success state. |
| `ENTRY_STATUS_FAILED` | `3` | ENTRY\_STATUS\_FAILED means the operation reached a terminal failure state. |
## `WalletEntryRequest`message
WalletEntryRequest describes the user-recognizable request that created a WalletEntry. It is a oneof because each operation is backed by exactly one request shape.
| Field | Type | Description |
| -------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `lightning_invoice`oneof request | [`LightningInvoiceRequest`](#LightningInvoiceRequest) | lightning\_invoice is populated for Lightning send and receive entries. |
| `onchain_address`oneof request | [`OnchainAddressRequest`](#OnchainAddressRequest) | onchain\_address is populated for deposit and exit entries. |
| `ark_address`oneof request | [`ArkAddressRequest`](#ArkAddressRequest) | ark\_address is populated for direct Ark send or receive entries. |
## `LightningInvoiceRequest`message
LightningInvoiceRequest captures the Lightning request associated with an activity entry.
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request. |
| `payment_hash` | `string` | payment\_hash identifies the invoice and remains stable after the invoice itself is no longer convenient to display. |
## `OnchainAddressRequest`message
OnchainAddressRequest captures the onchain address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | -------------------------------------------------------------------- |
| `address` | `string` | address is the bech32 onchain address originally issued or targeted. |
## `ArkAddressRequest`message
ArkAddressRequest captures the Ark address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | --------------------------------------------------------- |
| `address` | `string` | address is the Ark address originally issued or targeted. |
## `WalletEntryProgress`message
WalletEntryProgress carries lifecycle metadata normalized by the wallet service.
| Field | Type | Description |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`WalletEntryPhase`](#WalletEntryPhase) | phase is the coarse lifecycle phase for this entry. |
| `phase_label` | `string` | phase\_label is a short lowercase label that clients can render as-is while they migrate to enum-based presentation. |
| `payment_hash` | `string` | payment\_hash is populated for Lightning-backed send/recv entries. |
| `txid` | `string` | txid is populated when the backing ledger row has an onchain txid. |
| `confirmation_height` | `int32` | confirmation\_height is populated once the source records it. |
| `vtxo_outpoint` | `string` | vtxo\_outpoint is populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed Lightning-backed send this is the proof of payment for the paid invoice (sha256(preimage) == payment\_hash); it is empty until the preimage is durably known and for non-Lightning entries. |
## `WalletEntryPhase`enum
WalletEntryPhase is a coarse, wallet-facing lifecycle phase. It is not a replacement for status; status answers whether the operation is pending, complete, or failed, while phase explains the current backing-system step.
| Name | Number | Description |
| --------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_ENTRY_PHASE_UNSPECIFIED` | `0` | WALLET\_ENTRY\_PHASE\_UNSPECIFIED is the proto zero value used when the backing subsystem has no lifecycle hint. |
| `WALLET_ENTRY_PHASE_REQUEST_CREATED` | `1` | WALLET\_ENTRY\_PHASE\_REQUEST\_CREATED means the request was created but no payment has been observed. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_PAYMENT` | `2` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_PAYMENT means the wallet is waiting for an inbound payment or swap funding. |
| `WALLET_ENTRY_PHASE_PAYMENT_DETECTED` | `3` | WALLET\_ENTRY\_PHASE\_PAYMENT\_DETECTED means the backing subsystem has detected a payment but it is not yet settled. |
| `WALLET_ENTRY_PHASE_SETTLING` | `4` | WALLET\_ENTRY\_PHASE\_SETTLING means the operation is being settled through Ark, Lightning, or onchain machinery. |
| `WALLET_ENTRY_PHASE_CONFIRMED` | `5` | WALLET\_ENTRY\_PHASE\_CONFIRMED means the backing operation is confirmed or otherwise durably complete. |
| `WALLET_ENTRY_PHASE_REFUNDING` | `6` | WALLET\_ENTRY\_PHASE\_REFUNDING means the operation is currently refunding. |
| `WALLET_ENTRY_PHASE_REFUNDED` | `7` | WALLET\_ENTRY\_PHASE\_REFUNDED means the refund path completed. |
| `WALLET_ENTRY_PHASE_FAILED` | `8` | WALLET\_ENTRY\_PHASE\_FAILED means the backing operation reached a terminal failed state. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_CONFIRMATION` | `9` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_CONFIRMATION means an onchain payment has been detected and is waiting for block confirmation. |
## `EntryFailureCode`enum
EntryFailureCode is a stable, machine-readable classification of why a FAILED WalletEntry failed. It complements the free-text failure\_reason so clients can branch on failure cause without string matching.
| Name | Number | Description |
| --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `ENTRY_FAILURE_CODE_UNSPECIFIED` | `0` | ENTRY\_FAILURE\_CODE\_UNSPECIFIED is the zero value used for non-failed entries or when the cause is unknown. |
| `ENTRY_FAILURE_CODE_TIMED_OUT` | `1` | ENTRY\_FAILURE\_CODE\_TIMED\_OUT means the operation exceeded the wallet deadline before reaching a terminal state. |
| `ENTRY_FAILURE_CODE_EXPIRED` | `2` | ENTRY\_FAILURE\_CODE\_EXPIRED means the swap expired before it was funded. |
| `ENTRY_FAILURE_CODE_REFUNDED` | `3` | ENTRY\_FAILURE\_CODE\_REFUNDED means an outbound payment was refunded back to the wallet. |
| `ENTRY_FAILURE_CODE_NEEDS_INTERVENTION` | `4` | ENTRY\_FAILURE\_CODE\_NEEDS\_INTERVENTION means the swap reached an anomalous state requiring manual recovery. |
| `ENTRY_FAILURE_CODE_FAILED` | `5` | ENTRY\_FAILURE\_CODE\_FAILED is a generic terminal failure with no more specific classification. |
---
Source: https://wavelength.lightning.engineering/api/wallet/balance.md
WalletService[CLI`wavecli balance`](/cli/balance/)
# Balance
Balance returns the unified balance across confirmed VTXOs and in-flight inbound and outbound amounts.
## Endpoints
gRPC`rpc Balance(BalanceRequest) returns (BalanceResponse)`
REST`POST/v1/wallet/balance`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli balance
```
## Request`BalanceRequest`
BalanceRequest is the empty request for WalletService.Balance.
This message has no fields.
## Response`BalanceResponse`
BalanceResponse is the unified balance summary returned by WalletService.Balance across confirmed, in-flight, and credit amounts.
| Field | Type | Description |
| ---------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `confirmed_sat` | `int64` | confirmed\_sat is the total spendable VTXO amount in satoshis. |
| `pending_in_sat` | `int64` | pending\_in\_sat is the total in-flight inbound amount (boarding plus receive operations). |
| `pending_out_sat` | `int64` | pending\_out\_sat is the total in-flight outbound amount (send plus exit operations). |
| `credit_available_sat` | `uint64` | credit\_available\_sat is the server-authoritative available credit balance for the wallet identity. |
| `credit_reserved_sat` | `uint64` | credit\_reserved\_sat is the server-authoritative in-flight credit reservation amount. |
---
Source: https://wavelength.lightning.engineering/api/wallet/list.md
WalletService[CLI`wavecli activity`](/cli/activity/)
# List
List returns the unified wallet view selected by ListRequest.view: ACTIVITY (default) is the merged WalletEntry stream; VTXOS is the live VTXO inventory; ONCHAIN is the boarding-plus-sweep on-chain history. The body oneof on ListResponse discriminates the typed result so agents see a tagged union, not a polymorphic blob.
## Endpoints
gRPC`rpc List(ListRequest) returns (ListResponse)`
REST`POST/v1/wallet/list`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli activity
```
## Request`ListRequest`
ListRequest selects the wallet view and filters for WalletService.List.
| Field | Type | Description |
| -------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `view` | [`ListView`](#ListView) | view selects which slice of wallet state to return. The default (LIST\_VIEW\_UNSPECIFIED) is treated as LIST\_VIEW\_ACTIVITY.WIRE-BREAKING CHANGE: this message intentionally renumbers the PR #440 fields (pending\_only/kinds/limit/offset). wavewalletrpc has no deployed external consumers yet, so the cleaner shape with \`view\` at field 1 is preferred over a backwards-compatible append. Any binary built against PR #440's ListRequest must be recompiled. |
| `pending_only` | `bool` | pending\_only filters the returned entries to those still in flight. Applies to the ACTIVITY view; ignored for VTXOS and ONCHAIN. |
| `kinds` | [`repeated EntryKind`](#EntryKind) | kinds optionally narrows the response to specific entry categories. Applies to the ACTIVITY view; ignored for VTXOS and ONCHAIN. When empty, all kinds are returned. |
| `limit` | `uint32` | limit caps the response size. Zero means use the daemon default. |
| `offset` | `uint32` | offset is the pagination offset within the chosen view. It applies to the VTXOS and ONCHAIN views; the ACTIVITY view paginates by the opaque cursor below instead and ignores offset. |
| `cursor` | `string` | cursor is the opaque pagination token for the ACTIVITY view. Empty starts from the newest entry; otherwise it is the next\_cursor returned by the previous ActivityList page. It is stable across concurrent inserts, so paging never skips or duplicates rows. Ignored for the VTXOS and ONCHAIN views. |
## Response`ListResponse`
ListResponse carries the typed result of WalletService.List as a oneof discriminated by the requested view.
| Field | Type | Description |
| -------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `activity`oneof body | [`ActivityList`](#ActivityList) | activity is set when the request view is ACTIVITY (the default): the merged, time-sorted WalletEntry stream. |
| `vtxos`oneof body | [`VTXOInventory`](#VTXOInventory) | vtxos is set when the request view is VTXOS: the live spendable VTXO inventory. |
| `onchain`oneof body | [`OnchainHistory`](#OnchainHistory) | onchain is set when the request view is ONCHAIN: the on-chain transaction history. |
## `ListView`enum
ListView selects which slice of wallet state List returns. The default (LIST\_VIEW\_UNSPECIFIED) is treated as LIST\_VIEW\_ACTIVITY for backwards feel: callers that don't care about the new shape keep getting the activity stream.
| Name | Number | Description |
| ----------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `LIST_VIEW_UNSPECIFIED` | `0` | LIST\_VIEW\_UNSPECIFIED is the proto zero value; List treats it as LIST\_VIEW\_ACTIVITY. |
| `LIST_VIEW_ACTIVITY` | `1` | LIST\_VIEW\_ACTIVITY returns the merged WalletEntry stream (send / recv / deposit / exit) across the swap subsystem, OOR sessions, the boarding ledger, and the unroll registry. |
| `LIST_VIEW_VTXOS` | `2` | LIST\_VIEW\_VTXOS returns the live VTXO inventory (one row per spendable VTXO). |
| `LIST_VIEW_ONCHAIN` | `3` | LIST\_VIEW\_ONCHAIN returns the on-chain transaction history (boarding deposits, boarding sweeps, leave outputs, round commitment txs that confirmed against this wallet). |
## `EntryKind`enum
EntryKind tags each WalletEntry with the user-visible category of the underlying operation. Internal subtypes (same-Ark p2p vs real Lightning, OOR session correlators, round IDs) are deliberately not surfaced.
| Name | Number | Description |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------- |
| `ENTRY_KIND_UNSPECIFIED` | `0` | ENTRY\_KIND\_UNSPECIFIED is the proto zero value used when the operation category is unknown. |
| `ENTRY_KIND_SEND` | `1` | ENTRY\_KIND\_SEND is an outbound payment over any rail (Lightning, same-Ark, onchain, or credit). |
| `ENTRY_KIND_RECV` | `2` | ENTRY\_KIND\_RECV is an inbound receive. |
| `ENTRY_KIND_DEPOSIT` | `3` | ENTRY\_KIND\_DEPOSIT is a boarding deposit rolled into a VTXO. |
| `ENTRY_KIND_EXIT` | `4` | ENTRY\_KIND\_EXIT is a cooperative leave or unilateral exit to on-chain funds. |
## `ActivityList`message
ActivityList is the ACTIVITY-view body of ListResponse: the merged, time-sorted WalletEntry stream.
| Field | Type | Description |
| ------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entries` | [`repeated WalletEntry`](#WalletEntry) | entries are the unified, time-sorted wallet operations. |
| `total` | `uint32` | total is the number of entries in this page. It is a page count, not a full-feed count: the feed is paged by an opaque cursor, so callers use has\_more, not total, to decide whether to fetch again. |
| `has_more` | `bool` | has\_more reports whether more entries exist after this page. |
| `next_cursor` | `string` | next\_cursor is the opaque token to pass as ListRequest.cursor to fetch the next page. Empty when has\_more is false. |
## `WalletEntry`message
WalletEntry is the single flat row type returned by the ACTIVITY view of List and by SubscribeWallet. The shape is deliberately minimal: display clients should be able to render kind/status/amount/fee/phase directly without parsing daemon-specific swap or ledger internals. Power-user detail belongs in WalletInspectionService.InspectActivity.
| Field | Type | Description |
| ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the stable identifier for this entry. Swap-backed SEND and RECV rows use the Lightning payment\_hash. Credit-backed RECV rows use the credit operation id (see CreditReceive.operation\_id); the payment\_hash lives on recv progress/detail instead. EXIT rows use leave\_job\_id (or sweep txid); DEPOSIT rows use the boarding outpoint or txid. |
| `kind` | [`EntryKind`](#EntryKind) | kind is the user-visible category. |
| `status` | [`EntryStatus`](#EntryStatus) | status is the coarse user-facing outcome: PENDING, COMPLETE, or FAILED. It mirrors the backing source's durable state and should not be reinterpreted by display clients. |
| `amount_sat` | `int64` | amount\_sat is signed: positive values are incoming to the wallet, negative values are outgoing. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this operation. |
| `counterparty` | `string` | counterparty is a short, display-friendly identifier of the other side: a truncated invoice for Lightning, an ark peer address for OOR, a bech32 onchain address for exits, or "boarding" for deposits. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last persisted update timestamp in unix seconds. List ACTIVITY and SubscribeWallet sort by this field, descending. |
| `note` | `string` | note is the caller-supplied label captured at submit time, if any. |
| `failure_reason` | `string` | failure\_reason is populated only when status is FAILED. It is a single human-readable string, never a coded enum tree. |
| `request` | [`WalletEntryRequest`](#WalletEntryRequest) | request is the original payment request or destination associated with the entry, when the backing subsystem persists it. This is useful for expanded views and inspection; compact clients should not need to parse it to explain status or lifecycle. |
| `progress` | [`WalletEntryProgress`](#WalletEntryProgress) | progress carries lifecycle-specific metadata already normalized by the wallet service. Clients can render phase\_label directly or switch on phase without inferring meaning from swap/ledger internals. |
| `failure_code`optional | [`EntryFailureCode`](#EntryFailureCode) | failure\_code is a stable, machine-readable classification of why a FAILED entry failed. It is set ONLY on FAILED entries; a non-failed entry omits the field entirely (presence-tracked), so its absence is the canonical "no failure" signal and ENTRY\_FAILURE\_CODE\_UNSPECIFIED is never sent on the wire. failure\_reason remains the human-readable supplement. |
## `EntryStatus`enum
EntryStatus collapses every backing FSM into the three states user-facing surfaces actually need.
| Name | Number | Description |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `ENTRY_STATUS_UNSPECIFIED` | `0` | ENTRY\_STATUS\_UNSPECIFIED is the proto zero value used when the backing state has not been mapped yet. |
| `ENTRY_STATUS_PENDING` | `1` | ENTRY\_STATUS\_PENDING means the operation is still in flight. |
| `ENTRY_STATUS_COMPLETE` | `2` | ENTRY\_STATUS\_COMPLETE means the operation reached a durable success state. |
| `ENTRY_STATUS_FAILED` | `3` | ENTRY\_STATUS\_FAILED means the operation reached a terminal failure state. |
## `WalletEntryRequest`message
WalletEntryRequest describes the user-recognizable request that created a WalletEntry. It is a oneof because each operation is backed by exactly one request shape.
| Field | Type | Description |
| -------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `lightning_invoice`oneof request | [`LightningInvoiceRequest`](#LightningInvoiceRequest) | lightning\_invoice is populated for Lightning send and receive entries. |
| `onchain_address`oneof request | [`OnchainAddressRequest`](#OnchainAddressRequest) | onchain\_address is populated for deposit and exit entries. |
| `ark_address`oneof request | [`ArkAddressRequest`](#ArkAddressRequest) | ark\_address is populated for direct Ark send or receive entries. |
## `LightningInvoiceRequest`message
LightningInvoiceRequest captures the Lightning request associated with an activity entry.
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request. |
| `payment_hash` | `string` | payment\_hash identifies the invoice and remains stable after the invoice itself is no longer convenient to display. |
## `OnchainAddressRequest`message
OnchainAddressRequest captures the onchain address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | -------------------------------------------------------------------- |
| `address` | `string` | address is the bech32 onchain address originally issued or targeted. |
## `ArkAddressRequest`message
ArkAddressRequest captures the Ark address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | --------------------------------------------------------- |
| `address` | `string` | address is the Ark address originally issued or targeted. |
## `WalletEntryProgress`message
WalletEntryProgress carries lifecycle metadata normalized by the wallet service.
| Field | Type | Description |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`WalletEntryPhase`](#WalletEntryPhase) | phase is the coarse lifecycle phase for this entry. |
| `phase_label` | `string` | phase\_label is a short lowercase label that clients can render as-is while they migrate to enum-based presentation. |
| `payment_hash` | `string` | payment\_hash is populated for Lightning-backed send/recv entries. |
| `txid` | `string` | txid is populated when the backing ledger row has an onchain txid. |
| `confirmation_height` | `int32` | confirmation\_height is populated once the source records it. |
| `vtxo_outpoint` | `string` | vtxo\_outpoint is populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed Lightning-backed send this is the proof of payment for the paid invoice (sha256(preimage) == payment\_hash); it is empty until the preimage is durably known and for non-Lightning entries. |
## `WalletEntryPhase`enum
WalletEntryPhase is a coarse, wallet-facing lifecycle phase. It is not a replacement for status; status answers whether the operation is pending, complete, or failed, while phase explains the current backing-system step.
| Name | Number | Description |
| --------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_ENTRY_PHASE_UNSPECIFIED` | `0` | WALLET\_ENTRY\_PHASE\_UNSPECIFIED is the proto zero value used when the backing subsystem has no lifecycle hint. |
| `WALLET_ENTRY_PHASE_REQUEST_CREATED` | `1` | WALLET\_ENTRY\_PHASE\_REQUEST\_CREATED means the request was created but no payment has been observed. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_PAYMENT` | `2` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_PAYMENT means the wallet is waiting for an inbound payment or swap funding. |
| `WALLET_ENTRY_PHASE_PAYMENT_DETECTED` | `3` | WALLET\_ENTRY\_PHASE\_PAYMENT\_DETECTED means the backing subsystem has detected a payment but it is not yet settled. |
| `WALLET_ENTRY_PHASE_SETTLING` | `4` | WALLET\_ENTRY\_PHASE\_SETTLING means the operation is being settled through Ark, Lightning, or onchain machinery. |
| `WALLET_ENTRY_PHASE_CONFIRMED` | `5` | WALLET\_ENTRY\_PHASE\_CONFIRMED means the backing operation is confirmed or otherwise durably complete. |
| `WALLET_ENTRY_PHASE_REFUNDING` | `6` | WALLET\_ENTRY\_PHASE\_REFUNDING means the operation is currently refunding. |
| `WALLET_ENTRY_PHASE_REFUNDED` | `7` | WALLET\_ENTRY\_PHASE\_REFUNDED means the refund path completed. |
| `WALLET_ENTRY_PHASE_FAILED` | `8` | WALLET\_ENTRY\_PHASE\_FAILED means the backing operation reached a terminal failed state. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_CONFIRMATION` | `9` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_CONFIRMATION means an onchain payment has been detected and is waiting for block confirmation. |
## `EntryFailureCode`enum
EntryFailureCode is a stable, machine-readable classification of why a FAILED WalletEntry failed. It complements the free-text failure\_reason so clients can branch on failure cause without string matching.
| Name | Number | Description |
| --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `ENTRY_FAILURE_CODE_UNSPECIFIED` | `0` | ENTRY\_FAILURE\_CODE\_UNSPECIFIED is the zero value used for non-failed entries or when the cause is unknown. |
| `ENTRY_FAILURE_CODE_TIMED_OUT` | `1` | ENTRY\_FAILURE\_CODE\_TIMED\_OUT means the operation exceeded the wallet deadline before reaching a terminal state. |
| `ENTRY_FAILURE_CODE_EXPIRED` | `2` | ENTRY\_FAILURE\_CODE\_EXPIRED means the swap expired before it was funded. |
| `ENTRY_FAILURE_CODE_REFUNDED` | `3` | ENTRY\_FAILURE\_CODE\_REFUNDED means an outbound payment was refunded back to the wallet. |
| `ENTRY_FAILURE_CODE_NEEDS_INTERVENTION` | `4` | ENTRY\_FAILURE\_CODE\_NEEDS\_INTERVENTION means the swap reached an anomalous state requiring manual recovery. |
| `ENTRY_FAILURE_CODE_FAILED` | `5` | ENTRY\_FAILURE\_CODE\_FAILED is a generic terminal failure with no more specific classification. |
## `VTXOInventory`message
VTXOInventory is the VTXOS-view body of ListResponse: the live spendable VTXO inventory.
| Field | Type | Description |
| ------- | ------------------------------------ | ----------------------------------------------------------------------- |
| `vtxos` | [`repeated WalletVTXO`](#WalletVTXO) | vtxos are the live spendable VTXOs in the wallet. Order is unspecified. |
| `total` | `uint32` | total is the count of all live VTXOs before limit/offset. |
## `WalletVTXO`message
WalletVTXO is the wallet-facing view of one VTXO. Internal lifecycle detail (forfeiting flow, chain depth, etc.) is hidden; power-users can reach the full shape via \`ark vtxos list\`.
| Field | Type | Description |
| ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | outpoint is the VTXO's outpoint in "txid:index" format. |
| `amount_sat` | `int64` | amount\_sat is the value of the VTXO in satoshis. |
| `status` | `string` | status is a short lowercase string: "live", "pending\_forfeit", "forfeiting", "spending", "unilateral\_exit". Internal terminal states (forfeited / spent / failed) are filtered out of the wallet view. |
| `batch_expiry` | `int32` | batch\_expiry is the absolute block height at which the batch-level timelock expires. |
| `relative_expiry` | `uint32` | relative\_expiry is the CSV delay (in blocks) for the unilateral exit path. |
| `commitment_txid` | `string` | commitment\_txid is the hex-encoded txid of the on-chain commitment transaction anchoring this VTXO's tree. |
## `OnchainHistory`message
OnchainHistory is the ONCHAIN-view body of ListResponse: the on-chain transaction history.
| Field | Type | Description |
| ---------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `txs` | [`repeated OnchainTx`](#OnchainTx) | txs are the on-chain transaction history rows: boarding deposits, boarding sweeps, leave outputs, round commitment txs that touched this wallet. Sorted newest first. |
| `total` | `uint32` | total is the count of matching rows before limit/offset. |
| `has_more` | `bool` | has\_more is true when another page is available. |
## `OnchainTx`message
OnchainTx is the wallet-facing view of one on-chain transaction. It flattens the daemon's richer TransactionHistoryEntry shape to the fields a wallet user actually needs.
| Field | Type | Description |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `txid` | `string` | txid is the hex-encoded transaction id, when known. Some ledger rows (e.g. fee\_paid) do not carry a txid; those entries surface with txid="" and the row is still informative. |
| `kind` | `string` | kind is the high-level transaction type: "boarding", "sweep", "round", "oor", or "fee". |
| `amount_sat` | `int64` | amount\_sat is the signed transaction amount in satoshis. Positive values are credits to this wallet; negative values are debits. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this transaction, when known. |
| `status` | `string` | status is a short lowercase string: "confirmed", "pending", "recorded", or a backing-specific lifecycle value. |
| `confirmation_height` | `int32` | confirmation\_height is set when the source records a chain confirmation height. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `description` | `string` | description is a human-readable local note. |
---
Source: https://wavelength.lightning.engineering/api/wallet/subscribe-wallet.md
WalletServiceserver streaming
# SubscribeWallet
SubscribeWallet streams activity updates as they happen. The stream is resumable: each response carries a monotonic cursor the client can reconnect from to replay everything after it without gaps.
## Endpoints
gRPC`rpc SubscribeWallet(SubscribeWalletRequest) returns (stream SubscribeWalletResponse)`
REST`POST/v1/wallet/subscribe`
## Examples
GoPythonJavaScriptcurlfetch
```
conn, err := grpc.NewClient("localhost:10029",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := wavewalletrpc.NewWalletServiceClient(conn)
ctx := context.Background()
req := &wavewalletrpc.SubscribeWalletRequest{
IncludeExisting: true,
}
stream, err := client.SubscribeWallet(ctx, req)
if err != nil {
log.Fatal(err)
}
for {
update, err := stream.Recv()
if err != nil {
break
}
fmt.Println(update)
}
```
## Request`SubscribeWalletRequest`
SubscribeWalletRequest configures the WalletService.SubscribeWallet stream.
| Field | Type | Description |
| ------------------ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `include_existing` | `bool` | include\_existing replays the full activity history before live updates. It is ignored when cursor is set (cursor already selects the replay start). |
| `kinds` | [`repeated EntryKind`](#EntryKind) | kinds optionally narrows the stream to specific entry categories. When empty, all kinds are streamed. |
| `cursor` | `int64` | cursor resumes the stream from a prior SubscribeWalletResponse.cursor: the daemon replays every event after it, then continues live. Zero (unset) starts from the full history when include\_existing is set, or from the current tip otherwise. |
## Response`stream SubscribeWalletResponse`
SubscribeWalletResponse is one item in the SubscribeWallet stream: either a projected activity row or a gap signal telling the subscriber it fell behind and must reconcile.
| Field | Type | Description |
| ------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cursor` | `int64` | cursor is the monotonic event-log position of this update. A client persists it and reconnects with SubscribeWalletRequest.cursor set to the last value it processed to resume without gaps. The sequence is monotonic but NOT contiguous: a value may be skipped, so a client treats any cursor past its last as new and never infers a dropped event from a gap in the numbers. |
| `entry`oneof update | [`WalletEntry`](#WalletEntry) | entry is a projected activity row at cursor. |
| `gap`oneof update | [`SubscribeGap`](#SubscribeGap) | gap signals the subscriber fell behind the live buffer and should reconcile current state via List, then resume from cursor. |
## `EntryKind`enum
EntryKind tags each WalletEntry with the user-visible category of the underlying operation. Internal subtypes (same-Ark p2p vs real Lightning, OOR session correlators, round IDs) are deliberately not surfaced.
| Name | Number | Description |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------- |
| `ENTRY_KIND_UNSPECIFIED` | `0` | ENTRY\_KIND\_UNSPECIFIED is the proto zero value used when the operation category is unknown. |
| `ENTRY_KIND_SEND` | `1` | ENTRY\_KIND\_SEND is an outbound payment over any rail (Lightning, same-Ark, onchain, or credit). |
| `ENTRY_KIND_RECV` | `2` | ENTRY\_KIND\_RECV is an inbound receive. |
| `ENTRY_KIND_DEPOSIT` | `3` | ENTRY\_KIND\_DEPOSIT is a boarding deposit rolled into a VTXO. |
| `ENTRY_KIND_EXIT` | `4` | ENTRY\_KIND\_EXIT is a cooperative leave or unilateral exit to on-chain funds. |
## `WalletEntry`message
WalletEntry is the single flat row type returned by the ACTIVITY view of List and by SubscribeWallet. The shape is deliberately minimal: display clients should be able to render kind/status/amount/fee/phase directly without parsing daemon-specific swap or ledger internals. Power-user detail belongs in WalletInspectionService.InspectActivity.
| Field | Type | Description |
| ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the stable identifier for this entry. Swap-backed SEND and RECV rows use the Lightning payment\_hash. Credit-backed RECV rows use the credit operation id (see CreditReceive.operation\_id); the payment\_hash lives on recv progress/detail instead. EXIT rows use leave\_job\_id (or sweep txid); DEPOSIT rows use the boarding outpoint or txid. |
| `kind` | [`EntryKind`](#EntryKind) | kind is the user-visible category. |
| `status` | [`EntryStatus`](#EntryStatus) | status is the coarse user-facing outcome: PENDING, COMPLETE, or FAILED. It mirrors the backing source's durable state and should not be reinterpreted by display clients. |
| `amount_sat` | `int64` | amount\_sat is signed: positive values are incoming to the wallet, negative values are outgoing. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this operation. |
| `counterparty` | `string` | counterparty is a short, display-friendly identifier of the other side: a truncated invoice for Lightning, an ark peer address for OOR, a bech32 onchain address for exits, or "boarding" for deposits. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last persisted update timestamp in unix seconds. List ACTIVITY and SubscribeWallet sort by this field, descending. |
| `note` | `string` | note is the caller-supplied label captured at submit time, if any. |
| `failure_reason` | `string` | failure\_reason is populated only when status is FAILED. It is a single human-readable string, never a coded enum tree. |
| `request` | [`WalletEntryRequest`](#WalletEntryRequest) | request is the original payment request or destination associated with the entry, when the backing subsystem persists it. This is useful for expanded views and inspection; compact clients should not need to parse it to explain status or lifecycle. |
| `progress` | [`WalletEntryProgress`](#WalletEntryProgress) | progress carries lifecycle-specific metadata already normalized by the wallet service. Clients can render phase\_label directly or switch on phase without inferring meaning from swap/ledger internals. |
| `failure_code`optional | [`EntryFailureCode`](#EntryFailureCode) | failure\_code is a stable, machine-readable classification of why a FAILED entry failed. It is set ONLY on FAILED entries; a non-failed entry omits the field entirely (presence-tracked), so its absence is the canonical "no failure" signal and ENTRY\_FAILURE\_CODE\_UNSPECIFIED is never sent on the wire. failure\_reason remains the human-readable supplement. |
## `EntryStatus`enum
EntryStatus collapses every backing FSM into the three states user-facing surfaces actually need.
| Name | Number | Description |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `ENTRY_STATUS_UNSPECIFIED` | `0` | ENTRY\_STATUS\_UNSPECIFIED is the proto zero value used when the backing state has not been mapped yet. |
| `ENTRY_STATUS_PENDING` | `1` | ENTRY\_STATUS\_PENDING means the operation is still in flight. |
| `ENTRY_STATUS_COMPLETE` | `2` | ENTRY\_STATUS\_COMPLETE means the operation reached a durable success state. |
| `ENTRY_STATUS_FAILED` | `3` | ENTRY\_STATUS\_FAILED means the operation reached a terminal failure state. |
## `WalletEntryRequest`message
WalletEntryRequest describes the user-recognizable request that created a WalletEntry. It is a oneof because each operation is backed by exactly one request shape.
| Field | Type | Description |
| -------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `lightning_invoice`oneof request | [`LightningInvoiceRequest`](#LightningInvoiceRequest) | lightning\_invoice is populated for Lightning send and receive entries. |
| `onchain_address`oneof request | [`OnchainAddressRequest`](#OnchainAddressRequest) | onchain\_address is populated for deposit and exit entries. |
| `ark_address`oneof request | [`ArkAddressRequest`](#ArkAddressRequest) | ark\_address is populated for direct Ark send or receive entries. |
## `LightningInvoiceRequest`message
LightningInvoiceRequest captures the Lightning request associated with an activity entry.
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request. |
| `payment_hash` | `string` | payment\_hash identifies the invoice and remains stable after the invoice itself is no longer convenient to display. |
## `OnchainAddressRequest`message
OnchainAddressRequest captures the onchain address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | -------------------------------------------------------------------- |
| `address` | `string` | address is the bech32 onchain address originally issued or targeted. |
## `ArkAddressRequest`message
ArkAddressRequest captures the Ark address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | --------------------------------------------------------- |
| `address` | `string` | address is the Ark address originally issued or targeted. |
## `WalletEntryProgress`message
WalletEntryProgress carries lifecycle metadata normalized by the wallet service.
| Field | Type | Description |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`WalletEntryPhase`](#WalletEntryPhase) | phase is the coarse lifecycle phase for this entry. |
| `phase_label` | `string` | phase\_label is a short lowercase label that clients can render as-is while they migrate to enum-based presentation. |
| `payment_hash` | `string` | payment\_hash is populated for Lightning-backed send/recv entries. |
| `txid` | `string` | txid is populated when the backing ledger row has an onchain txid. |
| `confirmation_height` | `int32` | confirmation\_height is populated once the source records it. |
| `vtxo_outpoint` | `string` | vtxo\_outpoint is populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed Lightning-backed send this is the proof of payment for the paid invoice (sha256(preimage) == payment\_hash); it is empty until the preimage is durably known and for non-Lightning entries. |
## `WalletEntryPhase`enum
WalletEntryPhase is a coarse, wallet-facing lifecycle phase. It is not a replacement for status; status answers whether the operation is pending, complete, or failed, while phase explains the current backing-system step.
| Name | Number | Description |
| --------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_ENTRY_PHASE_UNSPECIFIED` | `0` | WALLET\_ENTRY\_PHASE\_UNSPECIFIED is the proto zero value used when the backing subsystem has no lifecycle hint. |
| `WALLET_ENTRY_PHASE_REQUEST_CREATED` | `1` | WALLET\_ENTRY\_PHASE\_REQUEST\_CREATED means the request was created but no payment has been observed. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_PAYMENT` | `2` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_PAYMENT means the wallet is waiting for an inbound payment or swap funding. |
| `WALLET_ENTRY_PHASE_PAYMENT_DETECTED` | `3` | WALLET\_ENTRY\_PHASE\_PAYMENT\_DETECTED means the backing subsystem has detected a payment but it is not yet settled. |
| `WALLET_ENTRY_PHASE_SETTLING` | `4` | WALLET\_ENTRY\_PHASE\_SETTLING means the operation is being settled through Ark, Lightning, or onchain machinery. |
| `WALLET_ENTRY_PHASE_CONFIRMED` | `5` | WALLET\_ENTRY\_PHASE\_CONFIRMED means the backing operation is confirmed or otherwise durably complete. |
| `WALLET_ENTRY_PHASE_REFUNDING` | `6` | WALLET\_ENTRY\_PHASE\_REFUNDING means the operation is currently refunding. |
| `WALLET_ENTRY_PHASE_REFUNDED` | `7` | WALLET\_ENTRY\_PHASE\_REFUNDED means the refund path completed. |
| `WALLET_ENTRY_PHASE_FAILED` | `8` | WALLET\_ENTRY\_PHASE\_FAILED means the backing operation reached a terminal failed state. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_CONFIRMATION` | `9` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_CONFIRMATION means an onchain payment has been detected and is waiting for block confirmation. |
## `EntryFailureCode`enum
EntryFailureCode is a stable, machine-readable classification of why a FAILED WalletEntry failed. It complements the free-text failure\_reason so clients can branch on failure cause without string matching.
| Name | Number | Description |
| --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `ENTRY_FAILURE_CODE_UNSPECIFIED` | `0` | ENTRY\_FAILURE\_CODE\_UNSPECIFIED is the zero value used for non-failed entries or when the cause is unknown. |
| `ENTRY_FAILURE_CODE_TIMED_OUT` | `1` | ENTRY\_FAILURE\_CODE\_TIMED\_OUT means the operation exceeded the wallet deadline before reaching a terminal state. |
| `ENTRY_FAILURE_CODE_EXPIRED` | `2` | ENTRY\_FAILURE\_CODE\_EXPIRED means the swap expired before it was funded. |
| `ENTRY_FAILURE_CODE_REFUNDED` | `3` | ENTRY\_FAILURE\_CODE\_REFUNDED means an outbound payment was refunded back to the wallet. |
| `ENTRY_FAILURE_CODE_NEEDS_INTERVENTION` | `4` | ENTRY\_FAILURE\_CODE\_NEEDS\_INTERVENTION means the swap reached an anomalous state requiring manual recovery. |
| `ENTRY_FAILURE_CODE_FAILED` | `5` | ENTRY\_FAILURE\_CODE\_FAILED is a generic terminal failure with no more specific classification. |
## `SubscribeGap`message
SubscribeGap tells a subscriber the daemon could not keep it current on the live stream (a slow consumer overflowed the send buffer). The subscriber should reconcile via List and resume from the enclosing cursor. No activity is lost: the canonical event log retains every event, so the resume replays anything missed.
| Field | Type | Description |
| -------- | -------- | ------------------------------------------------------------------------ |
| `reason` | `string` | reason is a short human-readable description of why the gap was emitted. |
---
Source: https://wavelength.lightning.engineering/api/wallet/inspect-activity.md
WalletInspectionService[CLI`wavecli activity`](/cli/activity/)
# InspectActivity
InspectActivity returns a technical trace for one WalletEntry id.
## Endpoints
gRPC`rpc InspectActivity(InspectActivityRequest) returns (InspectActivityResponse)`
REST`POST/v1/wallet/inspect/activity`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli activity inspect a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9
```
## Request`InspectActivityRequest`
InspectActivityRequest identifies the activity entry to inspect.
| Field | Type | Description |
| -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the WalletEntry.id to inspect. InspectActivity searches the daemon's current activity window, which is capped by the daemon maximum list limit. |
| `ledger_limit` | `uint32` | ledger\_limit caps how many ledger rows are scanned for correlation. Zero means use the daemon's maximum list limit. |
## Response`InspectActivityResponse`
InspectActivityResponse contains the friendly activity row plus the lower-level records that explain how the daemon derived it.
| Field | Type | Description |
| ------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entry` | [`WalletEntry`](#WalletEntry) | entry is the user-facing activity row being inspected. |
| `swap` | [`ActivitySwapTrace`](#ActivitySwapTrace) | swap is populated when the entry is backed by a pay or receive swap. |
| `vtxos` | [`repeated ActivityVTXOTrace`](#ActivityVTXOTrace) | vtxos contains best-effort VTXO movements correlated to this activity. Rows are derived from local swap summaries and ledger accounting, so ids may be OOR session/output ids when a full VTXO outpoint is not persisted by the source row. |
| `ledger_rows` | [`repeated ActivityLedgerTrace`](#ActivityLedgerTrace) | ledger\_rows are the local accounting rows used to build the trace, including rows hidden from the friendly activity feed. |
| `notes` | `repeated string` | notes contains plain-English caveats about best-effort correlation. |
## `WalletEntry`message
WalletEntry is the single flat row type returned by the ACTIVITY view of List and by SubscribeWallet. The shape is deliberately minimal: display clients should be able to render kind/status/amount/fee/phase directly without parsing daemon-specific swap or ledger internals. Power-user detail belongs in WalletInspectionService.InspectActivity.
| Field | Type | Description |
| ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the stable identifier for this entry. Swap-backed SEND and RECV rows use the Lightning payment\_hash. Credit-backed RECV rows use the credit operation id (see CreditReceive.operation\_id); the payment\_hash lives on recv progress/detail instead. EXIT rows use leave\_job\_id (or sweep txid); DEPOSIT rows use the boarding outpoint or txid. |
| `kind` | [`EntryKind`](#EntryKind) | kind is the user-visible category. |
| `status` | [`EntryStatus`](#EntryStatus) | status is the coarse user-facing outcome: PENDING, COMPLETE, or FAILED. It mirrors the backing source's durable state and should not be reinterpreted by display clients. |
| `amount_sat` | `int64` | amount\_sat is signed: positive values are incoming to the wallet, negative values are outgoing. |
| `fee_sat` | `int64` | fee\_sat is the absolute fee paid (or pending) for this operation. |
| `counterparty` | `string` | counterparty is a short, display-friendly identifier of the other side: a truncated invoice for Lightning, an ark peer address for OOR, a bech32 onchain address for exits, or "boarding" for deposits. |
| `created_at_unix` | `int64` | created\_at\_unix is the local creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last persisted update timestamp in unix seconds. List ACTIVITY and SubscribeWallet sort by this field, descending. |
| `note` | `string` | note is the caller-supplied label captured at submit time, if any. |
| `failure_reason` | `string` | failure\_reason is populated only when status is FAILED. It is a single human-readable string, never a coded enum tree. |
| `request` | [`WalletEntryRequest`](#WalletEntryRequest) | request is the original payment request or destination associated with the entry, when the backing subsystem persists it. This is useful for expanded views and inspection; compact clients should not need to parse it to explain status or lifecycle. |
| `progress` | [`WalletEntryProgress`](#WalletEntryProgress) | progress carries lifecycle-specific metadata already normalized by the wallet service. Clients can render phase\_label directly or switch on phase without inferring meaning from swap/ledger internals. |
| `failure_code`optional | [`EntryFailureCode`](#EntryFailureCode) | failure\_code is a stable, machine-readable classification of why a FAILED entry failed. It is set ONLY on FAILED entries; a non-failed entry omits the field entirely (presence-tracked), so its absence is the canonical "no failure" signal and ENTRY\_FAILURE\_CODE\_UNSPECIFIED is never sent on the wire. failure\_reason remains the human-readable supplement. |
## `EntryKind`enum
EntryKind tags each WalletEntry with the user-visible category of the underlying operation. Internal subtypes (same-Ark p2p vs real Lightning, OOR session correlators, round IDs) are deliberately not surfaced.
| Name | Number | Description |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------- |
| `ENTRY_KIND_UNSPECIFIED` | `0` | ENTRY\_KIND\_UNSPECIFIED is the proto zero value used when the operation category is unknown. |
| `ENTRY_KIND_SEND` | `1` | ENTRY\_KIND\_SEND is an outbound payment over any rail (Lightning, same-Ark, onchain, or credit). |
| `ENTRY_KIND_RECV` | `2` | ENTRY\_KIND\_RECV is an inbound receive. |
| `ENTRY_KIND_DEPOSIT` | `3` | ENTRY\_KIND\_DEPOSIT is a boarding deposit rolled into a VTXO. |
| `ENTRY_KIND_EXIT` | `4` | ENTRY\_KIND\_EXIT is a cooperative leave or unilateral exit to on-chain funds. |
## `EntryStatus`enum
EntryStatus collapses every backing FSM into the three states user-facing surfaces actually need.
| Name | Number | Description |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `ENTRY_STATUS_UNSPECIFIED` | `0` | ENTRY\_STATUS\_UNSPECIFIED is the proto zero value used when the backing state has not been mapped yet. |
| `ENTRY_STATUS_PENDING` | `1` | ENTRY\_STATUS\_PENDING means the operation is still in flight. |
| `ENTRY_STATUS_COMPLETE` | `2` | ENTRY\_STATUS\_COMPLETE means the operation reached a durable success state. |
| `ENTRY_STATUS_FAILED` | `3` | ENTRY\_STATUS\_FAILED means the operation reached a terminal failure state. |
## `WalletEntryRequest`message
WalletEntryRequest describes the user-recognizable request that created a WalletEntry. It is a oneof because each operation is backed by exactly one request shape.
| Field | Type | Description |
| -------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
| `lightning_invoice`oneof request | [`LightningInvoiceRequest`](#LightningInvoiceRequest) | lightning\_invoice is populated for Lightning send and receive entries. |
| `onchain_address`oneof request | [`OnchainAddressRequest`](#OnchainAddressRequest) | onchain\_address is populated for deposit and exit entries. |
| `ark_address`oneof request | [`ArkAddressRequest`](#ArkAddressRequest) | ark\_address is populated for direct Ark send or receive entries. |
## `LightningInvoiceRequest`message
LightningInvoiceRequest captures the Lightning request associated with an activity entry.
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `invoice` | `string` | invoice is the BOLT-11 payment request. |
| `payment_hash` | `string` | payment\_hash identifies the invoice and remains stable after the invoice itself is no longer convenient to display. |
## `OnchainAddressRequest`message
OnchainAddressRequest captures the onchain address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | -------------------------------------------------------------------- |
| `address` | `string` | address is the bech32 onchain address originally issued or targeted. |
## `ArkAddressRequest`message
ArkAddressRequest captures the Ark address associated with an activity entry.
| Field | Type | Description |
| --------- | -------- | --------------------------------------------------------- |
| `address` | `string` | address is the Ark address originally issued or targeted. |
## `WalletEntryProgress`message
WalletEntryProgress carries lifecycle metadata normalized by the wallet service.
| Field | Type | Description |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `phase` | [`WalletEntryPhase`](#WalletEntryPhase) | phase is the coarse lifecycle phase for this entry. |
| `phase_label` | `string` | phase\_label is a short lowercase label that clients can render as-is while they migrate to enum-based presentation. |
| `payment_hash` | `string` | payment\_hash is populated for Lightning-backed send/recv entries. |
| `txid` | `string` | txid is populated when the backing ledger row has an onchain txid. |
| `confirmation_height` | `int32` | confirmation\_height is populated once the source records it. |
| `vtxo_outpoint` | `string` | vtxo\_outpoint is populated when a swap observes the Ark vHTLC output. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed Lightning-backed send this is the proof of payment for the paid invoice (sha256(preimage) == payment\_hash); it is empty until the preimage is durably known and for non-Lightning entries. |
## `WalletEntryPhase`enum
WalletEntryPhase is a coarse, wallet-facing lifecycle phase. It is not a replacement for status; status answers whether the operation is pending, complete, or failed, while phase explains the current backing-system step.
| Name | Number | Description |
| --------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `WALLET_ENTRY_PHASE_UNSPECIFIED` | `0` | WALLET\_ENTRY\_PHASE\_UNSPECIFIED is the proto zero value used when the backing subsystem has no lifecycle hint. |
| `WALLET_ENTRY_PHASE_REQUEST_CREATED` | `1` | WALLET\_ENTRY\_PHASE\_REQUEST\_CREATED means the request was created but no payment has been observed. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_PAYMENT` | `2` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_PAYMENT means the wallet is waiting for an inbound payment or swap funding. |
| `WALLET_ENTRY_PHASE_PAYMENT_DETECTED` | `3` | WALLET\_ENTRY\_PHASE\_PAYMENT\_DETECTED means the backing subsystem has detected a payment but it is not yet settled. |
| `WALLET_ENTRY_PHASE_SETTLING` | `4` | WALLET\_ENTRY\_PHASE\_SETTLING means the operation is being settled through Ark, Lightning, or onchain machinery. |
| `WALLET_ENTRY_PHASE_CONFIRMED` | `5` | WALLET\_ENTRY\_PHASE\_CONFIRMED means the backing operation is confirmed or otherwise durably complete. |
| `WALLET_ENTRY_PHASE_REFUNDING` | `6` | WALLET\_ENTRY\_PHASE\_REFUNDING means the operation is currently refunding. |
| `WALLET_ENTRY_PHASE_REFUNDED` | `7` | WALLET\_ENTRY\_PHASE\_REFUNDED means the refund path completed. |
| `WALLET_ENTRY_PHASE_FAILED` | `8` | WALLET\_ENTRY\_PHASE\_FAILED means the backing operation reached a terminal failed state. |
| `WALLET_ENTRY_PHASE_WAITING_FOR_CONFIRMATION` | `9` | WALLET\_ENTRY\_PHASE\_WAITING\_FOR\_CONFIRMATION means an onchain payment has been detected and is waiting for block confirmation. |
## `EntryFailureCode`enum
EntryFailureCode is a stable, machine-readable classification of why a FAILED WalletEntry failed. It complements the free-text failure\_reason so clients can branch on failure cause without string matching.
| Name | Number | Description |
| --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `ENTRY_FAILURE_CODE_UNSPECIFIED` | `0` | ENTRY\_FAILURE\_CODE\_UNSPECIFIED is the zero value used for non-failed entries or when the cause is unknown. |
| `ENTRY_FAILURE_CODE_TIMED_OUT` | `1` | ENTRY\_FAILURE\_CODE\_TIMED\_OUT means the operation exceeded the wallet deadline before reaching a terminal state. |
| `ENTRY_FAILURE_CODE_EXPIRED` | `2` | ENTRY\_FAILURE\_CODE\_EXPIRED means the swap expired before it was funded. |
| `ENTRY_FAILURE_CODE_REFUNDED` | `3` | ENTRY\_FAILURE\_CODE\_REFUNDED means an outbound payment was refunded back to the wallet. |
| `ENTRY_FAILURE_CODE_NEEDS_INTERVENTION` | `4` | ENTRY\_FAILURE\_CODE\_NEEDS\_INTERVENTION means the swap reached an anomalous state requiring manual recovery. |
| `ENTRY_FAILURE_CODE_FAILED` | `5` | ENTRY\_FAILURE\_CODE\_FAILED is a generic terminal failure with no more specific classification. |
## `ActivitySwapTrace`message
ActivitySwapTrace is the swap-service snapshot correlated to one wallet activity entry.
| Field | Type | Description |
| -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_hash` | `string` | payment\_hash is the Lightning payment hash that identifies the swap-backed activity. |
| `direction` | `string` | direction is the raw swapclientrpc.SwapDirection enum name, kept as a string so this debug surface can expose swap state without importing the swap service proto into the wallet proto. |
| `state` | `string` | state is the raw swapclientrpc.SwapState enum name at inspection time. |
| `pending` | `bool` | pending is true while the backing swap state machine is still active. |
| `amount_sat` | `int64` | amount\_sat is the swap amount in satoshis using the swap service's unsigned amount convention. |
| `fee_sat` | `uint64` | fee\_sat is the fee reported by the swap service for this swap. |
| `invoice` | `string` | invoice is the BOLT-11 invoice associated with the swap when known. |
| `vhtlc_outpoint` | `string` | vhtlc\_outpoint is the Ark vHTLC output observed for the swap when known. |
| `vhtlc_amount_sat` | `int64` | vhtlc\_amount\_sat is the amount held by vhtlc\_outpoint when known. |
| `funding_session_id` | `string` | funding\_session\_id is the OOR session that funded the swap when known. |
| `claim_session_id` | `string` | claim\_session\_id is the OOR session that claimed the swap when known. |
| `refund_session_id` | `string` | refund\_session\_id is the OOR session that refunded the swap when known. |
| `terminal_reason` | `string` | terminal\_reason is the swap service's terminal reason string when the swap reached a failed or intervention state. |
| `created_at_unix` | `int64` | created\_at\_unix is the swap creation timestamp in unix seconds. |
| `updated_at_unix` | `int64` | updated\_at\_unix is the last swap update timestamp in unix seconds. |
| `deadline_unix` | `int64` | deadline\_unix is the swap deadline in unix seconds when the swap service exposes one. |
| `refund_locktime` | `uint32` | refund\_locktime is the absolute locktime after which the swap can be refunded. |
| `settlement_type` | `string` | settlement\_type is the raw swapclientrpc.SwapSettlementType enum name when known. |
| `sender_pubkey` | `string` | sender\_pubkey is the compressed SEC-encoded vHTLC sender key when known. |
| `preimage` | `string` | preimage is the hex-encoded Lightning payment preimage once the swap revealed it. For a completed pay swap this is the proof of payment for the paid invoice; it is empty until the preimage is durably known. |
## `ActivityVTXOTrace`message
ActivityVTXOTrace describes one VTXO movement correlated to a wallet activity entry.
| Field | Type | Description |
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | id is the best available identifier. It is an outpoint when the source has one, otherwise an OOR session/output identifier. |
| `amount_sat` | `int64` | amount\_sat is the VTXO amount in satoshis. |
| `role` | `string` | role is a short technical label such as "spent\_input", "change\_output", "materialized\_output", or "vhtlc\_output". |
| `ours` | `bool` | ours is true when the row represents wallet-owned funds. |
| `source` | `string` | source names the local source used to derive the row, such as "ledger" or "swap". |
| `session_id` | `string` | session\_id is the OOR session/correlation id when known. |
| `output_index` | `uint32` | output\_index is set when the source names a specific OOR output index. |
## `ActivityLedgerTrace`message
ActivityLedgerTrace describes one local ledger row correlated to a wallet activity entry.
| Field | Type | Description |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `source` | `string` | source names the subsystem that produced the row, such as "ledger" or "boarding\_sweep". |
| `type` | `string` | type is the broad transaction group used by the wallet history query. |
| `subtype` | `string` | subtype is the ledger event type or sweep state. |
| `amount_sat` | `int64` | amount\_sat is the row amount in satoshis using the ledger sign convention. |
| `fee_sat` | `int64` | fee\_sat is the row's fee contribution in satoshis when the history query can derive one. |
| `created_at_unix` | `int64` | created\_at\_unix is the row creation timestamp in unix seconds. |
| `confirmation_status` | `string` | confirmation\_status is the best-known chain confirmation state for the row, for example "pending", "confirmed", or "recorded". |
| `description` | `string` | description is the local ledger description persisted with the row. |
| `entry_id` | `int64` | entry\_id is the local ledger row id when the source is the ledger. |
| `txid` | `string` | txid is the associated chain transaction id when known. |
| `debit_account` | `string` | debit\_account is the local accounting debit account. |
| `credit_account` | `string` | credit\_account is the local accounting credit account. |
| `round_id` | `string` | round\_id is the Ark round id associated with the row when known. |
| `session_id` | `string` | session\_id is the OOR session/correlation id associated with the row when known. |
| `confirmation_height` | `int32` | confirmation\_height is the confirmed block height when known. |
| `hidden_from_activity` | `bool` | hidden\_from\_activity is true when WalletService.List suppresses the row because it is an internal execution leg. |
| `role` | `string` | role is the inspection role inferred for this row. |
| `output_index` | `int32` | output\_index is the transaction output index associated with txid. A negative value means the source has no output index. |
---
Source: https://wavelength.lightning.engineering/api/wallet/get-exit-plan.md
WalletService[CLI`wavecli exit`](/cli/exit/)
# GetExitPlan
GetExitPlan previews unilateral-exit readiness for one VTXO. The response includes CPFP fee input requirements and, when funding is needed, a backing-wallet address callers can fund before forced unroll.
## Endpoints
gRPC`rpc GetExitPlan(GetExitPlanRequest) returns (GetExitPlanResponse)`
REST`POST/v1/wallet/exit-plan`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli exit plan --outpoint 4b2e9f1a7c3d5e6f8091a2b3c4d5e6f70819a2b3c4d5e6f708192a3b4c5d6e7f:0
```
## Request`GetExitPlanRequest`
GetExitPlanRequest lists the VTXO outpoints to preview for unilateral exit via WalletService.GetExitPlan.
| Field | Type | Description |
| ------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------- |
| `outpoints` | `repeated string` | outpoints are the VTXO outpoints to preview, each "txid:index". |
| `conf_target` | `uint32` | conf\_target selects the fee-estimation target in blocks. Zero uses the daemon's default unroll confirmation target. |
## Response`GetExitPlanResponse`
GetExitPlanResponse returns one ExitPlanEntry per requested outpoint, plus the batch-level fee estimate and aggregate funding verdict.
| Field | Type | Description |
| ------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `plans` | [`repeated ExitPlanEntry`](#ExitPlanEntry) | plans holds one entry per requested outpoint, in request order. Entries are evaluated against a running wallet allocation: each feasible exit reserves its fee inputs before the next entry is planned, so a per-entry verdict reflects the wallet left after the earlier entries in the batch notionally start. |
| `fee_rate_sat_per_vbyte` | `int64` | fee\_rate\_sat\_per\_vbyte is the shared chain fee estimate used for all entries. |
| `can_start` | `bool` | can\_start is the AND over every entry that has no per-outpoint error. Because entries draw from a shared wallet via the running allocation above, it is true only when every requested exit can be funded simultaneously, not merely one at a time. |
| `total_funding_shortfall_sat` | `int64` | total\_funding\_shortfall\_sat is the summed shortfall across entries under simultaneous funding, so it already accounts for fee inputs two outpoints would otherwise both claim. |
| `total_recommended_funding_sat` | `int64` | total\_recommended\_funding\_sat is the summed recommended funding across entries. |
## `ExitPlanEntry`message
ExitPlanEntry is the per-outpoint unilateral-exit readiness preview: the backing-wallet fee requirements and, when an unroll job already exists, its current state.
| Field | Type | Description |
| ------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `outpoint` | `string` | outpoint is the previewed VTXO outpoint, formatted as "txid:index". |
| `funding_address` | `string` | funding\_address is empty when can\_start is true (no shortfall, so no address is allocated). |
| `required_confirmations` | `uint32` | required\_confirmations is the number of confirmations a backing-wallet UTXO must have before it can fund the unroll CPFP. |
| `required_fee_utxo_count` | `uint32` | required\_fee\_utxo\_count is the number of distinct confirmed wallet UTXOs the exit needs to fund CPFP fees, one per unroll ancestry path. |
| `usable_fee_utxo_count` | `uint32` | usable\_fee\_utxo\_count is the number of confirmed wallet UTXOs large enough to each fund a CPFP child on their own. |
| `recommended_utxo_amount_sat` | `int64` | recommended\_utxo\_amount\_sat is the suggested value in satoshis for each fee-funding UTXO the caller provisions. |
| `recommended_total_funding_sat` | `int64` | recommended\_total\_funding\_sat is the suggested total backing-wallet funding in satoshis: recommended\_utxo\_amount\_sat times the required fee UTXO count. |
| `funding_shortfall_sat` | `int64` | funding\_shortfall\_sat is the additional backing-wallet funding in satoshis needed before this exit can start. Zero when can\_start is true. |
| `can_start` | `bool` | can\_start is true when the backing wallet already has the fee inputs and balance to fund this exit's forced unroll. |
| `exit_job_found` | `bool` | exit\_job\_found is true when an unroll job already exists for this outpoint, in which case exit\_status, sweep\_txid, and last\_error describe it. |
| `exit_status` | [`ExitJobStatus`](#ExitJobStatus) | exit\_status is the current phase of the existing unroll job, when exit\_job\_found is true. |
| `sweep_txid` | `string` | sweep\_txid is the hex-encoded txid of the existing job's sweep transaction, set once it has been broadcast. |
| `last_error` | `string` | last\_error is the failure reason of the existing unroll job, when it is in a failed state. |
| `error` | `string` | error is a per-outpoint failure (e.g. VTXO not found) so one bad outpoint does not fail the whole batch. Empty on success. |
| `infeasibility_reason` | [`ExitInfeasibilityReason`](#ExitInfeasibilityReason) | infeasibility\_reason explains why can\_start is false. It may be a structural block - a dust or uneconomical VTXO the wallet can never make exitable (funding\_shortfall\_sat is zero) - or a funding shortfall the wallet could cover (wallet underfunded or too few fee inputs, also reflected in funding\_shortfall\_sat). It is EXIT\_INFEASIBILITY\_REASON\_UNSPECIFIED when can\_start is true. |
## `ExitJobStatus`enum
ExitJobStatus collapses the underlying unroll job phases to a short wallet-facing string set.
| Name | Number | Description |
| ------------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `EXIT_JOB_STATUS_UNSPECIFIED` | `0` | EXIT\_JOB\_STATUS\_UNSPECIFIED - the proto zero value; no job phase has been reported. |
| `EXIT_JOB_STATUS_PENDING` | `1` | EXIT\_JOB\_STATUS\_PENDING - job created but recovery transactions not yet materialized. |
| `EXIT_JOB_STATUS_MATERIALIZING` | `2` | EXIT\_JOB\_STATUS\_MATERIALIZING - recovery transactions are being broadcast and confirmed on-chain. |
| `EXIT_JOB_STATUS_CSV_PENDING` | `3` | EXIT\_JOB\_STATUS\_CSV\_PENDING - recovery transactions confirmed, waiting for the CSV delay to expire. |
| `EXIT_JOB_STATUS_SWEEPING` | `4` | EXIT\_JOB\_STATUS\_SWEEPING - CSV delay expired, sweep transaction is being broadcast/confirmed. |
| `EXIT_JOB_STATUS_COMPLETED` | `5` | EXIT\_JOB\_STATUS\_COMPLETED - terminal: the sweep confirmed and the funds are in the on-chain wallet. |
| `EXIT_JOB_STATUS_FAILED` | `6` | EXIT\_JOB\_STATUS\_FAILED - terminal: an unrecoverable error occurred. |
## `ExitInfeasibilityReason`enum
ExitInfeasibilityReason enumerates why a unilateral exit was judged infeasible. It mirrors the daemon's unroll feasibility verdict so a caller can distinguish a dust/uneconomical block (no amount of wallet funding fixes it) from a plain funding shortfall.
| Name | Number | Description |
| ------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EXIT_INFEASIBILITY_REASON_UNSPECIFIED` | `0` | EXIT\_INFEASIBILITY\_REASON\_UNSPECIFIED - the proto zero value; the exit is feasible, or the block is a plain funding shortfall reported via funding\_shortfall\_sat instead. |
| `EXIT_INFEASIBILITY_REASON_SWEEP_BELOW_DUST` | `1` | EXIT\_INFEASIBILITY\_REASON\_SWEEP\_BELOW\_DUST - the swept output, after deducting the sweep fee from the VTXO value, would fall at or below the dust limit, so the sweep could never relay. The exit is impossible regardless of wallet funding. |
| `EXIT_INFEASIBILITY_REASON_UNECONOMICAL` | `2` | EXIT\_INFEASIBILITY\_REASON\_UNECONOMICAL - the total on-chain cost to recover the VTXO exceeds the configured fraction of its value. The exit could technically complete but burns more than it returns. |
| `EXIT_INFEASIBILITY_REASON_WALLET_UNDERFUNDED` | `3` | EXIT\_INFEASIBILITY\_REASON\_WALLET\_UNDERFUNDED - the confirmed on-chain wallet balance is too small to cover the CPFP fees. Funding the wallet and retrying resolves it (also reported via funding\_shortfall\_sat). |
| `EXIT_INFEASIBILITY_REASON_WALLET_TOO_FEW_INPUTS` | `4` | EXIT\_INFEASIBILITY\_REASON\_WALLET\_TOO\_FEW\_INPUTS - the wallet has fewer usable confirmed UTXOs than the VTXO has independent ancestry paths, so it lacks distinct CPFP fee inputs. |
---
Source: https://wavelength.lightning.engineering/api/wallet/exit.md
WalletService[CLI`wavecli exit`](/cli/exit/)
# Exit
Exit queues a cooperative leave for the specified VTXO outpoint by default. When the caller supplies force\_unroll\_ack exactly, the daemon starts a unilateral unroll instead after checking that the outpoint is present in the local backing wallet's UTXO set.
## Endpoints
gRPC`rpc Exit(ExitRequest) returns (ExitResponse)`
REST`POST/v1/wallet/exit`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli exit --outpoint 4b2e9f1a7c3d5e6f8091a2b3c4d5e6f70819a2b3c4d5e6f708192a3b4c5d6e7f:0
```
## Request`ExitRequest`
ExitRequest identifies the VTXO to exit and selects the exit path for WalletService.Exit: cooperative leave by default, or unilateral unroll when force\_unroll\_ack is supplied.
| Field | Type | Description |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | outpoint is the VTXO outpoint to exit, formatted as "txid:index". |
| `onchain_address` | `string` | onchain\_address is the cooperative leave destination. Empty asks the daemon to generate a fresh backing-wallet address internally. |
| `force_unroll_ack` | `string` | force\_unroll\_ack must be exactly "I\_KNOW\_WHAT\_I\_AM\_DOING" to bypass cooperative leave and start unilateral unroll. It cannot be combined with onchain\_address, and the server requires at least one confirmed local backing-wallet UTXO before admitting forced unroll. |
## Response`ExitResponse`
ExitResponse reports the exit path the daemon took and its per-path detail for WalletService.Exit.
| Field | Type | Description |
| ------------------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `created` | `bool` | created indicates whether a new unilateral exit job was spawned. False if an existing job already covers this target. Cooperative exits leave this unset. |
| `actor_id` | `string` | actor\_id is the identifier of the durable unilateral exit job actor. |
| `mode` | [`ExitMode`](#ExitMode) | mode identifies the branch the daemon took. |
| `queued_outpoints` | `repeated string` | queued\_outpoints lists the outpoints accepted into cooperative leave. |
| `onchain_address` | `string` | onchain\_address is the cooperative leave destination used by the daemon. It may be caller-supplied or daemon-generated. |
## `ExitMode`enum
ExitMode identifies whether Exit queued a cooperative leave or started a forced unilateral unroll.
| Name | Number | Description |
| ----------------------- | ------ | ----------------------------------------------------------------------------------------- |
| `EXIT_MODE_UNSPECIFIED` | `0` | EXIT\_MODE\_UNSPECIFIED is the proto zero value used when the exit path was not reported. |
| `EXIT_MODE_COOPERATIVE` | `1` | EXIT\_MODE\_COOPERATIVE means Exit queued a cooperative leave (VTXO-to-onchain). |
| `EXIT_MODE_UNILATERAL` | `2` | EXIT\_MODE\_UNILATERAL means Exit started a forced unilateral unroll. |
---
Source: https://wavelength.lightning.engineering/api/wallet/exit-status.md
WalletService[CLI`wavecli exit`](/cli/exit/)
# ExitStatus
ExitStatus reports the current phase of an unroll job for the specified VTXO outpoint, including recovery chain progress and sweep state. Proxies waverpc.GetUnrollStatus.
## Endpoints
gRPC`rpc ExitStatus(ExitStatusRequest) returns (ExitStatusResponse)`
REST`POST/v1/wallet/exit-status`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli exit status --outpoint 4b2e9f1a7c3d5e6f8091a2b3c4d5e6f70819a2b3c4d5e6f708192a3b4c5d6e7f:0
```
## Request`ExitStatusRequest`
ExitStatusRequest identifies the VTXO whose exit job to query via WalletService.ExitStatus.
| Field | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | outpoint is the VTXO outpoint to query, formatted as "txid:index". |
| `detailed` | `bool` | detailed enriches the response with tree/CSV progress and a fee breakdown. It costs one extra child actor round-trip plus a fee estimate, so it should be set only for interactive status commands, not automated polling. |
## Response`ExitStatusResponse`
ExitStatusResponse reports the current phase of an unroll job for WalletService.ExitStatus.
| Field | Type | Description |
| ---------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `found` | `bool` | found is true if an exit job exists for the requested outpoint. |
| `status` | [`ExitJobStatus`](#ExitJobStatus) | status is the current high-level phase of the exit job. |
| `sweep_txid` | `string` | sweep\_txid is the txid of the sweep transaction, set once the sweep has been broadcast. |
| `last_error` | `string` | last\_error contains the failure reason if the job is in FAILED status. |
| `phase_detail` | `string` | phase\_detail is a human-readable one-line description of the current phase, e.g. "materializing layer 2 of 4 (3/7 txs confirmed)". Set only on a detailed query. |
| `progress` | [`ExitProgress`](#ExitProgress) | progress is the tree materialization progress. Set only on a detailed query against a live job. |
| `csv` | [`ExitCSV`](#ExitCSV) | csv is the CSV maturity countdown. Set only on a detailed query, once the target has confirmed. |
| `fees` | [`ExitFees`](#ExitFees) | fees is the on-chain cost breakdown for the exit. Set only on a detailed query. |
| `best_case_blocks_remaining` | `int32` | best\_case\_blocks\_remaining is the optimistic block count until a confirmed sweep. Set only on a detailed query. |
| `current_height` | `int32` | current\_height is the best block height the exit job has observed. Set only on a detailed query against a live job. |
## `ExitJobStatus`enum
ExitJobStatus collapses the underlying unroll job phases to a short wallet-facing string set.
| Name | Number | Description |
| ------------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `EXIT_JOB_STATUS_UNSPECIFIED` | `0` | EXIT\_JOB\_STATUS\_UNSPECIFIED - the proto zero value; no job phase has been reported. |
| `EXIT_JOB_STATUS_PENDING` | `1` | EXIT\_JOB\_STATUS\_PENDING - job created but recovery transactions not yet materialized. |
| `EXIT_JOB_STATUS_MATERIALIZING` | `2` | EXIT\_JOB\_STATUS\_MATERIALIZING - recovery transactions are being broadcast and confirmed on-chain. |
| `EXIT_JOB_STATUS_CSV_PENDING` | `3` | EXIT\_JOB\_STATUS\_CSV\_PENDING - recovery transactions confirmed, waiting for the CSV delay to expire. |
| `EXIT_JOB_STATUS_SWEEPING` | `4` | EXIT\_JOB\_STATUS\_SWEEPING - CSV delay expired, sweep transaction is being broadcast/confirmed. |
| `EXIT_JOB_STATUS_COMPLETED` | `5` | EXIT\_JOB\_STATUS\_COMPLETED - terminal: the sweep confirmed and the funds are in the on-chain wallet. |
| `EXIT_JOB_STATUS_FAILED` | `6` | EXIT\_JOB\_STATUS\_FAILED - terminal: an unrecoverable error occurred. |
## `ExitProgress`message
ExitProgress describes materialization progress through the exit proof tree. It is populated only for a detailed query against a live exit job.
| Field | Type | Description |
| --------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `confirmed_txs` | `uint32` | confirmed\_txs is the number of proof transactions confirmed on-chain. |
| `in_flight_txs` | `uint32` | in\_flight\_txs is the number of proof transactions broadcast but not yet observed confirmed. |
| `ready_txs` | `uint32` | ready\_txs is the number of proof transactions ready to broadcast now. |
| `blocked_txs` | `uint32` | blocked\_txs is the number of proof transactions still waiting on an unconfirmed in-proof parent. |
| `total_txs` | `uint32` | total\_txs is the total number of transactions in the exit proof tree. |
| `current_layer` | `uint32` | current\_layer is the frontier layer index: the shallowest topological layer (roots first) that still holds an unconfirmed transaction. |
| `total_layers` | `uint32` | total\_layers is the depth of the exit proof tree. |
| `target_confirmed` | `bool` | target\_confirmed is true once the target VTXO transaction has confirmed. |
| `all_proof_confirmed` | `bool` | all\_proof\_confirmed is true once every proof-tree transaction has confirmed, so only the CSV wait and final sweep remain. |
## `ExitCSV`message
ExitCSV describes the target's CSV maturity countdown. It is populated only once the target transaction has confirmed.
| Field | Type | Description |
| ----------------------- | ------- | ----------------------------------------------------------------------------------- |
| `target_confirm_height` | `int32` | target\_confirm\_height is the block height at which the target confirmed. |
| `maturity_height` | `int32` | maturity\_height is the block height at which the target becomes timeout-spendable. |
| `blocks_remaining` | `int32` | blocks\_remaining is how many blocks remain until CSV maturity. |
| `mature` | `bool` | mature is true once the current height is at or past maturity. |
## `ExitFees`message
ExitFees breaks down the on-chain cost of the exit. The CPFP total is estimated from the current fee rate; the sweep fee is the actual built-sweep fee once known, otherwise estimated.
| Field | Type | Description |
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cpfp_fee_sat` | `int64` | cpfp\_fee\_sat is the wallet-funded CPFP fee across every recovery transaction (estimated). |
| `sweep_fee_sat` | `int64` | sweep\_fee\_sat is the fee the final sweep pays out of the VTXO value. |
| `total_cost_sat` | `int64` | total\_cost\_sat is cpfp\_fee\_sat + sweep\_fee\_sat. |
| `vtxo_amount_sat` | `int64` | vtxo\_amount\_sat is the value of the VTXO being exited. |
| `net_recovered_sat` | `int64` | net\_recovered\_sat is vtxo\_amount\_sat - sweep\_fee\_sat. |
| `fee_rate_sat_vbyte` | `int64` | fee\_rate\_sat\_vbyte is the fee rate used for the estimate. |
| `sweep_fee_actual` | `bool` | sweep\_fee\_actual is true when sweep\_fee\_sat is the real built-sweep fee rather than an estimate. |
| `spent_so_far_sat` | `int64` | spent\_so\_far\_sat is the estimated on-chain fee already committed at this point in the exit: CPFP for the confirmed and in-flight recovery transactions, plus the sweep fee once the sweep is broadcast. It is an estimate, not a realized total; total\_cost\_sat remains the projected cost of the whole exit. |
---
Source: https://wavelength.lightning.engineering/api/wallet/exit-summary.md
WalletService[CLI`wavecli exit`](/cli/exit/)
# ExitSummary
ExitSummary reports the wallet-wide portfolio of in-progress exits: one row per active exit plus aggregate totals for the amount still being recovered, the estimated fees, and the estimated net recoverable.
## Endpoints
gRPC`rpc ExitSummary(ExitSummaryRequest) returns (ExitSummaryResponse)`
REST`POST/v1/wallet/exit-summary`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli exit summary
```
## Request`ExitSummaryRequest`
ExitSummaryRequest asks for the wallet-wide portfolio of in-progress exits.
This message has no fields.
## Response`ExitSummaryResponse`
ExitSummaryResponse is the wallet-wide portfolio of in-progress exits plus aggregate totals. Only non-terminal exits are included: a completed or failed exit has no amount "left" to recover.
| Field | Type | Description |
| ----------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `exits` | [`repeated ExitSummaryItem`](#ExitSummaryItem) | exits is one row per in-progress exit. |
| `total_exits` | `uint32` | total\_exits is the number of in-progress exits. |
| `total_vtxo_amount_sat` | `int64` | total\_vtxo\_amount\_sat is the summed value of every VTXO still being exited: the total amount currently locked in in-progress exits. |
| `total_est_fee_sat` | `int64` | total\_est\_fee\_sat is the summed projected on-chain fee across every in-progress exit. |
| `total_est_net_recovered_sat` | `int64` | total\_est\_net\_recovered\_sat is the summed estimated value that will land back in the wallet once every in-progress exit completes. |
## `ExitSummaryItem`message
ExitSummaryItem is one in-progress exit's coarse contribution to the portfolio. It is intentionally cheap (phase plus amounts and estimated fees from the persisted descriptor), so the summary does not query each live actor; use ExitStatus --detailed for one exit's tree/CSV progress.
| Field | Type | Description |
| ----------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `outpoint` | `string` | outpoint is the exiting VTXO outpoint, formatted as "txid:index". |
| `status` | [`ExitJobStatus`](#ExitJobStatus) | status is the current high-level phase of the exit job. |
| `vtxo_amount_sat` | `int64` | vtxo\_amount\_sat is the value of the VTXO being exited. |
| `est_total_fee_sat` | `int64` | est\_total\_fee\_sat is the projected total on-chain fee for this exit (estimated CPFP total plus sweep fee). |
| `est_net_recovered_sat` | `int64` | est\_net\_recovered\_sat is the estimated value that lands back in the wallet after the sweep fee. |
## `ExitJobStatus`enum
ExitJobStatus collapses the underlying unroll job phases to a short wallet-facing string set.
| Name | Number | Description |
| ------------------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `EXIT_JOB_STATUS_UNSPECIFIED` | `0` | EXIT\_JOB\_STATUS\_UNSPECIFIED - the proto zero value; no job phase has been reported. |
| `EXIT_JOB_STATUS_PENDING` | `1` | EXIT\_JOB\_STATUS\_PENDING - job created but recovery transactions not yet materialized. |
| `EXIT_JOB_STATUS_MATERIALIZING` | `2` | EXIT\_JOB\_STATUS\_MATERIALIZING - recovery transactions are being broadcast and confirmed on-chain. |
| `EXIT_JOB_STATUS_CSV_PENDING` | `3` | EXIT\_JOB\_STATUS\_CSV\_PENDING - recovery transactions confirmed, waiting for the CSV delay to expire. |
| `EXIT_JOB_STATUS_SWEEPING` | `4` | EXIT\_JOB\_STATUS\_SWEEPING - CSV delay expired, sweep transaction is being broadcast/confirmed. |
| `EXIT_JOB_STATUS_COMPLETED` | `5` | EXIT\_JOB\_STATUS\_COMPLETED - terminal: the sweep confirmed and the funds are in the on-chain wallet. |
| `EXIT_JOB_STATUS_FAILED` | `6` | EXIT\_JOB\_STATUS\_FAILED - terminal: an unrecoverable error occurred. |
---
Source: https://wavelength.lightning.engineering/api/wallet/sweep-wallet.md
WalletService[CLI`wavecli wallet-sweep`](/cli/wallet-sweep/)
# SweepWallet
SweepWallet previews or broadcasts a normal backing-wallet sweep to a caller-supplied Bitcoin address. This sweeps wallet-managed funds left after CPFP/change/unroll proceeds and does not sweep boarding outputs.
## Endpoints
gRPC`rpc SweepWallet(SweepWalletRequest) returns (SweepWalletResponse)`
REST`POST/v1/wallet/sweep-wallet`
## Examples
CLIGoPythonJavaScriptcurlfetch
```
wavecli wallet-sweep --destination bcrt1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh
```
## Request`SweepWalletRequest`
SweepWalletRequest describes a backing-wallet sweep to preview or broadcast via WalletService.SweepWallet.
| Field | Type | Description |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `destination_address` | `string` | destination\_address is the Bitcoin address that receives the backing wallet funds after fees. |
| `broadcast` | `bool` | broadcast controls whether the sweep is only previewed or also signed and published. |
| `fee_rate_sat_per_vbyte` | `int64` | fee\_rate\_sat\_per\_vbyte overrides chain fee estimation when positive. |
| `conf_target` | `uint32` | conf\_target selects the fee-estimation target in blocks when fee\_rate\_sat\_per\_vbyte is unset. Zero uses the daemon's default unroll confirmation target. |
## Response`SweepWalletResponse`
SweepWalletResponse is the preview or broadcast result of WalletService.SweepWallet.
| Field | Type | Description |
| ------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `inputs` | [`repeated WalletSweepInput`](#WalletSweepInput) | inputs are the confirmed backing-wallet UTXOs selected for the sweep. |
| `total_input_sat` | `int64` | total\_input\_sat is the gross value in satoshis of every selected input. |
| `estimated_fee_sat` | `int64` | estimated\_fee\_sat is the absolute miner fee in satoshis for the aggregate sweep transaction at the resolved fee rate. |
| `net_amount_sat` | `int64` | net\_amount\_sat is total\_input\_sat minus estimated\_fee\_sat: the amount in satoshis paid to the destination address. |
| `fee_rate_sat_per_vbyte` | `int64` | fee\_rate\_sat\_per\_vbyte is the (capped) fee rate used to build the sweep transaction. |
| `can_broadcast` | `bool` | can\_broadcast is true when the preview cleared the dust floor and the sweep can be published. |
| `txid` | `string` | txid is the hex-encoded sweep transaction id, set only when the sweep was broadcast. |
| `failure_reason` | `string` | failure\_reason is populated when the sweep cannot broadcast (no confirmed inputs, dust after fees) or the broadcast failed after preview. |
## `WalletSweepInput`message
WalletSweepInput is one backing-wallet UTXO selected for a sweep.
| Field | Type | Description |
| ------------ | -------- | ------------------------------------------------------------------------ |
| `outpoint` | `string` | outpoint is the selected backing-wallet UTXO, formatted as "txid:index". |
| `amount_sat` | `int64` | amount\_sat is the value of the selected UTXO in satoshis. |
---
Source: https://wavelength.lightning.engineering/cli.md
# wavecli
`wavecli` is the command-line client for the wallet daemon (`waved`). It issues gRPC calls to a running daemon and prints structured JSON to stdout, suitable for both human and agent consumption. The everyday surface is a small set of wallet verbs (create, unlock, send, recv, activity, balance, exit); a handful of daemon-introspection commands and advanced subtrees round it out.
This slice documents the CLI itself. If you are integrating over gRPC or REST from your own code, see the [API reference](/api/) for the underlying `WalletService` and `WalletInspectionService`.
## Connecting
Every command talks to a running daemon over gRPC. The connection is configured with a set of persistent flags, available on every command:
| Flag | Default | Description |
| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `--rpcserver` | `localhost:10029` | Daemon gRPC server address (`host:port`). |
| `--network` | `mainnet` | Bitcoin network used to resolve the default TLS certificate and macaroon paths. |
| `--tlscertpath` | (none) | Path to the daemon TLS certificate. |
| `--macaroonpath` | (none) | Path to the daemon RPC macaroon. |
| `--no-tls` | `false` | Disable TLS for the daemon connection (development and regtest). |
| `--no-macaroons` | `false` | Disable macaroon authentication for the daemon connection (development and regtest). |
| `--json` | (none) | Raw JSON request payload that maps directly to the RPC request proto. When set, the command’s bespoke flags are ignored. |
The `--json` flag is the raw-request escape hatch: pass a JSON object shaped like the method’s request proto and it is sent verbatim, so an agent can drive any command from a single serialized payload instead of assembling flags. It is distinct from the per-command `--format json` output selector.
> **Regtest quickstart**
>
> A local regtest daemon typically runs without TLS or macaroon auth on the default address, so pass `--no-tls --no-macaroons` and keep the default server. Export the connection flags once, for example via a shell alias, so you do not repeat them on every call.
## Output conventions
Successful commands print a structured JSON object to **stdout**. Proto-backed responses are marshaled with proto field names (snake\_case) and unpopulated fields emitted, so the shape is stable across calls. Human-oriented views (the `activity` table, confirmation prompts, the generated seed on `create`) and progress lines go to **stderr**, keeping stdout a clean JSON stream for pipelines.
On failure the command writes a JSON error envelope to **stderr** and exits non-zero:
```json
{
"error": {
"code": "WALLET_LOCKED",
"message": "wallet is locked; run `wavecli unlock`",
"details": ""
}
}
```
`code` is a stable machine-readable string (for example `INVALID_ARGS`, `WALLET_LOCKED`, `NOT_FOUND`, `METHOD_NOT_FOUND`, `DRY_RUN_OK`), `message` is a human-facing description, and `details` carries optional diagnostic context (such as the wrapping RPC call chain).
## Exit codes
The exit code lets an agent or shell branch on the failure category without parsing prose. The codes are semantic:
| Exit code | Name | Meaning |
| --------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0` | success | The command completed. Set implicitly when the command returns without error. |
| `1` | generic error | Catch-all failure not covered by a more specific code. |
| `2` | invalid arguments | The invocation was rejected before reaching the daemon: a missing required flag, a malformed outpoint, conflicting `--offchain`/`--onchain`, and similar. |
| `3` | auth failure | Wallet authentication failed: a wrong password, or a locked wallet on a verb that requires it. |
| `4` | not found | A queried resource does not exist on the daemon (an unknown round id, a missing exit job). |
| `10` | dry-run OK | A `--dry-run` invocation passed all local validation; no RPC was dispatched. |
Daemon-side gRPC failures are mapped onto the same table: `InvalidArgument`, `OutOfRange`, and `FailedPrecondition` map to exit code `2`; `Unauthenticated` and `PermissionDenied` to `3`; `NotFound` to `4`; everything else to `1`.
## Command index
### Wallet
The everyday verbs. They call `WalletService` (and, for inspection, `WalletInspectionService`) and cross-link to the matching [API reference](/api/) page.
| Command | What it does |
| ------------------------------------ | ------------------------------------------------------------- |
| [`create`](/cli/create/) | Create a new wallet from a fresh seed. |
| [`unlock`](/cli/unlock/) | Unlock an existing wallet. |
| [`send`](/cli/send/) | Send a Lightning or on-chain payment. |
| [`recv`](/cli/recv/) | Receive a payment: a Lightning invoice or a boarding address. |
| [`activity`](/cli/activity/) | Show wallet activity, and inspect a single entry. |
| [`balance`](/cli/balance/) | Display the unified wallet balance. |
| [`exit`](/cli/exit/) | Cooperatively exit a VTXO, or start a forced unroll. |
| [`wallet-sweep`](/cli/wallet-sweep/) | Sweep the backing wallet to a destination address. |
### Daemon
Introspection and integration surfaces that live at the root.
| Command | What it does |
| -------------------------- | ----------------------------------------------- |
| [`getinfo`](/cli/getinfo/) | Display daemon status information. |
| [`schema`](/cli/schema/) | Dump machine-readable method schemas as JSON. |
| [`mcp`](/cli/mcp/) | Run a Model Context Protocol server over stdio. |
### Advanced
Power-user subtrees over the raw daemon RPC. Most integrations never need these.
| Command | What it does |
| ---------------------------- | ----------------------------------------------------------------------------- |
| [`ark`](/cli/ark/) | Low-level Ark protocol commands (VTXOs, rounds, OOR, boarding, sweeps, fees). |
| [`recovery`](/cli/recovery/) | Manage daemon-owned vHTLC recovery rows. |
| [`dev`](/cli/dev/) | Generated low-level access to every daemon gRPC method. |
---
Source: https://wavelength.lightning.engineering/cli/create.md
# create
Initializes a new wallet. The daemon generates a 24-word aezeed mnemonic, prints it to stderr so you can record it offline, and encrypts the seed with the supplied password. Pass `--recover` with a `--mnemonic-file` to import an existing mnemonic and rebuild Ark wallet state instead.
The wallet password is never read from the command line. It comes from stdin, the `WAVED_WALLET_PASSWORD` environment variable, or `--wallet_password_file`; the interactive prompt asks for confirmation. The optional aezeed seed passphrase comes from `WAVED_SEED_PASSPHRASE` or `--seed_passphrase_file`.
```bash
echo -n 'hunter2hunter2' | wavecli create
```
> **The mnemonic is shown once**
>
> On a fresh create the 24-word mnemonic is written to stderr exactly once and is NOT included in the JSON response on stdout. Capture it from stderr, or pass `--print-mnemonic-json` to opt into the machine-consumable form. Losing it makes the wallet unrecoverable.
## Flags
| Flag | Default | Description |
| ------------------------ | ------- | ----------------------------------------------------------------------------------------------------- |
| `--wallet_password_file` | (none) | Path to a file containing the wallet password. |
| `--seed_passphrase_file` | (none) | Path to a file containing the optional aezeed passphrase. |
| `--print-mnemonic-json` | `false` | Include the mnemonic in the JSON response on stdout (default: stderr only). |
| `--recover` | `false` | Import an existing mnemonic and recover Ark wallet state. |
| `--mnemonic-file` | (none) | Path to a file containing an existing 24-word aezeed mnemonic. Requires `--recover`. |
| `--recovery-window` | `0` | Number of key indexes to scan per recovery family. Requires `--recover`; `0` uses the daemon default. |
## Example
```bash
echo -n 'hunter2hunter2' | wavecli --no-tls create
```
The mnemonic goes to stderr; stdout carries the identity and recovery summary:
```json
{
"identity_pubkey": "02a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
"recovery_ran": false,
"recovered_boarding_addresses": 0,
"recovered_boarding_utxos": 0,
"recovered_vtxos": 0,
"recovered_oor_receive_scripts": 0,
"recovered_oor_events": 0
}
```
## Underlying RPC
Calls [`WalletService.Create`](/api/wallet/create/), which proxies the daemon’s seed generation and wallet initialization.
---
Source: https://wavelength.lightning.engineering/cli/unlock.md
# unlock
Decrypts the on-disk wallet seed and starts the wallet subsystem, making the wallet RPCs usable. Like `create`, the password is never accepted on the command line: it is read from stdin, the `WAVED_WALLET_PASSWORD` environment variable, or `--wallet_password_file`.
```bash
echo -n 'hunter2hunter2' | wavecli unlock
```
## Flags
| Flag | Default | Description |
| ------------------------ | ------- | ---------------------------------------------- |
| `--wallet_password_file` | (none) | Path to a file containing the wallet password. |
## Example
```bash
echo -n 'hunter2hunter2' | wavecli --no-tls unlock
```
```json
{
"identity_pubkey": "02a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90"
}
```
A wrong password exits with code `3` (auth failure) and a `WALLET_LOCKED` error envelope on stderr.
## Underlying RPC
Calls [`WalletService.Unlock`](/api/wallet/unlock/).
---
Source: https://wavelength.lightning.engineering/cli/send.md
# send
Dispatches an outbound payment. With `--offchain` (the default) the positional destination is treated as a BOLT-11 Lightning invoice and routed through the swap subsystem, which transparently picks a same-Ark peer-to-peer transfer or real Lightning. With `--onchain` the destination is a bech32 address: the daemon lands exactly `--amt` sats there and returns the residual to the wallet as a change VTXO. Use `--sweep-all` with `--amt 0` to drain every live VTXO instead.
Direction is always explicit. The CLI never sniffs the destination string, so an agent cannot accidentally dispatch an on-chain send by passing what it thinks is an invoice; the daemon performs the authoritative parse.
```bash
wavecli send
```
By default a Lightning `send` prepares, dispatches, and then waits until the payment reaches a terminal state, printing each lifecycle phase to stderr. A completed Lightning send prints the payment preimage as proof of payment.
Onchain sends never block. An onchain send settles by forfeiting its source VTXO into a cooperative-leave round and waiting for that round to confirm on chain, which can take many minutes, and the funds are committed the moment the dispatch returns. So the onchain rail always prints a pending receipt and returns immediately, regardless of `--no-wait`. The entry stays `PENDING` until its round confirms, and the residual comes back as a fresh change VTXO when the round seals; a mid-flight balance that looks drained by the whole source VTXO is expected, not a loss. Track a dispatched onchain send with `wavecli activity inspect `.
## Flags
| Flag | Default | Description |
| ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--offchain` | `false` | Force offchain (BOLT-11 invoice) dispatch. Default when neither `--offchain` nor `--onchain` is set. |
| `--onchain` | `false` | Force onchain dispatch. |
| `--amt` | `0` | Amount in satoshis. Required for on-chain sends unless `--sweep-all`; ignored for amount-bearing invoices. |
| `--max_fee` | `0` | Max swap fee in satoshis for invoice sends. `0` lets the daemon cap the fee at roughly 1% of the amount. |
| `--note` | (none) | Caller-supplied label attached to the entry. |
| `--sweep-all` | `false` | On-chain only: drain the wallet to the destination. `--amt` must be `0`. |
| `--dry-run` | `false` | Prepare and print the preview without dispatching funds. |
| `--force` | `false` | Skip the interactive confirmation after prepare. |
| `--yes` | `false` | Alias for `--force`. |
| `--no-wait` | `false` | Return as soon as the send is dispatched instead of blocking until a terminal state. Onchain sends never block and return a pending receipt regardless of this flag. |
| `--wait-timeout` | `5m` | While waiting, give up after this long and return the last observed status. `0` waits indefinitely. |
| `--wait-poll-interval` | `200ms` | While waiting, how often to poll the daemon for status updates. |
> **Non-interactive sends need --force**
>
> On non-interactive stdin (an agent or pipeline) `send` refuses to prompt for confirmation. Pass `--force` or `--yes`, or the command exits with `INVALID_ARGS`.
## Example
```bash
wavecli --no-tls send lnbcrt250u1p3xyz... --offchain --force
```
Phase transitions stream to stderr; stdout receives the compact settled summary:
```json
{
"status": "COMPLETE",
"kind": "SEND",
"amount_sat": -25000,
"fee_sat": 12,
"settlement": "LIGHTNING",
"destination": "lnbcrt250u1p3xyz...",
"payment_hash": "9a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9",
"preimage": "3f9c2a7b8e1d45608192a3b4c5d6e7f80112233445566778899aabbccddeeff0",
"id": "a1b2c3d4e5f60718293a4b5c6d7e8f9"
}
```
## Exit codes
With `--dry-run` the command runs prepare-time validation only, prints the preview, and exits `10` (`DRY_RUN_OK`) without dispatching any funds. A failed Lightning send exits non-zero with a `SEND_FAILED` envelope; a timeout while waiting emits the pending receipt on stdout and a `WAIT_TIMEOUT` envelope.
## Underlying RPC
Calls [`WalletService.PrepareSend`](/api/wallet/prepare-send/) to build and preview the send, then [`WalletService.Send`](/api/wallet/send/) to dispatch the prepared intent. When waiting for settlement it polls [`WalletInspectionService.InspectActivity`](/api/wallet/inspect-activity/).
---
Source: https://wavelength.lightning.engineering/cli/recv.md
# recv
Asks the daemon to materialize an inbound payment surface. With `--offchain` (the default) the daemon opens a receive swap and returns a BOLT-11 Lightning invoice signed with a daemon-managed key; `--amt` is required. With `--onchain` the daemon returns a fresh boarding address: fund it, and the daemon rolls the boarding output into the next round.
```bash
wavecli recv --offchain --amt 5000 --memo "coffee"
wavecli recv --onchain
```
## Flags
| Flag | Default | Description |
| ------------ | ------- | -------------------------------------------------------------------------------------------------- |
| `--offchain` | `false` | Force offchain (Lightning invoice) recv. Default when neither `--offchain` nor `--onchain` is set. |
| `--onchain` | `false` | Force onchain (boarding address) recv. |
| `--amt` | `0` | Amount in satoshis. Required for `--offchain`. |
| `--memo` | (none) | Optional human-readable memo embedded in the offchain invoice. |
| `--amt_hint` | `0` | Optional expected amount for `--onchain` (accounting only). |
## Example
```bash
wavecli --no-tls recv --offchain --amt 25000 --memo "invoice for coffee run"
```
```json
{
"invoice": "lnbcrt250u1p3xyz...",
"entry": {
"id": "b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f901",
"kind": "ENTRY_KIND_RECV",
"status": "ENTRY_STATUS_PENDING",
"amount_sat": 25000
},
"credit_receive": null
}
```
An `--onchain` recv returns a fresh boarding address instead:
```json
{
"onchain_address": "bcrt1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"entry": {
"id": "c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9012a",
"kind": "ENTRY_KIND_DEPOSIT",
"status": "ENTRY_STATUS_PENDING"
}
}
```
## Underlying RPC
The `--offchain` path calls [`WalletService.Recv`](/api/wallet/recv/); the `--onchain` path calls [`WalletService.Deposit`](/api/wallet/deposit/).
---
Source: https://wavelength.lightning.engineering/cli/activity.md
# activity
`activity` shows the wallet’s user-facing feed of sends, receives, deposits, and exits. The `activity inspect` subcommand drills into one entry and correlates its swap, VTXO, and ledger detail.
```bash
wavecli activity
wavecli activity --pending --kind send,recv
wavecli activity --format json
```
## Flags
| Flag | Default | Description |
| ----------- | ------- | ------------------------------------------------------------------------------------------ |
| `--pending` | `false` | Only show entries still in flight. |
| `--kind` | (none) | Filter by kind (send, recv, deposit, exit); repeatable. |
| `--limit` | `0` | Page size; 0 uses the daemon default. |
| `--cursor` | (none) | Activity page token from a prior page’s `next_cursor`. Empty starts from the newest entry. |
| `--format` | `table` | Output format: table, expanded, x, or json. |
## Example
By default `activity` renders a compact table to stdout, with columns `LAST UPDATE`, `KIND`, `STATUS`, `AMOUNT`, `FEE`, `PHASE`, `ID`, `NOTE`. `--format json` emits the raw response for pipelines instead:
```bash
wavecli activity --format json
```
```json
{
"activity": {
"entries": [
{
"id": "a1b2c3d4e5f60718293a4b5c6d7e8f9",
"kind": "ENTRY_KIND_SEND",
"status": "ENTRY_STATUS_COMPLETE",
"amount_sat": -25000,
"fee_sat": 12,
"note": "coffee run",
"updated_at_unix": 1751328000
}
]
}
}
```
## Subcommands
### `activity inspect`
Inspects one activity entry by id and shows correlated swap, VTXO, and ledger detail. Renders the expanded view by default; `--format json` emits the raw inspection response.
| Flag | Default | Description |
| ---------------- | ---------- | ------------------------------------------------------- |
| `` | (none) | Required. The activity entry id to inspect. |
| `--ledger-limit` | `0` | Maximum ledger rows to scan; 0 uses the daemon maximum. |
| `--format` | `expanded` | Output format: expanded, x, or json. |
```bash
wavecli activity inspect
```
## Underlying RPC
`activity` calls [`WalletService.List`](/api/wallet/list/) with the activity view. `activity inspect` calls [`WalletInspectionService.InspectActivity`](/api/wallet/inspect-activity/).
---
Source: https://wavelength.lightning.engineering/cli/balance.md
# balance
Returns the unified wallet balance: confirmed spendable VTXOs (`confirmed_sat`), in-flight inbound (`pending_in_sat`: boarding plus receive), and in-flight outbound (`pending_out_sat`: send plus exit), all in satoshis. The response also carries the server-authoritative credit balances.
```bash
wavecli balance
```
## Flags
`balance` takes no bespoke flags beyond the global connection flags.
## Example
```bash
wavecli --no-tls balance
```
```json
{
"confirmed_sat": 125000,
"pending_in_sat": 25000,
"pending_out_sat": 0,
"credit_available_sat": 0,
"credit_reserved_sat": 0
}
```
## Underlying RPC
Calls [`WalletService.Balance`](/api/wallet/balance/).
---
Source: https://wavelength.lightning.engineering/cli/exit.md
# exit
Queues the specified VTXO outpoint for cooperative leave. If `--onchain-address` is omitted the daemon generates a fresh backing-wallet destination. Unilateral unroll is only started when `--force-unroll-ack` is exactly `I_KNOW_WHAT_I_AM_DOING`.
```bash
wavecli exit --outpoint TXID:VOUT
wavecli exit --outpoint TXID:VOUT --onchain-address bcrt1...
```
## Flags
| Flag | Default | Description |
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `--outpoint` | (none) | Required. VTXO outpoint to exit, as `txid:vout`. |
| `--onchain-address` | (none) | Cooperative leave destination. Omitted means a fresh wallet-owned address. Cannot be combined with `--force-unroll-ack`. |
| `--force-unroll-ack` | (none) | Exact acknowledgement (`I_KNOW_WHAT_I_AM_DOING`) required to force unilateral unroll. |
| `--dry-run` | `false` | Validate inputs locally and print the preview without dispatching to the daemon. |
## Example
```bash
wavecli --no-tls exit --outpoint 4b2e9f1a7c3d5e6f...:0
```
```json
{
"created": false,
"actor_id": "",
"mode": "EXIT_MODE_COOPERATIVE",
"queued_outpoints": [
"4b2e9f1a7c3d5e6f8091a2b3c4d5e6f70819a2b3c4d5e6f708192a3b4c5d6e7f:0"
],
"onchain_address": "bcrt1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
}
```
## Subcommands
### `exit status`
Returns the status of a forced unroll job for one outpoint. By default the response includes recovery-tree progress (layer and transaction counts), the CSV maturity countdown, a best-case block estimate to full exit, and the on-chain fee breakdown, alongside the job phase, sweep txid, and any errors. The endpoint stays queryable after the exit completes.
| Flag | Default | Description |
| ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--outpoint` | (none) | Required. VTXO outpoint to check, as `txid:vout`. |
| `--detailed` | `true` | Include tree/CSV progress, the best-case block countdown, and the fee breakdown. Pass `--detailed=false` for a coarse, cheaper phase-only status. |
Calls [`WalletService.ExitStatus`](/api/wallet/exit-status/).
```bash
wavecli exit status --outpoint TXID:VOUT
wavecli exit status --outpoint TXID:VOUT --detailed=false
```
### `exit summary`
Lists every in-progress unilateral exit and the aggregate totals across them: the amount still being recovered, the estimated on-chain fees, and the estimated net recoverable. Completed and failed exits are omitted; they have no amount left to recover. Takes no flags.
Calls [`WalletService.ExitSummary`](/api/wallet/exit-summary/).
```bash
wavecli exit summary
```
### `exit plan`
Previews the backing-wallet funding readiness for one or more outpoints, without dispatching an exit.
| Flag | Default | Description |
| ------------ | ------- | --------------------------------------------------------------- |
| `--outpoint` | (none) | Required. VTXO outpoint to preview, as `txid:vout`; repeatable. |
Calls [`WalletService.GetExitPlan`](/api/wallet/get-exit-plan/).
```bash
wavecli exit plan --outpoint TXID:VOUT
```
## Exit codes
With `--dry-run` the command validates the request locally and prints the preview without dispatching; a valid preview exits `10` (`DRY_RUN_OK`), a validation failure exits non-zero.
> **Unilateral unroll is expensive**
>
> A forced unroll pays on-chain fees and cannot be combined with a custom `--onchain-address`. Reach for it only when cooperative leave is unavailable.
## Underlying RPC
The default cooperative path calls [`WalletService.Exit`](/api/wallet/exit/). The subcommands call [`WalletService.ExitStatus`](/api/wallet/exit-status/), [`WalletService.ExitSummary`](/api/wallet/exit-summary/), and [`WalletService.GetExitPlan`](/api/wallet/get-exit-plan/).
---
Source: https://wavelength.lightning.engineering/cli/wallet-sweep.md
# wallet-sweep
Previews, or with `--broadcast` publishes, a sweep of every confirmed backing-wallet UTXO to a single destination address. Boarding outputs are excluded; use [`ark sweep`](/cli/ark/) for those. Without `--broadcast` the command returns a preview of the selected inputs, fee, and net amount without leasing inputs or broadcasting.
```bash
wavecli wallet-sweep --destination bcrt1...
wavecli wallet-sweep --destination bcrt1... --broadcast
```
## Flags
| Flag | Default | Description |
| --------------- | ------- | ---------------------------------------------------------------------------------------- |
| `--destination` | (none) | Required. On-chain destination address for the swept funds. |
| `--broadcast` | `false` | Publish the sweep. Omitted means preview only. |
| `--fee-rate` | `0` | Explicit fee rate in sat/vByte. `0` estimates from the chain backend at `--conf-target`. |
| `--conf-target` | `0` | Confirmation target for fee estimation when `--fee-rate` is `0`. |
## Example
```bash
wavecli --no-tls wallet-sweep --destination bcrt1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh
```
```json
{
"inputs": [
{
"outpoint": "4b2e9f1a7c3d5e6f8091a2b3c4d5e6f70819a2b3c4d5e6f708192a3b4c5d6e7f:0",
"amount_sat": 100000
}
],
"total_input_sat": 100000,
"estimated_fee_sat": 480,
"net_amount_sat": 99520,
"fee_rate_sat_per_vbyte": 2,
"can_broadcast": true,
"txid": "",
"failure_reason": ""
}
```
When run with `--broadcast`, `txid` carries the published sweep transaction id.
## Underlying RPC
Calls [`WalletService.SweepWallet`](/api/wallet/sweep-wallet/).
---
Source: https://wavelength.lightning.engineering/cli/getinfo.md
# getinfo
Returns version, network, wallet state, block height, and identity pubkey from the running daemon. It is the first call to make when checking that the daemon is reachable and which state the wallet is in.
```bash
wavecli getinfo
```
## Flags
`getinfo` takes no bespoke flags beyond the global connection flags.
## Example
```bash
wavecli --no-tls getinfo
```
```json
{
"version": "0.1.0",
"commit": "abcd1234",
"network": "regtest",
"lnd_identity_pubkey": "",
"block_height": 214,
"server_connected": true,
"lnd_alias": "",
"wallet_type": "lwwallet",
"wallet_state": "WALLET_STATE_READY",
"identity_pubkey": "02a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90"
}
```
## Underlying RPC
Calls the daemon’s `GetInfo` RPC on `DaemonService`. This is a daemon-level method with no `WalletService` equivalent, so it has no API reference page in the [wallet API](/api/).
---
Source: https://wavelength.lightning.engineering/cli/schema.md
# schema
Returns the full method signature for a CLI command as machine-readable JSON: parameters, types, required fields, enum values, and the request and response proto type names. Agents use this to self-serve API documentation at runtime rather than relying on static docs baked into a system prompt.
Called with no arguments it lists the available method names. Pass a method name to dump one schema, or `--all` to dump every method schema at once.
```bash
wavecli schema
wavecli schema balance
wavecli schema --all
```
## Flags
| Flag | Default | Description |
| ------- | ------- | ------------------------ |
| `--all` | `false` | Dump all method schemas. |
## Example
```bash
wavecli schema balance
```
```json
{
"method": "balance",
"description": "Display wallet balance",
"params": null,
"request_type": "BalanceRequest",
"response_type": "BalanceResponse",
"json_input": false,
"mcp_tool": true
}
```
Each schema entry reports whether the command supports `--dry-run` / `--dry_run`, whether it accepts `--json` raw input (`json_input`), and whether it is exposed as an MCP tool (`mcp_tool`). An unknown method name exits `1` with a `METHOD_NOT_FOUND` envelope.
## Underlying RPC
`schema` reads from an in-process method registry; it makes no RPC call. The [`dev`](/cli/dev/) command tree exposes the equivalent per-method schema for the generated low-level RPC surface via its `--describe` flag.
---
Source: https://wavelength.lightning.engineering/cli/mcp.md
# mcp
`mcp` hosts a Model Context Protocol (MCP) server that exposes the daemon RPC as typed tool calls over stdio. Agents invoke tools with structured JSON parameters and receive raw proto-JSON responses from the daemon. The parent `mcp` command is a group; the work happens in `mcp serve`.
## Flags
`mcp` itself takes no bespoke flags; it dispatches to `mcp serve` below. The subcommand accepts `--help` and the global connection flags (`--rpcserver`, `--tlscertpath`, `--no-tls`).
## Example
`mcp` alone prints usage and exits; the subcommand does the work:
```bash
wavecli mcp serve
```
## Subcommands
### `mcp serve`
Starts an MCP server on stdio that exposes each daemon RPC as a typed tool call. The server runs until the client disconnects. It registers the everyday wallet-verb tools first (so an agent listing tools sees the day-to-day surface first), then the legacy daemon tools, then the swap tools when the daemon was built with the matching build tag.
`mcp serve` takes no bespoke flags beyond the global connection flags; it reuses the same TLS, `--no-tls`, and `--tlscertpath` handling as every other command so the MCP surface honors the daemon connection settings.
```bash
wavecli mcp serve
```
> **create and unlock are intentionally omitted**
>
> Wallet setup verbs that handle secret material (create, unlock, seed generation) are never registered as MCP tools: passwords and seed phrases must not transit the MCP protocol, where they could leak into agent logs or provider APIs. Use the CLI directly for wallet setup.
Registered tools include the everyday `balance`, `getinfo`, and send surfaces plus the advanced `ark.*` namespace (for example `ark.vtxos.list`, `ark.vtxos.refresh`, `ark.vtxos.leave`, `ark.oor.receive`, `ark.send.inround`, `ark.send.oor`) and, in swapruntime builds, the `swap.*` tools.
`ark.vtxos.refresh` carries the same fee-consent contract as the CLI. Since there’s no interactive prompt to fall back to, a real refresh requires `yes: true`; without it the tool returns an error telling the agent to call `dry_run: true` first for the itemized advisory estimate, then re-call with `yes: true` to acknowledge the fee and queue.
---
Source: https://wavelength.lightning.engineering/cli/ark.md
# ark
`ark` is the power-user entry point. It surfaces the raw daemon RPC methods that underlie the everyday wallet verbs, for inspection, debugging, and operator runbooks. The everyday surface (create, unlock, send, recv, activity, balance, exit) lives at the top level; reach for `ark` only when you need the low-level knobs.
List-shaped subcommands accept two shared output modifiers: `--fields` (comma-separated field names to include) and `--ndjson` (emit one JSON object per item, newline-delimited). These give agents context-window discipline over large responses.
## Flags
`ark` itself takes no bespoke flags; it dispatches to one of the subcommand groups below. Every subcommand accepts `--help` and the global connection flags (`--rpcserver`, `--tlscertpath`, `--no-tls`).
## Example
`ark` alone prints usage and exits; pick a subcommand, for example:
```bash
wavecli ark vtxos list --status live
```
## `vtxos`
VTXO inventory and lifecycle: `list`, `refresh`, `leave`.
### `vtxos list`
Returns VTXOs known to the wallet, optionally filtered by status and minimum amount.
| Flag | Default | Description |
| -------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| `--status` | (none) | Filter by status: live, pending\_forfeit, forfeiting, forfeited, spent, unilateral\_exit, failed, spending. |
| `--min_amount` | `0` | Minimum amount in sats. |
| `--fields` | (none) | Comma-separated field names to include in the response. |
| `--ndjson` | `false` | Emit one JSON object per VTXO, newline-delimited. |
Calls the daemon `ListVTXOs` RPC.
```bash
wavecli ark vtxos list --status live
```
### `vtxos refresh`
Queues one or more VTXOs for refresh, then commits the queued intents to the next round, extending their expiry. By default this also issues `ark rounds join` on the caller’s behalf, so refresh is a one-shot operation; pass `--no_join` to batch multiple refresh or leave intents into the same round and join explicitly afterward.
A refresh is charged an operator fee, so the command asks before it queues anything. On an interactive terminal it fetches a fee preview, prints the summary to stderr, and waits for a y/N confirmation. On non-interactive stdin it refuses to prompt and exits `INVALID_ARGS`, so scripts and agents have to pass either `--yes` to consent up front or `--dry_run` to preview.
| Flag | Default | Description |
| ------------ | ------- | ------------------------------------------------------------------------------------------------------------------ |
| `--outpoint` | (none) | VTXO outpoint(s) to refresh (txid:index); repeatable. Required unless –all is set, and mutually exclusive with it. |
| `--all` | `false` | Refresh all live VTXOs. |
| `--dry_run` | `false` | Validate without queuing and preview the estimated operator fee. |
| `--yes` | `false` | Skip the interactive fee confirmation. Required on non-interactive stdin. |
| `--no_join` | `false` | Skip the implicit `ark rounds join` follow-up so this refresh can batch with other queued intents. |
Calls the daemon `RefreshVTXOs` RPC.
```bash
wavecli ark vtxos refresh --all --yes
```
#### Fee preview
`--dry_run` returns an itemized advisory estimate alongside the usual validation. The per-outpoint rows land in the JSON body on stdout and carry the amount, remaining lifetime, and the fee components (liquidity fee, on-chain share, and margin) that sum to each row’s total. A human-readable summary goes to stderr so piping stdout into `jq` doesn’t pull prose with it.
```bash
wavecli ark vtxos refresh --all --dry_run
```
Every number here is advisory. The binding fee comes from the server-issued quote when the round seals, so it can differ from the estimate. When the operator quotes can’t be fetched the preview still returns as a validity probe, with an `estimate_error` set and the per-outpoint fee components absent rather than zero.
If every selected VTXO sits inside the operator’s advertised free-refresh window, the estimate reports the selection as free. That waiver is all-or-nothing per round: batching the selection with boarding, leave, or out-of-window inputs forfeits it. The per-outpoint rows still show the ordinary paid price, so they read as what the waiver saves. Rows can also carry a below-dust warning, meaning the VTXO amount is under the minimum economically viable amount at current rates and refreshing it pays fees on a coin that already costs more to exit than it holds.
### `vtxos leave`
Queues one or more VTXOs for cooperative leave, then commits the queued intents to the next round. Each forfeited VTXO pays out its amount, minus the operator fee, to the specified on-chain destination. Auto-joins the next round like `vtxos refresh` unless `--no_join` is set.
| Flag | Default | Description |
| --------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `--outpoint` | (none) | VTXO outpoint(s) to leave (txid:index); repeatable. Required unless –all is set. |
| `--all` | `false` | Leave all live VTXOs. |
| `--address` | (none) | Default on-chain destination address. Mutually exclusive with –pk\_script. |
| `--pk_script` | (none) | Default destination pk\_script, hex-encoded. Mutually exclusive with –address. |
| `--destination` | (none) | Per-outpoint destination override: `outpoint=address` or `outpoint=script:`. Not supported with –all. |
| `--dry_run` | `false` | Validate without queuing. |
| `--yes` | `false` | Skip the interactive confirmation required for –all. |
| `--no_join` | `false` | Skip the implicit `ark rounds join` follow-up so this leave can batch with other queued intents. |
Calls the daemon `LeaveVTXOs` RPC.
```bash
wavecli ark vtxos leave --outpoint abcd...:0 --address bcrt1...
```
## `rounds`
Round participation: `get`, `join`, `list`, `watch`.
### `rounds get`
Fetches one round’s status by its server-assigned id.
| Flag | Default | Description |
| ------------ | ------- | ---------------------------------- |
| `--round-id` | (none) | Server-assigned round id to fetch. |
Calls the daemon `GetRound` RPC.
```bash
wavecli ark rounds get --round-id r-1234
```
### `rounds join`
Commits currently queued round intents (refresh or leave) and emits a join request to the operator. `ark vtxos refresh` and `ark vtxos leave` invoke this automatically; call it directly only after passing `--no_join` to one of those commands, to batch multiple intents into the same round.
Calls the daemon `JoinNextRound` RPC.
```bash
wavecli ark rounds join
```
### `rounds list`
Lists round FSM states, optionally filtered.
| Flag | Default | Description |
| ------------------ | ------- | ------------------------------------------------------------------------ |
| `--persisted-only` | `false` | Only show persisted rounds, skipping in-memory pending ones. |
| `--page-size` | `0` | Maximum number of persisted rounds to return; 0 uses the daemon default. |
| `--page-token` | (none) | Cursor from a previous response, for pagination. |
| `--state` | (none) | Optional state filter, for example confirmed or failed. |
| `--created-after` | `0` | Only show rounds created at or after this Unix time; 0 is unbounded. |
| `--created-before` | `0` | Only show rounds created before this Unix time; 0 is unbounded. |
| `--fields` | (none) | Comma-separated field names to include in the response. |
| `--ndjson` | `false` | Emit one JSON object per round, newline-delimited. |
Calls the daemon `ListRounds` RPC.
```bash
wavecli ark rounds list --state confirmed
```
### `rounds watch`
Opens a server-streaming connection that prints round state transitions as they occur. Press Ctrl-C to stop.
Calls the daemon `WatchRounds` RPC.
```bash
wavecli ark rounds watch
```
## `oor`
Out-of-round session operations: `get`, `list`, `receive`.
### `oor get`
Fetches one out-of-round session’s status by its id.
| Flag | Default | Description |
| -------------- | ------- | ------------------------ |
| `--session-id` | (none) | OOR session id to fetch. |
Calls the daemon `GetOORSession` RPC.
```bash
wavecli ark oor get --session-id s-1234
```
### `oor list`
Lists out-of-round session statuses, optionally filtered.
| Flag | Default | Description |
| -------------- | ------- | ---------------------------------------------------------------- |
| `--direction` | `all` | Direction filter: all, outgoing, or incoming. |
| `--status` | `all` | Status filter: all, pending, completed, or failed. |
| `--page-size` | `0` | Maximum number of sessions to return; 0 uses the daemon default. |
| `--page-token` | (none) | Cursor from a previous response, for pagination. |
| `--fields` | (none) | Comma-separated field names to include in the response. |
| `--ndjson` | `false` | Emit one JSON object per session, newline-delimited. |
Calls the daemon `ListOORSessions` RPC.
```bash
wavecli ark oor list --direction incoming
```
### `oor receive`
Allocates a fresh wallet key, registers the matching taproot receive script with the indexer, and prints the resulting destination details.
| Flag | Default | Description |
| --------- | ------- | ----------------------------------------------------------- |
| `--label` | (none) | Optional label stored with the receive-script registration. |
Calls the daemon `NewReceiveScript` RPC.
```bash
wavecli ark oor receive --label "invoice-42"
```
## `board`
Triggers the client to join the next round with any confirmed boarding UTXOs. The resulting VTXO set replaces the on-chain boarding balance. By default the whole balance boards into one VTXO.
| Flag | Default | Description |
| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `--target-vtxo-count` | `0` | Number of VTXOs to fan the boarded balance into; 0 boards into a single VTXO. |
| `--no-persist` | `false` | Opt out of restart-safe Board replay: do not persist the Board intent, so a daemon restart between admission and round seal silently drops it. |
Calls the daemon `Board` RPC.
```bash
wavecli ark board
```
## `sweep`
Boarding UTXO recovery: `sweep` (broadcast) and `sweep list`.
### `sweep`
Scans boarding UTXOs whose CSV timeout path has matured and prints the recoverable amount, estimated fee, and net amount. Pass `--broadcast` to publish one aggregate sweep transaction; without it the command only previews.
| Flag | Default | Description |
| -------------------------- | ------- | --------------------------------------------------------------------- |
| `--outpoint` | (none) | Boarding UTXO outpoint to sweep (txid:index); repeatable. |
| `--broadcast` | `false` | Broadcast the aggregate sweep and track it until confirmed. |
| `--fee-rate-sat-per-vbyte` | `0` | Fee-rate override in sat/vByte; zero estimates by conf target. |
| `--conf-target` | `0` | Confirmation target for fee estimation; zero uses the daemon default. |
| `--sweep-address` | (none) | Sweep destination. Empty uses a fresh wallet address. |
Calls the daemon `SweepBoardingUTXOs` RPC.
```bash
wavecli ark sweep --broadcast
```
### `sweep list`
Lists aggregate boarding sweep transactions persisted by the daemon, including their lifecycle status, fee, confirmation height, and per-input spend state.
| Flag | Default | Description |
| -------------- | ------- | ------------------------------------------------------------------------------------- |
| `--status` | (none) | Optional status filter: pending, published, confirmed, external\_resolved, or failed. |
| `--page-size` | `0` | Maximum tracked sweeps to return; zero uses the daemon default. |
| `--page-token` | (none) | Page token returned by a previous sweep list response. |
| `--fields` | (none) | Comma-separated field names to include in the response. |
| `--ndjson` | `false` | Emit one JSON object per sweep, newline-delimited. |
Calls the daemon `ListBoardingSweeps` RPC.
```bash
wavecli ark sweep list --status pending
```
## `fees`
Fee estimation and history: `estimate`, `history`.
### `fees estimate`
Returns an itemized, advisory fee estimate for a given amount at the operator’s current rates and treasury utilization. The value is advisory: the binding per-round fee is set by the server-issued quote at seal time and may differ from this estimate.
| Flag | Default | Description |
| -------------------- | ------- | ------------------------------------------------------------------------------- |
| `--amount` | (none) | VTXO amount in satoshis to estimate fees for. Required and must be positive. |
| `--boarding` | `true` | Estimate for boarding (true) or refresh (false). |
| `--remaining-blocks` | `0` | Remaining VTXO lifetime in blocks. Refresh only; required when –boarding=false. |
Calls the daemon `EstimateFee` RPC.
```bash
wavecli ark fees estimate --amount 50000
```
To price a refresh of VTXOs you already hold, reach for [`ark vtxos refresh --dry_run`](#fee-preview) instead. It resolves each selected VTXO’s amount and remaining lifetime from the daemon, so you don’t have to look either up and pass them by hand.
### `fees history`
Returns paginated fee ledger entries from the client’s local accounting database, including total fees paid.
| Flag | Default | Description |
| ---------- | ------- | -------------------------- |
| `--limit` | `50` | Maximum number of entries. |
| `--offset` | `0` | Number of entries to skip. |
Calls the daemon `GetFeeHistory` RPC.
```bash
wavecli ark fees history --limit 20
```
## `listtransactions`
Returns the daemon’s unfiltered local transaction history (ledger plus boarding sweeps) with full internal correlators. The everyday `activity` view composes the same data with the internal correlators stripped.
| Flag | Default | Description |
| ---------- | ------- | --------------------------------------------------------------- |
| `--from` | (none) | Only include entries at or after this ISO 8601 time (or date). |
| `--to` | (none) | Only include entries at or before this ISO 8601 time (or date). |
| `--limit` | `50` | Maximum number of entries. |
| `--offset` | `0` | Number of filtered entries to skip. |
| `--type` | (none) | Optional type filter: boarding, round, oor, or sweep. |
| `--fields` | (none) | Comma-separated field names to include in the response. |
| `--ndjson` | `false` | Emit one JSON object per entry, newline-delimited. |
Calls the daemon `ListTransactions` RPC.
```bash
wavecli ark listtransactions --type round --limit 25
```
## `send`
Raw in-round and out-of-round transfer: `inround`, `oor`.
### `send inround`
Initiates an in-round transfer by submitting a refresh request to the round coordinator; the transfer completes when the next round commits. Pass `--to` for bech32m addresses, `--pubkey` for recipient x-only pubkeys, or both. Recipients pair with `--amount` positionally in the order they appear, with `--to` entries before `--pubkey` entries. The everyday `send` verb composes the same path via `WalletService.Send`; reach for this only when you need the raw daemon RPC directly.
| Flag | Default | Description |
| ----------- | ------- | ------------------------------------------------------------------------------ |
| `--to` | (none) | Recipient bech32m taproot address(es); repeatable. |
| `--pubkey` | (none) | Recipient 32-byte x-only pubkey hex; repeatable, paired after any –to entries. |
| `--amount` | (none) | Amount(s) in sats, one per recipient, in –to then –pubkey order. |
| `--dry_run` | `false` | Validate without submitting. |
Calls the daemon `SendVTXO` RPC.
```bash
wavecli ark send inround --to bcrt1... --amount 10000
```
### `send oor`
Initiates an out-of-round transfer directly between the client and operator, without waiting for a round.
| Flag | Default | Description |
| ------------------- | ------- | ----------------------------------------------------------------- |
| `--to` | (none) | Recipient address. Mutually exclusive with –pubkey. |
| `--pubkey` | (none) | Recipient 32-byte x-only pubkey hex. Mutually exclusive with –to. |
| `--amount` | (none) | Amount in sats. |
| `--dry_run` | `false` | Validate without initiating. |
| `--idempotency_key` | (none) | Caller-provided key for retrying the same OOR send intent. |
Calls the daemon `SendOOR` RPC.
```bash
wavecli ark send oor --to bcrt1... --amount 10000
```
---
Source: https://wavelength.lightning.engineering/cli/recovery.md
# recovery
`recovery` manages already-armed vHTLC recovery rows. Automatic swap execution arms recovery early and escalates it only when policy says on-chain recovery is needed; this subtree lets an operator inspect or override that decision for a specific recovery id. Normal swap clients should let the swap state machine arm and cancel recovery automatically.
> **Operator tooling**
>
> These are manual escalation and intervention commands. Escalating a recovery starts costly on-chain unroll; prefer letting the swap FSM drive recovery unless you have a specific reason to intervene.
## Flags
`recovery` itself takes no bespoke flags; it dispatches to one of the subcommands below. Every subcommand accepts `--help` and the global connection flags (`--rpcserver`, `--tlscertpath`, `--no-tls`).
## Example
`recovery` alone prints usage and exits; pick a subcommand, for example:
```bash
wavecli recovery list
```
## Subcommands
### `recovery list`
Lists vHTLC recovery rows from the daemon. ARMED rows are dormant: funds are not being unrolled unless an operator escalates, or auto-escalation is enabled by policy.
| Flag | Default | Description |
| -------------------- | ------- | ------------------------------------------------------- |
| `--include-terminal` | `false` | Include completed, cancelled, and failed recovery rows. |
Calls the daemon `ListVHTLCRecoveries` RPC.
```bash
wavecli recovery list
```
### `recovery status`
Shows one vHTLC recovery row and its current unroll status.
| Flag | Default | Description |
| --------------- | ------- | ----------------------------------- |
| `` | (none) | Required. The recovery id to query. |
Calls the daemon `GetVHTLCRecoveryStatus` RPC.
```bash
wavecli recovery status
```
### `recovery escalate`
Manually starts on-chain recovery for one previously armed vHTLC. Requires explicit consent: on non-interactive stdin you must pass `--yes` or the command exits `INVALID_ARGS`.
| Flag | Default | Description |
| ---------------------- | ---------------------------- | -------------------------------------------------------------------- |
| `` | (none) | Required. The armed recovery id to escalate. |
| `--reason` | `"manual client escalation"` | Operator-facing reason stored with the escalation. |
| `--claim-preimage-hex` | (none) | Optional 32-byte claim preimage for claim recoveries. |
| `--yes` | `false` | Skip the interactive confirmation before starting on-chain recovery. |
Calls the daemon `EscalateVHTLCRecovery` RPC.
```bash
wavecli recovery escalate --yes
```
### `recovery cancel`
Records that cooperative settlement won and the armed recovery row is no longer needed.
| Flag | Default | Description |
| -------------------- | ------------------------------ | ---------------------------------------------------- |
| `` | (none) | Required. The armed recovery id to cancel. |
| `--reason` | `"manual client cancellation"` | Operator-facing reason stored with the cancellation. |
| `--cooperative-txid` | (none) | Optional cooperative OOR txid / session id that won. |
Calls the daemon `CancelVHTLCRecovery` RPC.
```bash
wavecli recovery cancel
```
---
Source: https://wavelength.lightning.engineering/cli/dev.md
# dev
`dev` is a generated low-level command tree that exposes every daemon gRPC method directly. Unlike the curated wallet verbs, its structure is not hand-written: it is built at startup from the daemon proto descriptors compiled into the binary (the `devrpc` registry), so it always mirrors the exact RPC surface the daemon ships. Method flags are generated from each request message’s fields, and each leaf accepts `--json` for a raw request payload.
Run `dev` with no arguments to print a JSON summary of the available services and their methods. The command tree is three levels deep: `dev `, where the service is a fully-qualified proto name (with a short alias) and the call is a method on that service.
```bash
wavecli dev
wavecli dev waverpc.DaemonService GetInfo
wavecli dev daemon get-info --describe
```
> **Prefer schema for the everyday surface**
>
> For the curated CLI surface, [`schema`](/cli/schema/) is the friendlier introspection command: it lists the everyday methods with their parameters, required fields, and enum values. `dev --describe` is the equivalent for a generated RPC method: it emits that method’s input-field schema as JSON without dispatching the call.
## Flags
`dev` itself takes no bespoke flags beyond `--help` and the global connection flags (`--rpcserver`, `--tlscertpath`, `--no-tls`); each generated method leaf takes flags derived from its own request message, or `--json` for a raw payload.
## Example
Run with no arguments to print a JSON summary of the available services:
```bash
wavecli dev
```
## Services
The generated tree currently exposes these proto services (each has a short alias for terser invocation):
- `waverpc.DaemonService` (alias `daemon`): the daemon’s own API (info, wallet init, VTXO and round operations, sends, sweeps, fees, recovery).
- `swapclientrpc.SwapClientService`: the Lightning swap client service.
- `wavewalletrpc.WalletService` and `wavewalletrpc.WalletInspectionService`: the wallet API the curated verbs are built on.
- `walletrpc.VersionService` and `walletrpc.WalletService`: the embedded backing-wallet services.
```bash
wavecli dev daemon
```
Running a service with no call prints a JSON summary of its methods, each with its request and response proto type and whether it is server-streaming.
## Invoking a method
Each method leaf takes flags generated from its request message fields, or a `--json` raw payload. Server-streaming methods print one JSON object per message until the stream ends; client-streaming methods are not supported.
```bash
wavecli dev waverpc.DaemonService GetInfo
wavecli dev waverpc.DaemonService GetInfo --describe
```
`--describe` short-circuits dispatch and dumps the method’s input-field schema, teaching an agent the exact request shape before the first real call.
---
Source: https://wavelength.lightning.engineering/agents.md
# Build with agents
Your coding agent can do most of a Wavelength SDK integration for you. This page wires it up: installable skills that teach the agent our APIs and conventions, machine-readable docs it can fetch efficiently, and prompts that kick off the work.
## Install the skills
The fastest setup. One command installs four Wavelength skills into every agent the [skills CLI](https://skills.sh) supports (Claude Code, Cursor, Codex, Copilot, Gemini CLI, and more):
```sh
npx skills add lightninglabs/wavelength-sdk
```
- **wavelength-web**: embedding the wallet in a browser or React web app.
- **wavelength-react-native**: embedding the wallet in a React Native or Expo app.
- **wavelength-api**: integrating against the wallet daemon’s gRPC or REST API.
- **wavelength-cli**: automating wallet operations with the CLI.
Claude Code users can install through the plugin marketplace instead:
```sh
/plugin marketplace add lightninglabs/wavelength-sdk
```
The same catalog is served from this site, so `npx skills add wavelength.lightning.engineering` also works.
## Machine-readable docs
- [llms.txt](/llms.txt): an index of every page, with descriptions, in the llms.txt convention.
- [llms-full.txt](/llms-full.txt): the entire docs corpus in one file.
- Every page on this site has a markdown twin: append `.md` to any URL. Agents get the same content as the HTML at a fraction of the tokens.
## Add the Wavelength SDK to your AGENTS.md
Paste this into your repo’s `AGENTS.md` (or `CLAUDE.md`); your agent then carries the ground rules in every session:
```markdown
## Wavelength SDK
The Wavelength SDK embeds a self-custodial Lightning wallet in this app: Lightning
payments with no node, channels, or liquidity to manage.
- Packages: @lightninglabs/wavelength-web (browser transport, has
createWebWalletEngine), @lightninglabs/wavelength-react-native (React Native
transport, has createNativeWalletEngine, daemon compiled into the app
binary), @lightninglabs/wavelength-react (provider and hooks, takes an
injected engine), @lightninglabs/wavelength-core (shared contract).
- Docs: https://wavelength.lightning.engineering/llms.txt. Every docs page has
a markdown twin at the same URL with .md appended; fetch those.
- Web: create the engine with createWebWalletEngine() and inject it via
. Never import wavelength-web from
framework-agnostic code.
- React Native: create the engine with createNativeWalletEngine() and
inject it via the same way.
- Self-host the wasm runtime assets and set runtimeBaseUrl; see
https://wavelength.lightning.engineering/web/get-started/hosting-runtime-assets.md
- Check npm for current package versions instead of memorized ones.
```
## Kick-off prompts
```text
Integrate the Wavelength SDK into this app so users get an embedded
self-custodial Lightning wallet.
1. Read https://wavelength.lightning.engineering/web/get-started/quickstart.md
and https://wavelength.lightning.engineering/integrations/react.md first.
2. Install @lightninglabs/wavelength-web and @lightninglabs/wavelength-react at
their current npm versions.
3. Copy the wasm runtime assets into our static assets and configure
runtimeBaseUrl per
https://wavelength.lightning.engineering/web/get-started/hosting-runtime-assets.md
4. Create the engine with createWebWalletEngine() and mount at the app root. Do not import wavelength-web anywhere
except where the engine is created.
5. Add a minimal wallet screen: create or unlock, balance, receive invoice,
send payment.
6. Verify: the app builds, wallet creation reaches the ready state, an
invoice renders, and the wallet unlocks after a page reload.
```
```text
Integrate the Wavelength SDK into this React Native app so users get an
embedded self-custodial Lightning wallet.
1. Read https://wavelength.lightning.engineering/react-native/get-started/quickstart.md
first.
2. Install @lightninglabs/wavelength-react-native and
@lightninglabs/wavelength-react at their current npm versions.
3. Stage the native wallet runtime binaries before the first build per
https://wavelength.lightning.engineering/react-native/get-started/installation.md
4. Create the engine with createNativeWalletEngine() and mount
at the app root. Do not construct the
engine outside the provider's module scope.
5. Add a minimal wallet screen: create or unlock, balance, receive invoice,
send payment.
6. Verify: the app builds and launches on a device or simulator through a
development build (not Expo Go), wallet creation reaches the ready
state, and an invoice renders.
```
```text
Integrate this backend with the Wavelength wallet daemon over its API.
1. Read https://wavelength.lightning.engineering/api/get-started.md and
https://wavelength.lightning.engineering/api/rest.md first.
2. Decide gRPC or REST for our use case and say why.
3. For each operation we need, fetch the RPC's page from the API section of
https://wavelength.lightning.engineering/llms.txt and use its exact request
and response fields; do not guess field names from other Lightning tools.
4. Implement connection setup including TLS and auth exactly as the
get-started page describes.
5. Verify: a getinfo-style call succeeds and one end-to-end operation
round-trips against a dev daemon.
```
```text
Write automation for the Wavelength wallet using wavecli.
1. Read https://wavelength.lightning.engineering/cli.md first: global flags,
JSON output mode, and exit codes.
2. Use JSON output for all parsing; never scrape table output.
3. For each command you use, read its page (for example
https://wavelength.lightning.engineering/cli/send.md) and handle the
documented exit codes.
4. Verify: run the script against a dev daemon and show the output.
```
## MCP
We do not run a hosted docs MCP server. If your workflow expects one, any llms.txt-compatible docs server works against this site, for example [mcpdoc](https://github.com/langchain-ai/mcpdoc) pointed at `https://wavelength.lightning.engineering/llms.txt`. Most agents get further with the skills plus the markdown mirrors above. Integrators running the wallet daemon themselves can also run [`wavecli mcp serve`](https://wavelength.lightning.engineering/cli/mcp.md) to start a local MCP server exposing each daemon RPC as a typed tool call.
> **Repository access**
>
> The GitHub install commands go live when the repository is public at launch. Until then, use the docs-domain install.