Skip to content

ios: add native Wavelength wallet - #6

Draft
bhandras wants to merge 6 commits into
mainfrom
codex/ios-wallet
Draft

ios: add native Wavelength wallet#6
bhandras wants to merge 6 commits into
mainfrom
codex/ios-wallet

Conversation

@bhandras

@bhandras bhandras commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

  • turn the iOS sample into a native SwiftUI wallet with balance, searchable
    activity, activity inspection, and Lightning/on-chain send and receive
  • use Wavelength's Esplora-backed lightweight wallet rather than bundling LND
  • add camera payment-request scanning and reusable QR codes for pending receive
    requests
  • isolate signet, testnet3, mainnet, and debug regtest wallet state, with an
    explicit mainnet warning and network selection available before unlock
  • add one-command Simulator build/test/run targets and opt-in live regtest tests

Activity and payment behavior

  • incomplete Lightning invoices remain requests/pending; "received" is only
    shown after settlement
  • on-chain deposit rows use Esplora-observed amounts rather than receive hints
  • on-chain addresses and transaction IDs link to a network-appropriate explorer
  • send confirmation presents the Wavelength quote before the user commits
  • the app refreshes its snapshot every five seconds in addition to consuming
    the activity stream

Mobile transport recovery

  • publish Activity and balance refresh results independently so one stalled
    source cannot suppress a successful update from the other
  • request a 20-second native deadline for invoice creation
  • treat the binding's stable uncertain-outcome error as a reconcile-before-retry
    signal for both deadline and lifecycle cancellation
  • recover an invoice from the authoritative Activity view instead of blindly
    creating a second invoice after an uncertain result
  • show a clear long-running state after five seconds rather than an unexplained
    spinner
  • after a real iOS background/foreground transition, restart and unlock the
    same embedded wallet to re-dial external transports on the current path
  • defer lifecycle teardown while state-creating operations are in flight, so
    transport recovery cannot cancel a payment or receive at an ambiguous point

Wavelength dependency

wavelength#1131 is
merged as da8903b633d0a45122eb2f5cbc72aeb307c52c98. Its source tree contains:

  • bounded repeatable mobile reads;
  • bounded Receive with a stable uncertain-outcome error contract;
  • bounded optional credit enrichment; and
  • btcwallet v0.18.1-0.20260826052527-33c252f3b4d6, which releases the wallet
    database transaction before remote recovery filtering.

This PR was built and tested against an xcframework generated from that exact
merged source tree. The reviewed PR head and merge commit have the same Git tree.

No Wavelength release tag currently contains the merge. The latest stable asset
is still v0.1.1. Until the next release is published, reproduce the validated
build by checking out da8903b633d0a45122eb2f5cbc72aeb307c52c98 in Wavelength
and running:

WAVELENGTH_DIR=/path/to/wavelength ./scripts/fetch-xcframework.sh

The framework remains an ignored build artifact. After the next Wavelength
release, scripts/fetch-xcframework.sh will download its published
Wavewalletdk.xcframework.tar.gz asset through the normal latest-release path.

Compatibility and rollout

Wavelength binding Mobile wrapper Result Rollout
v0.1.1 this PR TimeoutSeconds is ignored safely, but native reads and Receive remain unbounded compatible fallback, not the complete fix
merged #1131 previous PR head native bounds work; lifecycle-canceled Receive is not recognized as uncertain by Swift short-lived source-validation combination only
merged #1131 this PR every uncertain Receive outcome reconciles Activity before retry intended final combination

The wrapper change only classifies returned errors. It does not alter persisted
wallet state, requests already in flight, or the Wavelength wire schema.

Safe rollout order:

  1. publish a Wavelength release containing merge commit da8903b;
  2. build the mobile app with that release's xcframework; and
  3. validate signet or regtest receive recovery before distribution.

Validation

  • built Wavewalletdk.xcframework from the exact merged Wavelength tree
  • make test against that framework
    • 14 unit tests passed
    • 3 live regtest UI tests skipped as designed without endpoint opt-in
  • new tests cover the stable uncertain-outcome prefix, the legacy deadline
    fallback, and an unrelated cancellation negative control
  • companion Wavelength exact-head CI passed all 20 required checks, including
    mobile bindings, Postgres, race, lint, static checks, and cross-compilation
  • manual Simulator validation of onboarding, network switching, balance,
    activity/details, QR scanning, and Lightning/on-chain send and receive
  • manual signet validation on Simulator and a physical iPhone

Add typed network configuration for the Esplora-backed lightweight wallet,
including explicit mainnet opt-in and local regtest transport handling.

Expose richer activity metadata, parse Lightning and BIP-21 payment requests,
and link the resolver dependency required by the embedded Go runtime.
Replace the minimal sample with an idiomatic SwiftUI wallet covering balance,
activity and details, Lightning and on-chain send and receive, QR scanning,
recovery, and network-isolated settings.

Keep incomplete receives visibly pending, make mainnet an explicit choice, and
add unit and opt-in live UI coverage for the core wallet flows.
Add Make targets that select and boot an iPhone Simulator, generate the Xcode
project, and build, test, install, or launch the wallet from the command line.

Document the native app, release framework setup, and environment-driven live
regtest workflow without requiring endpoint variables for normal builds.
Re-dial the embedded wallet after a background transition, but wait for
state-creating calls to return before teardown. Publish balance and
activity independently so one slow source cannot suppress the other.

Bound invoice creation through the native request and reconcile Activity
before allowing a deliberate retry after an uncertain timeout.
Add a Make target that auto-selects a connected iOS device and relaunches the
installed wallet under devicectl's attached console. This streams Swift and
embedded daemon output directly to the terminal without requiring developers
to copy the in-app diagnostic buffer.

Document the workflow and forward the targets from ios/Sample.
The merged mobile binding marks request deadlines and lifecycle
cancellation as uncertain because invoice creation may already be
durable. The Swift wrapper only recognized generic timeout text, so a
canceled receive could bypass Activity reconciliation and appear safe to
retry.

Recognize the binding's stable uncertainty prefix and route it through
the same recover-before-retry path. Keep generic deadline matching for
older bindings. Test the new prefix, legacy timeout, and negative case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant