Skip to content

feat: connect hardware flow#1033

Merged
ovitrif merged 45 commits into
masterfrom
feat/hw-wallet-connect
Jun 22, 2026
Merged

feat: connect hardware flow#1033
ovitrif merged 45 commits into
masterfrom
feat/hw-wallet-connect

Conversation

@ovitrif

@ovitrif ovitrif commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Closes #1027
Refs #998

This PR adds Bitkit's Connect Hardware flow for pairing a Trezor as a watch-only hardware wallet from the Home hardware suggestion, Hardware Wallets settings, or Android's USB "Open with Bitkit" chooser.

Description

  • Adds the guided in-sheet pairing flow: Intro → Searching → Found → Paired, covering device discovery, connect confirmation, balance display, editable Label Funds, and Finish.
  • Supports USB and BLE discovery from the same flow. USB discovery can run without BLE access; BLE scanning requests the required runtime permissions, prompts to enable Bluetooth when it is off, and shows a Settings recovery dialog if permissions were denied.
  • Handles Android USB attach intents for supported Trezor devices so choosing Bitkit from the OS chooser can open directly on the Found Device step, then refreshes USB discovery before connecting.
  • Shows the one-time THP pairing code step inline when a device asks for it, with the PIN characters collapsing into the submit spinner while pairing completes.
  • Persists the Bitkit-side Label Funds value per hardware wallet and applies it consistently across USB/BLE entries for the same wallet. After Finish, the flow returns to Home -> Wallet tab so the paired hardware-wallet tile is visible immediately.
  • Keeps compact hardware wallet surfaces resilient by capping/editing long labels so the Home tile and connection indicator remain usable.
  • Keeps the flow sheet-native: cancel/back dismisses the sheet, the Searching step uses the Figma dashed-ring loading animation, and the Paired step follows existing sheet layout/button patterns.
  • Reconnects known devices silently when USB/BLE transport becomes available again and updates connection indicators when transport is lost.

Figma Designs:

Preview

Connect via Android OS chooser when USB is attached & using BLE via settings screen:

Normal Speed 2x Speed
hwconnect.mp4
hwconnect2x.mp4

Connect via suggestion card:

hwviacard.mp4

QA Notes

Figma does not include a dedicated pairing-code screen, so that step follows Bitkit's existing in-sheet patterns.

Manual Tests

  • 1. Home → Hardware suggestion card → Continue: in-sheet nav runs Intro → Searching, shows the dashed-ring loader, then advances to Found when a device is discovered.
  • 2. Settings → General → Payments → Hardware Wallets → Add Hardware Wallet: the same connect flow opens and pairs a Trezor.
  • 3. Android USB chooser → Open with Bitkit for a supported Trezor: Bitkit opens the hardware sheet on Found Device; Connect pairs the device without going through Intro first.
  • 4. BLE permission/Bluetooth unavailable → Continue: USB search still starts; BLE access is requested gracefully, Bluetooth enablement is prompted when needed, and denied permissions can be recovered via Settings.
  • 5. Found → Connect → Paired: shows the device balance and editable Label Funds field; Finish routes to Home -> Wallet tab and the Home hardware tile / Settings list show the entered label.
  • 6. Physical first-ever pair → Pair Device: enter the 6-digit code; the code row collapses into the spinner, then the flow advances to Paired.
  • 7. regression: Back gesture or Cancel from Intro / Searching / Found / Paired: dismisses the sheet instead of stepping backward through completed connect screens.
  • 8. regression: Disconnect/reconnect USB or toggle Bluetooth for a known device: the hardware connection indicator turns grey when unavailable and green again after silent reconnect.

Automated Checks

  • Unit tests added/updated in HwConnectViewModelTest.kt: cover Intro→Searching discovery with and without Bluetooth, Found-route USB refresh before connect, Connect→Paired, inline pairing-code navigation, balance updates, label capping, and Finish persistence.
  • Unit tests added/updated in HwWalletRepoTest.kt: cover scan/connect delegation, Label Funds persistence/clearing across transport entries, and wallet-name resolution.
  • Unit tests added/updated in TrezorRepoTest.kt, TrezorTransportTest.kt, and AppViewModelSendFlowTest.kt: cover setup gating before scan/connect paths, quiet USB opens, BLE-scan opt-out, USB attach handling, and app-wide pairing-code routing.
  • Journeys added/updated: connect-flow.xml, suggestion-intro-sheet.xml, and usb-reconnect.xml cover the Settings Add flow, Home suggestion flow, and USB attach reconnect path.
  • Ran locally: just test file HwConnectViewModelTest, just compile, just test, just lint, and git diff --check.
  • GitHub checks: pending for the latest pushed head.

@ovitrif ovitrif added this to the 2.4.0 milestone Jun 18, 2026
@ovitrif ovitrif self-assigned this Jun 18, 2026
@ovitrif ovitrif force-pushed the feat/hw-wallet-connect branch from 4f59345 to 16e800c Compare June 19, 2026 08:19
@piotr-iohk

