Skip to content

feat: add blockstream jade hardware wallet - #765

Open
coreyphillips wants to merge 21 commits into
masterfrom
feat/jade-hardware-wallet
Open

coreyphillips wants to merge 21 commits into
masterfrom
feat/jade-hardware-wallet

Conversation

@coreyphillips

Copy link
Copy Markdown
Contributor

This PR:

  1. Adds Blockstream Jade as a second hardware wallet vendor, over Bluetooth
  2. Generalises the hardware wallet layer so every device call is routed by the vendor of the paired entry
  3. Releases an open Jade Bluetooth link while Bitkit is backgrounded and reconnects silently on return

It is the iOS counterpart to bitkit-android#1231 and requires bitkit-core 0.5.17, which carries the Jade module (synonymdev/bitkit-core#153).

Description

A Jade can now be paired from Connect Hardware, unlocked with the PIN entered on the device, and used like a paired Trezor: watch-only balances, on-device receive address verification, and on-device signing for both a send and a transfer to spending. The protocol, the pinserver round trip and every deadline live in bitkit-core. This app supplies the byte transport over Bluetooth and the UI that drives the flows.

  • Bumps bitkit-core to 0.5.17 so the Jade protocol, the PIN unlock and signing come from core, as on Android.
  • Adds a Jade Bluetooth transport over the Nordic UART Service with its own central, so Trezor links and scans are untouched. It subscribes to indications (the Jade's TX characteristic is indicate-only), writes with response, sizes chunks from the link MTU, and only starts Bluetooth when a Jade is actually used, so there is no new permission prompt at launch.
  • Adds a Jade session manager ported from Android: pairing with the PIN on the device, finding a paired Jade again by its advertised name after its Bluetooth identifier changes, rejecting a different Jade before it asks for a PIN, silent reconnects that never ask for one, and a retry without taproot on old firmware.
  • Stores the vendor on every paired entry and reads entries saved before this change as Trezor, so existing Trezor wallets keep their ids and names, and the same seed on a Trezor and a Jade stays two separate wallets.
  • Routes connect, verify, compose and sign by the wallet's vendor and keeps one vendor session at a time, releasing the other vendor first.
  • Gives Jade its own copy for a wrong PIN, an unreachable pinserver, a busy device, firmware that is too old, a device with no wallet yet, a network mismatch and a transaction too large to sign.
  • Lets Connect Hardware find Jades next to Trezors, shows a hint to enter the PIN on the Jade while it unlocks, and hides the passphrase option for a Jade, which has one wallet per device.
  • Shows the Jade illustration and "Sign With Jade" on send and transfer, and allows 5 minutes to reach a Jade that may be waiting for its PIN.
  • Lets the send sheet be closed while the device connects, and releases the device session when a send, a receive verification or a pairing is left part-way, for both vendors, matching Android.
  • Labels the receive tab with the wallet's vendor.
  • Releases a Jade Bluetooth link 30 seconds after Bitkit is backgrounded and on termination, because a Jade left holding a dead link can refuse connections until it is power cycled. The release runs inside a background task since iOS suspends the app. Coming back reconnects without a prompt.
  • Makes the Bluetooth permission and Bluetooth-off copy vendor neutral.

Differences from Android worth knowing:

  • Bluetooth only. There is no USB on iOS.
  • The receive tab identifier follows the vendor: Tab-trezor for a Trezor wallet (unchanged) and Tab-jade for a Jade. bitkit-android#1231 renames its tag to Tab-hardware for both, while the shared helper in bitkit-e2e-tests and Android's ReceiveHardwareFlowTest still tap Tab-trezor.
  • The transfer to spending sign button also reads "Sign With Jade" for a Jade. Android changed only the illustration there.

Out of Scope

  • Bitkit/Assets.xcassets/Illustrations/jade-placeholder.imageset: the Jade illustration is a placeholder vector until design supplies the real asset.
  • USB connections to a Jade.
  • Signet: Jade does not support it, so that combination throws rather than mapping to a network.
  • journeys/hardware-wallet: no Jade journeys, since there is no Jade emulator in bitkit-docker and the simulator has no Bluetooth.
  • A PIN hint outside the Found step. Send, receive and transfer rely on the prompt on the device, as on Android.

Design

N/A: no design available.

Preview

Recording of the Bluetooth pair, verify and sign flow to be attached from a physical Jade.

QA Notes

Manual Tests

These need a physical Jade and a physical iPhone, since the simulator has no Bluetooth.

  • 1. Jade on → Connect Hardware → Search → Connect: the Found step shows "Enter your PIN on your Jade to unlock it." while the Jade asks for its PIN. After unlocking, the Paired step reads "Paired Jade" with no Passphrase button and the wallet tile appears.
  • 2. Connect Hardware → Connect on a Jade → Cancel while the Jade waits for its PIN: the sheet closes and no PIN prompt comes back later.
  • 3. Enter a wrong PIN while pairing: shows "Wrong PIN. Try again on your Jade."
  • 4. Jade wallet → Receive: the hardware tab reads "Jade". Show Details → Verify on Device: the address on the Jade matches the one in the app.
  • 5a. Jade wallet → Send → Sign With Jade with a locked Jade: the Jade asks for its PIN and the sheet can be swiped away while it waits.
    • 5b. Sign again and unlock: the transaction shows on the Jade, the sheet stays locked while it signs, and confirming broadcasts it.
  • 6. Jade wallet → Transfer to Spending → sign on the Jade: the transfer completes.
  • 7. Power cycle the Jade → Send → Sign With Jade: Bitkit finds it under its new Bluetooth identifier and connects.
  • 8. Connected Jade → background Bitkit for about 45 seconds → reopen: the link was released and Bitkit reconnects with no pairing prompt and no PIN prompt.
  • 9. Settings → Hardware Wallets: rename the Jade wallet, then remove it.
  • 10. Pair a Trezor and a Jade holding the same seed: they show as two wallets.
  • 11. regression: Trezor → pair, Receive → Verify on Device, Send and Transfer to Spending: all work as before, and the receive tab reads "Trezor".
  • 12. regression: Trezor connected → background and reopen Bitkit: the Trezor reconnects.

Automated Checks

  • New tests:
    • JadeManagerTests.swift: pairing, unlock, account export with the taproot fallback, identity checks, reconnect by advertised name, silent reconnect, the reconnect loop, cancel and teardown ordering, the disconnect notice, and background release. It ports the Bluetooth-relevant cases of Android's JadeRepoTest.kt.
    • JadeTransportTests.swift, JadeBLELinkStateTests.swift and BlockingQueueTests.swift: chunk sizing, read timeouts, close waking every waiter, error codes, the stale bond message, and that Bluetooth stays off until a Jade is used.
    • JadeServiceTests.swift: finalizePsbt reaches core instead of recursing.
    • JadeDeviceIdentityTests.swift: device ids, models, advertised name matching and network mapping.
    • HwWalletManagerVendorTests.swift: vendor routing, one vendor session at a time, the Jade signing identity check, on-device verification, the Jade fingerprint on compose, passphrase refusal for a Jade, per-vendor reconnect timeouts, and foreground reconnect.
    • HwErrorPredicateTests.swift and HwErrorPresenterTests.swift: Jade error classification and copy.
    • HwEngagedSessionTests.swift: the receive sheet releases only a session it used.
    • TrezorManagerVendorIsolationTests.swift: Trezor loads, renames and forgets never touch a Jade entry.
    • HwWalletVendorPresentationTests.swift: vendor assets, copy and the receive tab identifier.
  • Modified tests:
    • HwKnownDeviceMatchingTests.swift and HwKnownDeviceStorageTests.swift (renamed from their Trezor names): vendor slices, migration of entries saved before this change, and entries of an unknown vendor being kept.
    • HwConnectViewModelTests.swift: Jade discovery, cancel and unlock hint.
    • HwFundingSignerTests.swift: when the send sheet can be left, cancel release, and the attempt counter.
    • TransferViewModelHwTests.swift, HwWalletIdTests.swift, HwWalletNameTests.swift, HwWalletManagerTests.swift and HwWalletManagerPassphraseTests.swift.
  • Local: the unit test suite with CI's skip list passes (1,565 tests, 0 failures), node scripts/validate-translations.js reports 0 errors, and SwiftFormat passes on the changed files.

@coreyphillips coreyphillips self-assigned this Sep 18, 2026
@coreyphillips coreyphillips added the enhancement New feature or request label Sep 18, 2026
@greptile-apps

greptile-apps Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

The PR is not yet safe to merge because Trezor foreground reconnects can escape cross-vendor serialization and survive an app wipe.

Findings

  1. P1 Reconnect Escapes Session Lock ▶
  2. P1 Wipe Leaves Trezor Running ▶

Summary

This PR adds Bluetooth support for Blockstream Jade and generalizes hardware-wallet persistence, routing, signing, presentation, and lifecycle management across Jade and Trezor.

  • Adds the Jade Nordic UART Bluetooth transport, core service adapter, session manager, reconnect handling, and device identity checks.
  • Routes pairing, address verification, funding composition, signing, rename, removal, and foreground reconnect by stored vendor.
  • Introduces vendor-aware wallet identifiers and migrates existing paired entries as Trezor while preserving unknown vendors.
  • Adds Jade-specific UI, localization, error handling, background-link release, and extensive unit coverage.
  • Two lifecycle serialization gaps remain around foreground Trezor reconnects and app wipe.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    UI[Hardware wallet operation] --> WM[HwWalletManager]
    WM --> V{Stored wallet vendor}
    V -->|Trezor| TS[TrezorManager]
    V -->|Jade| JS[JadeManager]
    TS --> TB[Trezor Bluetooth session]
    JS --> JB[Jade Nordic UART session]
    WM --> L[Shared vendor-session lock]
    FG[Foreground lifecycle] --> L
    L --> RT[Schedule Trezor reconnect task]
    RT -. escapes lock .-> TS
    WIPE[App wipe] --> RESET[resetForWipe]
    RESET --> JS
    RESET -. Trezor omitted .-> TS
    TS --> STORE[Hardware device UserDefaults]
    WIPE --> CLEAR[Clear UserDefaults domain]
Loading

Reviews (1) · Last reviewed commit: "fix: stop cancelled jade connects and re..."

Comment thread Bitkit/Managers/HwWalletManager.swift Outdated
Comment thread Bitkit/Managers/HwWalletManager.swift
…wipe

The foreground Trezor reconnect was launched in a task that only marked
the session active once it ran, so a Jade operation taking the lock in
between could dial alongside it. The reconnect now registers before the
lock is released and is cancelled when the session is released.

A wipe only tore down the Jade, so a Trezor reconnect finishing after
the wipe could save its device back. The Trezor session is now released
and its loaded devices dropped before the wipe clears storage.
ovi-reviewer[bot]
ovi-reviewer Bot previously requested changes Sep 18, 2026

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ⛔️ Request Changes


Review: diff 89 files.
Counterpart synonymdev/bitkit-android#1231: equivalent.

Findings:
2 inline (2 blocking)

Audit:
Awaits QA.

Coverage:
Unit tests: 85% - The 26 changed test files cover Jade transport, sessions, storage, routing, presentation, connect, send, and transfer paths, but not the two lifecycle races.
QA: journeys and manual tests await all reviewers to approve, author can run it now via comment: @ovi-reviewer test


Reviewed by gpt-5.6-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve


Reaudit: diff 6 files.
Counterpart synonymdev/bitkit-android#1231: not compared.

Findings:
N/A

Audit:
Skipped - no security surface in the diff (score 0, threshold 3).

Coverage:
QA: journeys and manual tests await all reviewers to approve, author can run it now via comment: @ovi-reviewer test


Reviewed by gpt-5.6-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)

