| 1 | import 'dart:async'; |
| 2 | |
| 3 | import 'package:cake_wallet/core/wallet_loading_service.dart'; |
| 4 | import 'package:cake_wallet/entities/wallet_group.dart'; |
| 5 | import 'package:cake_wallet/entities/wallet_manager.dart'; |
| 6 | import 'package:cake_wallet/reactions/wallet_utils.dart'; |
| 7 | import 'package:cake_wallet/store/app_store.dart'; |
| 8 | import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; |
| 9 | import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; |
| 10 | import 'package:cake_wallet/wallet_types.g.dart'; |
| 11 | import 'package:cw_core/wallet_info.dart'; |
| 12 | import 'package:cw_core/wallet_type.dart'; |
| 13 | import 'package:mobx/mobx.dart'; |
| 14 | |
| 15 | part 'wallet_groups_display_view_model.g.dart'; |
| 16 | |
| 17 | class WalletGroupsDisplayViewModel = WalletGroupsDisplayViewModelBase |
| 18 | with _$WalletGroupsDisplayViewModel; |
| 19 | |
| 20 | abstract class WalletGroupsDisplayViewModelBase with Store { |
| 21 | WalletGroupsDisplayViewModelBase( |
| 22 | this._appStore, |
| 23 | this._walletLoadingService, |
| 24 | this._walletManager, |
| 25 | this.walletListViewModel, { |
| 26 | required this.type, |
| 27 | }) : isFetchingMnemonic = false { |
| 28 | reaction((_) => _appStore.wallet, (_) => unawaited(updateWalletInfoSourceList())); |
| 29 | unawaited(updateWalletInfoSourceList()); |
| 30 | } |
| 31 | |
| 32 | final WalletType type; |
| 33 | final AppStore _appStore; |
| 34 | final WalletManager _walletManager; |
| 35 | final WalletLoadingService _walletLoadingService; |
| 36 | final WalletListViewModel walletListViewModel; |
| 37 | |
| 38 | @observable |
| 39 | ObservableList<WalletGroup> multiWalletGroups = ObservableList<WalletGroup>(); |
| 40 | |
| 41 | @observable |
| 42 | ObservableList<WalletInfo> singleWalletsList = ObservableList<WalletInfo>(); |
| 43 | |
| 44 | @observable |
| 45 | WalletGroup? selectedWalletGroup; |
| 46 | |
| 47 | @observable |
| 48 | WalletInfo? selectedSingleWallet; |
| 49 | |
| 50 | @observable |
| 51 | bool isFetchingMnemonic; |
| 52 | |
| 53 | @computed |
| 54 | bool get hasNoFilteredWallet { |
| 55 | return singleWalletsList.isEmpty && multiWalletGroups.isEmpty; |
| 56 | } |
| 57 | |
| 58 | @action |
| 59 | Future<String?> getSelectedWalletMnemonic() async { |
| 60 | WalletListItem walletToUse; |
| 61 | |
| 62 | bool isGroupSelected = selectedWalletGroup != null; |
| 63 | |
| 64 | if (isGroupSelected) { |
| 65 | walletToUse = convertWalletInfoToWalletListItem(selectedWalletGroup!.wallets.first); |
| 66 | } else { |
| 67 | walletToUse = convertWalletInfoToWalletListItem(selectedSingleWallet!); |
| 68 | } |
| 69 | |
| 70 | try { |
| 71 | isFetchingMnemonic = true; |
| 72 | final wallet = await _walletLoadingService.load( |
| 73 | walletToUse.type, |
| 74 | walletToUse.name, |
| 75 | ); |
| 76 | |
| 77 | return wallet.seed; |
| 78 | } catch (e) { |
| 79 | return null; |
| 80 | } finally { |
| 81 | isFetchingMnemonic = false; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | @action |
| 86 | void selectWalletGroup(WalletGroup walletGroup) { |
| 87 | selectedWalletGroup = walletGroup; |
| 88 | selectedSingleWallet = null; |
| 89 | } |
| 90 | |
| 91 | @action |
| 92 | void selectSingleWallet(WalletInfo singleWallet) { |
| 93 | selectedSingleWallet = singleWallet; |
| 94 | selectedWalletGroup = null; |
| 95 | } |
| 96 | |
| 97 | @action |
| 98 | Future<void> updateWalletInfoSourceList() async { |
| 99 | List<WalletGroup> wallets = []; |
| 100 | |
| 101 | multiWalletGroups.clear(); |
| 102 | singleWalletsList.clear(); |
| 103 | |
| 104 | await _walletManager.updateWalletGroups(); |
| 105 | |
| 106 | final walletGroups = List<WalletGroup>.from(_walletManager.walletGroups); |
| 107 | |
| 108 | // Iterate through the wallet groups to filter and categorize wallets |
| 109 | for (var group in walletGroups) { |
| 110 | // Handle group wallet filtering |
| 111 | bool shouldExcludeGroup = false; |
| 112 | for (final wallet in group.wallets) { |
| 113 | // Check for non-BIP39 wallet types |
| 114 | bool isNonBIP39Wallet = !isBIP39Wallet(wallet.type); |
| 115 | |
| 116 | // Check for nano derivation type |
| 117 | final di = await wallet.getDerivationInfo(); |
| 118 | bool isNanoDerivationType = |
| 119 | wallet.type == WalletType.nano && di.derivationType == DerivationType.nano; |
| 120 | |
| 121 | // Check for electrum derivation type |
| 122 | bool isElectrumDerivationType = |
| 123 | (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && |
| 124 | di.derivationType == DerivationType.electrum; |
| 125 | |
| 126 | // Check that selected wallet type is not present already in group |
| 127 | bool isSameTypeAsSelectedWallet = wallet.type == type; |
| 128 | |
| 129 | bool isNonSeedWallet = wallet.isNonSeedWallet; |
| 130 | |
| 131 | bool isNotMoneroBip39Wallet = |
| 132 | wallet.type == WalletType.monero && di.derivationType != DerivationType.bip39; |
| 133 | |
| 134 | bool isNotDecredBip39Wallet = wallet.type == WalletType.decred && |
| 135 | di.derivationType != DerivationType.bip39 && |
| 136 | di.derivationType != DerivationType.unknown; |
| 137 | |
| 138 | // Exclude if any of these conditions are true |
| 139 | shouldExcludeGroup = shouldExcludeGroup || |
| 140 | isNonBIP39Wallet || |
| 141 | isNanoDerivationType || |
| 142 | isElectrumDerivationType || |
| 143 | isSameTypeAsSelectedWallet || |
| 144 | isNonSeedWallet || |
| 145 | isNotMoneroBip39Wallet || |
| 146 | isNotDecredBip39Wallet; |
| 147 | } |
| 148 | |
| 149 | if (shouldExcludeGroup) continue; |
| 150 | |
| 151 | // If the group passes the filters, add it to the wallets list |
| 152 | wallets.add(group); |
| 153 | } |
| 154 | |
| 155 | for (var group in wallets) { |
| 156 | if (group.wallets.length == 1) { |
| 157 | singleWalletsList.add(group.wallets.first); |
| 158 | } else { |
| 159 | multiWalletGroups.add(group); |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | WalletListItem convertWalletInfoToWalletListItem(WalletInfo info) { |
| 165 | return WalletListItem( |
| 166 | name: info.name, |
| 167 | type: info.type, |
| 168 | key: info.id, |
| 169 | isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type, |
| 170 | isEnabled: availableWalletTypes.contains(info.type), |
| 171 | isTestnet: info.network?.toLowerCase().contains('testnet') ?? false, |
| 172 | isHardware: info.isHardwareWallet, |
| 173 | ); |
| 174 | } |
| 175 | } |