This comment was marked as resolved.

@ovitrif

This comment was marked as resolved.

@ovitrif ovitrif force-pushed the feat/hw-wallet-connect branch 3 times, most recently from 0c6fb90 to cb5f127 Compare June 19, 2026 16:43
@ovitrif

ovitrif commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Re-enable pairing input after a failed code

When the user enters a wrong pairing code, or the connect attempt fails after the code is submitted, this local submitted state stays true and the number pad remains disabled with the spinner showing.

Handled by returning the flow to Found when connect fails, including failures after the pair-code step, so the stale local submitted/spinner state is discarded and the user can retry Connect. The sheet reset/cancel path now also cancels any pending pairing-code request. Added coverage in HwConnectViewModelTest.

Resolved in d35810d

@ovitrif ovitrif force-pushed the feat/hw-wallet-connect branch from 3b2f3ff to 316bf77 Compare June 22, 2026 09:37
@piotr-iohk

Copy link
Copy Markdown
Collaborator

Testing (Samsung S22, Android 16, Trezor Safe 7, fresh install)

  • Connect via USB — OK. Device found, Connect advances to pairing code (first THP pair), then Paired.
  • Connect via BLE — Fails on Found → Connect. BLE search finds Trezor Safe 7, but Connect does not advance (no pairing code / Paired). Logs show connect enumerate with BLE disabled → 0 devices → Trezor connect failed. Likely cause: USB-only rescan before connect runs for BLE-discovered devices too (onFoundRoute always sets scanUsbBeforeConnect).
    Artifacts: recording + logs attached below.
Screen_Recording_20260622_111352_Bitkit.Regtest.mp4

bitkit_logs_2026-06-22_09-14-36.zip

Suggested fix: only USB rescan before connect for /dev/... ids (USB attach); enable BLE enumerate during connect for BLE device ids.

@ovitrif

ovitrif commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

BLE search finds Trezor Safe 7, but Connect does not advance (no pairing code / Paired). Logs show connect enumerate with BLE disabled → 0 devices → Trezor connect failed.
Suggested fix: only USB rescan before connect for /dev/... ids (USB attach); enable BLE enumerate during connect for BLE device ids.

Fixed by limiting the USB pre-connect rescan to Android USB attach route ids (/dev/...). BLE-found devices now connect directly with the discovered BLE id, and HwConnectViewModelTest covers both the USB attach route path and BLE route path.

Resolved in 7ca386e

Also investigating why I didn't encounter this bug during testings, given that I also connected first with USB then tested BLE in same session, while USB connection was known.

@piotr-iohk

Copy link
Copy Markdown
Collaborator

Re-tested

BLE + USB connect LGTM on latest revision.

Two suggestions, non-blocking.

Finish flow on Wallet screen
After Finish, we stay on the screen that launched the flow (HW settings or Home Widgets tab). Consider navigating to Home → Wallet tab on successful Finish so the paired Trezor tile is visible immediately (HwDevices on wallet overview). Cancel / mid-flow dismiss should keep current behavior.
Likely reuse existing navigateToHome() + wallet page request from ContentView.

Wrong pairing code UX
Wrong 6-digit code → Bitkit spinner with no error text (reads like loading). Trezor shows the pairing prompt again (“Allow Bitkit…”, Confirm/Cancel). After Confirm on device, Trezor shows a new code; Bitkit stops spinning and accepts a new code — pairing then succeeds. Recording ~30-40s.

rec2.mov

This matches THP retry on the device; Bitkit doesn’t explain the Trezor step or wrong-code state. On code mismatch, If possible, it would be good to show error message + “Confirm on your Trezor and enter the new code”

Attaching also logs from the session:
bitkit_logs_2026-06-22_11-31-09.zip

@ovitrif

ovitrif commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Also investigating why I didn't encounter this bug during testings, given that I also connected first with USB then tested BLE in same session, while USB connection was known.

I think this explains it: my initial pass tested USB through the Android chooser first, then BLE in the same session while the same Trezor was still USB-present/known. The broken Connect step was always doing a USB-only rescan for any Found route, so that setup could silently find/connect the device over USB and mask the BLE bug.

The failing repro is the cleaner BLE-only Found → Connect path where no matching USB device is available. BLE discovery succeeds, then the old code disables BLE during the pre-connect enumerate, gets 0 devices, and never advances to pairing/paired.

Latest revision should be verified with BLE-only Found → Connect, with the Trezor unplugged from USB before tapping Connect.

@ovitrif

ovitrif commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Finish flow on Wallet screen
After Finish, we stay on the screen that launched the flow (HW settings or Home Widgets tab). Consider navigating to Home → Wallet tab on successful Finish so the paired Trezor tile is visible immediately (HwDevices on wallet overview).

