Fix db getting deleted on iOS (#2736)
What changed, and why it matters
This update fixes a bug where iPhones could accidentally delete or fail to find the app's wallet database, which could make users think their wallets were lost. It also patches related problems where restored wallets might pick the wrong address type (derivation path) after the database move. The release notes only say 'Bug fixes' and do not describe the security relevance.
Treat this as a stability/data-integrity fix. Users who updated to the affected 5.6.3 build on iOS should be advised to update to 5.6.4 and verify wallet access. Review whether any iOS users need seed-based restoration due to database loss. No active exploit path is evident, but the data-loss condition is severe for affected users.
Security signals we found
Data-loss bug: iOS database file could be removed or overwritten by unconditional migration logic
Migration guard now platform-gated and path-deduplicated
Derivation-type fallback changes affect wallet address generation for restored wallets
Release notes omit security relevance
Evidence from the diff
The core fix is in cw_core/lib/db/sqlite.dart: the old database migration/copy logic previously ran on every platform, but now it only runs on Android (Platform.isAndroid) and only when the old and new paths differ. On iOS this prevents the migration code from incorrectly deleting or replacing the database. The commit also adds fallback logic in Bitcoin, Litecoin, and Nano wallet creation/restoration to resolve DerivationType.unknown to a concrete derivation type/path, plus a mounted check before Navigator.pop and a removed stale state field in the restore UI. Version numbers are bumped to 5.6.4.
Changed components
cw_core/lib/db/sqlite.dartcw_bitcoin/lib/bitcoin_wallet.dartcw_bitcoin/lib/litecoin_wallet.dartcw_bitcoin/lib/electrum_derivations.dartcw_nano/lib/nano_wallet.dartlib/src/screens/new_wallet/new_wallet_page.dartlib/src/screens/restore/wallet_restore_choose_derivation.dartInspect captured patch +36 / −15
diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt
index 76b14c4e..67726850 100644
--- a/assets/text/Monerocom_Release_Notes.txt
+++ b/assets/text/Monerocom_Release_Notes.txt
@@ -1,3 +1 @@
-Fix QR scanning
-UI enhancements
Bug fixes
\ No newline at end of file
diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt
index 76b14c4e..67726850 100644
--- a/assets/text/Release_Notes.txt
+++ b/assets/text/Release_Notes.txt
@@ -1,3 +1 @@
-Fix QR scanning
-UI enhancements
Bug fixes
\ No newline at end of file
diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart
index 0a2b5491..b1feeb7a 100644
--- a/cw_bitcoin/lib/bitcoin_wallet.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet.dart
@@ -224,6 +224,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
snp?.derivationPath ?? electrum_path;
derivationInfo.derivationType ??=
snp?.derivationType ?? DerivationType.electrum;
+ if (derivationInfo.derivationType == DerivationType.unknown) {
+ if (snp?.derivationPath == electrum_path || snp?.derivationType == DerivationType.electrum) {
+ derivationInfo.derivationPath = electrum_path;
+ derivationInfo.derivationType = DerivationType.electrum;
+ } else {
+ derivationInfo.derivationPath = segwit_path;
+ derivationInfo.derivationType = DerivationType.bip39;
+ }
+ }
await derivationInfo.save();
Uint8List? seedBytes = null;
diff --git a/cw_bitcoin/lib/electrum_derivations.dart b/cw_bitcoin/lib/electrum_derivations.dart
index 26dc1f82..b17ed9cf 100644
--- a/cw_bitcoin/lib/electrum_derivations.dart
+++ b/cw_bitcoin/lib/electrum_derivations.dart
@@ -115,3 +115,4 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
};
String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!;
+String segwit_path = "m/84'/0'/0'";
diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart
index bdbbe3f6..d80dedf9 100644
--- a/cw_bitcoin/lib/litecoin_wallet.dart
+++ b/cw_bitcoin/lib/litecoin_wallet.dart
@@ -283,6 +283,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
// set the default if not present:
derivationInfo.derivationPath ??= snp?.derivationPath ?? electrum_path;
derivationInfo.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
+ if (derivationInfo.derivationType == DerivationType.unknown) {
+ if (snp?.derivationPath == electrum_path || snp?.derivationType == DerivationType.electrum) {
+ derivationInfo.derivationPath = electrum_path;
+ derivationInfo.derivationType = DerivationType.electrum;
+ } else {
+ derivationInfo.derivationPath = segwit_path;
+ derivationInfo.derivationType = DerivationType.bip39;
+ }
+ }
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
diff --git a/cw_core/lib/db/sqlite.dart b/cw_core/lib/db/sqlite.dart
index 4ac263c8..f4e23502 100644
--- a/cw_core/lib/db/sqlite.dart
+++ b/cw_core/lib/db/sqlite.dart
@@ -15,7 +15,7 @@ Future<void> initDb({String? pathOverride}) async {
final dbFileOld = File("${await getDatabasesPath()}/cake.db");
final dbFile = File("${(await getAppDir()).path}/cake.db");
- if (dbFileOld.existsSync()) {
+ if (Platform.isAndroid && dbFileOld.existsSync() && dbFileOld.path != dbFile.path) {
final copied = dbFileOld.copySync(dbFile.path);
if (copied.existsSync()) {
dbFileOld.deleteSync();
diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart
index 2cf7fd30..44b4a3c8 100644
--- a/cw_nano/lib/nano_wallet.dart
+++ b/cw_nano/lib/nano_wallet.dart
@@ -436,6 +436,13 @@ abstract class NanoWalletBase
final derivationInfo = await walletInfo.getDerivationInfo();
derivationInfo.derivationType ??= derivationType;
+ if (derivationInfo.derivationType == DerivationType.unknown) {
+ if (data?['derivationType'] != null) {
+ derivationInfo.derivationType = derivationType;
+ } else {
+ derivationInfo.derivationType = DerivationType.bip39;
+ }
+ }
derivationInfo.save();
return NanoWallet(
diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart
index 2b489766..7fed51a7 100644
--- a/lib/src/screens/new_wallet/new_wallet_page.dart
+++ b/lib/src/screens/new_wallet/new_wallet_page.dart
@@ -360,7 +360,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
alertContent: S.of(context).wallet_name_exists,
buttonText: S.of(context).ok,
buttonAction: () {
- if (Navigator.of(context).canPop()) {
+ if (mounted && Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
});
diff --git a/lib/src/screens/restore/wallet_restore_choose_derivation.dart b/lib/src/screens/restore/wallet_restore_choose_derivation.dart
index 9da97f60..fb869bd0 100644
--- a/lib/src/screens/restore/wallet_restore_choose_derivation.dart
+++ b/lib/src/screens/restore/wallet_restore_choose_derivation.dart
@@ -17,7 +17,6 @@ class WalletRestoreChooseDerivationPage extends BasePage {
);
final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel;
- DerivationType derivationType = DerivationType.unknown;
@override
Widget body(BuildContext context) {
diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh
index 218bd96f..b3bfed22 100644
--- a/scripts/android/app_env.sh
+++ b/scripts/android/app_env.sh
@@ -14,15 +14,15 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
-MONERO_COM_VERSION="5.6.3"
-MONERO_COM_BUILD_NUMBER=4148
+MONERO_COM_VERSION="5.6.4"
+MONERO_COM_BUILD_NUMBER=4149
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
-CAKEWALLET_VERSION="5.6.3"
-CAKEWALLET_BUILD_NUMBER=4294
+CAKEWALLET_VERSION="5.6.4"
+CAKEWALLET_BUILD_NUMBER=4295
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"
diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh
index d572c893..e0cccc37 100644
--- a/scripts/ios/app_env.sh
+++ b/scripts/ios/app_env.sh
@@ -12,13 +12,13 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
-MONERO_COM_VERSION="5.6.3"
-MONERO_COM_BUILD_NUMBER=151
+MONERO_COM_VERSION="5.6.4"
+MONERO_COM_BUILD_NUMBER=152
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
-CAKEWALLET_VERSION="5.6.3"
-CAKEWALLET_BUILD_NUMBER=357
+CAKEWALLET_VERSION="5.6.4"
+CAKEWALLET_BUILD_NUMBER=359
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
Why this scored 54/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.