popup ui for wownero deprecation (#3186)
What changed, and why it matters
This commit changes how the Cake Wallet app tells users that Wownero support has been removed. Instead of crashing with a raw error message that exposed the seed phrase in logs, the app now shows a friendly popup that lets the user reveal their seed only when they choose to. It is a user-experience and safety improvement, not a security vulnerability.
No security action required; this is a defensive UX improvement. Continue reviewing any remaining deprecated-coin handling for similar seed-exposure patterns.
Security signals we found
Seed phrase previously embedded in exception message
New UI reduces seed exposure in logs/crash reports
No cryptographic, network, or permission changes
No input validation, authentication, or access-control changes
Evidence from the diff
The patch introduces a WalletDeprecationException carrying the wallet seed and currency, replaces the generic Exception thrown in WowneroWalletService.openWallet, and adds a WalletDeprecationPopup bottom sheet shown by WalletLoadingService when that exception is caught. The popup displays the coin icon, a deprecation message, and an optional seed behind an AnimatedDropdown. The previous behavior already exposed the seed in the exception string; the new behavior reduces accidental seed exposure in logs and gives the user a controlled UI to view it.
Changed components
cw_wownero/lib/wownero_wallet_service.dartlib/core/wallet_loading_service.dartlib/new-ui/widgets/wallet_deprecation_popup.dartInspect captured patch +93 / −5
diff --git a/cw_wownero/lib/wownero_wallet_service.dart b/cw_wownero/lib/wownero_wallet_service.dart
index 4709258c..4933c37d 100644
--- a/cw_wownero/lib/wownero_wallet_service.dart
+++ b/cw_wownero/lib/wownero_wallet_service.dart
@@ -1,5 +1,6 @@
import 'dart:ffi';
import 'dart:io';
+import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/unspent_coins_info.dart';
@@ -20,6 +21,16 @@ import 'package:hive/hive.dart';
import 'package:polyseed/polyseed.dart';
import 'package:monero/wownero.dart' as wownero;
+class WalletDeprecationException implements Exception {
+ final String seed;
+ final CryptoCurrency curr;
+
+ @override
+ String toString() => "Wallet type no longer supported";
+
+ WalletDeprecationException({required this.seed, required this.curr});
+}
+
class WowneroNewWalletCredentials extends WalletCredentials {
WowneroNewWalletCredentials(
{required String name, required this.language, required this.isPolyseed, this.passphrase, String? password})
@@ -138,8 +149,8 @@ class WowneroWalletService extends WalletService<
}
wallet = WowneroWallet(walletInfo: walletInfo, derivationInfo: await walletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
- throw Exception("support for coin removed, your seedphrase: ${wallet.seed}");
-
+ throw WalletDeprecationException(seed: wallet.seed, curr: wallet.currency);
+
final isValid = wallet.walletAddresses.validate();
if (!isValid) {
diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart
index 46038122..022f2836 100644
--- a/lib/core/wallet_loading_service.dart
+++ b/lib/core/wallet_loading_service.dart
@@ -5,6 +5,7 @@ import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/main.dart';
+import 'package:cake_wallet/new-ui/widgets/wallet_deprecation_popup.dart';
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
@@ -15,6 +16,7 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
+import 'package:cw_wownero/wownero_wallet_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -73,9 +75,12 @@ class WalletLoadingService {
return wallet;
} catch (error, stack) {
String corruptedWalletsSeeds = "Corrupted wallets seeds (if retrievable, empty otherwise):";
-
- if ([WalletType.wownero, WalletType.haven].contains(type)) {
- corruptedWalletsSeeds += "\n\n$type $name: $error";
+
+ if(error is WalletDeprecationException) {
+ if(navigatorKey.currentContext != null) {
+ showModalBottomSheet(
+ context: navigatorKey.currentContext!, builder: (context)=>WalletDeprecationPopup(type: type, seed: error.seed,));
+ }
} else {
await ExceptionHandler.resetLastPopupDate();
final isLedgerError = await ExceptionHandler.isLedgerError(error);
diff --git a/lib/new-ui/widgets/wallet_deprecation_popup.dart b/lib/new-ui/widgets/wallet_deprecation_popup.dart
new file mode 100644
index 00000000..bccc8b5d
--- /dev/null
+++ b/lib/new-ui/widgets/wallet_deprecation_popup.dart
@@ -0,0 +1,72 @@
+import 'package:cake_wallet/new-ui/widgets/animated_dropdown.dart';
+import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
+import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
+import 'package:cw_core/currency_for_wallet_type.dart';
+import 'package:cw_core/wallet_type.dart';
+import 'package:flutter/material.dart';
+
+class WalletDeprecationPopup extends StatelessWidget {
+ const WalletDeprecationPopup({super.key, required this.type, this.seed});
+
+ final WalletType type;
+ final String? seed;
+
+ @override
+ Widget build(BuildContext context) {
+ final curr = walletTypeToCryptoCurrency(type);
+
+ return Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
+ color: Theme.of(context).colorScheme.surface),
+ child: SafeArea(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ModalTopBar(
+ title: "",
+ leadingIcon: Icon(Icons.close),
+ onLeadingPressed: Navigator.of(context).pop,
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0),
+ child: Column(
+ spacing: 24,
+ children: [
+ CakeImageWidget(
+ imageUrl: curr.iconSvgPath ?? curr.iconPath,
+ width: 64,
+ height: 64,
+ ),
+ Text(
+ "${curr.fullName} is no longer supported",
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
+ textAlign: TextAlign.center,
+ ),
+ Text(
+ "Apologies for the inconvenience.",
+ textAlign: TextAlign.center,
+ ),
+ if (seed != null) ...[
+ Text(
+ "If you need to migrate your seed, you can see it below.",
+ textAlign: TextAlign.center,
+ ),
+ AnimatedDropdown(content: Text(seed!), dropdownText: "Tap to show seed"),
+ ],
+ NewPrimaryButton(
+ onPressed: Navigator.of(context).pop,
+ text: "Close",
+ color: Theme.of(context).colorScheme.primary,
+ textColor: Theme.of(context).colorScheme.onPrimary),
+ SizedBox()
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
Why this scored 17/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.