@ovitrif
ovitrif dismissed ovi-reviewer[bot]’s stale review September 18, 2026 18:43

addressed - reaudit confirmed

@jvsena42 jvsena42 left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No HIGH/MEDIUM findings, and no LOW worth posting.

Checked:

  • Funds. The amount and address on HwSendSignView are the values passed to signAndBroadcast. The fee shown comes from the compose that produced the PSBT. The PSBT is built app-side from the stored xpub, and the device contributes only the fingerprint. Jade results go through finalizePsbt(originalPsbt:signedPsbt:), and core 0.5.17's guards pin the unsigned tx and each input's previous_output and run interpreter_check. No double broadcast: pendingPayment caches the signed tx per request, re-sign happens only when no result reached the app, and cancel() is a no-op once isBroadcastUnresolved. The transfer path does the same via pendingHwFundingBroadcast.
  • Device identity. requireJadeSession(holding:) runs before signPsbt. rejectUnusableDevice fails closed on a nil efuse MAC. Known-device reconnects are efuse-checked before unlock, and the advertised name is only a hint.
  • Key material. No xpub, PSBT, fingerprint or PIN in logs. HwKnownDevice only gains vendor and jadeDeviceId. The backup snapshot is still {walletId: customLabel}.
  • Upgrade from Trezor-only state. Storage keys are unchanged. A missing vendor decodes as Trezor unless the id has the jade: prefix, and unknown vendors are preserved on rewrite. bitkit-core 0.5.14 → 0.5.17 adds only Jade code and the new finalize_psbt guards, with no storage or migration change.
  • Lifecycle. A pairing failure before addOrUpdateKnownDevice stores nothing. resetForWipe covers both vendors ahead of removePersistentDomain, and the connectEpoch bump stops a late connect from persisting after a wipe. Both greptile threads are fixed at head.
  • Parity with synonymdev/bitkit-android#1231. Signing identity check, efuse nil check, and the Receive tab not tearing down a live Trezor session: present. Activity-teardown cache and USB-attach disconnect: absent, since iOS has no activity lifecycle or USB. Locked Jade with no exit from Send: canLeave covers it.

