2023-01-25 09:29:20 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
2023-03-01 21:52:13 +00:00
|
|
|
import 'package:stackwallet/db/hive/db.dart';
|
2023-01-25 09:29:20 +00:00
|
|
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|
|
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
|
|
|
|
|
|
|
class TokenInfo {
|
|
|
|
final Coin coin;
|
|
|
|
final String walletId;
|
|
|
|
final String contractAddress;
|
|
|
|
|
|
|
|
const TokenInfo(
|
|
|
|
{required this.coin,
|
|
|
|
required this.walletId,
|
|
|
|
required this.contractAddress});
|
|
|
|
|
|
|
|
factory TokenInfo.fromJson(Map<String, dynamic> jsonObject) {
|
|
|
|
return TokenInfo(
|
|
|
|
coin: Coin.values.byName(jsonObject["coin"] as String),
|
|
|
|
walletId: jsonObject["id"] as String,
|
|
|
|
contractAddress: jsonObject["contractAddress"] as String,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Map<String, String> toMap() {
|
|
|
|
return {
|
|
|
|
"contractAddress": contractAddress,
|
|
|
|
"walletId": walletId,
|
|
|
|
"coin": coin.name,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
String toJsonString() {
|
|
|
|
return jsonEncode(toMap());
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
String toString() {
|
|
|
|
return "TokenInfo: ${toJsonString()}";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TokensService extends ChangeNotifier {
|
|
|
|
late final SecureStorageInterface _secureStore;
|
|
|
|
|
|
|
|
// Future<Map<String, TokenInfo>>? _walletNames;
|
|
|
|
// Future<Map<String, TokenInfo>> get walletNames =>
|
|
|
|
// _walletNames ??= _fetchWalletNames();
|
|
|
|
|
|
|
|
TokensService({
|
|
|
|
required SecureStorageInterface secureStorageInterface,
|
|
|
|
}) {
|
|
|
|
_secureStore = secureStorageInterface;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Future<Coin> getWalletCryptoCurrency({required String walletName}) async {
|
|
|
|
// final id = await getWalletId(walletName);
|
|
|
|
// final currency = DB.instance.get<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: "${id}_cryptoCurrency");
|
|
|
|
// return Coin.values.byName(currency as String);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Future<bool> renameWallet({
|
|
|
|
// required String from,
|
|
|
|
// required String to,
|
|
|
|
// required bool shouldNotifyListeners,
|
|
|
|
// }) async {
|
|
|
|
// if (from == to) {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// final walletInfo = DB.instance
|
|
|
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map;
|
|
|
|
//
|
|
|
|
// final info = walletInfo.values.firstWhere(
|
|
|
|
// (element) => element['name'] == from,
|
|
|
|
// orElse: () => <String, String>{}) as Map;
|
|
|
|
//
|
|
|
|
// if (info.isEmpty) {
|
|
|
|
// // tried to rename a non existing wallet
|
|
|
|
// Logging.instance
|
|
|
|
// .log("Tried to rename a non existing wallet!", level: LogLevel.Error);
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// if (from != to &&
|
|
|
|
// (walletInfo.values.firstWhere((element) => element['name'] == to,
|
|
|
|
// orElse: () => <String, String>{}) as Map)
|
|
|
|
// .isNotEmpty) {
|
|
|
|
// // name already exists
|
|
|
|
// Logging.instance.log("wallet with name \"$to\" already exists!",
|
|
|
|
// level: LogLevel.Error);
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// info["name"] = to;
|
|
|
|
// walletInfo[info['id']] = info;
|
|
|
|
//
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: walletInfo);
|
|
|
|
// await refreshWallets(shouldNotifyListeners);
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Future<Map<String, WalletInfo>> _fetchWalletNames() async {
|
|
|
|
// final names = DB.instance
|
|
|
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
|
|
|
// if (names == null) {
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "Fetched wallet 'names' returned null. Setting initializing 'names'",
|
|
|
|
// level: LogLevel.Info);
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: 'names',
|
|
|
|
// value: <String, dynamic>{});
|
|
|
|
// return {};
|
|
|
|
// }
|
|
|
|
// Logging.instance.log("Fetched wallet names: $names", level: LogLevel.Info);
|
|
|
|
// final mapped = Map<String, dynamic>.from(names);
|
|
|
|
// mapped.removeWhere((name, dyn) {
|
|
|
|
// final jsonObject = Map<String, dynamic>.from(dyn as Map);
|
|
|
|
// try {
|
|
|
|
// Coin.values.byName(jsonObject["coin"] as String);
|
|
|
|
// return false;
|
|
|
|
// } catch (e, s) {
|
|
|
|
// Logging.instance.log("Error, ${jsonObject["coin"]} does not exist",
|
|
|
|
// level: LogLevel.Error);
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
//
|
|
|
|
// return mapped.map((name, dyn) => MapEntry(
|
|
|
|
// name, WalletInfo.fromJson(Map<String, dynamic>.from(dyn as Map))));
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Future<void> addExistingStackWallet({
|
|
|
|
// required String name,
|
|
|
|
// required String walletId,
|
|
|
|
// required Coin coin,
|
|
|
|
// required bool shouldNotifyListeners,
|
|
|
|
// }) async {
|
|
|
|
// final _names = DB.instance
|
|
|
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
|
|
|
//
|
|
|
|
// Map<String, dynamic> names;
|
|
|
|
// if (_names == null) {
|
|
|
|
// names = {};
|
|
|
|
// } else {
|
|
|
|
// names = Map<String, dynamic>.from(_names);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// if (names.keys.contains(walletId)) {
|
|
|
|
// throw Exception("Wallet with walletId \"$walletId\" already exists!");
|
|
|
|
// }
|
|
|
|
// if (names.values.where((element) => element['name'] == name).isNotEmpty) {
|
|
|
|
// throw Exception("Wallet with name \"$name\" already exists!");
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// names[walletId] = {
|
|
|
|
// "id": walletId,
|
|
|
|
// "coin": coin.name,
|
|
|
|
// "name": name,
|
|
|
|
// };
|
|
|
|
//
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${walletId}_cryptoCurrency",
|
|
|
|
// value: coin.name);
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${walletId}_mnemonicHasBeenVerified",
|
|
|
|
// value: false);
|
|
|
|
// await DB.instance.addWalletBox(walletId: walletId);
|
|
|
|
// await refreshWallets(shouldNotifyListeners);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// /// returns the new walletId if successful, otherwise null
|
|
|
|
// Future<String?> addNewWallet({
|
|
|
|
// required String name,
|
|
|
|
// required Coin coin,
|
|
|
|
// required bool shouldNotifyListeners,
|
|
|
|
// }) async {
|
|
|
|
// final _names = DB.instance
|
|
|
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
|
|
|
//
|
|
|
|
// Map<String, dynamic> names;
|
|
|
|
// if (_names == null) {
|
|
|
|
// names = {};
|
|
|
|
// } else {
|
|
|
|
// names = Map<String, dynamic>.from(_names);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // Prevent overwriting or storing empty names
|
|
|
|
// if (name.isEmpty ||
|
|
|
|
// names.values.where((element) => element['name'] == name).isNotEmpty) {
|
|
|
|
// return null;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// final id = const Uuid().v1();
|
|
|
|
// names[id] = {
|
|
|
|
// "id": id,
|
|
|
|
// "coin": coin.name,
|
|
|
|
// "name": name,
|
|
|
|
// };
|
|
|
|
//
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${id}_cryptoCurrency",
|
|
|
|
// value: coin.name);
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${id}_mnemonicHasBeenVerified",
|
|
|
|
// value: false);
|
|
|
|
// await DB.instance.addWalletBox(walletId: id);
|
|
|
|
// await refreshWallets(shouldNotifyListeners);
|
|
|
|
// return id;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Future<List<String>> getFavoriteWalletIds() async {
|
|
|
|
// return DB.instance
|
|
|
|
// .values<String>(boxName: DB.boxNameFavoriteWallets)
|
|
|
|
// .toList();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Future<void> saveFavoriteWalletIds(List<String> walletIds) async {
|
|
|
|
// await DB.instance.deleteAll<String>(boxName: DB.boxNameFavoriteWallets);
|
|
|
|
// await DB.instance
|
|
|
|
// .addAll(boxName: DB.boxNameFavoriteWallets, values: walletIds);
|
|
|
|
// debugPrint("saveFavoriteWalletIds list: $walletIds");
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Future<void> addFavorite(String walletId) async {
|
|
|
|
// final list = await getFavoriteWalletIds();
|
|
|
|
// if (!list.contains(walletId)) {
|
|
|
|
// list.add(walletId);
|
|
|
|
// }
|
|
|
|
// await saveFavoriteWalletIds(list);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Future<void> removeFavorite(String walletId) async {
|
|
|
|
// final list = await getFavoriteWalletIds();
|
|
|
|
// list.remove(walletId);
|
|
|
|
// await saveFavoriteWalletIds(list);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Future<void> moveFavorite({
|
|
|
|
// required int fromIndex,
|
|
|
|
// required int toIndex,
|
|
|
|
// }) async {
|
|
|
|
// final list = await getFavoriteWalletIds();
|
|
|
|
// if (fromIndex < toIndex) {
|
|
|
|
// toIndex -= 1;
|
|
|
|
// }
|
|
|
|
// final walletId = list.removeAt(fromIndex);
|
|
|
|
// list.insert(toIndex, walletId);
|
|
|
|
// await saveFavoriteWalletIds(list);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Future<bool> checkForDuplicate(String name) async {
|
|
|
|
// final names = DB.instance
|
|
|
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
|
|
|
// if (names == null) return false;
|
|
|
|
//
|
|
|
|
// return names.values.where((element) => element['name'] == name).isNotEmpty;
|
|
|
|
// }
|
|
|
|
|
|
|
|
Future<String?> getWalletId(String walletName) async {
|
|
|
|
final names = DB.instance
|
|
|
|
.get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map;
|
|
|
|
final shells =
|
|
|
|
names.values.where((element) => element['name'] == walletName);
|
|
|
|
if (shells.isEmpty) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return shells.first["id"] as String;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Future<bool> isMnemonicVerified({required String walletId}) async {
|
|
|
|
// final isVerified = DB.instance.get<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${walletId}_mnemonicHasBeenVerified") as bool?;
|
|
|
|
//
|
|
|
|
// if (isVerified == null) {
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "isMnemonicVerified(walletId: $walletId) returned null which should never happen!",
|
|
|
|
// level: LogLevel.Error,
|
|
|
|
// );
|
|
|
|
// throw Exception(
|
|
|
|
// "isMnemonicVerified(walletId: $walletId) returned null which should never happen!");
|
|
|
|
// } else {
|
|
|
|
// return isVerified;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Future<void> setMnemonicVerified({required String walletId}) async {
|
|
|
|
// final isVerified = DB.instance.get<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${walletId}_mnemonicHasBeenVerified") as bool?;
|
|
|
|
//
|
|
|
|
// if (isVerified == null) {
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "setMnemonicVerified(walletId: $walletId) tried running on non existent wallet!",
|
|
|
|
// level: LogLevel.Error,
|
|
|
|
// );
|
|
|
|
// throw Exception(
|
|
|
|
// "setMnemonicVerified(walletId: $walletId) tried running on non existent wallet!");
|
|
|
|
// } else if (isVerified) {
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "setMnemonicVerified(walletId: $walletId) tried running on already verified wallet!",
|
|
|
|
// level: LogLevel.Error,
|
|
|
|
// );
|
|
|
|
// throw Exception(
|
|
|
|
// "setMnemonicVerified(walletId: $walletId) tried running on already verified wallet!");
|
|
|
|
// } else {
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${walletId}_mnemonicHasBeenVerified",
|
|
|
|
// value: true);
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "setMnemonicVerified(walletId: $walletId) successful",
|
|
|
|
// level: LogLevel.Error,
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // pin + mnemonic as well as anything else in secureStore
|
|
|
|
// Future<int> deleteWallet(String name, bool shouldNotifyListeners) async {
|
|
|
|
// final names = DB.instance.get<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: 'names') as Map? ??
|
|
|
|
// {};
|
|
|
|
//
|
|
|
|
// final walletId = await getWalletId(name);
|
|
|
|
// if (walletId == null) {
|
|
|
|
// return 3;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "deleteWallet called with name=$name and id=$walletId",
|
|
|
|
// level: LogLevel.Warning,
|
|
|
|
// );
|
|
|
|
//
|
|
|
|
// final shell = names.remove(walletId);
|
|
|
|
//
|
|
|
|
// if (shell == null) {
|
|
|
|
// return 0;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // TODO delete derivations!!!
|
|
|
|
// await _secureStore.delete(key: "${walletId}_pin");
|
|
|
|
// await _secureStore.delete(key: "${walletId}_mnemonic");
|
|
|
|
//
|
|
|
|
// await DB.instance.delete<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: "${walletId}_cryptoCurrency");
|
|
|
|
// await DB.instance.delete<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData,
|
|
|
|
// key: "${walletId}_mnemonicHasBeenVerified");
|
|
|
|
// if (coinFromPrettyName(shell['coin'] as String) == Coin.wownero) {
|
|
|
|
// final wowService =
|
|
|
|
// wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
|
|
|
|
// await wowService.remove(walletId);
|
|
|
|
// Logging.instance
|
|
|
|
// .log("monero wallet: $walletId deleted", level: LogLevel.Info);
|
|
|
|
// } else if (coinFromPrettyName(shell['coin'] as String) == Coin.monero) {
|
|
|
|
// final xmrService =
|
|
|
|
// monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
|
|
|
|
// await xmrService.remove(walletId);
|
|
|
|
// Logging.instance
|
|
|
|
// .log("monero wallet: $walletId deleted", level: LogLevel.Info);
|
|
|
|
// } else if (coinFromPrettyName(shell['coin'] as String) == Coin.epicCash) {
|
|
|
|
// final deleteResult =
|
|
|
|
// await deleteEpicWallet(walletId: walletId, secureStore: _secureStore);
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "epic wallet: $walletId deleted with result: $deleteResult",
|
|
|
|
// level: LogLevel.Info);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // box data may currently still be read/written to if wallet was refreshing
|
|
|
|
// // when delete was requested so instead of deleting now we mark the wallet
|
|
|
|
// // as needs delete by adding it's id to a list which gets checked on app start
|
|
|
|
// await DB.instance.add<String>(
|
|
|
|
// boxName: DB.boxNameWalletsToDeleteOnStart, value: walletId);
|
|
|
|
//
|
|
|
|
// final lookupService = TradeSentFromStackService();
|
|
|
|
// for (final lookup in lookupService.all) {
|
|
|
|
// if (lookup.walletIds.contains(walletId)) {
|
|
|
|
// // update lookup data to reflect deleted wallet
|
|
|
|
// await lookupService.save(
|
|
|
|
// tradeWalletLookup: lookup.copyWith(
|
|
|
|
// walletIds: lookup.walletIds.where((id) => id != walletId).toList(),
|
|
|
|
// ),
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // delete notifications tied to deleted wallet
|
|
|
|
// for (final notification in NotificationsService.instance.notifications) {
|
|
|
|
// if (notification.walletId == walletId) {
|
|
|
|
// await NotificationsService.instance.delete(notification, false);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// if (names.isEmpty) {
|
|
|
|
// await DB.instance.deleteAll<dynamic>(boxName: DB.boxNameAllWalletsData);
|
|
|
|
// _walletNames = Future(() => {});
|
|
|
|
// notifyListeners();
|
|
|
|
// return 2; // error code no wallets on device
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// await DB.instance.put<dynamic>(
|
|
|
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
|
|
|
|
// await refreshWallets(shouldNotifyListeners);
|
|
|
|
// return 0;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Future<void> refreshWallets(bool shouldNotifyListeners) async {
|
|
|
|
// final newNames = await _fetchWalletNames();
|
|
|
|
// _walletNames = Future(() => newNames);
|
|
|
|
// if (shouldNotifyListeners) notifyListeners();
|
|
|
|
// }
|
|
|
|
}
|