feat: show Bitcoin master fingerprint in wallet keys (#3489)

* feat: show Bitcoin master fingerprint in wallet keys Add the BIP-32 master fingerprint (XFP) to the Bitcoin wallet keys shown under Settings > Seed & Keys > Keys, labeled "Master fingerprint". * fix: keep xPub in Keys tab by splitting silent payment keys into a dedicated list The Keys tab / Silent Payments tab split relied on a hardcoded item index (`items.sublist(0, 4)` / `sublist(4)`) that assumed the first four items were always WIF/private key/public key/xPub. Adding the master fingerprint as the first item pushed xPub into the Silent Payments tab. Route Bitcoin silent payment keys into their own `silentPaymentItems` list instead, and render each tab from its list.

Seth For Privacy committed Aug 7, 2026 at 15:47 UTC b60b5a59f7389e61554b1ce5ed32f4eb00df6c76
4 files changed +29 -12
cw_bitcoin/lib/bitcoin_wallet_keys.dart
+15 -4
@@ -1,12 +1,23 @@
1 class BitcoinWalletKeys {
2 - const BitcoinWalletKeys(
3 - {required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
2 + const BitcoinWalletKeys({
3 + required this.wif,
4 + required this.privateKey,
5 + required this.publicKey,
6 + required this.xpub,
7 + this.masterFingerprint = '',
8 + });
9
10 final String wif;
11 final String privateKey;
12 final String publicKey;
13 final String xpub;
14 + final String masterFingerprint;
15
10 - Map<String, String> toJson() =>
11 - {'wif': wif, 'privateKey': privateKey, 'publicKey': publicKey, 'xpub': xpub};
16 + Map<String, String> toJson() => {
17 + 'wif': wif,
18 + 'privateKey': privateKey,
19 + 'publicKey': publicKey,
20 + 'xpub': xpub,
21 + 'masterFingerprint': masterFingerprint,
22 + };
23 }
cw_bitcoin/lib/electrum_wallet.dart
+1
@@ -471,6 +471,7 @@ abstract class ElectrumWalletBase
471 privateKey: privateKey ?? '',
472 publicKey: publicKey ?? '',
473 xpub: xpub,
474 + masterFingerprint: _masterHD?.fingerPrint.toHex() ?? '',
475 );
476 }
477
lib/src/screens/wallet_keys/wallet_keys_page.dart
+3 -7
@@ -78,7 +78,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
78
79 showKeyTab = widget.walletKeysViewModel.items.isNotEmpty;
80 showSilentPaymentsTab =
81 - widget.walletKeysViewModel.isBitcoin && widget.walletKeysViewModel.items.length > 4;
81 + widget.walletKeysViewModel.isBitcoin && widget.walletKeysViewModel.silentPaymentItems.isNotEmpty;
82 showLegacySeedTab = widget.walletKeysViewModel.legacySeedSplit.isNotEmpty;
83 isLegacySeedOnly = widget.walletKeysViewModel.isLegacySeedOnly;
84
@@ -157,16 +157,12 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
157 if (showKeyTab)
158 Padding(
159 padding: const EdgeInsets.only(left: 22, right: 22),
160 - child: _buildKeysTab(
161 - context,
162 - showSilentPaymentsTab
163 - ? widget.walletKeysViewModel.items.sublist(0, 4)
164 - : widget.walletKeysViewModel.items),
160 + child: _buildKeysTab(context, widget.walletKeysViewModel.items),
161 ),
162 if (showSilentPaymentsTab)
163 Padding(
164 padding: const EdgeInsets.only(left: 22, right: 22),
169 - child: _buildKeysTab(context, widget.walletKeysViewModel.items.sublist(4)),
165 + child: _buildKeysTab(context, widget.walletKeysViewModel.silentPaymentItems),
166 ),
167 if (showLegacySeedTab)
168 Padding(
lib/view_model/wallet_keys_view_model.dart
+10 -1
@@ -30,6 +30,7 @@ abstract class WalletKeysViewModelBase with Store {
30 _restoreHeight = _appStore.wallet!.walletInfo.restoreHeight,
31 _restoreHeightByTransactions = 0,
32 items = ObservableList<StandartListItem>(),
33 + silentPaymentItems = ObservableList<StandartListItem>(),
34 _title = _getInitialTitle(_appStore.wallet!) {
35 _populateKeysItems();
36
@@ -87,6 +88,7 @@ abstract class WalletKeysViewModelBase with Store {
88 // this is incomplete, needs legacy seed toggle for XMR
89 bool get shouldShowHeightBox => [WalletType.bitcoin, WalletType.zcash].contains(_wallet.type);
90 final ObservableList<StandartListItem> items;
91 + final ObservableList<StandartListItem> silentPaymentItems;
92
93 @observable
94 String _title;
@@ -150,6 +152,7 @@ abstract class WalletKeysViewModelBase with Store {
152
153 void _populateKeysItems() {
154 items.clear();
155 + silentPaymentItems.clear();
156
157 Map<String, String>? keys;
158
@@ -217,6 +220,11 @@ abstract class WalletKeysViewModelBase with Store {
220 final electrumKeys = bitcoin!.getWalletKeys(_appStore.wallet!);
221
222 items.addAll([
223 + if ((electrumKeys['masterFingerprint'] ?? '').isNotEmpty)
224 + StandartListItem(
225 + title: "Master fingerprint",
226 + value: electrumKeys['masterFingerprint']!,
227 + ),
228 if ((electrumKeys['wif'] ?? '').isNotEmpty)
229 StandartListItem(title: "WIF", value: electrumKeys['wif']!),
230 if ((electrumKeys['privateKey'] ?? '').isNotEmpty)
@@ -233,7 +241,8 @@ abstract class WalletKeysViewModelBase with Store {
241 }
242
243 if (keys != null) {
236 - items.addAll([
244 + final keysList = _wallet.type == WalletType.bitcoin ? silentPaymentItems : items;
245 + keysList.addAll([
246 if ((keys['primaryAddress'] ?? '').isNotEmpty)
247 StandartListItem(
248 key: ValueKey('${_walletName}_wallet_primary_address_item_key'),