Skip to content

feat: add trezor receive - #1189

Open
ben-kaufman wants to merge 4 commits into
masterfrom
feat/trezor-receive
Open

feat: add trezor receive#1189
ben-kaufman wants to merge 4 commits into
masterfrom
feat/trezor-receive

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Description

This PR:

  1. Adds a Trezor tab to the Receive flow with the current next-unused native SegWit watcher address, falling back to the existing account scan when watcher state is unavailable.
  2. Supports on-chain-only amount and note editing without starting Lightning receive work.
  3. Lets users copy or share the Trezor payment request and verify the displayed address on the connected device.
  4. Matches hardware receive styling with a white selected-tab underline, blue Bitcoin mark, and blue action icons.

Stack

QA Notes

Manual Tests

  • 1. Paired Trezor → Home → Receive → Trezor: the current native SegWit address and QR load with the hardware-wallet styling.
  • 2. Trezor Receive → Show Details → Verify on Device: the button shows a loading state, the device address exactly matches Bitkit, and approval completes without changing the displayed address or showing an error.
  • 3. Trezor Receive → edit amount and note → Show QR → Show Details → Copy/Share: the edit returns directly to the Trezor QR without tags or Lightning setup, and the payment request contains the Trezor address with the entered amount and note.
  • 4. Passphrase Trezor → Receive → Show Details → Verify on Device → enter the passphrase: the wallet reconnects and address verification resumes.
  • 5. regression: Start Verify on Device → while device approval is pending, fund the displayed regtest address and advance the watcher: the stale verification is cancelled and Bitkit displays and copies the new receive address.

Automated Tests

  • Unit tests added or extended in HwWalletRepoTest.kt: cover watcher-backed receive-address selection, stored-xpub fallback, and device-address mismatch rejection.
  • Unit tests added in HwReceiveViewModelTest.kt: cover address loading and reset, passphrase reconnection, live watcher-address updates, and cancellation of stale verification.
  • Unit tests extended in ReceiveInvoiceUtilsTest.kt: cover the hardware-only BIP21 destination, shared amount and note details, and zero-amount omission.
  • Compose UI coverage added in EditInvoiceContentTest.kt: verifies the on-chain-only hardware edit callback and hidden tag actions.
  • Hardware-wallet journey coverage added in receive-onchain.xml: defines the Trezor tab, QR, details, and exact on-device address-verification flow.
  • GitHub CI build and the full testDevDebugUnitTest suite pass; lint and detekt pass.
  • git diff --check passes.

@ben-kaufman
ben-kaufman requested a review from ovitrif August 27, 2026 15:38
@ben-kaufman ben-kaufman mentioned this pull request Aug 27, 2026
6 tasks
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown

Greptile Summary

Adds hardware-wallet receiving to the existing receive sheet, including watcher-backed address resolution, BIP21 editing, and on-device Trezor verification.

  • Adds a Trezor receive tab with QR, copy, share, details, and device-verification actions.
  • Resolves the next unused watcher address with an account-scan fallback.
  • Adds passphrase-aware verification state and watcher event coverage.
  • Updates bitkit-core and adds repository, view-model, invoice utility, and journey tests.

Confidence Score: 4/5

The PR should not merge until receive amount and note state is scoped so Savings edits cannot silently alter the Trezor payment request.

The Trezor QR directly consumes the same BIP21 metadata updated by Savings editing, causing a request for one account to be displayed for another account with unintended payment details.

Files Needing Attention: app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt, app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveSheet.kt

Important Files Changed

Filename Overview
app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt Adds watcher-backed receive-address lookup, account-scan fallback, typed watcher address data, and identity-aware Trezor address verification.
app/src/main/java/to/bitkit/ui/screens/wallets/receive/HwReceiveViewModel.kt Coordinates address loading, watcher updates, verification retries, passphrase prompts, cancellation, and user-facing errors.
app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt Adds the Trezor tab and its actions, but incorrectly sources its amount and note from primary-wallet BIP21 metadata.
app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveSheet.kt Wires hardware receive navigation and editing into the shared receive sheet, including the shared metadata path implicated in the finding.
app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveInvoiceUtils.kt Builds hardware-wallet BIP21 requests and selects the blue Bitcoin QR logo.
app/src/main/java/to/bitkit/ui/screens/wallets/receive/EditInvoiceScreen.kt Adds an on-chain-only editing path that avoids Lightning receive work and hides tags.

