feat(status): expose cached device status for unseeded-device detection#29
Merged
Merged
Conversation
A BitBox paired without a wallet (no seed) cannot derive an ETH address: ETHGetAddress returns "" on the device error, which the host could not tell apart from a transient empty read after a BLE stall. Both surfaced the same way, so a brand-new, unseeded device dropped the user into a silent retry loop with no explanation. Expose the SDK's cached firmware status (uninitialized / seeded / initialized) as a new gomobile-exported DeviceStatus(), wired through the Android and iOS bridges, the Dart platform interface, BitboxManager, and the testkit. It reads the locally cached status with no device round-trip, so the host app can detect an unseeded device up front and show a dedicated state instead of the generic failure. Regenerated api.aar, api-sources.jar and Api.xcframework via gomobile.
This was referenced Jun 9, 2026
Brings in #30's 16 KB page-alignment fix. The api.aar conflict is resolved by rebuilding the gomobile binding with BOTH the 16 KB alignment flag (-extldflags=-Wl,-z,max-page-size=16384, from the merged build script) AND the new DeviceStatus export, so the merged artefact keeps Android 15+ support and exposes the device status. libgojni.so PT_LOAD p_align verified at 0x4000.
Author
|
Code review (quality + logic) clean and CI green, but holding as Draft on purpose: the native artefacts (api.aar / xcframework) were rebuilt and are not yet on-device verified. Before merge + tag, please run an on-device smoke test — pair an already set-up BitBox and confirm it reaches the wallet normally (proves the rebuilt binding + new status read don't regress the existing flow), and optionally an un-set-up device → explanatory screen. My DeviceStatus lands in v0.0.10 (v0.0.9 = the 16 KB alignment release). |
TaprootFreak
added a commit
to RealUnitCH/app
that referenced
this pull request
Jun 9, 2026
## Problem Connecting a **brand-new BitBox that has no wallet set up** (no seed) left the user stuck. Pairing succeeds, but the device has no seed to derive an ETH address from, so `getETHAddress` comes back empty. That empty read failed as a generic error → `BitboxNotConnected` → a SnackBar "something went wrong" → and because the re-scan timer is re-armed, the device is immediately found again and the user is walked through the pairing code → fail → SnackBar **loop**, with no hint that the real problem is simply an un-set-up device. (The earlier fix #710 stopped the empty address from being *persisted* — no more grey screen — but did not distinguish "no seed" from a transient empty read.) ## What changed After channel-hash verify, read the device's firmware status via the new `bitbox_flutter` `getDeviceStatus()` (cached read, no device round-trip). When it reports `uninitialized`, emit a dedicated **`BitboxNotInitialized`** state that explains the user must set up / restore a wallet on the device first. - The state offers a **retry** (`recheckDeviceStatus`) that re-reads the status — if the user has since set up a wallet, the connection continues without re-pairing. - It deliberately **does not arm the re-scan timer**, so the silent re-pair loop is gone. - Only `uninitialized` is treated as "no wallet". Other non-ready statuses (e.g. firmware-upgrade-required) intentionally keep the existing failure path rather than being mislabelled. The address-derivation/observe/sign tail of `confirmPairing` was extracted into `_acquireWalletAndConnect()` so the initial flow and the retry share one path. ## Dependency Requires `bitbox_flutter` `getDeviceStatus()` from **[DFXswiss/bitbox_flutter#29](DFXswiss/bitbox_flutter#29. This PR temporarily pins the plugin to that fix branch; it will be moved to the **`v0.0.9`** tag once #29 is merged and tagged. **Kept as Draft until then.** ## Test plan - [x] `flutter analyze` — clean (only the pre-existing generated-`i18n.dart` warning) - [x] `flutter test` — bitbox cubit / service / view suites all green - [x] Cubit: unseeded → `BitboxNotInitialized`, no wallet created, no re-scan loop (state stays stable); retry continues once seeded; retry stays while still unseeded; no-op off-state - [x] Service: `getDeviceStatus` pass-through via the simulator - [x] Widget: `BitboxNotInitialized` renders retry + cancel; retry calls `recheckDeviceStatus` - [x] i18n: new keys in both `de`/`en` ARBs (case-sensitive ASCII order), regenerated - [ ] On-device: pair an un-set-up BitBox → lands on the explanatory screen; set up a wallet + retry → continues to the dashboard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A BitBox paired without a wallet set up (no seed) cannot derive an ETH address. The device-side error makes
ETHGetAddressreturn"", which the host app could not tell apart from a transient empty read after a BLE stall. Both failed the same way, so a brand-new, unseeded device dropped the user into a silent retry loop with no explanation.The host needs a reliable, proactive signal that the device has no seed — before it tries (and fails) to read an address.
What changed
Expose the SDK's cached firmware status as a new gomobile-exported
DeviceStatus(), returninguninitialized/seeded/initialized(empty string when no device). It reads the locally cachedfirmware.Status— no device round-trip, cannot block — following the same boundary discipline asgetChannelHash.Wired through every layer per CONTRIBUTING's "Adding a new platform method":
Status()added to thebitboxDeviceinterface; new//export DeviceStatusguarded byrecoverPanic.GetDeviceStatusOperation+ registration inBitboxFlutterPlugin.getDeviceStatuscase in the method-channel handler.BitboxUsbPlatform.getDeviceStatus()→MethodChannelBitboxUsb→BitboxManager.getDeviceStatus().SimulatedBitboxPlatformsimulates the status (deviceStatus, defaultinitialized) +SimulatedBitboxMethod.getDeviceStatus.api.aar,api-sources.jar,Api.xcframeworkregenerated via gomobile.The host-app side (detect unseeded device after pairing → dedicated state) lands in a companion
RealUnitCH/appPR once this is tagged.Test plan
go vet ./...cleango test -race ./...green —DeviceStatusasserted in the pairing harness (statusinitialized) and the nil-device harness (returns"")dart format --set-exit-if-changedcleanflutter analyze --no-fatal-infos— no issuesflutter test— all green, incl. two new testkit cases (default + unseeded status)getDeviceStatus()returnsuninitialized