stack_wallet/lib/db/migrate_wallets_to_isar.dart
2023-11-09 16:27:17 -06:00

160 lines
4.9 KiB
Dart

import 'dart:convert';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/transaction_note.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/wallet/supporting/epiccash_wallet_info_extension.dart';
Future<void> migrateWalletsToIsar({
required SecureStorageInterface secureStore,
}) async {
final allWalletsBox = await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
final names = DB.instance
.get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
if (names == null) {
// no wallets to migrate
return;
}
//
// Parse the old data from the Hive map into a nice list
//
final List<
({
Coin coin,
String name,
String walletId,
})> oldInfo = Map<String, dynamic>.from(names).values.map((e) {
final map = e as Map;
return (
coin: Coin.values.byName(map["coin"] as String),
walletId: map["id"] as String,
name: map["name"] as String,
);
}).toList();
//
// Get current ordered list of favourite wallet Ids
//
final List<String> favourites =
(await Hive.openBox<String>(DB.boxNameFavoriteWallets)).values.toList();
final List<WalletInfo> newInfo = [];
final List<TransactionNote> migratedNotes = [];
//
// Convert each old info into the new Isar WalletInfo
//
for (final old in oldInfo) {
final walletBox = await Hive.openBox<dynamic>(old.walletId);
//
// First handle transaction notes
//
final newNoteCount = await MainDB.instance.isar.transactionNotes
.where()
.walletIdEqualTo(old.walletId)
.count();
if (newNoteCount == 0) {
final map = walletBox.get('notes') as Map?;
if (map != null) {
final notes = Map<String, String>.from(map);
for (final txid in notes.keys) {
final note = notes[txid];
if (note != null && note.isNotEmpty) {
final newNote = TransactionNote(
walletId: old.walletId,
txid: txid,
value: note,
);
migratedNotes.add(newNote);
}
}
}
}
//
// Set other data values
//
Map<String, dynamic> otherData = {};
otherData[WalletInfoKeys.cachedSecondaryBalance] = walletBox.get(
DBKeys.cachedBalanceSecondary,
) as String?;
otherData[WalletInfoKeys.tokenContractAddresses] = walletBox.get(
DBKeys.ethTokenContracts,
) as List<String>?;
// epiccash specifics
if (old.coin == Coin.epicCash) {
final epicWalletInfo = ExtraEpiccashWalletInfo.fromMap({
"receivingIndex": walletBox.get("receivingIndex") as int? ?? 0,
"changeIndex": walletBox.get("changeIndex") as int? ?? 0,
"slatesToAddresses": walletBox.get("slate_to_address") as Map? ?? {},
"slatesToCommits": walletBox.get("slatesToCommits") as Map? ?? {},
"lastScannedBlock": walletBox.get("lastScannedBlock") as int? ?? 0,
"restoreHeight": walletBox.get("restoreHeight") as int? ?? 0,
"creationHeight": walletBox.get("creationHeight") as int? ?? 0,
});
otherData[WalletInfoKeys.epiccashData] = jsonEncode(
epicWalletInfo.toMap(),
);
}
//
// Clear out any keys with null values as they are not needed
//
otherData.removeWhere((key, value) => value == null);
final info = WalletInfo(
coinName: old.coin.name,
walletId: old.walletId,
name: old.name,
mainAddressType: old.coin.primaryAddressType,
favouriteOrderIndex: favourites.indexOf(old.walletId),
isMnemonicVerified: allWalletsBox
.get("${old.walletId}_mnemonicHasBeenVerified") as bool? ??
false,
cachedChainHeight: walletBox.get(
DBKeys.storedChainHeight,
) as int? ??
0,
cachedBalanceString: walletBox.get(
DBKeys.cachedBalance,
) as String?,
otherDataJsonString: jsonEncode(otherData),
);
newInfo.add(info);
}
if (migratedNotes.isNotEmpty) {
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.transactionNotes.putAll(migratedNotes);
});
}
await MainDB.instance.isar.writeTxn(() async {
await MainDB.instance.isar.walletInfo.putAll(newInfo);
});
await _cleanupOnSuccess(walletIds: newInfo.map((e) => e.walletId).toList());
}
Future<void> _cleanupOnSuccess({required List<String> walletIds}) async {
await Hive.deleteBoxFromDisk(DB.boxNameFavoriteWallets);
await Hive.deleteBoxFromDisk(DB.boxNameAllWalletsData);
for (final walletId in walletIds) {
await Hive.deleteBoxFromDisk(walletId);
}
}