At a glance
Typed contract
WavelengthClient, WalletEngine primitives, request and result shapes, errors, and events share one interface.
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.
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 or
wavelength-react-native’s
defaultConfig instead.
Only the preset networks are accepted. mainnet and regtest have no
preset: a RuntimeConfig for either is built by hand.
function networkDefaults( network: PresetNetwork, transport: ServerTransport,): Partial<RuntimeConfig>networkPresetNetworktransportServerTransportPartial<RuntimeConfig>The preset config fields for that network and transport.
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'.
const DEBUG_LEVELS = [ 'trace', 'debug', 'info', 'warn', 'error', 'critical', 'off',] as const;
type DebugLevel = (typeof DEBUG_LEVELS)[number];Configuration passed to WavelengthClient.start(). For the common case, start
from your transport package’s defaultConfig(network) and override only the
fields you need.
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:
const config: RuntimeConfig = { network: 'signet', walletType: 'btcwallet', walletFeeUrl: 'https://fees.example.com', walletBlockHeadersSource: 'neutrino', walletFilterHeadersSource: 'neutrino', walletRecoveryWindow: 250, maxOperatorFeeSat: 100, signingWorkers: 4, bufferSize: 64,};networkNetworkallowMainnetbooleandataDirstringdebugLevelstringarkServerAddressstringarkServerTlsCertPathstringarkServerInsecurebooleanwalletType'lwwallet' | 'btcwallet'walletEsploraUrlstringwalletPasswordFilestringwalletPollIntervalSecondsnumberwalletRecoveryWindownumberwalletFeeUrlstringwalletBlockHeadersSourcestringwalletFilterHeadersSourcestringswapServerAddressstringswapServerTlsCertPathstringswapServerInsecurebooleanswapDatabaseFileNamestringdisableSwapsbooleanmaxOperatorFeeSatnumbersigningWorkersnumberbufferSizenumberThe 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.
Some daemon-only options (such as the LND wallet backend) are deliberately not
exposed through RuntimeConfig. Most apps should use defaultConfig() from
their transport package and override only the fields they need.
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.
testnet4 is part of the union and carries a preset, but its deployment is
not supported yet, so its endpoints are not listed below.
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):
https://signet.wavelength-rest.lightning.financesignet.wavelength.lightning.finance:443https://mempool-signet.testnet.lightningcluster.com/apihttps://signet.swapd-rest.lightning.financeswap.signet.wavelength.lightning.finance:443https://test.wavelength-rest.lightning.financetest.wavelength.lightning.finance:443https://mempool-testnet3.testnet.lightningcluster.com/apihttps://test.swapd-rest.lightning.financeswap.test.wavelength.lightning.finance:443No public preset - build the RuntimeConfig by hand with your own arkServerAddress, walletEsploraUrl, swapServerAddress, and allowMainnet.
The wire protocol used by a transport to connect to the Ark and swap servers.
Browser transports use rest; native transports use grpc.
type ServerTransport = 'rest' | 'grpc';The flat configuration shape passed to the embedded mobile facade. Application
code normally supplies RuntimeConfig instead. This internal
shape is shown for transport debugging and is not exported from the core
package.
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;};The daemon’s start verb accepts this lower-level shape; the SDK builds it from
your RuntimeConfig when you call client.start(config). The
lifecycle verbs are not reachable through callFacade, which rejects 'start'
and 'stop' so the cross-tab runtime lock is never bypassed.
The typed RPC surface every transport implements. Create a client with
createWebClient() from wavelength-web or
createNativeClient() from wavelength-react-native,
call start(config), then use the wallet methods documented in the
sections below.
interface WavelengthClient { ready(): Promise<void>; start(config: RuntimeConfig): Promise<WalletInfo>; stop(): Promise<void>; getInfo(): Promise<WalletInfo>; status(): Promise<WalletStatus>; balance(): Promise<Balance>; createWallet(req: CreateWalletRequest): Promise<CreateWalletResult>; unlockWallet(req: UnlockWalletRequest): Promise<UnlockWalletResult>; openWalletFromPasskey( req: OpenWalletFromPasskeyRequest, ): Promise<OpenWalletFromPasskeyResult>; deposit(req?: DepositRequest): Promise<DepositResult>; receive(req: ReceiveRequest): Promise<ReceiveResult>; prepareSend(req: SendRequest): Promise<PrepareSendResult>; sendPrepared(prepared: PrepareSendResult): Promise<SendResult>; send(req: SendRequest): Promise<SendResult>; list(req?: ListRequest): Promise<ListResult>; exit(req: ExitRequest): Promise<ExitResult>; exitStatus(req: ExitStatusRequest): Promise<ExitStatusResult>; exitSummary(req?: ExitSummaryRequest): Promise<ExitSummaryResult>; getExitPlan(req: GetExitPlanRequest): Promise<GetExitPlanResult>; sweepWallet(req: SweepWalletRequest): Promise<SweepWalletResult>; callFacade<T = unknown>(method: FacadeMethod, params?: unknown): Promise<T>; isRunning(): Promise<boolean>; subscribe(listener: WavelengthListener): () => void; startActivity(opts?: ActivityStreamOptions): Promise<void>; 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.
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().
ready(): Promise<void>Promise<void>Resolves when the client is ready to start(). Rejects with a WavelengthError
if the runtime assets fail to load (code asset_load_failed) or fail digest
verification (code asset_integrity_failed).
Starts the embedded daemon with the given config and resolves with its
initial WalletInfo. Calling start() while a session is
already running coalesces onto it and resolves with the current info; the
passed config is ignored, so call stop() first to start under a different one.
start(config: RuntimeConfig): Promise<WalletInfo>configRuntimeConfigPromise<WalletInfo>The daemon’s info immediately after startup, reflecting whatever wallet state already exists on disk (none, locked, or ready).
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).
stop(): Promise<void>Promise<void>Resolves once the daemon has stopped.
Releases the client’s resources and unsubscribes all listeners: it closes the
activity stream opened by startActivity() and, for the
worker transport, terminates the underlying Worker. The client is unusable
afterward; construct a new one to start again.
dispose(): voidvoidNothing; the client is torn down synchronously.
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(), or reach for a transport’s own factory (createWebWalletEngine() from wavelength-web, createNativeWalletEngine() from wavelength-react-native), which builds the client for you. Pass the engine to WavelengthProvider from wavelength-react, or drive it directly: getSnapshot()/subscribe() work the same for a vanilla consumer as for a framework binding.
Creates a WalletEngine over any transport client.
function createWalletEngine(options: WalletEngineOptions): WalletEngineoptionsWalletEngineOptionsWalletEngineA 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.
type WalletEngineOptions = { onPerformance?: WavelengthPerformanceListener } & ( | { client: WavelengthClient; config: RuntimeConfig; autoStart: true } | { client: WavelengthClient; config?: RuntimeConfig; autoStart?: false } );clientWavelengthClientconfigRuntimeConfigautoStarttrue | falseonPerformanceWavelengthPerformanceListenerDistributiveOmit
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.
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;The engine interface. Every method mirrors the matching WavelengthClient method plus engine bookkeeping: refetching info, kicking a background refresh, or advancing the phase machine.
interface WalletEngine { readonly client: WavelengthClient; getSnapshot(): WalletSnapshot; subscribe(listener: () => void): () => void; start(config?: RuntimeConfig): Promise<WalletInfo>; stop(): Promise<void>; refresh(): Promise<void>; createWallet(req: CreateWalletRequest): Promise<CreateWalletResult>; restoreWallet(req: RestoreWalletRequest): Promise<WalletInfo>; acknowledgeRecovery(): void; unlockWallet(req: UnlockWalletRequest): Promise<UnlockWalletResult>; openWalletFromPasskey( req: OpenWalletFromPasskeyRequest, ): Promise<OpenWalletFromPasskeyResult>; deposit(req?: DepositRequest): Promise<DepositResult>; receive(req: ReceiveRequest): Promise<ReceiveResult>; prepareSend(req: SendRequest): Promise<PrepareSendResult>; sendPrepared(prepared: PrepareSendResult): Promise<SendResult>; send(req: SendRequest): Promise<SendResult>; exit(req: ExitRequest): Promise<ExitResult>; exitStatus(req: ExitStatusRequest): Promise<ExitStatusResult>; exitSummary(req?: ExitSummaryRequest): Promise<ExitSummaryResult>; getExitPlan(req: GetExitPlanRequest): Promise<GetExitPlanResult>; sweepWallet(req: SweepWalletRequest): Promise<SweepWalletResult>; exitBatch(opts: ExitBatchOptions & { signal?: AbortSignal; onEvent?: (event: ExitBatchEvent) => void; }): Promise<ExitBatchResult>; list(req: ListRequest): Promise<ListResult>; clearLogs(): void; dispose(): void;}clientWavelengthClientgetSnapshot()() => WalletSnapshotsubscribe(listener)(listener: () => void) => () => voidstart(config?)(config?: RuntimeConfig) => Promise<WalletInfo>stop()() => Promise<void>refresh()() => Promise<void>createWallet(req)(req: CreateWalletRequest) => Promise<CreateWalletResult>restoreWallet(req)(req: RestoreWalletRequest) => Promise<WalletInfo>acknowledgeRecovery()() => voidunlockWallet(req)(req: UnlockWalletRequest) => Promise<UnlockWalletResult>openWalletFromPasskey(req)(req: OpenWalletFromPasskeyRequest) => Promise<OpenWalletFromPasskeyResult>deposit(req?)(req?: DepositRequest) => Promise<DepositResult>receive(req)(req: ReceiveRequest) => Promise<ReceiveResult>prepareSend(req)(req: SendRequest) => Promise<PrepareSendResult>sendPrepared(prepared)(prepared: PrepareSendResult) => Promise<SendResult>send(req)(req: SendRequest) => Promise<SendResult>exit(req)(req: ExitRequest) => Promise<ExitResult>exitStatus(req)(req: ExitStatusRequest) => Promise<ExitStatusResult>exitSummary(req?)(req?: ExitSummaryRequest) => Promise<ExitSummaryResult>getExitPlan(req)(req: GetExitPlanRequest) => Promise<GetExitPlanResult>sweepWallet(req)(req: SweepWalletRequest) => Promise<SweepWalletResult>exitBatch(opts)(opts: ExitBatchOptions & { signal?, onEvent? }) => Promise<ExitBatchResult>list(req)(req: ListRequest) => Promise<ListResult>clearLogs()() => voiddispose()() => voidThe engine runs these automatically, keyed off phase, so hosts never
reimplement them.
Activity-stream auto-refreshphase: 'ready'Activity-stream recoveryon activityStreamSync auto-pollphase: 'syncing'Restore readiness pollphase: 'restoring'Background-refresh failure budgetall of the aboveUnsolicited stopped transitionon runtimeStoppedThe engine’s immutable state snapshot, returned by getSnapshot() and delivered to subscribe() 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.
type WalletSnapshot = {phase: RuntimePhase;error: Error | null;info: WalletInfo | null;balance: Balance | null;activity: Entry[];recovery: RecoveryState;logs: WavelengthLogPayload[];};phaseRuntimePhaseerrorError | nullinfoWalletInfo | nullbalanceBalance | nullactivityEntry[]recoveryRecoveryStatelogsWavelengthLogPayload[]The state of a background wallet recovery started by restoreWallet(), a discriminated union keyed on status.
type RecoveryState =| { status: 'idle' }| { status: 'restoring' }| { status: 'done'; result: CreateWalletResult }| { status: 'failed'; error: Error; walletUsable: boolean };{ status: 'idle' }variant{ status: 'restoring' }variant{ status: 'done', result }variant{ status: 'failed', error, walletUsable }variantRead 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'.
Parameters for WalletEngine.restoreWallet(): everything CreateWalletRequest accepts, with mnemonic promoted from optional to required, since a restore is meaningless without one.
type RestoreWalletRequest = CreateWalletRequest & { mnemonic: string[];};mnemonicstring[]passwordstringseedPassphrasestringrecoverStatebooleanrecoveryWindownumberCreates a new wallet from the given request: generates (or imports) a
mnemonic, enciphers it with password, and returns the seed words for backup
display.
createWallet(req: CreateWalletRequest): Promise<CreateWalletResult>reqCreateWalletRequestPromise<CreateWalletResult>The generated (or imported) mnemonic, the daemon identity, and recovery counters.
CreateWalletRequest
type CreateWalletRequest = { password: string; mnemonic?: string[]; seedPassphrase?: string; recoverState?: boolean; recoveryWindow?: number;};passwordstringmnemonicstring[]seedPassphrasestringrecoverStatebooleanrecoveryWindownumberCreateWalletResult
type CreateWalletResult = { mnemonic: string[]; encipheredSeed: string; identityPubKey: string; recoveryRan: boolean; recoveredBoardingAddresses: number; recoveredBoardingUTXOs: number; recoveredVTXOs: number; recoveredOORReceiveScripts: number; recoveredOORRecipientEvents: number;};mnemonicstring[]encipheredSeedstringidentityPubKeystringrecoveryRanbooleanrecoveredBoardingAddressesnumberrecoveredBoardingUTXOsnumberrecoveredVTXOsnumberrecoveredOORReceiveScriptsnumberrecoveredOORRecipientEventsnumberUnlocks an existing wallet with the given password.
unlockWallet(req: UnlockWalletRequest): Promise<UnlockWalletResult>reqUnlockWalletRequestPromise<UnlockWalletResult>The daemon identity after unlock.
UnlockWalletRequest
type UnlockWalletRequest = { password: string;};passwordstringUnlockWalletResult
type UnlockWalletResult = { identityPubKey: string;};identityPubKeystringOpens a wallet from a passkey assertion: derives a wallet from the PRF output
of a PasskeyCeremony ceremony, creating one on first use
per device or unlocking the existing one thereafter.
openWalletFromPasskey( req: OpenWalletFromPasskeyRequest,): Promise<OpenWalletFromPasskeyResult>reqOpenWalletFromPasskeyRequestPromise<OpenWalletFromPasskeyResult>Whether a wallet was freshly created from the derived seed, plus the mnemonic (on import only) and the daemon identity.
OpenWalletFromPasskeyRequest
type OpenWalletFromPasskeyRequest = { prfOutput: string;};prfOutputstringOpenWalletFromPasskeyResult
type OpenWalletFromPasskeyResult = { imported: boolean; mnemonic: string[]; identityPubKey: string;};importedbooleanmnemonicstring[]identityPubKeystringWhen 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:
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() for
portable low-level integrations.
Returns the current normalized wallet info.
getInfo(): Promise<WalletInfo>Promise<WalletInfo>The current wallet and daemon info.
Returns the daemon’s runtime status snapshot.
status(): Promise<WalletStatus>Promise<WalletStatus>The current readiness, lock, network, balance, and pending-activity snapshot.
Returns the current wallet balance.
balance(): Promise<Balance>Promise<Balance>The current confirmed and pending balances, in satoshis.
Reports whether the embedded daemon is running. This does not report wallet
readiness; use getInfo() when you need walletReady.
isRunning(): Promise<boolean>Promise<boolean>true while the embedded daemon process is running.
Wallet and daemon info returned by getInfo() and
start().
type WalletInfo = { version: string; commit: string; network: string; blockHeight: number; serverConnected: boolean; walletType: string; identityPubKey: string; walletState: WalletState; walletReady: boolean; serverInfo?: ServerInfo;};versionstringcommitstringnetworkstringblockHeightnumberserverConnectedbooleanwalletTypestringidentityPubKeystringwalletStateWalletStatewalletReadybooleanserverInfoServerInfoOperator policy hints surfaced on WalletInfo.serverInfo. The
daemon learns these from its cached operator terms at bootstrap and on later
terms refreshes.
type ServerInfo = { freeRefreshWindowBlocks: number;};freeRefreshWindowBlocksnumberRuntime status snapshot returned by status().
type WalletStatus = { ready: boolean; unlocked: boolean; network: string; balance: Balance; pendingCount: number;};readybooleanunlockedbooleannetworkstringbalanceBalancependingCountnumberWallet-level balance returned by balance() and embedded in
WalletStatus.balance. All amounts are in satoshis.
type Balance = { confirmedSat: number; pendingInSat: number; pendingOutSat: number;};confirmedSatnumberpendingInSatnumberpendingOutSatnumberGenerates an on-chain deposit (boarding) address.
deposit(req?: DepositRequest): Promise<DepositResult>reqDepositRequestDepositRequest
type DepositRequest = { amountSatHint?: number;};amountSatHintnumberDepositResult
type DepositResult = { address: string; entry: Entry;};addressstringentryEntryGenerates a receive invoice for the requested amount.
receive(req: ReceiveRequest): Promise<ReceiveResult>reqReceiveRequestReceiveRequest
type ReceiveRequest = { amountSat: number; memo?: string;};amountSatnumbermemostringReceiveResult
type ReceiveResult = { invoice: string; entry: Entry;};invoicestringentryEntrySending a payment follows a quote → confirm → pay flow.
prepareSend() validates the request and returns a quote (the
expected fee, rail, and a single-use sendIntentId) without moving funds;
sendPrepared() then dispatches that exact quote.
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.
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() for a quote → confirm → pay
flow.
prepareSend(req: SendRequest): Promise<PrepareSendResult>reqSendRequestPromise<PrepareSendResult>The quote: fee, rail, and the sendIntentId to pass to sendPrepared().
Dispatches a payment previously quoted by prepareSend().
sendPrepared(prepared: PrepareSendResult): Promise<SendResult>preparedPrepareSendResultPromise<SendResult>The dispatched send’s activity entry and actual amounts.
Quotes and dispatches a payment in a single call, folding
prepareSend() and sendPrepared()
together.
send(req: SendRequest): Promise<SendResult>reqSendRequestPromise<SendResult>The dispatched send’s activity entry and actual amounts.
Parameters accepted by prepareSend() and 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.
type SendRequest = | { invoice: string; amountSat?: number; note?: string; maxFeeSat?: number; } | { onchainAddress: string; amountSat?: number; sweepAll?: boolean; note?: string; maxFeeSat?: number; };Lightning arm
invoicestringamountSatnumbernotestringmaxFeeSatnumberOn-chain arm
onchainAddressstringamountSatnumbersweepAllbooleannotestringmaxFeeSatnumberThe quote and intent id returned by prepareSend().
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;};sendIntentIdstringamountSatnumberexpectedFeeSatnumberfeeKnownbooleanexpectedTotalOutflowSatnumbertotalOutflowKnownbooleanrailSendRailquoteStatus'unspecified' | 'complete' | 'local_only'destinationSummarystringinvoiceDescriptionstringpaymentHashstringexpiresAtUnixnumberselectedOutpointsstring[]warningstringThe result of a dispatched send, returned by sendPrepared()
and send().
type SendResult = { entry: Entry; actualAmountSat: number; paymentHash?: string;};entryEntryactualAmountSatnumberpaymentHashstringIdentifies the expected settlement rail for a prepared send.
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. |
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.
function classifyDestination(raw: string): DestinationThe result of classifyDestination().
type Destination = | { kind: 'empty' } | { kind: 'invoice'; amount: InvoiceAmount } | { kind: 'address' };{ kind: 'empty' }variant{ kind: 'invoice', amount }variant{ kind: 'address' }variantThe amount an invoice carries, when it can be read from the human-readable
part. A discriminated union keyed on status.
type InvoiceAmount = | { status: 'known'; sat: number } | { status: 'amountless' } | { status: 'unrepresentable' };{ status: 'known', sat }variant{ status: 'amountless' }variant{ status: 'unrepresentable' }variantLists wallet activity or UTXOs per the request.
list(req?: ListRequest): Promise<ListResult>reqListRequestPromise<ListResult>A tagged union with only the field matching the requested view populated.
Filter the activity view by one or more entry kinds:
await client.list({ view: 'activity', kinds: ['receive', 'deposit'],});ListRequest
type ListRequest = { view?: ListView; pendingOnly?: boolean; kinds?: EntryKind[]; limit?: number; offset?: number; cursor?: string;};viewListViewpendingOnlybooleankindsEntryKind[]limitnumberoffsetnumbercursorstringListResult
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.
type ListResult = { view: ListView; activity?: ActivityList; vtxos?: VTXOInventory; onchain?: OnchainHistory;};viewListViewactivityActivityListvtxosVTXOInventoryonchainOnchainHistoryListView
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
type ActivityList = { entries: Entry[]; total: number; hasMore: boolean; nextCursor: string;};entriesEntry[]totalnumberhasMorebooleannextCursorstringThe paginated inventory returned for the vtxos list view.
type VTXOInventory = { vtxos: WalletVTXO[]; total: number;};vtxosWalletVTXO[]totalnumberOne spendable or spent VTXO in a wallet inventory.
type WalletVTXO = { outpoint: string; amountSat: number; status: string; batchExpiry: number; relativeExpiry: number; commitmentTxid: string;};The paginated history returned for the onchain list view.
type OnchainHistory = { txs: OnchainTx[]; total: number; hasMore: boolean;};txsOnchainTx[]totalnumberhasMorebooleanOne transaction in the on-chain history returned by the daemon.
type OnchainTx = { txid: string; kind: string; amountSat: number; feeSat: number; status: string; confirmationHeight: number; createdAt: string; description: string;};Subscribes a listener to runtime events.
subscribe(listener: WavelengthListener): () => voidlistenerWavelengthListener() => voidAn unsubscribe function; call it to stop receiving events on this listener.
WavelengthListener
type WavelengthListener = (event: WavelengthEvent) => void;See WavelengthEvent in the events section below for the
event shapes delivered to a listener.
Opens the wallet activity stream and forwards each entry to subscribers as an
'activity' event until stopActivity() is called.
startActivity(opts?: ActivityStreamOptions): Promise<void>// 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,});opts.includeExistingbooleanopts.kindsEntryKind[]opts.cursornumberActivityStreamOptions
type ActivityStreamOptions = { includeExisting?: boolean; kinds?: EntryKind[]; cursor?: number;};Promise<void>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
adds reconciliation and bounded cursor-based recovery for apps that want that
managed behavior.
Closes the activity stream opened by startActivity().
stopActivity(): voidvoidNothing; stops emitting 'activity' events synchronously.
A single row in the wallet activity feed, returned by list() and
streamed via startActivity() as activity events.
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;};idstringkindEntryKindstatusEntryStatusamountSatnumberfeeSatnumbercounterpartystringcreatedAtstringupdatedAtstringnotestringfailureReasonstringfailureCodeEntryFailureCodecursornumberprogressEntryProgressrequestEntryRequestThe wrapper-owned view of the lifecycle metadata the daemon computes for an
Entry. Fields are populated on a best-effort basis by the backing
subsystem; an empty field means not applicable or not yet known.
type EntryProgress = { phase: EntryPhase; phaseLabel: string; paymentHash: string; txid: string; confirmationHeight: number; vTXOOutpoint: string; preimage: string;};phaseEntryPhasephaseLabelstringpaymentHashstringtxidstringconfirmationHeightnumbervTXOOutpointstringpreimagestringThe wrapper-owned, flattened view of the request that created an
Entry. Exactly one variant’s fields are populated, named by
type; read type first and treat the other fields as zero.
type EntryRequest = { type: EntryRequestType; lightningInvoice: string; paymentHash: string; onchainAddress: string; arkAddress: string;};typeEntryRequestTypelightningInvoicestringpaymentHashstringonchainAddressstringarkAddressstringThe user-visible wallet activity category.
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. |
The collapsed wallet activity state.
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. |
A coarse, wrapper-owned lifecycle phase for an Entry.
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. |
A wrapper-owned, stable classification of why a failed Entry
failed.
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. |
Discriminates which request shape an EntryRequest carries.
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. |
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.
exit(req: ExitRequest): Promise<ExitResult>reqExitRequestPromise<ExitResult>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:
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:
await client.exit({ outpoint, forceUnrollAck: FORCE_UNROLL_ACK,});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;outpointstringdestinationstringforceUnrollAcktypeof FORCE_UNROLL_ACKExitResult
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.
type ExitResult = { path: ExitPath; cooperative: boolean; queuedOutpoints: string[]; created: boolean; actorID: string; cooperativeError: string;};pathExitPathcooperativebooleanqueuedOutpointsstring[]createdbooleanactorIDstringcooperativeErrorstringExitPath
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. |
Queries the status of an exit.
exitStatus(req: ExitStatusRequest): Promise<ExitStatusResult>reqExitStatusRequestPromise<ExitStatusResult>Whether a job exists for the outpoint, and its current status.
ExitStatusRequest
type ExitStatusRequest = { outpoint: string; detailed?: boolean;};outpointstringdetailedbooleanExitStatusResult
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.
type ExitStatusResult = { found: boolean; status: ExitJobStatus; sweepTxid: string; lastError: string; phaseDetail: string; progress?: ExitProgress; cSV?: ExitCSV; fees?: ExitFees; bestCaseBlocksRemaining: number; currentHeight: number;};foundbooleanstatusExitJobStatussweepTxidstringlastErrorstringphaseDetailstringprogressExitProgresscSVExitCSVfeesExitFeesbestCaseBlocksRemainingnumbercurrentHeightnumberExitProgress
Materialization progress through the recovery tree. Populated only for a detailed query against a live exit job.
type ExitProgress = { confirmedTxs: number; inFlightTxs: number; readyTxs: number; blockedTxs: number; totalTxs: number; currentLayer: number; totalLayers: number; targetConfirmed: boolean; allProofConfirmed: boolean;};confirmedTxsnumberinFlightTxsnumberreadyTxsnumberblockedTxsnumbertotalTxsnumbercurrentLayernumbertotalLayersnumbertargetConfirmedbooleanallProofConfirmedbooleanExitCSV
The target’s CSV maturity countdown. Populated only once the target transaction has confirmed.
type ExitCSV = { targetConfirmHeight: number; maturityHeight: number; blocksRemaining: number; mature: boolean;};targetConfirmHeightnumbermaturityHeightnumberblocksRemainingnumbermaturebooleanExitFees
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.
type ExitFees = { cPFPFeeSat: number; sweepFeeSat: number; totalCostSat: number; spentSoFarSat: number; vTXOAmountSat: number; netRecoveredSat: number; feeRateSatVByte: number; sweepFeeActual: boolean;};cPFPFeeSatnumbersweepFeeSatnumbertotalCostSatnumberspentSoFarSatnumbervTXOAmountSatnumbernetRecoveredSatnumberfeeRateSatVBytenumbersweepFeeActualbooleanExitJobStatus
Collapses the underlying unroll job phases to a short wallet-facing string set.
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. |
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.
exitSummary(req?: ExitSummaryRequest): Promise<ExitSummaryResult>reqExitSummaryRequestPromise<ExitSummaryResult>The in-progress exits plus aggregate totals.
ExitSummaryRequest
Takes no arguments.
type ExitSummaryRequest = Record<string, never>;ExitSummaryResult
type ExitSummaryResult = { exits: ExitSummaryEntry[]; totalExits: number; totalVTXOAmountSat: number; totalEstFeeSat: number; totalEstNetRecoveredSat: number;};exitsExitSummaryEntry[]totalExitsnumbertotalVTXOAmountSatnumbertotalEstFeeSatnumbertotalEstNetRecoveredSatnumberExitSummaryEntry
One in-progress exit’s coarse contribution to the portfolio.
type ExitSummaryEntry = { outpoint: string; status: ExitJobStatus; vTXOAmountSat: number; estTotalFeeSat: number; estNetRecoveredSat: number;};outpointstringstatusExitJobStatusvTXOAmountSatnumberestTotalFeeSatnumberestNetRecoveredSatnumberPreviews unilateral-exit readiness (and the backing-wallet funding required) for a set of VTXO outpoints, without moving funds.
getExitPlan(req: GetExitPlanRequest): Promise<GetExitPlanResult>reqGetExitPlanRequestPromise<GetExitPlanResult>Per-outpoint funding plans plus aggregate totals.
GetExitPlanRequest
type GetExitPlanRequest = { outpoints: string[]; confTarget?: number;};outpointsstring[]confTargetnumberGetExitPlanResult
Describes the combined backing-wallet funding plan for every previewed outpoint plus aggregate totals.
type GetExitPlanResult = { plans: ExitPlanEntry[]; feeRateSatPerVByte: number; canStart: boolean; totalFundingShortfallSat: number; totalRecommendedFundingSat: number;};plansExitPlanEntry[]feeRateSatPerVBytenumbercanStartbooleantotalFundingShortfallSatnumbertotalRecommendedFundingSatnumberExitPlanEntry
Describes how to fund the backing wallet before exit() for a single
previewed VTXO outpoint.
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;};outpointstringfundingAddressstringrequiredConfirmationsnumberrequiredFeeUTXOCountnumberusableFeeUTXOCountnumberrecommendedUTXOAmountSatnumberrecommendedTotalFundingSatnumberfundingShortfallSatnumbercanStartbooleaninfeasibilityReasonExitInfeasibilityReasonexitJobFoundbooleanexitStatusExitJobStatussweepTxidstringlastErrorstringerrstringExitInfeasibilityReason
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.
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. |
Starts a batch of exits, one outpoint per exit() call, and reports
which started, which were skipped, and which never started.
function exitBatch( opts: ExitBatchOptions & { client: WavelengthClient; signal?: AbortSignal; onEvent?: (event: ExitBatchEvent) => void; },): Promise<ExitBatchResult>On the unilateral path it previews funding with 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.
optsExitBatchOptionsPromise<ExitBatchResult>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.
type ExitBatchOptions = | { mode: 'cooperative'; outpoints: string[]; destination?: string } | { mode: 'unilateral'; outpoints: string[]; confTarget?: number };{ mode: 'cooperative', outpoints, destination? }variant{ mode: 'unilateral', outpoints, confTarget? }variantExitBatchEvent
Progress events delivered to exitBatch()’s onEvent callback, in order, as
it works through a batch.
type ExitBatchEvent = | { type: 'planned'; plan: GetExitPlanResult } | { type: 'starting'; outpoint: string } | { type: 'started'; outpoint: string; result: ExitResult } | { type: 'stopped'; stoppedBy: ExitBatchStop; remaining: string[] };{ type: 'planned', plan }variant{ type: 'starting', outpoint }variant{ type: 'started', outpoint, result }variant{ type: 'stopped', stoppedBy, remaining }variantExitBatchStop
Why an exitBatch() run stopped before finishing every outpoint.
type ExitBatchStop = | { reason: 'infeasible'; plan: GetExitPlanResult } | { reason: 'rejected'; outpoint: string; error: Error };{ reason: 'infeasible', plan }variant{ reason: 'rejected', outpoint, error }variantExitBatchResult
The outcome of exitBatch().
type ExitBatchResult = { started: { outpoint: string; result: ExitResult }[]; skipped: string[]; remaining: string[]; stoppedBy?: ExitBatchStop;};started{ outpoint: string, result: ExitResult }[]skippedstring[]remainingstring[]stoppedByExitBatchStopDistinguishes 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.
function isExitInfeasibilityFundable( reason: ExitInfeasibilityReason,): booleanreasonExitInfeasibilityReasonbooleantrue 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.
Previews or broadcasts a sweep of the backing wallet. Call it with
broadcast: false first to preview; broadcast: true moves funds.
sweepWallet(req: SweepWalletRequest): Promise<SweepWalletResult>reqSweepWalletRequestPromise<SweepWalletResult>The selected inputs, fees, and (on broadcast) the resulting txid.
SweepWalletRequest
type SweepWalletRequest = { destinationAddress: string; broadcast?: boolean; feeRateSatPerVByte?: number; confTarget?: number;};destinationAddressstringbroadcastbooleanfeeRateSatPerVBytenumberconfTargetnumberSweepWalletResult
type SweepWalletResult = { inputs: WalletSweepInput[]; totalInputSat: number; estimatedFeeSat: number; netAmountSat: number; feeRateSatPerVByte: number; canBroadcast: boolean; txid: string; failureReason: string;};inputsWalletSweepInput[]totalInputSatnumberestimatedFeeSatnumbernetAmountSatnumberfeeRateSatPerVBytenumbercanBroadcastbooleantxidstringfailureReasonstringWalletSweepInput
Describes one backing-wallet UTXO selected by sweepWallet().
type WalletSweepInput = { outpoint: string; amountSat: number;};outpointstringamountSatnumberSee the leaving Ark guide for a walkthrough of the exit and sweep flow end to end.
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.
callFacade<T = unknown>( method: FacadeMethod, params?: unknown,): Promise<T>methodFacadeMethodparamsunknownPromise<T>The response with normal SDK casing and method-aware null normalization, typed
as T (unchecked; the caller asserts the shape).
FacadeMethod
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. The lifecycle verbs start and
stop are rejected too: call the typed start() / stop()
methods, which own the runtime lifecycle (and, on the web transports, the
cross-tab runtime lock). 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.
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.
function camelizeKeys<T = unknown>(value: unknown): TvalueunknownTThe same structure with every object key rewritten to camelCase. Arrays are walked recursively; primitives pass through unchanged.
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().
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. |
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(). restoring is
owned by the 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.
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. |
The subset of lifecycle phases derived from wallet information by
phaseFromInfo(). Runtime-owned phases such as starting
and stopped are represented by RuntimePhase, not this type.
type WalletPhase = 'needsWallet' | 'locked' | 'syncing' | 'ready';Derives the wallet-state phase from a WalletInfo-shaped value. Runtime
phases are not represented here; the caller owns those.
function phaseFromInfo(info: { walletState?: WalletState; walletReady?: boolean;}): WalletPhaseinfo.walletStateWalletStateinfo.walletReadybooleanWalletPhase'ready' when walletReady is true or walletState === 'ready'; otherwise
'locked' or 'syncing' matching walletState; otherwise 'needsWallet'
(the fallback for 'none' or any unrecognized value).
Maps a raw daemon Info payload (numeric walletState, no walletReady)
onto the public WalletInfo: converts walletState to the string union via
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.
function normalizeInfo(raw: unknown): WalletInforawunknownWalletInfoThe normalized WalletInfo, with walletState as the SDK string union and
walletReady backfilled if the raw payload did not already carry it.
Normalizes a raw daemon walletState (a proto number) to the SDK string
union. Already-string values pass through unchanged.
function walletStateFromProto( value: number | WalletState | undefined,): WalletStatevaluenumber | WalletState | undefinedWalletState'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.
Discriminated union of runtime events delivered to subscribers. Narrow on
type to read the payload: 'activity' carries the changed
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.
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.
Whether an activity stream ended normally or failed unexpectedly. A consumer-initiated close emits no stream-status event.
type ActivityStreamState = 'ended' | 'failed';The payload carried by an activityStream event. Narrow on state to access
the failure message.
type ActivityStreamPayload = | { state: 'ended' } | { state: 'failed'; message: string };The set of WavelengthEvent discriminants.
type WavelengthEventType = WavelengthEvent['type'];A subscriber callback invoked with each WavelengthEvent, passed to
subscribe().
type WavelengthListener = (event: WavelengthEvent) => void;The payload carried by a 'log' event.
type WavelengthLogPayload = { level: WavelengthLogLevel; message: string;};levelWavelengthLogLevelmessagestringThe severity of a 'log' event emitted by the runtime.
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. |
A structured timing sample. These are opt-in diagnostics: without an
onPerformance listener no timing is measured and no sample is delivered, so
leaving it off costs little more than a check per measured boundary.
type WavelengthPerformanceEvent = { stage: 'runtime' | 'wallet' | 'passkey'; phase: string; durationMs: number; detail?: Record<string, string | number | boolean>;};stage'runtime' | 'wallet' | 'passkey'phasestringdurationMsnumberdetailRecord<string, string | number | boolean>Receives performance samples. Pass one as onPerformance to a client factory,
createWalletEngine, or createWebPasskeyCeremony. Delivery is isolated from
wallet behavior: a listener that throws is swallowed rather than failing the
operation being measured.
type WavelengthPerformanceListener = ( event: WavelengthPerformanceEvent,) => void;Base error class thrown by all Wavelength SDK packages. Carries a machine-readable
code alongside the human-readable message.
class WavelengthError extends Error { readonly code: WavelengthErrorCode; constructor( message: string, code?: WavelengthErrorCode, options?: { cause?: unknown }, );}messagestringcodeWavelengthErrorCodeoptions.causeunknownStable, machine-readable error classification on WavelengthError. Named SDK
codes are listed below; most daemon-originated errors fall back to
wavelength_error, except recognized storage-contention messages on start(),
which the web transports map to wallet_locked. The (string & {}) arm keeps the union
open for forward compatibility while still offering autocomplete on the known
codes.
type WavelengthErrorCode = | 'wavelength_error' | 'runtime_not_ready' | 'asset_load_failed' | 'asset_integrity_failed' | 'worker_error' | 'wallet_locked' | 'runtime_lock_unavailable' | 'unsupported_facade_method' | 'invalid_cursor' | 'invalid_config' | (string & {});| Code | Thrown when |
|---|---|
wavelength_error |
The generic default: any SDK error without a more specific code, and daemon-originated failures today, except recognized storage-contention messages on start() that map to wallet_locked. |
runtime_not_ready |
The wasm runtime is not callable: it exited before signaling ready, or its call entry point is missing once loading has finished. |
asset_load_failed |
ready() fails to load the runtime assets (wasm binary or its supporting files). |
asset_integrity_failed |
A runtime asset was downloaded in full and recognized as a runtime asset, but its bytes did not match the pinned digest. asset_load_failed covers most other ways a runtime asset failed to reach a usable state, including a body that does not look like a runtime asset at all, or, on the main thread, an already-verified script blocked from executing. In the default worker transport that block reaches the client as an unrecognized browser message and surfaces as wavelength_error, as does a downloaded, verified module that fails to compile, which is not an asset problem. |
worker_error |
The worker transport’s underlying Worker fails or crashes. |
wallet_locked |
The wallet is already running in another tab or window of the same origin. The daemon’s browser storage is exclusive to one runtime, so start() fails immediately rather than corrupting or losing data. Unrelated to the locked phase, which means a wallet on this device is waiting to be unlocked. |
runtime_lock_unavailable |
The browser refused or dropped the runtime lock request itself (for example while the document is shutting down). Unlike wallet_locked, this says nothing about another tab holding the wallet. |
unsupported_facade_method |
A call names a method the daemon facade does not expose. |
invalid_cursor |
startActivity() is given a cursor that is not a nonnegative safe integer. |
invalid_config |
RuntimeConfig validation fails before the daemon starts. |
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.
class PasskeyCancelledError extends Error { constructor(message?: string);}messagestringNormalizes 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.
function toError(value: unknown): ErrorvalueunknownErrorvalue unchanged if it is already an Error; otherwise a new Error built from it via errorMessage().
Labels how a local wallet is unlocked.
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(). |
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.
type PasskeyAssertion = { prfOutput: string; credentialId: string;};prfOutputstringcredentialIdstringThe per-platform passkey ceremony contract a host injects to drive
openWalletFromPasskey(). Injecting it keeps
consumers of this contract free of any transport dependency:
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).
type PasskeyCeremony = { supportsPasskeyPrf(): Promise<boolean>; registerPasskeyWallet(appName: string): Promise<PasskeyAssertion>; assertPasskeyPrf(allowCredentialId?: string): Promise<PasskeyAssertion>;};supportsPasskeyPrf() => Promise<boolean>registerPasskeyWallet(appName: string) => Promise<PasskeyAssertion>assertPasskeyPrf(allowCredentialId?: string) => Promise<PasskeyAssertion>