cw-1683-prepare-zano-removal (#3668)

* wip zano removal

* add zano removal popup

* constantly show popup if user keeps using deprecated wallet

* add confirmation before viewing seed

* fix viewed check

* prevent wallet staying open after seed backup

* Update lib/entities/default_settings_migration.dart

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* add passphrase

* make popup scrollable

* delete deprecated seeds when wallet is deleted

* encrypt seeds

* also deprecate decred

* remove decred from available types

* remove backup logic

* showArrow: false,

* Remove deprecated wallet seeds deletion

Removed deprecated wallet seeds deletion from delete method.

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
malik1004x
2026-10-03 14:39:38 +02:00
committed by GitHub
co-authored by Omar Hatem
parent 1972efd037
commit 8661681110
12 changed files with 192 additions and 9 deletions
+18 -1
View File
@@ -66,7 +66,7 @@ Future<void> _initDb({String? pathOverride}) async {
await db?.close();
db = await openDatabase(
dbFile.path,
version: 14,
version: 15,
onUpgrade: (db, oldVersion, newVersion) async {
printV("migrating: $oldVersion, $newVersion");
if (oldVersion <= 1) {
@@ -193,6 +193,9 @@ CREATE TABLE IF NOT EXISTS BalanceCardStyleSettings (
await _migrateBitcoinCardStylesForAccounts(db);
}
if(oldVersion <= 14) {
await _createDeprecatedWalletSeedTable(db);
}
},
onCreate: (Database db, int version) async {
await db.execute('''
@@ -299,6 +302,8 @@ CREATE TABLE BalanceCardStyleSettings (
await _createTronTokenTable(db);
await _createImportedNFTTable(db);
await _createWalletInfoAccountTable(db);
await _createDeprecatedWalletSeedTable(db);
},
);
}
@@ -589,3 +594,15 @@ isDefault BOOLEAN DEFAULT FALSE
);
""");
}
Future<void> _createDeprecatedWalletSeedTable(Database db) async {
await db.execute("""
CREATE TABLE DeprecatedWalletSeeds (
walletInfoId INTEGER PRIMARY KEY,
seed TEXT NOT NULL,
passphrase TEXT NOT NULL,
FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
);
""");
}
+2 -1
View File
@@ -793,7 +793,8 @@ class WalletInfo {
}
static Future<int> delete(WalletInfo walletInfo) async {
return await db!.delete(tableName, where: 'id = ?', whereArgs: [walletInfo.id]);
final deleted = await db!.delete(tableName, where: 'id = ?', whereArgs: [walletInfo.id]);
return deleted;
}
static Future<List<WalletInfo>> selectList(String where, List<dynamic> whereArgs,
+5
View File
@@ -122,6 +122,7 @@ abstract class ZanoWalletBase
bool _isTransactionUpdating;
bool _hasSyncAfterStartup;
Timer? _autoSaveTimer;
bool _isClosed = false;
/// number of transactions in each request
static final int _txChunkSize = (pow(2, 32) - 1).toInt();
@@ -422,6 +423,7 @@ abstract class ZanoWalletBase
@override
Future<void> close({bool shouldCleanup = true}) async {
_isClosed = true;
closeWallet(null);
_updateSyncInfoTimer?.cancel();
_autoSaveTimer?.cancel();
@@ -534,6 +536,9 @@ abstract class ZanoWalletBase
await walletAddresses.init();
await walletAddresses.updateAddress(address);
await updateTransactions();
if (_isClosed) {
return;
}
_autoSaveTimer = Timer.periodic(Duration(seconds: _autoSaveIntervalSeconds), (_) async {
await save();
});
+7
View File
@@ -458,6 +458,13 @@ mixin ZanoWalletApi {
static Map<String, CreateWalletResult> openWalletCache = {};
static Future<void> closeCachedWallet(String path) async {
final cached = openWalletCache.remove(path);
if (cached != null) {
await _closeWallet(cached.walletId);
}
}
Future<TransferResult> transfer(
List<Destination> destinations, BigInt fee, String comment) async {
final params = TransferParams(
+1
View File
@@ -112,6 +112,7 @@ class ZanoWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
await ZanoWalletApi.closeCachedWallet(await pathForWallet(name: wallet, type: getType()));
final path = await pathForWalletDir(name: wallet, type: getType());
final file = Directory(path);
final isExist = file.existsSync();
+9 -1
View File
@@ -2,7 +2,10 @@ import 'dart:convert';
import 'dart:io' show Directory, File, Platform;
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import "package:cake_wallet/core/key_service.dart";
import 'package:cake_wallet/core/secure_storage.dart';
import "package:cake_wallet/decred/decred.dart";
import "package:cake_wallet/di.dart";
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
@@ -14,6 +17,9 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/new-ui/model/charts/charts_asset.dart';
import "package:cake_wallet/store/settings_store.dart";
import "package:cake_wallet/zano/zano.dart";
import "package:cw_core/cake_hive.dart";
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/crypto_currency.dart';
@@ -23,8 +29,10 @@ import 'package:cw_core/node_list.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/spl_token.dart';
import "package:cw_core/unspent_coins_info.dart";
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import "package:cw_core/wallet_service.dart";
import 'package:cw_core/wallet_type.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:hive/hive.dart';
@@ -1377,4 +1385,4 @@ Future<void> _addTbbTokenToExistingSolanaWallets() async {
} catch (e) {
printV("Error in TBB migration: $e");
}
}
}
+4
View File
@@ -1,3 +1,5 @@
import "package:cw_core/wallet_type.dart";
class PreferencesKey {
static const currentWalletType = 'current_wallet_type';
static const currentWalletName = 'current_wallet_name';
@@ -147,4 +149,6 @@ class PreferencesKey {
static const balanceHideCounter = "balance_hide_counter";
static const zcashMigrationModalViewed = "zcash_migration_modal_viewed";
static const showCiBuildOverlay = "show_ci_build_overlay";
static String deprecationPopupViewed(WalletType type) =>
"deprecation_popup_viewed_${walletTypeToString(type)}";
}
+13
View File
@@ -7,6 +7,7 @@ import "package:cake_wallet/new-ui/page_open_listener.dart";
import 'package:cake_wallet/new-ui/pages/charts_page.dart';
import 'package:cake_wallet/new-ui/pages/home_page.dart';
import 'package:cake_wallet/new-ui/widgets/changelog_modal.dart';
import "package:cake_wallet/new-ui/widgets/wallet_removal_popup.dart";
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/new_main_navbar_widget.dart';
@@ -55,6 +56,7 @@ class _NewDashboardState extends State<NewDashboard> {
setState(() {
_selectedPage = 0;
});
_showWalletRemovalPopup(context);
});
Future.delayed(Duration(milliseconds: 300)).then((_) {
@@ -63,6 +65,7 @@ class _NewDashboardState extends State<NewDashboard> {
});
});
_showVulnerableSeedsPopup(context);
_showWalletRemovalPopup(context);
}
@override
@@ -182,4 +185,14 @@ class _NewDashboardState extends State<NewDashboard> {
);
}
}
Future<void> _showWalletRemovalPopup(BuildContext context) async {
if(await widget.dashboardViewModel.shouldShowRemovalPopup()) {
await Future.delayed(const Duration(seconds: 1));
final toBeRemoved = await widget.dashboardViewModel.walletsToBeRemoved;
if(context.mounted) {
await showPopUp<void>(context: context, builder: (context)=>WalletRemovalPopup(affectedWallets: toBeRemoved,));
}
}
}
}
@@ -0,0 +1,102 @@
import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart";
import "package:cake_wallet/generated/i18n.dart";
import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
import "package:cake_wallet/src/widgets/cake_image_widget.dart";
import "package:cake_wallet/src/widgets/new_list_row/new_list_section.dart";
import "package:cake_wallet/themes/core/theme_extension.dart";
import "package:cw_core/currency_for_wallet_type.dart";
import "package:cw_core/wallet_info.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter/material.dart";
class WalletRemovalPopup extends StatelessWidget {
const WalletRemovalPopup({required this.affectedWallets, super.key});
final List<WalletInfo> affectedWallets;
@override
Widget build(BuildContext context) {
final types = affectedWallets.map((item) => item.type).toSet().toList();
final iconPaths = types.map((item) => walletTypeToCryptoCurrency(item).iconPath ?? "").toList();
final typeNames = types.map(walletTypeToDisplayName).toList();
return Center(
child: Material(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(18),
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 18,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: iconPaths
.map(
(item) => CakeImageWidget(
imageUrl: item,
width: 64,
height: 64,
),
)
.toList(),
),
Text(
textAlign: TextAlign.center,
S.of(context).support_will_soon_be_removed(typeNames.join(", ")),
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
Text(
textAlign: TextAlign.center,
types.map(deprecationReasonForType).whereType<String>().join("\n\n"),
),
Text(
textAlign: TextAlign.center,
S.of(context).make_sure_you_migrated(typeNames.join("/")),
style: TextStyle(
fontWeight: FontWeight.w500,
color: context.currentTheme.customColors.warningOutlineColor,
),
),
NewListSections(
sections: {
"": affectedWallets
.map(
(item) => ListItemRegularRow(
iconPath: walletTypeToCryptoCurrency(item.type).iconPath ?? "",
keyValue: item.name,
label: item.name,
showArrow: false,
),
)
.toList(),
},
),
NewPrimaryButton(
onPressed: Navigator.of(context).pop,
text: S.of(context).close,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
],
),
),
),
),
),
),
);
}
String? deprecationReasonForType(WalletType type) =>
switch (type) { WalletType.zano => S.current.zano_removal_reason, _ => null };
}
@@ -1697,4 +1697,25 @@ abstract class DashboardViewModelBase with Store {
Future<void> refreshDashboard() async {
reconnect();
}
static const walletTypesToBeRemoved = [
WalletType.zano,
WalletType.decred,
];
Future<bool> shouldShowRemovalPopup() async {
final show = (await WalletInfo.getAll()).any((item) =>
walletTypesToBeRemoved.contains(item.type) &&
!(sharedPreferences.getBool(PreferencesKey.deprecationPopupViewed(item.type)) ?? false),);
for (final type in walletTypesToBeRemoved) {
await sharedPreferences.setBool(PreferencesKey.deprecationPopupViewed(type), true);
}
// if user is actively using a wallet type that's about to be removed, keep nagging them
return show || walletTypesToBeRemoved.contains(wallet.type);
}
Future<List<WalletInfo>> get walletsToBeRemoved async => (await WalletInfo.getAll())
.where((item) => walletTypesToBeRemoved.contains(item.type))
.toList();
}
+4
View File
@@ -668,6 +668,7 @@
"low_fee_alert": "You currently are using a low network fee priority. This could cause long waits, different rates, or canceled trades. We recommend setting a higher fee for a better experience.",
"low_kyc": "Low KYC",
"made_easy": "made easy",
"make_sure_you_migrated": "Please be sure you've backed up your seed phrase from Recovery & Keys in the meantime, and consider migrating to another ${walletTypes} wallet.",
"manage_nodes": "Manage nodes",
"manage_pow_nodes": "Manage PoW nodes",
"manage_providers": "Manage providers",
@@ -1180,6 +1181,7 @@
"show_recovery_phrase": "Show me my Recovery Phrase",
"show_recovery_phrase_and_keys": "Show my Recovery Phrase & Keys",
"show_seed": "Show seed",
"show_seed_confirmation": "View seed of this wallet?",
"show_zcash_card": "Show zcash missing funds card",
"sign_all": "Sign All",
"sign_message": "Sign Message",
@@ -1239,6 +1241,7 @@
"support_title_guides": "Cake Wallet docs",
"support_title_live_chat": "Live support",
"support_title_other_links": "Other support links",
"support_will_soon_be_removed": "Support for ${walletTypes} will soon be removed",
"supported": "Supported",
"swap": "Swap",
"swap_from_network": "Swap from ${network}",
@@ -1578,6 +1581,7 @@
"youCanGoBackToYourDapp": "You can go back to your dApp now",
"your": "Your",
"yy": "YY",
"zano_removal_reason": "Zano has undergone multiple hard-forks to resolve a critical network issue, including a rollback of the chain. Due to serious issues with the forks and the maintenance burden it brings to us, we will be removing Zano from Cake Wallet in a future release.",
"zcash_card_description": "If you don't see your full balance Cake Wallet can attempt to sweep funds from internal change addresses used by some wallets.",
"zcash_card_dismiss": "Dismiss",
"zcash_card_enable_later": "You can always enable this card later in settings",
+6 -6
View File
@@ -2077,13 +2077,13 @@ Future<void> generateWalletTypes({
outputContent += '\tWalletType.nano,\n';
}
if (hasDecred) {
outputContent += '\tWalletType.decred,\n';
}
// if (hasDecred) {
// outputContent += '\tWalletType.decred,\n';
// }
if (hasZano) {
outputContent += '\tWalletType.zano,\n';
}
// if (hasZano) {
// outputContent += '\tWalletType.zano,\n';
// }
if (hasBanano) {
outputContent += '\tWalletType.banano,\n';