/* * 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 get wallets => _wallets.values.toList(); List<({Coin coin, List wallets})> get walletsByCoin { final Map 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 wallets})> results = []; for (final coin in Coin.values) { if (map[coin] != null) { results.add(map[coin]!); } } return results; } static bool hasLoaded = false; final Map _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 deleteWallet(String walletId) async { throw UnimplementedError("Delete wallet unimplemented"); } Future 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(boxName: DB.boxNameWalletsToDeleteOnStart)) { await mainDB.isar.writeTxn(() async => await mainDB.isar.walletInfo .where() .walletIdEqualTo(walletId) .deleteAll()); } // clear list await DB.instance .deleteAll(boxName: DB.boxNameWalletsToDeleteOnStart); final walletInfoList = await mainDB.isar.walletInfo.where().findAll(); if (walletInfoList.isEmpty) { return; } List> walletInitFutures = []; List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = []; List 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 loadAfterStackRestore( Prefs prefs, List wallets, ) async { List> walletInitFutures = []; List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = []; List 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 _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 _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()); } }