Not verified: device-only behaviour, i.e. link release during on-device confirm at 30s in background, and firmware dropping the confirm screen. That needs a real Jade and iPhone (manual tests 5b/8).

@ovi-reviewer ovi-reviewer Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve


Reaudit: diff 6 files.
Counterpart synonymdev/bitkit-android#1231: not compared.

Findings:
N/A

Audit:
Already done in comment.

QA:
Tested on sim-1 iPhone 17 Pro simulator on iOS 26.5

Test 1 ⚠️ skipped: hardware absent in env

Test 2 ⚠️ skipped: hardware absent in env

Test 3 ⚠️ skipped: hardware absent in env

Test 4 ⚠️ skipped: hardware absent in env

Test 5 ⚠️ skipped: hardware absent in env

Test 6 ⚠️ skipped: hardware absent in env

Test 7 ⚠️ skipped: hardware absent in env

Test 8 ⚠️ skipped: hardware absent in env

Test 9 ⚠️ skipped: hardware absent in env

Test 10 ⚠️ skipped: hardware absent in env

Test 11 ⚠️ skipped: hardware absent in env

Test 12 ⚠️ skipped: hardware absent in env

Test 13 ⚠️ skipped: hardware absent in env

Approve.

Coverage:
Unit tests: 100% - Focused tests cover synchronous reconnect activity, cross-vendor release, wipe cancellation, both-vendor reset, and loaded-device clearing.
QA: 0 of 13 manual tests passed


