stack_wallet/lib/services/wallets.dart

256 lines
8.2 KiB
Dart

/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
import 'package:isar/isar.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
class Wallets {
Wallets._private();
static final Wallets _sharedInstance = Wallets._private();
static Wallets get sharedInstance => _sharedInstance;
late NodeService nodeService;
late MainDB mainDB;
bool get hasWallets => _wallets.isNotEmpty;
List<Wallet> get wallets => _wallets.values.toList();
List<({Coin coin, List<Wallet> wallets})> get walletsByCoin {
final Map<Coin, ({Coin coin, List<Wallet> wallets})> map = {};
for (final wallet in wallets) {
if (map[wallet.info.coin] == null) {
map[wallet.info.coin] = (coin: wallet.info.coin, wallets: []);
}
map[wallet.info.coin]!.wallets.add(wallet);
}
final List<({Coin coin, List<Wallet> wallets})> results = [];
for (final coin in Coin.values) {
if (map[coin] != null) {
results.add(map[coin]!);
}
}
return results;
}
static bool hasLoaded = false;
final Map<String, Wallet> _wallets = {};
Wallet getWallet(String walletId) => _wallets[walletId]!;
void addWallet(Wallet wallet) {
if (_wallets[wallet.walletId] != null) {
throw Exception(
"Tried to add wallet that already exists, according to it's wallet Id!",
);
}
_wallets[wallet.walletId] = wallet;
}
Future<void> deleteWallet(String walletId) async {
throw UnimplementedError("Delete wallet unimplemented");
}
Future<void> load(Prefs prefs, MainDB mainDB) async {
if (hasLoaded) {
return;
}
hasLoaded = true;
// clear out any wallet hive boxes where the wallet was deleted in previous app run
for (final walletId in DB.instance
.values<String>(boxName: DB.boxNameWalletsToDeleteOnStart)) {
await mainDB.isar.writeTxn(() async => await mainDB.isar.walletInfo
.where()
.walletIdEqualTo(walletId)
.deleteAll());
}
// clear list
await DB.instance
.deleteAll<String>(boxName: DB.boxNameWalletsToDeleteOnStart);
final walletInfoList = await mainDB.isar.walletInfo.where().findAll();
if (walletInfoList.isEmpty) {
return;
}
List<Future<void>> walletInitFutures = [];
List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = [];
List<String> walletIdsToEnableAutoSync = [];
bool shouldAutoSyncAll = false;
switch (prefs.syncType) {
case SyncingType.currentWalletOnly:
// do nothing as this will be set when going into a wallet from the main screen
break;
case SyncingType.selectedWalletsAtStartup:
walletIdsToEnableAutoSync.addAll(prefs.walletIdsSyncOnStartup);
break;
case SyncingType.allWalletsOnStartup:
shouldAutoSyncAll = true;
break;
}
for (final walletInfo in walletInfoList) {
try {
Logging.instance.log(
"LOADING WALLET: ${walletInfo.name}:${walletInfo.walletId} "
"IS VERIFIED: ${walletInfo.isMnemonicVerified}",
level: LogLevel.Info,
);
if (walletInfo.isMnemonicVerified) {
// TODO: integrate this into the new wallets somehow?
// requires some thinking
final txTracker =
TransactionNotificationTracker(walletId: walletInfo.walletId);
final wallet = await Wallet.load(
walletId: walletInfo.walletId,
mainDB: mainDB,
secureStorageInterface: nodeService.secureStorageInterface,
nodeService: nodeService,
prefs: prefs,
);
final shouldSetAutoSync = shouldAutoSyncAll ||
walletIdsToEnableAutoSync.contains(walletInfo.walletId);
if (walletInfo.coin == Coin.monero ||
walletInfo.coin == Coin.wownero) {
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
} else {
walletInitFutures.add(wallet.init().then((_) {
if (shouldSetAutoSync) {
wallet.shouldAutoSync = true;
}
}));
}
_wallets[wallet.walletId] = wallet;
} else {
// wallet creation was not completed by user so we remove it completely
await _deleteWallet(walletInfo.walletId);
// await walletsService.deleteWallet(walletInfo.name, false);
}
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Fatal);
continue;
}
}
if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
await Future.wait([
_initLinearly(walletsToInitLinearly),
...walletInitFutures,
]);
} else if (walletInitFutures.isNotEmpty) {
await Future.wait(walletInitFutures);
} else if (walletsToInitLinearly.isNotEmpty) {
await _initLinearly(walletsToInitLinearly);
}
}
Future<void> loadAfterStackRestore(
Prefs prefs,
List<Wallet> wallets,
) async {
List<Future<void>> walletInitFutures = [];
List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = [];
List<String> walletIdsToEnableAutoSync = [];
bool shouldAutoSyncAll = false;
switch (prefs.syncType) {
case SyncingType.currentWalletOnly:
// do nothing as this will be set when going into a wallet from the main screen
break;
case SyncingType.selectedWalletsAtStartup:
walletIdsToEnableAutoSync.addAll(prefs.walletIdsSyncOnStartup);
break;
case SyncingType.allWalletsOnStartup:
shouldAutoSyncAll = true;
break;
}
for (final wallet in wallets) {
Logging.instance.log(
"LOADING WALLET: ${wallet.info.name}:${wallet.walletId} IS VERIFIED: ${wallet.info.isMnemonicVerified}",
level: LogLevel.Info,
);
if (wallet.info.isMnemonicVerified) {
final shouldSetAutoSync = shouldAutoSyncAll ||
walletIdsToEnableAutoSync.contains(wallet.walletId);
if (wallet.info.coin == Coin.monero ||
wallet.info.coin == Coin.wownero) {
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
} else {
walletInitFutures.add(wallet.init().then((value) {
if (shouldSetAutoSync) {
wallet.shouldAutoSync = true;
}
}));
}
_wallets[wallet.walletId] = wallet;
} else {
// wallet creation was not completed by user so we remove it completely
await _deleteWallet(wallet.walletId);
// await walletsService.deleteWallet(walletInfo.name, false);
}
}
if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
await Future.wait([
_initLinearly(walletsToInitLinearly),
...walletInitFutures,
]);
} else if (walletInitFutures.isNotEmpty) {
await Future.wait(walletInitFutures);
} else if (walletsToInitLinearly.isNotEmpty) {
await _initLinearly(walletsToInitLinearly);
}
}
Future<void> _initLinearly(
List<({Wallet wallet, bool shouldAutoSync})> dataList,
) async {
for (final data in dataList) {
await data.wallet.init();
if (data.shouldAutoSync && !data.wallet.shouldAutoSync) {
data.wallet.shouldAutoSync = true;
}
}
}
Future<void> _deleteWallet(String walletId) async {
// TODO proper clean up of other wallet data in addition to the following
await mainDB.isar.writeTxn(() async => await mainDB.isar.walletInfo
.where()
.walletIdEqualTo(walletId)
.deleteAll());
}
}