Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
79da0ad
feat: storage identity
jvsena42 Aug 11, 2026
7751e9c
feat: replace device id with wallet id
jvsena42 Aug 11, 2026
2e0b0d0
TrezorManager session identity + Bridge fix
jvsena42 Aug 11, 2026
41689b7
feat: identity ops on HwWalletManager
jvsena42 Aug 11, 2026
e807a05
feat: connect-flow UI for pairing a hidden wallet
jvsena42 Aug 11, 2026
a47815d
feat: transfer sign with passphrase
jvsena42 Aug 11, 2026
2d0984c
doc: changelog
jvsena42 Aug 11, 2026
c4f6ad4
Merge branch 'master' into feat/hw-passphrase-wallets
jvsena42 Aug 12, 2026
f52fc4e
fix: stale session cleanup and device name selection
jvsena42 Aug 12, 2026
0ef02ce
fix: isIdentity separates unresolved case by whether the target needs…
jvsena42 Aug 12, 2026
9e630e3
fix: connectWithWalletMode reopens whatever device holds de sections …
jvsena42 Aug 12, 2026
020eb04
fix: dont request passphrase for broadcast retry
jvsena42 Aug 12, 2026
11d4e23
fix: cancelHwSigning must drop reopen too
jvsena42 Aug 12, 2026
3b9eeed
fix: isIdentity guard on ensureConnected now throws AppError instead …
jvsena42 Aug 12, 2026
c5cb845
fix: separate passprase mismatch and passphrase error reading cases o…
jvsena42 Aug 12, 2026
aa958e2
fix: a dropped session may be reported as off passphrase
jvsena42 Aug 12, 2026
dc4cac6
fix: do not wamup hidden wallet whose session is gone
jvsena42 Aug 12, 2026
4fedfed
fix: prevent binding to an arbitrary identity when session isn't reso…
jvsena42 Aug 12, 2026
7c199f0
fix: isIdentity always falling back to default wallet
jvsena42 Aug 13, 2026
b5aa7f9
Merge branch 'master' into feat/hw-passphrase-wallets
jvsena42 Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ struct AppScene: View {
// Created ahead of `transfer` so the hardware-wallet transfer flow can reach the funding
// (compose/sign/broadcast) and device-session (reconnect) capabilities.
let trezorManager = TrezorManager()
let hwWalletManager = HwWalletManager()
let hwWalletManager = HwWalletManager(session: trezorManager)

_transfer = StateObject(wrappedValue: TransferViewModel(
transferService: transferService,
sheetViewModel: sheetViewModel,
hwFunding: hwWalletManager,
hwConnecting: trezorManager,
hwConnecting: hwWalletManager,
hwFeeRateProvider: {
guard let rates = await feeEstimatesManager.getEstimates() else { return nil }
return UInt64(TransactionSpeed.fast.getFeeRate(from: rates))
Expand Down Expand Up @@ -903,7 +903,8 @@ struct AppScene: View {
private func pushHardwareDevices() {
hwWalletManager.updateDevices(
knownDevices: trezorManager.knownDevices,
connectedDeviceId: trezorManager.connectedDevice?.id
connectedDeviceId: trezorManager.connectedDevice?.id,
connectedWalletId: trezorManager.connectedWalletId
)
}

Expand Down
8 changes: 4 additions & 4 deletions Bitkit/MainNavView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -420,17 +420,17 @@ struct MainNavView: View {
case .buyBitcoin: BuyBitcoinView()
case .savingsWallet: SavingsWalletScreen()
case .spendingWallet: SpendingWalletScreen()
case let .hardwareWallet(deviceId): HardwareWalletScreen(deviceId: deviceId)
case let .hardwareWallet(walletId): HardwareWalletScreen(walletId: walletId)
case .scanner: ScannerScreen()

// Transfer
case .transferIntro: TransferIntroView()
case .fundingOptions: FundingOptions()
case .spendingIntro: SpendingIntroView()
case let .spendingIntroHw(deviceId): SpendingIntroView(deviceId: deviceId)
case let .spendingIntroHw(walletId): SpendingIntroView(walletId: walletId)
case .spendingAmount: SpendingAmount()
case let .spendingAmountHw(deviceId): SpendingAmountHw(deviceId: deviceId)
case let .spendingHwSign(deviceId): SpendingHwSign(deviceId: deviceId)
case let .spendingAmountHw(walletId): SpendingAmountHw(walletId: walletId)
case let .spendingHwSign(walletId): SpendingHwSign(walletId: walletId)
case .spendingHwSigned: SpendingHwSigned()
case let .spendingConfirm(order): SpendingConfirm(order: order)
case let .spendingAdvanced(order): SpendingAdvancedView(order: order)
Expand Down
47 changes: 47 additions & 0 deletions Bitkit/Managers/HwDeviceSessioning.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import BitkitCore

/// The live Trezor session and its stored entries, as the watch-only layer needs them.
///
/// Implemented by `TrezorManager` and injected into `HwWalletManager`, so the watch-only layer can
/// reason about which wallet a session belongs to without either manager referencing the other.
/// Everything here is device-level on purpose: resolving a wallet identity to the transport it is
/// reachable over is the watch-only layer's job, since it owns the wallet grouping.
@MainActor
protocol HwDeviceSessioning: AnyObject, Sendable {
/// Stored entries read fresh. A connect that just wrote one lands here before the
/// `updateDevices(...)` push does, so session operations must not read the pushed snapshot.
var storedDevices: [TrezorKnownDevice] { get }
var connectedDeviceId: String? { get }
/// Identity the live session opened; nil when no session is open or none could be resolved.
var connectedWalletId: String? { get }
var connectedFeatures: TrezorFeatures? { get }

func ensureConnected(deviceId: String) async throws
/// Opens `deviceId` with an explicit wallet selection, with or without a live session.
@discardableResult
func connectWithWalletMode(
deviceId: String,
mode: TrezorWalletMode,
passphrase: String
) async throws -> TrezorFeatures
func disconnectStaleSession(deviceId: String) async
func isKnownBluetoothDevice(deviceId: String) -> Bool
func warmUpConnection(deviceId: String)
/// Forgets every stored entry of `walletId`, keeping transport credentials while another
/// identity of the same device remains paired.
func forgetWallet(walletId: String) async
}

extension TrezorManager: HwDeviceSessioning {
var storedDevices: [TrezorKnownDevice] {
knownDevices
}

var connectedDeviceId: String? {
connectedDevice?.id
}

var connectedFeatures: TrezorFeatures? {
deviceFeatures
}
}
Loading
Loading