Reviewed by gpt-5.6-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)

Warning

Thirteen of thirteen manual tests need a physical Blockstream Jade and a Bluetooth-capable iPhone, and the simulator has neither: pairing, PIN handling, on-device address verification, signing and broadcast stand unverified here.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Advice: ✅ Approve


Reaudit: diff 1 file. No new findings; the rest is in the review.


Reviewed by gpt-5.6-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve


Reaudit: diff 1 file.

Findings:
N/A

Audit:
Already done in review.

QA:
Tested on iOS 26.5 simulator (iPhone 17 Pro)

Test 1 ⚠️ skipped: hardware absent in env

Test 2 ⚠️ skipped: hardware absent in env

Test 3 ⚠️ skipped: hardware absent in env

Test 4 ⚠️ skipped: hardware absent in env

Test 5 ⚠️ skipped: hardware absent in env

Test 6 ⚠️ skipped: hardware absent in env

Test 7 ⚠️ skipped: hardware absent in env

Test 8 ⚠️ skipped: hardware absent in env

Test 9 ⚠️ skipped: hardware absent in env

Test 10 ⚠️ skipped: hardware absent in env

Test 11 ⚠️ skipped: hardware absent in env

Test 12 ⚠️ skipped: hardware absent in env

Test 13 ⚠️ skipped: hardware absent in env

