wavelength-mobile runs the
wavelength wallet — a
self-custodial Bitcoin wallet system written in Go that unifies an Ark client, a
Lightning swap engine, and an on-chain wallet behind one daemon (waved) —
directly inside a mobile app. The whole daemon is embedded in the app's own
process through gomobile,
so a user can board into Ark, hold and transfer VTXOs, swap into and out of the
Lightning Network, send and receive on-chain, and unilaterally exit to the chain
at any time — all from their phone, while keeping sole custody of their coins.
There is no separate daemon process and no open network port: the app calls the embedded wallet across a private in-memory gRPC channel, so nothing ever listens on a socket. This repo holds the idiomatic Kotlin and Swift wrappers over the gomobile bindings, plus sample apps that drive them end to end.
flowchart TD
SDK["wavelength / sdk/wavewalletdk/mobile<br/>gomobile-safe Go facade over the embedded daemon"]
AAR["Wavewalletdk.aar<br/>(Android native lib + Kotlin classes)"]
XCF["Wavewalletdk.xcframework<br/>(iOS native lib + Swift classes)"]
KT["android/walletkit<br/>Kotlin wrapper: suspend + Flow"]
SW["ios/WalletKit<br/>Swift wrapper: async + AsyncThrowingStream"]
APP_A["android/app<br/>sample app"]
APP_I["ios/Sample<br/>sample app"]
SDK -->|gomobile bind| AAR
SDK -->|gomobile bind| XCF
AAR --> KT
XCF --> SW
KT --> APP_A
SW --> APP_I
The Go facade and the binding build live in wavelength
(sdk/wavewalletdk/mobile, make mobile-android / mobile-ios). This repo
consumes the bindings and shows an app driving them end to end: boot the
embedded wallet, create a key, sync the chain from Esplora, and read balances.
| Path | Contents |
|---|---|
android/walletkit/ |
Idiomatic Kotlin wrapper (suspend + Flow + typed models) over the generated bindings. The Android library other apps depend on. |
android/app/ |
Sample Android app (Jetpack Compose, AGP 9) that drives walletkit. |
ios/WalletKit/ |
Idiomatic Swift wrapper (actor + async + AsyncThrowingStream + Codable). Mirrors the Kotlin library. |
ios/Sample/ |
Native SwiftUI wallet with dark/light themes, Face ID, balance and activity, send/receive, refresh, exit, and recovery. |
scripts/fetch-aar.sh |
Downloads Wavewalletdk.aar from the wavelength GitHub release (or builds it from a WAVELENGTH_DIR checkout) and stages it under android/walletkit/libs. |
scripts/fetch-xcframework.sh |
Builds the pinned Wavelength source with the carried iOS recovery bridge and stages Wavewalletdk.xcframework; verifies source/patch provenance. |
Makefile |
One-command iOS Simulator build, test, run, and opt-in live regtest workflows. |
docs/ |
Architecture, the Android workflow, and signet setup. |
Both the Android and iOS wrappers and their sample apps run end to end — Android
on an emulator, iOS on the Simulator — booting the embedded wallet, creating a
wallet, and syncing signet to the chain tip. wavelength CI builds the bindings
on release tags. iOS uses the source commit in
.wavelength-ios-version plus
scripts/wavelength-ios-recovery.patch;
Android uses .wavelength-version (currently v0.1.2). The separate pins let
each host wrapper adopt SDK changes after its own validation. iOS build, test,
and run commands replace a staged framework when its recorded source commit or bridge digest
differs from the iOS pin and patch. For Android, rerun the fetch script after changing its pin.
You need three things:
- The
ghCLI authenticated to an account with read access towavelength(gh auth login).fetch-aar.shdownloads the.aarfrom thewavelengthGitHub release by default; no Go or gomobile toolchain is needed. (To build the binding from source against an unreleased daemon instead, setWAVELENGTH_DIRto a local checkout — that path needs the Android + gomobile toolchain and a modern JDK.) - The Android SDK and NDK. The
androidCLI installs them:android sdk install platform-tools emulator platforms/android-36 build-tools/36.1.0 ndk/29.0.14206865 system-images/android-36/google_apis/arm64-v8a. - A modern JDK (17 or newer).
# 1. Fetch the binding and stage it here. Uses .wavelength-version by
# default; set WAVELENGTH_VERSION=<tag> to override the pin, or
# WAVELENGTH_DIR=<checkout> to build from source instead.
./scripts/fetch-aar.sh
# 2. Build the sample app.
cd android && ./gradlew :app:assembleDebug
# 3. Run it on an emulator (or a device).
android emulator start medium_phone
android run --apks=app/build/outputs/apk/debug/app-debug.apkThe .aar carries the daemon compiled for every Android ABI, so it is large
(150 MB and up). fetch-aar.sh stages it and .gitignore keeps it out of
the repo.
Use the wrapper, not the raw bindings. The Kotlin WalletClient
(engineering.lightning.wavewalletdk.client) gives every call a suspend function
that runs off the main thread and returns a typed model, and exposes wallet
activity as a Flow:
val client = WalletClient()
client.start(WalletConfig.signet(dataDir = dir)) // suspend; blocks off-thread
client.createWallet("my-password".toByteArray())
val info = client.getInfo() // typed Info
client.activity(includeExisting = true).collect { e -> /* Entry */ }Swift's WalletClient mirrors this with async/throws and an
AsyncThrowingStream (see ios/).
Underneath, the generated Mobile class is the callback-free escape hatch:
start(configJson) (synchronous, blocks until serving), JSON-bytes verbs that
throw, subscribe(req) returning a pull-Subscription, and scalar shortcuts
(confirmedBalanceSat(), walletReady()). docs/architecture.md explains the
design; the full method list is in wavelength's
docs/wavewalletdk_mobile.md.
docs/api-guide.md— common operations as a cookbook, with Kotlin and Swift side by side (boot, create/unlock, sync, balance, activity stream, errors).docs/architecture.md— how the embedded wallet, the in-memory transport, and the JSON boundary work.docs/android.md— the Android build and run workflow in detail, including theandroidCLI and emulator.docs/signet.md— pointing the wallet at a signet environment and watching it sync.ios/README.md— the SwiftWalletKitwrapper, native wallet, one-command Simulator workflow, and environment-driven regtest validation.