Wrong pairing code UX
Wrong 6-digit code → Bitkit spinner with no error text (reads like loading). Trezor shows the pairing prompt again (“Allow Bitkit…”, Confirm/Cancel). After Confirm on device, Trezor shows a new code; Bitkit stops spinning and accepts a new code — pairing then succeeds.

This matches THP retry on the device; Bitkit doesn’t explain the Trezor step or wrong-code state. On code mismatch, If possible, it would be good to show error message + “Confirm on your Trezor and enter the new code”

Thanks, agreed on both.

Update: the Finish routing is now handled in this PR. Successful Finish saves the label, dismisses the sheet, and routes to Home → Wallet tab so the paired Trezor tile is visible; Cancel / mid-flow dismiss keep current behavior.

For the wrong-code/device-prompt UX, I’d still rather defer it to the same device-state/backoff follow-up instead of adding a partial spinner-copy fix here. The robust version should model pairing-code retry, PIN retry, and device-busy states together so Bitkit doesn’t reconnect, refresh features, or reopen transports while the Trezor is waiting for user action. Core already added lock/busy primitives in synonymdev/bitkit-core#104, and I opened synonymdev/bitkit-core#105 to preserve typed Trezor PIN failures like InvalidPin through the mobile bindings. Once the Bitkit App consumes those typed states, we can show explicit retry copy such as “Confirm on your Trezor and enter the new code” / “Incorrect PIN. Try again.” without guessing from generic failures.

@ovitrif

ovitrif commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Finish flow on Wallet screen
After Finish, we stay on the screen that launched the flow (HW settings or Home Widgets tab). Consider navigating to Home → Wallet tab on successful Finish so the paired Trezor tile is visible immediately (HwDevices on wallet overview). Cancel / mid-flow dismiss should keep current behavior.

Handled the Finish path in this PR now. Successful Paired → Finish saves the label, dismisses the sheet, and routes to Home → Wallet tab via the existing home wallet page request. Cancel, back, and mid-flow dismiss still only dismiss the sheet.

Resolved in 6352d03

@piotr-iohk

Copy link
Copy Markdown
Collaborator

Handled the Finish path in this PR now. Successful Paired → Finish saves the label, dismisses the sheet, and routes to Home → Wallet tab via the existing home wallet page request. Cancel, back, and mid-flow dismiss still only dismiss the sheet.

This lgtm functionally, but the animation is a bit awkward. Especially when running the flow from Hardware Wallet Settings (see sec 12-13 of recording). The trezor device image seems to stall for a bit on the screen.
Recording from emulator, but also seen on device (Samsung S22):

Screen.Recording.2026-06-22.at.16.44.23.mov

@ovitrif ovitrif force-pushed the feat/hw-wallet-connect branch from b668b21 to 6352d03 Compare June 22, 2026 15:00
@ovitrif

ovitrif commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Handled the Finish path in this PR now. Successful Paired → Finish saves the label, dismisses the sheet, and routes to Home → Wallet tab via the existing home wallet page request. Cancel, back, and mid-flow dismiss still only dismiss the sheet.

This lgtm functionally, but the animation is a bit awkward. Especially when running the flow from Hardware Wallet Settings (see sec 12-13 of recording). The trezor device image seems to stall for a bit on the screen.

Adjusted the Finish handoff so the hardware sheet owns only the Finish event, while ContentView dismisses the sheet and defers the Home → Wallet navigation until after the sheet transition delay. That should avoid the Paired/Trezor illustration sitting over the destination transition when launching from Hardware Wallet Settings.

Resolved in 1e49f94

EDIT: nvm the above, it doesn't really work yet, continuing to iterate on this.

@piotr-iohk

Copy link
Copy Markdown
Collaborator

Issue is still visible:

Screen.Recording.2026-06-22.at.17.36.53.mov

@ovitrif ovitrif force-pushed the feat/hw-wallet-connect branch from 79fc456 to 10a6327 Compare June 22, 2026 16:47
@ovitrif

ovitrif commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Issue is still visible:
[media]

Found the actual cause: the visible Trezor image was coming from the Hardware Wallets settings screen behind the sheet, not from the sheet itself. I removed the keyboard/sheet animation experiments and fixed this at the screen boundary by clipping Modifier.screen() / ScreenColumn to bounds, so the underlying settings illustration can no longer bleed during the transition.

Recording done after the fix:

updatedConnect.mp4

Resolved in 10a6327

@piotr-iohk

Copy link
Copy Markdown
Collaborator

Re-tested. LGTM.

Screen.Recording.2026-06-22.at.21.38.00.mov

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

@ovitrif ovitrif merged commit 217f26a into master Jun 22, 2026
17 checks passed
@ovitrif ovitrif deleted the feat/hw-wallet-connect branch June 22, 2026 21:48
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.

Connect Hardware flow

2 participants