Warning

No physical Blockstream Jade, Trezor, or compatible iPhone was available on the host. All 13 physical-device checks remain unverified; the exact-head simulator build launched but cannot substitute for either signer. Tests 5 and 6 above correspond to the checklist's nested 5a and 5b, with the later tests in PR order.

Approve.


Reviewed by gpt-6-sol-xhigh via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)

@ovi-reviewer

ovi-reviewer Bot commented Sep 24, 2026

Copy link
Copy Markdown

@coreyphillips conflicts

…s pin

The Send sheet already allowed leaving while the device was being reached.
The transfer sign screen kept Back and the drawer disabled for the whole
reconnect, which for a locked Jade can last up to five minutes. Track the
connecting phase and gate navigation on it, so leaving cancels the connect
and releases the link.
A Jade restored with another seed keeps its efuse MAC, so a reconnect
stored the new wallet with no pairing step and left the old one looking
usable. A reconnect now fails closed when the exported keys match no wallet
paired on that device; adding the new wallet stays an explicit pairing.
…wallet

# Conflicts:
#	Bitkit.xcodeproj/project.pbxproj
#	Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One LOW inline. The two commits that port my android findings both check out.

  • b3879ac9. isConnectingDevice is set only inside the two ensureConnected brackets, and canLeaveHwSign drives the navigation bar, so Back is available while the device waits for its PIN and disabled again once it is asked to sign or a broadcast is out. Leaving tears the link down through beginStaleSessionTeardown, which bumps the connect epoch and closes the transport without waiting for the 5-minute unlock. The Send path already had the equivalent through canLeave.
  • 5c3c9420. The xpub-overlap rejection runs before anything is written, and on throw the connect tears down with no store write. A silent reconnect of a re-seeded Jade is rejected when unlocked, and when locked it only refreshes the expected entry's path.

Parity note: android also added an hwTransferAttempt counter so a cancelled job cannot reset a newer attempt's flags. TransferViewModel here resets unconditionally in its defer. I could not build a reachable clobber — every device phase is inside withTimeout, whose cancellation unwinds on the same main-actor turn as cancelHwSigning(), and the one await that is not cancellation-aware runs while isConnectingDevice is false — and the send path already has signingAttempt. Worth knowing the two sides differ.