Sequence Diagram

sequenceDiagram
  participant U as User
  participant UI as Receive Sheet
  participant VM as Hardware Receive VM
  participant HW as Hardware Wallet Repo
  participant W as Watcher
  participant T as Trezor

  U->>UI: Open hardware-wallet receive
  UI->>VM: loadAddress(walletId)
  VM->>HW: getReceiveAddress(walletId)
  HW->>W: Read next unused address
  alt Watcher address available
    W-->>HW: Address and derivation path
  else Watcher state unavailable
    HW->>T: Scan public account
    T-->>HW: First unused address
  end
  HW-->>VM: Hardware receive address
  VM-->>UI: Display QR and details
  U->>UI: Verify on device
  UI->>VM: verifyAddress()
  VM->>HW: verifyReceiveAddress()
  HW->>T: Display address at derivation path
  T-->>HW: Derived address
  HW-->>VM: Match or mismatch
Loading

Reviews (1): Last reviewed commit: "feat: add trezor receive" | Re-trigger Greptile

@ovitrif ovitrif 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.

The receive flow can complete verification for an address that is no longer displayed. I also found two smaller gaps in the Details actions and editor coverage.

Comment thread app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt Outdated
Comment thread app/src/main/java/to/bitkit/ui/screens/wallets/receive/EditInvoiceScreen.kt Outdated
@ovitrif

ovitrif commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

PLS add QA notes and Manual Tests + Automated Tests sections with details like we usually do for apps PRs. (this is more about formatting than content, content is a bit already there, just not structured in the same format we usually do)

@ovitrif ovitrif added this to the 2.5.0 milestone Aug 27, 2026
@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Updated the PR description with structured QA Notes, Manual Tests, and Automated Tests sections. I left the manual checks unchecked until they are run on Android.

coreyphillips
coreyphillips previously approved these changes Aug 28, 2026

@ovitrif ovitrif 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.

The global hardware edit path can return on a software-wallet QR. I also found misleading passphrase copy and two untested recovery paths.

isEditingHardwareInvoice.value = true
navController.navigateTo(ReceiveRoute.EditInvoice)
},
initialTab = if (hardwareWalletId != null) ReceiveTab.TREZOR else null,

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.

I see the global one-wallet flow resolves selectedHardwareWalletId, but initialTab stays null because no explicit hardwareWalletId was supplied. After the user selects TREZOR, opens the hardware editor, and pops back, the ReceiveRoute.QR destination is recreated and the state stored with remember defaults to SAVINGS (then may auto-switch to AUTO), so the edited request is shown for the software wallet instead of the Trezor address. Could we preserve the selected receive tab across navigation or return to TREZOR when isEditingHardwareInvoice initiated the editor?

}

if (hwReceiveState.isPassphraseRequired) {
HwPassphrasePromptSheet(

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.

I see this receive-only branch reuses HwPassphrasePromptSheet, whose body reads hardware__passphrase_sign_text and says the device will “sign the transaction.” Address verification does not sign anything, so hidden-wallet users are shown a misleading spend-authorization prompt before a read-only check. Could we pass receive-specific body copy that explains the passphrase is reopening the wallet to verify its address?

val response = if (firstError == null) {
firstAttempt.getOrThrow()
} else {
if (!firstError.isTrezorSessionFailure()) throw firstError

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.

I couldn't find coverage for the session-failure retry branch in verifyReceiveAddress: HwWalletRepoTest exercises watcher lookup, scan fallback, and final address mismatch, but never makes the first getAddress call fail with a session error. If the disconnect, reconnect, or second device read regresses, the claimed recovery path fails while the current tests still pass. Could we add a focused test that makes the first device read fail with a session error and verifies reconnect plus a successful second read?

}
}

fun retryAddress() {

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.

I don't see coverage that makes getReceiveAddress fail and then exercises retryAddress; HwReceiveViewModelTest only covers successful loading, cancellation, watcher updates, and passphrase verification. If the new addressLoadFailed state or retry behavior regresses, users can remain stuck without an address while the current tests still pass. Could we add a focused test that fails the first load, asserts addressLoadFailed, retries, and verifies the recovered address?

Base automatically changed from feat/trezor-send to master August 28, 2026 21:41
@ovitrif
ovitrif dismissed coreyphillips’s stale review August 28, 2026 21:41

The base branch was changed.

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.

4 participants