feat: show Bitcoin master fingerprint in wallet keys (#3489)
What changed, and why it matters
This commit is a user-facing feature, not a security fix. It adds a new 'Master fingerprint' value to the Bitcoin wallet keys screen and fixes a small UI bug where adding that new item accidentally moved another key (xPub) into the wrong tab. There is no indication of a vulnerability being patched.
No security action required; review as normal feature/UI code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extends BitcoinWalletKeys with an optional masterFingerprint field, populates it from _masterHD?.fingerPrint.toHex(), serializes it in toJson(), and displays it in the wallet keys UI. It also refactors the Keys/Silent Payments split from a hardcoded sublist(0, 4)/sublist(4) approach into a dedicated silentPaymentItems list so the new item does not shift xPub into the Silent Payments tab.
Changed components
cw_bitcoin/lib/bitcoin_wallet_keys.dartcw_bitcoin/lib/electrum_wallet.dartlib/src/screens/wallet_keys/wallet_keys_page.dartlib/view_model/wallet_keys_view_model.dartInspect captured patch +29 / −12
diff --git a/cw_bitcoin/lib/bitcoin_wallet_keys.dart b/cw_bitcoin/lib/bitcoin_wallet_keys.dart
index 9a9bdcb..abc2d8f 100644
--- a/cw_bitcoin/lib/bitcoin_wallet_keys.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet_keys.dart
@@ -1,12 +1,23 @@
class BitcoinWalletKeys {
- const BitcoinWalletKeys(
- {required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
+ const BitcoinWalletKeys({
+ required this.wif,
+ required this.privateKey,
+ required this.publicKey,
+ required this.xpub,
+ this.masterFingerprint = '',
+ });
final String wif;
final String privateKey;
final String publicKey;
final String xpub;
+ final String masterFingerprint;
- Map<String, String> toJson() =>
- {'wif': wif, 'privateKey': privateKey, 'publicKey': publicKey, 'xpub': xpub};
+ Map<String, String> toJson() => {
+ 'wif': wif,
+ 'privateKey': privateKey,
+ 'publicKey': publicKey,
+ 'xpub': xpub,
+ 'masterFingerprint': masterFingerprint,
+ };
}
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index ceba0ec..5d3af65 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -471,6 +471,7 @@ abstract class ElectrumWalletBase
privateKey: privateKey ?? '',
publicKey: publicKey ?? '',
xpub: xpub,
+ masterFingerprint: _masterHD?.fingerPrint.toHex() ?? '',
);
}
diff --git a/lib/src/screens/wallet_keys/wallet_keys_page.dart b/lib/src/screens/wallet_keys/wallet_keys_page.dart
index e5cb40c..2fb7690 100644
--- a/lib/src/screens/wallet_keys/wallet_keys_page.dart
+++ b/lib/src/screens/wallet_keys/wallet_keys_page.dart
@@ -78,7 +78,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
showKeyTab = widget.walletKeysViewModel.items.isNotEmpty;
showSilentPaymentsTab =
- widget.walletKeysViewModel.isBitcoin && widget.walletKeysViewModel.items.length > 4;
+ widget.walletKeysViewModel.isBitcoin && widget.walletKeysViewModel.silentPaymentItems.isNotEmpty;
showLegacySeedTab = widget.walletKeysViewModel.legacySeedSplit.isNotEmpty;
isLegacySeedOnly = widget.walletKeysViewModel.isLegacySeedOnly;
@@ -157,16 +157,12 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
if (showKeyTab)
Padding(
padding: const EdgeInsets.only(left: 22, right: 22),
- child: _buildKeysTab(
- context,
- showSilentPaymentsTab
- ? widget.walletKeysViewModel.items.sublist(0, 4)
- : widget.walletKeysViewModel.items),
+ child: _buildKeysTab(context, widget.walletKeysViewModel.items),
),
if (showSilentPaymentsTab)
Padding(
padding: const EdgeInsets.only(left: 22, right: 22),
- child: _buildKeysTab(context, widget.walletKeysViewModel.items.sublist(4)),
+ child: _buildKeysTab(context, widget.walletKeysViewModel.silentPaymentItems),
),
if (showLegacySeedTab)
Padding(
diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart
index 9b2fa4d..73e6a63 100644
--- a/lib/view_model/wallet_keys_view_model.dart
+++ b/lib/view_model/wallet_keys_view_model.dart
@@ -30,6 +30,7 @@ abstract class WalletKeysViewModelBase with Store {
_restoreHeight = _appStore.wallet!.walletInfo.restoreHeight,
_restoreHeightByTransactions = 0,
items = ObservableList<StandartListItem>(),
+ silentPaymentItems = ObservableList<StandartListItem>(),
_title = _getInitialTitle(_appStore.wallet!) {
_populateKeysItems();
@@ -87,6 +88,7 @@ abstract class WalletKeysViewModelBase with Store {
// this is incomplete, needs legacy seed toggle for XMR
bool get shouldShowHeightBox => [WalletType.bitcoin, WalletType.zcash].contains(_wallet.type);
final ObservableList<StandartListItem> items;
+ final ObservableList<StandartListItem> silentPaymentItems;
@observable
String _title;
@@ -150,6 +152,7 @@ abstract class WalletKeysViewModelBase with Store {
void _populateKeysItems() {
items.clear();
+ silentPaymentItems.clear();
Map<String, String>? keys;
@@ -217,6 +220,11 @@ abstract class WalletKeysViewModelBase with Store {
final electrumKeys = bitcoin!.getWalletKeys(_appStore.wallet!);
items.addAll([
+ if ((electrumKeys['masterFingerprint'] ?? '').isNotEmpty)
+ StandartListItem(
+ title: "Master fingerprint",
+ value: electrumKeys['masterFingerprint']!,
+ ),
if ((electrumKeys['wif'] ?? '').isNotEmpty)
StandartListItem(title: "WIF", value: electrumKeys['wif']!),
if ((electrumKeys['privateKey'] ?? '').isNotEmpty)
@@ -233,7 +241,8 @@ abstract class WalletKeysViewModelBase with Store {
}
if (keys != null) {
- items.addAll([
+ final keysList = _wallet.type == WalletType.bitcoin ? silentPaymentItems : items;
+ keysList.addAll([
if ((keys['primaryAddress'] ?? '').isNotEmpty)
StandartListItem(
key: ValueKey('${_walletName}_wallet_primary_address_item_key'),
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.