Checked and clean:

  • Signing. finalizePsbt pins the unsigned transaction and every previous output, fails when an input lacks a valid signature, runs the interpreter check and accepts only SIGHASH_ALL / Taproot default. The broadcast bytes are that finalized transaction, and the session is pinned to the wallet before signing.
  • Change. Composed by core with [fingerprint/path]xpub origins on both keychains, with the fingerprint read from the live device at compose time, so change is derived from the pinned account xpub and the device can verify it as its own.
  • Shown vs broadcast. The Send view's amount and address are the values passed to sign-and-broadcast, the fee is refreshed from the composed PSBT, and the transfer path pins order, wallet, address and fee in PendingHwFundingBroadcast, so a retry never re-signs.
  • Transport. No secret crosses the app: the PIN is typed on the device and the pinserver exchange happens in core. No PSBT, xpub, fingerprint or PIN reaches a log. A dropped response fails the waiters, and the retry after a reconnect produces the same txid; a broadcast timeout keeps the pending payment for a retry rather than re-signing.
  • Trezor. The shared-code changes are renames plus session helpers; legacy entries without a vendor decode as Trezor, storage keys are unchanged and unknown-vendor entries survive a rewrite.

Still device-only: the 30 s background release during an on-device confirm, and firmware dropping the confirm screen. Those need a physical Jade.

}
guard let knownTrezorId = known.trezorDeviceId, let entryTrezorId = entry.trezorDeviceId else { return false }
return entryTrezorId != knownTrezorId
guard let knownHardwareId = known.hardwareId, let entryHardwareId = entry.hardwareId else { return false }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An explicit re-pair of a re-seeded Jade keeps the old, unsignable wallet forever. LOW, and shared with the android twin rather than new here.

connect(path:) passes expected: nil, so 5c3c9420's reject is deliberately skipped and addOrUpdateKnownDevice reaches isReplaced. There, the vendor and the id match, the walletKeys differ, refreshed is nil, and the hardware ids are equal — because a Jade's hardware id is its efuse MAC, which survives a wipe — so it returns false and the seed-A entry stays. The comment says wallets of a seed the device no longer carries go too, and for Trezor that holds because its device id regenerates on wipe.

After wiping a Jade and restoring seed B, Home shows two Jade tiles. The seed-A tile still shows its balance, Send and Transfer from it fail every time with "reconnect hardware device", and Receive reports a mismatch for an address that is actually correct. Nothing is lost, but it is a dead end with no explanation.

Fix: when expected == nil and previous == nil, drop the other entries sharing this id whose xpubs are disjoint from the fresh export, or give isReplaced a vendor hook for "same hardware id, disjoint xpubs on an explicit pairing". synonymdev/bitkit-android#1231 has the same gap, so it is worth fixing on both.

ovi-reviewer[bot]

This comment was marked as resolved.

The master merge took the old build setting, which says Bitkit does not use
Bluetooth. With GENERATE_INFOPLIST_FILE on, that setting is what ships, so
set it to the same sentence as Info.plist for Debug and Release.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Advice: ✅ Approve


Reaudit: diff 1 file.
No new findings; the rest is in the review.

Coverage:
QA: waits for the other reviewers' approval, or @ovi-reviewer test


Reviewed by gpt-6-sol-xhigh via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner) · wrong <why> (owner)

@ovi-reviewer
ovi-reviewer Bot dismissed their stale review September 24, 2026 17:22

addressed - reaudit confirmed

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Delta review of fed7027 (and the ab3d949 merge). No findings.

Checked:

  • INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription is set in both configurations of the Bitkit target (Debug and Release). These are the only two configurations in the project; E2E is a scheme, not a configuration. Info.plist carries the same sentence, so there is no conflicting text. The key was present in every commit; this commit restores the wording the merge replaced.
  • The merge took master's side for project.pbxproj and Package.resolved: CodeScanner removed, bitkit-core 0.5.18, which contains the Jade module and the PSBT finalize guards. The project uses synchronized root groups, so no file references were lost.

Parity note, non-blocking: synonymdev/bitkit-android#1231 now shows a dedicated wallet-mismatch message when a re-seeded Jade is used from the old tile (commit 8383cfe89). iOS JadeManager.rejectUnpairedWallet still throws "Reconnect Hardware Device" for that case, which is the wording the Android thread objected to.

My earlier LOW at HwKnownDeviceMatching.swift:64 (re-pairing a re-seeded Jade keeps the old tile) is still unaddressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants