2023-05-26 21:21:16 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:event_bus/event_bus.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
2023-03-01 21:52:13 +00:00
|
|
|
import 'package:stackwallet/db/hive/db.dart';
|
2023-01-11 18:21:11 +00:00
|
|
|
import 'package:stackwallet/models/balance.dart';
|
|
|
|
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'package:stackwallet/models/models.dart';
|
|
|
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
|
|
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
|
|
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
|
|
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
2023-03-06 16:34:41 +00:00
|
|
|
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
|
2023-07-26 22:06:17 +00:00
|
|
|
import 'package:stackwallet/services/mixins/fusion_interface.dart';
|
2023-01-25 21:49:57 +00:00
|
|
|
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
|
2023-04-08 00:44:43 +00:00
|
|
|
import 'package:stackwallet/services/mixins/xpubable.dart';
|
2023-05-15 17:15:35 +00:00
|
|
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|
|
|
import 'package:stackwallet/utilities/logger.dart';
|
|
|
|
|
|
|
|
class Manager with ChangeNotifier {
|
|
|
|
final CoinServiceAPI _currentWallet;
|
|
|
|
StreamSubscription<dynamic>? _backgroundRefreshListener;
|
|
|
|
StreamSubscription<dynamic>? _nodeStatusListener;
|
|
|
|
|
|
|
|
/// optional eventbus parameter for testing only
|
|
|
|
Manager(this._currentWallet, [EventBus? globalEventBusForTesting]) {
|
|
|
|
final bus = globalEventBusForTesting ?? GlobalEventBus.instance;
|
|
|
|
_backgroundRefreshListener = bus.on<UpdatedInBackgroundEvent>().listen(
|
|
|
|
(event) async {
|
|
|
|
if (event.walletId == walletId) {
|
|
|
|
notifyListeners();
|
|
|
|
Logging.instance.log(
|
|
|
|
"UpdatedInBackgroundEvent activated notifyListeners() in Manager instance $hashCode $walletName with: ${event.message}",
|
|
|
|
level: LogLevel.Info);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
_nodeStatusListener = bus.on<NodeConnectionStatusChangedEvent>().listen(
|
|
|
|
(event) async {
|
|
|
|
if (event.walletId == walletId) {
|
|
|
|
notifyListeners();
|
|
|
|
Logging.instance.log(
|
|
|
|
"NodeConnectionStatusChangedEvent activated notifyListeners() in Manager instance $hashCode $walletName with: ${event.newStatus}",
|
|
|
|
level: LogLevel.Info);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool _isActiveWallet = false;
|
|
|
|
bool get isActiveWallet => _isActiveWallet;
|
|
|
|
set isActiveWallet(bool isActive) {
|
|
|
|
if (_isActiveWallet != isActive) {
|
|
|
|
_isActiveWallet = isActive;
|
|
|
|
_currentWallet.onIsActiveWalletChanged?.call(isActive);
|
2022-12-13 00:17:02 +00:00
|
|
|
//todo: check if print needed
|
2022-08-26 08:11:35 +00:00
|
|
|
debugPrint(
|
|
|
|
"wallet ID: ${_currentWallet.walletId} is active set to: $isActive");
|
|
|
|
} else {
|
|
|
|
debugPrint("wallet ID: ${_currentWallet.walletId} is still: $isActive");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> updateNode(bool shouldRefresh) async {
|
|
|
|
await _currentWallet.updateNode(shouldRefresh);
|
|
|
|
}
|
|
|
|
|
|
|
|
CoinServiceAPI get wallet => _currentWallet;
|
|
|
|
|
|
|
|
bool get hasBackgroundRefreshListener => _backgroundRefreshListener != null;
|
|
|
|
|
|
|
|
Coin get coin => _currentWallet.coin;
|
|
|
|
|
|
|
|
bool get isRefreshing => _currentWallet.isRefreshing;
|
|
|
|
|
|
|
|
bool get shouldAutoSync => _currentWallet.shouldAutoSync;
|
|
|
|
set shouldAutoSync(bool shouldAutoSync) =>
|
|
|
|
_currentWallet.shouldAutoSync = shouldAutoSync;
|
|
|
|
|
|
|
|
bool get isFavorite => _currentWallet.isFavorite;
|
|
|
|
|
|
|
|
set isFavorite(bool markFavorite) {
|
|
|
|
_currentWallet.isFavorite = markFavorite;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
dispose() async {
|
|
|
|
await exitCurrentWallet();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Map<String, dynamic>> prepareSend({
|
|
|
|
required String address,
|
2023-04-05 22:06:31 +00:00
|
|
|
required Amount amount,
|
2022-08-26 08:11:35 +00:00
|
|
|
Map<String, dynamic>? args,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
final txInfo = await _currentWallet.prepareSend(
|
|
|
|
address: address,
|
2023-04-05 22:06:31 +00:00
|
|
|
amount: amount,
|
2022-08-26 08:11:35 +00:00
|
|
|
args: args,
|
|
|
|
);
|
|
|
|
// notifyListeners();
|
|
|
|
return txInfo;
|
|
|
|
} catch (e) {
|
|
|
|
// rethrow to pass error in alert
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
|
|
|
|
try {
|
|
|
|
final txid = await _currentWallet.confirmSend(txData: txData);
|
|
|
|
|
2023-02-02 22:19:14 +00:00
|
|
|
try {
|
|
|
|
txData["txid"] = txid;
|
|
|
|
await _currentWallet.updateSentCachedTxData(txData);
|
|
|
|
} catch (e, s) {
|
|
|
|
// do not rethrow as that would get handled as a send failure further up
|
|
|
|
// also this is not critical code and transaction should show up on \
|
|
|
|
// refresh regardless
|
|
|
|
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
|
|
|
|
}
|
2022-11-07 16:24:08 +00:00
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
notifyListeners();
|
|
|
|
return txid;
|
|
|
|
} catch (e) {
|
|
|
|
// rethrow to pass error in alert
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<FeeObject> get fees => _currentWallet.fees;
|
|
|
|
Future<int> get maxFee => _currentWallet.maxFee;
|
|
|
|
|
|
|
|
Future<String> get currentReceivingAddress =>
|
|
|
|
_currentWallet.currentReceivingAddress;
|
|
|
|
|
2023-01-11 18:21:11 +00:00
|
|
|
Balance get balance => _currentWallet.balance;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2023-01-11 18:21:11 +00:00
|
|
|
Future<List<isar_models.Transaction>> get transactions =>
|
|
|
|
_currentWallet.transactions;
|
|
|
|
Future<List<isar_models.UTXO>> get utxos => _currentWallet.utxos;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
Future<void> refresh() async {
|
|
|
|
await _currentWallet.refresh();
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
// setter for updating on rename
|
|
|
|
set walletName(String newName) {
|
|
|
|
if (newName != _currentWallet.walletName) {
|
|
|
|
_currentWallet.walletName = newName;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
String get walletName => _currentWallet.walletName;
|
|
|
|
String get walletId => _currentWallet.walletId;
|
|
|
|
|
|
|
|
bool validateAddress(String address) =>
|
|
|
|
_currentWallet.validateAddress(address);
|
|
|
|
|
|
|
|
Future<List<String>> get mnemonic => _currentWallet.mnemonic;
|
2023-02-03 22:34:06 +00:00
|
|
|
Future<String?> get mnemonicPassphrase => _currentWallet.mnemonicPassphrase;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
Future<bool> testNetworkConnection() =>
|
|
|
|
_currentWallet.testNetworkConnection();
|
|
|
|
|
|
|
|
Future<void> initializeNew() => _currentWallet.initializeNew();
|
|
|
|
Future<void> initializeExisting() => _currentWallet.initializeExisting();
|
|
|
|
Future<void> recoverFromMnemonic({
|
|
|
|
required String mnemonic,
|
2023-02-03 22:34:06 +00:00
|
|
|
String? mnemonicPassphrase,
|
2022-08-26 08:11:35 +00:00
|
|
|
required int maxUnusedAddressGap,
|
|
|
|
required int maxNumberOfIndexesToCheck,
|
|
|
|
required int height,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
await _currentWallet.recoverFromMnemonic(
|
|
|
|
mnemonic: mnemonic,
|
2023-02-03 22:34:06 +00:00
|
|
|
mnemonicPassphrase: mnemonicPassphrase,
|
2022-08-26 08:11:35 +00:00
|
|
|
maxUnusedAddressGap: maxUnusedAddressGap,
|
|
|
|
maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck,
|
|
|
|
height: height,
|
|
|
|
);
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("e: $e, S: $s", level: LogLevel.Error);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> exitCurrentWallet() async {
|
|
|
|
final name = _currentWallet.walletName;
|
|
|
|
final id = _currentWallet.walletId;
|
|
|
|
await _backgroundRefreshListener?.cancel();
|
|
|
|
_backgroundRefreshListener = null;
|
|
|
|
await _nodeStatusListener?.cancel();
|
|
|
|
_nodeStatusListener = null;
|
|
|
|
await _currentWallet.exit();
|
|
|
|
Logging.instance.log("manager.exitCurrentWallet completed for $id $name",
|
|
|
|
level: LogLevel.Info);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> fullRescan(
|
|
|
|
int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async {
|
|
|
|
try {
|
|
|
|
await _currentWallet.fullRescan(
|
|
|
|
maxUnusedAddressGap, maxNumberOfIndexesToCheck);
|
|
|
|
} catch (e) {
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool get isConnected => _currentWallet.isConnected;
|
|
|
|
|
2023-04-05 22:06:31 +00:00
|
|
|
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
|
|
|
|
return _currentWallet.estimateFeeFor(amount, feeRate);
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
2022-09-06 01:18:45 +00:00
|
|
|
|
|
|
|
Future<bool> generateNewAddress() async {
|
|
|
|
final success = await _currentWallet.generateNewAddress();
|
|
|
|
if (success) {
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
2023-01-10 23:50:22 +00:00
|
|
|
|
|
|
|
int get currentHeight => _currentWallet.storedChainHeight;
|
2023-01-25 21:46:48 +00:00
|
|
|
|
|
|
|
bool get hasPaynymSupport => _currentWallet is PaynymWalletInterface;
|
2023-02-08 13:29:27 +00:00
|
|
|
|
2023-03-06 16:34:41 +00:00
|
|
|
bool get hasCoinControlSupport => _currentWallet is CoinControlInterface;
|
|
|
|
|
2023-03-22 15:39:28 +00:00
|
|
|
bool get hasTokenSupport => _currentWallet.coin == Coin.ethereum;
|
|
|
|
|
2023-03-13 20:37:05 +00:00
|
|
|
bool get hasWhirlpoolSupport => false;
|
|
|
|
|
2023-07-26 22:06:17 +00:00
|
|
|
bool get hasFusionSupport => _currentWallet is FusionInterface;
|
|
|
|
|
2023-02-08 13:29:27 +00:00
|
|
|
int get rescanOnOpenVersion =>
|
|
|
|
DB.instance.get<dynamic>(
|
|
|
|
boxName: DB.boxNameDBInfo,
|
|
|
|
key: "rescan_on_open_$walletId",
|
|
|
|
) as int? ??
|
|
|
|
0;
|
|
|
|
|
|
|
|
Future<void> resetRescanOnOpen() async {
|
|
|
|
await DB.instance.delete<dynamic>(
|
|
|
|
key: "rescan_on_open_$walletId",
|
|
|
|
boxName: DB.boxNameDBInfo,
|
|
|
|
);
|
|
|
|
}
|
2023-04-08 00:44:43 +00:00
|
|
|
|
2023-05-15 17:15:35 +00:00
|
|
|
// TODO: re enable once xpubs have been redone
|
|
|
|
bool get hasXPub => false; //_currentWallet is XPubAble;
|
2023-04-08 00:44:43 +00:00
|
|
|
|
|
|
|
Future<String> get xpub async {
|
|
|
|
if (!hasXPub) {
|
|
|
|
throw Exception(
|
|
|
|
"Tried to read xpub from wallet that does not support it");
|
|
|
|
}
|
|
|
|
return (_currentWallet as XPubAble).xpub;
|
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|