// /* // * 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 'dart:async'; // import 'dart:io'; // import 'dart:math'; // // import 'package:cw_core/monero_transaction_priority.dart'; // import 'package:cw_core/node.dart'; // import 'package:cw_core/pending_transaction.dart'; // import 'package:cw_core/sync_status.dart'; // import 'package:cw_core/transaction_direction.dart'; // import 'package:cw_core/wallet_base.dart'; // import 'package:cw_core/wallet_credentials.dart'; // import 'package:cw_core/wallet_info.dart'; // import 'package:cw_core/wallet_service.dart'; // import 'package:cw_core/wallet_type.dart'; // import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; // import 'package:cw_monero/monero_wallet.dart'; // import 'package:cw_monero/pending_monero_transaction.dart'; // import 'package:decimal/decimal.dart'; // import 'package:flutter/cupertino.dart'; // import 'package:flutter_libmonero/core/key_service.dart'; // import 'package:flutter_libmonero/core/wallet_creation_service.dart'; // import 'package:flutter_libmonero/monero/monero.dart'; // import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output; // import 'package:isar/isar.dart'; // import 'package:mutex/mutex.dart'; // import 'package:stackwallet/db/hive/db.dart'; // import 'package:stackwallet/db/isar/main_db.dart'; // import 'package:stackwallet/models/balance.dart'; // import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; // import 'package:stackwallet/models/node_model.dart'; // import 'package:stackwallet/models/paymint/fee_object_model.dart'; // import 'package:stackwallet/services/coins/coin_service.dart'; // import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart'; // import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; // import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; // import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; // import 'package:stackwallet/services/event_bus/global_event_bus.dart'; // import 'package:stackwallet/services/mixins/wallet_cache.dart'; // import 'package:stackwallet/services/mixins/wallet_db.dart'; // import 'package:stackwallet/services/node_service.dart'; // import 'package:stackwallet/utilities/amount/amount.dart'; // import 'package:stackwallet/utilities/default_nodes.dart'; // import 'package:stackwallet/utilities/enums/coin_enum.dart'; // import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; // import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; // import 'package:stackwallet/utilities/logger.dart'; // import 'package:stackwallet/utilities/prefs.dart'; // import 'package:stackwallet/utilities/stack_file_system.dart'; // import 'package:tuple/tuple.dart'; // // const int MINIMUM_CONFIRMATIONS = 10; // // class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB { // MoneroWallet({ // required String walletId, // required String walletName, // required Coin coin, // required SecureStorageInterface secureStorage, // Prefs? prefs, // MainDB? mockableOverride, // }) { // _walletId = walletId; // _walletName = walletName; // _coin = coin; // _secureStorage = secureStorage; // _prefs = prefs ?? Prefs.instance; // initCache(walletId, coin); // initWalletDB(mockableOverride: mockableOverride); // } // // late final String _walletId; // late final Coin _coin; // late final SecureStorageInterface _secureStorage; // late final Prefs _prefs; // // late String _walletName; // // bool _shouldAutoSync = false; // bool _isConnected = false; // bool _hasCalledExit = false; // bool refreshMutex = false; // bool longMutex = false; // // WalletService? walletService; // KeyService? keysStorage; // MoneroWalletBase? walletBase; // WalletCreationService? _walletCreationService; // Timer? _autoSaveTimer; // // Future get _currentReceivingAddress => // db.getAddresses(walletId).sortByDerivationIndexDesc().findFirst(); // Future? _feeObject; // // Mutex prepareSendMutex = Mutex(); // Mutex estimateFeeMutex = Mutex(); // // @override // set isFavorite(bool markFavorite) { // _isFavorite = markFavorite; // updateCachedIsFavorite(markFavorite); // } // // @override // bool get isFavorite => _isFavorite ??= getCachedIsFavorite(); // // bool? _isFavorite; // // @override // bool get shouldAutoSync => _shouldAutoSync; // // @override // set shouldAutoSync(bool shouldAutoSync) { // if (_shouldAutoSync != shouldAutoSync) { // _shouldAutoSync = shouldAutoSync; // // xmr wallets cannot be open at the same time // // leave following commented out for now // // // if (!shouldAutoSync) { // // timer?.cancel(); // // moneroAutosaveTimer?.cancel(); // // timer = null; // // moneroAutosaveTimer = null; // // stopNetworkAlivePinging(); // // } else { // // startNetworkAlivePinging(); // // // Walletbase needs to be open for this to work // // refresh(); // // } // } // } // // // @override // // String get walletName => _walletName; // // // // // setter for updating on rename // // @override // // set walletName(String newName) => _walletName = newName; // // // // @override // // Coin get coin => _coin; // // // // @override // // Future confirmSend({required Map txData}) async { // // try { // // Logging.instance.log("confirmSend txData: $txData", level: LogLevel.Info); // // final pendingMoneroTransaction = // // txData['pendingMoneroTransaction'] as PendingMoneroTransaction; // // try { // // await pendingMoneroTransaction.commit(); // // Logging.instance.log( // // "transaction ${pendingMoneroTransaction.id} has been sent", // // level: LogLevel.Info); // // return pendingMoneroTransaction.id; // // } catch (e, s) { // // Logging.instance.log("$walletName monero confirmSend: $e\n$s", // // level: LogLevel.Error); // // rethrow; // // } // // } catch (e, s) { // // Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s", // // level: LogLevel.Info); // // rethrow; // // } // // } // // // @override // // Future get currentReceivingAddress async => // // (await _currentReceivingAddress)?.value ?? // // (await _generateAddressForChain(0, 0)).value; // // // // @override // // Future estimateFeeFor(Amount amount, int feeRate) async { // // MoneroTransactionPriority priority; // // // // switch (feeRate) { // // case 1: // // priority = MoneroTransactionPriority.regular; // // break; // // case 2: // // priority = MoneroTransactionPriority.medium; // // break; // // case 3: // // priority = MoneroTransactionPriority.fast; // // break; // // case 4: // // priority = MoneroTransactionPriority.fastest; // // break; // // case 0: // // default: // // priority = MoneroTransactionPriority.slow; // // break; // // } // // // // final fee = walletBase!.calculateEstimatedFee(priority, amount.raw.toInt()); // // // // return Amount(rawValue: BigInt.from(fee), fractionDigits: coin.decimals); // // } // // @override // Future exit() async { // if (!_hasCalledExit) { // walletBase?.onNewBlock = null; // walletBase?.onNewTransaction = null; // walletBase?.syncStatusChanged = null; // _hasCalledExit = true; // _autoSaveTimer?.cancel(); // await walletBase?.save(prioritySave: true); // walletBase?.close(); // } // } // // @override // Future get fees => _feeObject ??= _getFees(); // // @override // Future fullRescan( // int maxUnusedAddressGap, // int maxNumberOfIndexesToCheck, // ) async { // // clear blockchain info // await db.deleteWalletBlockchainData(walletId); // // var restoreHeight = walletBase?.walletInfo.restoreHeight; // highestPercentCached = 0; // await walletBase?.rescan(height: restoreHeight); // await refresh(); // } // // @override // Future generateNewAddress() async { // try { // final currentReceiving = await _currentReceivingAddress; // // final newReceivingIndex = currentReceiving!.derivationIndex + 1; // // // Use new index to derive a new receiving address // final newReceivingAddress = await _generateAddressForChain( // 0, // newReceivingIndex, // ); // // // Add that new receiving address // await db.putAddress(newReceivingAddress); // // return true; // } catch (e, s) { // Logging.instance.log( // "Exception rethrown from generateNewAddress(): $e\n$s", // level: LogLevel.Error); // return false; // } // } // // @override // bool get hasCalledExit => _hasCalledExit; // // @override // Future initializeExisting() async { // Logging.instance.log( // "initializeExisting() ${coin.prettyName} wallet $walletName...", // level: LogLevel.Info, // ); // // if (getCachedId() == null) { // throw Exception( // "Attempted to initialize an existing wallet using an unknown wallet ID!"); // } // // walletService = // monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox); // keysStorage = KeyService(_secureStorage); // // await _prefs.init(); // // // final data = // // DB.instance.get(boxName: walletId, key: "latest_tx_model") // // as TransactionData?; // // if (data != null) { // // _transactionData = Future(() => data); // // } // // String password; // try { // password = await keysStorage!.getWalletPassword(walletName: _walletId); // } catch (_) { // throw Exception("Monero password not found for $walletName"); // } // walletBase = (await walletService!.openWallet(_walletId, password)) // as MoneroWalletBase; // // // await _checkCurrentReceivingAddressesForTransactions(); // // Logging.instance.log( // "Opened existing ${coin.prettyName} wallet $walletName", // level: LogLevel.Info, // ); // } // // @override // Future initializeNew( // ({String mnemonicPassphrase, int wordCount})? data, // ) async { // await _prefs.init(); // // // this should never fail // if ((await mnemonicString) != null || (await mnemonicPassphrase) != null) { // throw Exception( // "Attempted to overwrite mnemonic on generate new wallet!"); // } // // walletService = // monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox); // keysStorage = KeyService(_secureStorage); // WalletInfo walletInfo; // WalletCredentials credentials; // try { // String name = _walletId; // final dirPath = // await _pathForWalletDir(name: name, type: WalletType.monero); // final path = await _pathForWallet(name: name, type: WalletType.monero); // credentials = monero.createMoneroNewWalletCredentials( // name: name, // language: "English", // ); // // // subtract a couple days to ensure we have a buffer for SWB // final bufferedCreateHeight = monero.getHeigthByDate( // date: DateTime.now().subtract(const Duration(days: 2))); // // await DB.instance.put( // boxName: walletId, key: "restoreHeight", value: bufferedCreateHeight); // // walletInfo = WalletInfo.external( // id: WalletBase.idFor(name, WalletType.monero), // name: name, // type: WalletType.monero, // isRecovery: false, // restoreHeight: bufferedCreateHeight, // date: DateTime.now(), // path: path, // dirPath: dirPath, // // TODO: find out what to put for address // address: ''); // credentials.walletInfo = walletInfo; // // _walletCreationService = WalletCreationService( // secureStorage: _secureStorage, // walletService: walletService, // keyService: keysStorage, // ); // _walletCreationService?.changeWalletType(); // // To restore from a seed // final wallet = await _walletCreationService?.create(credentials); // // await _secureStorage.write( // key: '${_walletId}_mnemonic', value: wallet?.seed.trim()); // await _secureStorage.write( // key: '${_walletId}_mnemonicPassphrase', // value: "", // ); // walletInfo.address = wallet?.walletAddresses.address; // await DB.instance // .add(boxName: WalletInfo.boxName, value: walletInfo); // walletBase?.close(); // walletBase = wallet as MoneroWalletBase; // // walletBase!.onNewBlock = onNewBlock; // // walletBase!.onNewTransaction = onNewTransaction; // // walletBase!.syncStatusChanged = syncStatusChanged; // } catch (e, s) { // //todo: come back to this // debugPrint("some nice searchable string thing"); // debugPrint(e.toString()); // debugPrint(s.toString()); // walletBase?.close(); // } // final node = await _getCurrentNode(); // final host = Uri.parse(node.host).host; // await walletBase!.connectToNode( // node: Node( // uri: "$host:${node.port}", // type: WalletType.monero, // trusted: node.trusted ?? false, // ), // ); // await walletBase!.startSync(); // // await Future.wait([ // updateCachedId(walletId), // updateCachedIsFavorite(false), // ]); // // // Generate and add addresses to relevant arrays // final initialReceivingAddress = await _generateAddressForChain(0, 0); // // final initialChangeAddress = await _generateAddressForChain(1, 0); // // await db.putAddress(initialReceivingAddress); // // walletBase?.close(); // Logging.instance // .log("initializeNew for $walletName $walletId", level: LogLevel.Info); // } // // @override // bool get isConnected => _isConnected; // // @override // bool get isRefreshing => refreshMutex; // // @override // // not used in xmr // Future get maxFee => throw UnimplementedError(); // // @override // Future> get mnemonic async { // final _mnemonicString = await mnemonicString; // if (_mnemonicString == null) { // return []; // } // final List data = _mnemonicString.split(' '); // return data; // } // // @override // Future get mnemonicString => // _secureStorage.read(key: '${_walletId}_mnemonic'); // // @override // Future get mnemonicPassphrase => _secureStorage.read( // key: '${_walletId}_mnemonicPassphrase', // ); // // @override // Future> prepareSend({ // required String address, // required Amount amount, // Map? args, // }) async { // String toAddress = address; // try { // final feeRate = args?["feeRate"]; // if (feeRate is FeeRateType) { // MoneroTransactionPriority feePriority; // switch (feeRate) { // case FeeRateType.fast: // feePriority = MoneroTransactionPriority.fast; // break; // case FeeRateType.average: // feePriority = MoneroTransactionPriority.regular; // break; // case FeeRateType.slow: // feePriority = MoneroTransactionPriority.slow; // break; // default: // throw ArgumentError("Invalid use of custom fee"); // } // // Future? awaitPendingTransaction; // try { // // check for send all // bool isSendAll = false; // final balance = await _availableBalance; // if (amount == balance) { // isSendAll = true; // } // Logging.instance // .log("$toAddress $amount $args", level: LogLevel.Info); // String amountToSend = amount.decimal.toString(); // Logging.instance.log("$amount $amountToSend", level: LogLevel.Info); // // monero_output.Output output = monero_output.Output(walletBase!); // output.address = toAddress; // output.sendAll = isSendAll; // output.setCryptoAmount(amountToSend); // // List outputs = [output]; // Object tmp = monero.createMoneroTransactionCreationCredentials( // outputs: outputs, priority: feePriority); // // await prepareSendMutex.protect(() async { // awaitPendingTransaction = walletBase!.createTransaction(tmp); // }); // } catch (e, s) { // Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", // level: LogLevel.Warning); // } // // PendingMoneroTransaction pendingMoneroTransaction = // await (awaitPendingTransaction!) as PendingMoneroTransaction; // // final int realFee = Amount.fromDecimal( // Decimal.parse(pendingMoneroTransaction.feeFormatted), // fractionDigits: coin.decimals, // ).raw.toInt(); // // Map txData = { // "pendingMoneroTransaction": pendingMoneroTransaction, // "fee": realFee, // "addresss": toAddress, // "recipientAmt": amount, // }; // // Logging.instance.log("prepare send: $txData", level: LogLevel.Info); // return txData; // } else { // throw ArgumentError("Invalid fee rate argument provided!"); // } // } catch (e, s) { // Logging.instance.log("Exception rethrown from prepare send(): $e\n$s", // level: LogLevel.Info); // // if (e.toString().contains("Incorrect unlocked balance")) { // throw Exception("Insufficient balance!"); // } else if (e is CreationTransactionException) { // throw Exception("Insufficient funds to pay for transaction fee!"); // } else { // throw Exception("Transaction failed with error code $e"); // } // } // } // // @override // Future recoverFromMnemonic({ // required String mnemonic, // String? mnemonicPassphrase, // not used at the moment // required int maxUnusedAddressGap, // required int maxNumberOfIndexesToCheck, // required int height, // }) async { // await _prefs.init(); // longMutex = true; // final start = DateTime.now(); // try { // // Logging.instance.log("IS_INTEGRATION_TEST: $integrationTestFlag"); // // if (!integrationTestFlag) { // // final features = await electrumXClient.getServerFeatures(); // // Logging.instance.log("features: $features"); // // if (_networkType == BasicNetworkType.main) { // // if (features['genesis_hash'] != GENESIS_HASH_MAINNET) { // // throw Exception("genesis hash does not match main net!"); // // } // // } else if (_networkType == BasicNetworkType.test) { // // if (features['genesis_hash'] != GENESIS_HASH_TESTNET) { // // throw Exception("genesis hash does not match test net!"); // // } // // } // // } // // check to make sure we aren't overwriting a mnemonic // // this should never fail // if ((await mnemonicString) != null || // (await this.mnemonicPassphrase) != null) { // longMutex = false; // throw Exception("Attempted to overwrite mnemonic on restore!"); // } // await _secureStorage.write( // key: '${_walletId}_mnemonic', value: mnemonic.trim()); // await _secureStorage.write( // key: '${_walletId}_mnemonicPassphrase', // value: mnemonicPassphrase ?? "", // ); // // await DB.instance // .put(boxName: walletId, key: "restoreHeight", value: height); // // walletService = // monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox); // keysStorage = KeyService(_secureStorage); // WalletInfo walletInfo; // WalletCredentials credentials; // String name = _walletId; // final dirPath = // await _pathForWalletDir(name: name, type: WalletType.monero); // final path = await _pathForWallet(name: name, type: WalletType.monero); // credentials = monero.createMoneroRestoreWalletFromSeedCredentials( // name: name, // height: height, // mnemonic: mnemonic.trim(), // ); // try { // walletInfo = WalletInfo.external( // id: WalletBase.idFor(name, WalletType.monero), // name: name, // type: WalletType.monero, // isRecovery: false, // restoreHeight: credentials.height ?? 0, // date: DateTime.now(), // path: path, // dirPath: dirPath, // // TODO: find out what to put for address // address: ''); // credentials.walletInfo = walletInfo; // // _walletCreationService = WalletCreationService( // secureStorage: _secureStorage, // walletService: walletService, // keyService: keysStorage, // ); // _walletCreationService!.changeWalletType(); // // To restore from a seed // final wallet = // await _walletCreationService!.restoreFromSeed(credentials); // walletInfo.address = wallet.walletAddresses.address; // await DB.instance // .add(boxName: WalletInfo.boxName, value: walletInfo); // walletBase?.close(); // walletBase = wallet as MoneroWalletBase; // // walletBase!.onNewBlock = onNewBlock; // // walletBase!.onNewTransaction = onNewTransaction; // // walletBase!.syncStatusChanged = syncStatusChanged; // // await Future.wait([ // updateCachedId(walletId), // updateCachedIsFavorite(false), // ]); // } catch (e, s) { // debugPrint(e.toString()); // debugPrint(s.toString()); // } // final node = await _getCurrentNode(); // final host = Uri.parse(node.host).host; // await walletBase!.connectToNode( // node: Node( // uri: "$host:${node.port}", // type: WalletType.monero, // trusted: node.trusted ?? false, // ), // ); // await walletBase!.rescan(height: credentials.height); // walletBase!.close(); // } catch (e, s) { // Logging.instance.log( // "Exception rethrown from recoverFromMnemonic(): $e\n$s", // level: LogLevel.Error); // longMutex = false; // rethrow; // } // longMutex = false; // // final end = DateTime.now(); // Logging.instance.log( // "$walletName Recovery time: ${end.difference(start).inMilliseconds} millis", // level: LogLevel.Info); // } // // @override // Future refresh() async { // if (refreshMutex) { // Logging.instance.log("$walletId $walletName refreshMutex denied", // level: LogLevel.Info); // return; // } else { // refreshMutex = true; // } // // GlobalEventBus.instance.fire( // WalletSyncStatusChangedEvent( // WalletSyncStatus.syncing, // walletId, // coin, // ), // ); // // await _refreshTransactions(); // await _updateBalance(); // // await _checkCurrentReceivingAddressesForTransactions(); // // if (walletBase?.syncStatus is SyncedSyncStatus) { // refreshMutex = false; // GlobalEventBus.instance.fire( // WalletSyncStatusChangedEvent( // WalletSyncStatus.synced, // walletId, // coin, // ), // ); // } // } // // @override // Future testNetworkConnection() async { // return await walletBase?.isConnected() ?? false; // } // // bool _isActive = false; // // @override // void Function(bool)? get onIsActiveWalletChanged => (isActive) async { // if (_isActive == isActive) { // return; // } // _isActive = isActive; // // if (isActive) { // _hasCalledExit = false; // String? password; // try { // password = // await keysStorage?.getWalletPassword(walletName: _walletId); // } catch (e, s) { // throw Exception("Password not found $e, $s"); // } // walletBase = (await walletService?.openWallet(_walletId, password!)) // as MoneroWalletBase?; // // walletBase!.onNewBlock = onNewBlock; // walletBase!.onNewTransaction = onNewTransaction; // walletBase!.syncStatusChanged = syncStatusChanged; // // if (!(await walletBase!.isConnected())) { // final node = await _getCurrentNode(); // final host = Uri.parse(node.host).host; // await walletBase?.connectToNode( // node: Node( // uri: "$host:${node.port}", // type: WalletType.monero, // trusted: node.trusted ?? false, // ), // ); // } // await walletBase?.startSync(); // await refresh(); // _autoSaveTimer?.cancel(); // _autoSaveTimer = Timer.periodic( // const Duration(seconds: 193), // (_) async => await walletBase?.save(), // ); // } else { // await exit(); // // _autoSaveTimer?.cancel(); // // await walletBase?.save(prioritySave: true); // // walletBase?.close(); // } // }; // // Future _updateCachedBalance(int sats) async { // await DB.instance.put( // boxName: walletId, // key: "cachedMoneroBalanceSats", // value: sats, // ); // } // // int _getCachedBalance() => // DB.instance.get( // boxName: walletId, // key: "cachedMoneroBalanceSats", // ) as int? ?? // 0; // // Future _updateBalance() async { // final total = await _totalBalance; // final available = await _availableBalance; // _balance = Balance( // total: total, // spendable: available, // blockedTotal: Amount( // rawValue: BigInt.zero, // fractionDigits: coin.decimals, // ), // pendingSpendable: total - available, // ); // await updateCachedBalance(_balance!); // } // // Future get _availableBalance async { // try { // int runningBalance = 0; // for (final entry in walletBase!.balance!.entries) { // runningBalance += entry.value.unlockedBalance; // } // return Amount( // rawValue: BigInt.from(runningBalance), // fractionDigits: coin.decimals, // ); // } catch (_) { // return Amount( // rawValue: BigInt.zero, // fractionDigits: coin.decimals, // ); // } // } // // Future get _totalBalance async { // try { // final balanceEntries = walletBase?.balance?.entries; // if (balanceEntries != null) { // int bal = 0; // for (var element in balanceEntries) { // bal = bal + element.value.fullBalance; // } // await _updateCachedBalance(bal); // return Amount( // rawValue: BigInt.from(bal), // fractionDigits: coin.decimals, // ); // } else { // final transactions = walletBase!.transactionHistory!.transactions; // int transactionBalance = 0; // for (var tx in transactions!.entries) { // if (tx.value.direction == TransactionDirection.incoming) { // transactionBalance += tx.value.amount!; // } else { // transactionBalance += -tx.value.amount! - tx.value.fee!; // } // } // // await _updateCachedBalance(transactionBalance); // return Amount( // rawValue: BigInt.from(transactionBalance), // fractionDigits: coin.decimals, // ); // } // } catch (_) { // return Amount( // rawValue: BigInt.from(_getCachedBalance()), // fractionDigits: coin.decimals, // ); // } // } // // @override // Future updateNode(bool shouldRefresh) async { // final node = await _getCurrentNode(); // // final host = Uri.parse(node.host).host; // await walletBase?.connectToNode( // node: Node( // uri: "$host:${node.port}", // type: WalletType.monero, // trusted: node.trusted ?? false, // ), // ); // // // TODO: is this sync call needed? Do we need to notify ui here? // await walletBase?.startSync(); // // if (shouldRefresh) { // await refresh(); // } // } // // @override // Future updateSentCachedTxData(Map txData) async { // // not used for xmr // return; // } // // @override // bool validateAddress(String address) => walletBase!.validateAddress(address); // // @override // String get walletId => _walletId; // // Future _generateAddressForChain( // int chain, // int index, // ) async { // // // String address = walletBase!.getTransactionAddress(chain, index); // // if (address.contains("111")) { // return await _generateAddressForChain(chain, index + 1); // } // // return isar_models.Address( // walletId: walletId, // derivationIndex: index, // derivationPath: null, // value: address, // publicKey: [], // type: isar_models.AddressType.cryptonote, // subType: chain == 0 // ? isar_models.AddressSubType.receiving // : isar_models.AddressSubType.change, // ); // } // // Future _getFees() async { // // TODO: not use random hard coded values here // return FeeObject( // numberOfBlocksFast: 10, // numberOfBlocksAverage: 15, // numberOfBlocksSlow: 20, // fast: MoneroTransactionPriority.fast.raw!, // medium: MoneroTransactionPriority.regular.raw!, // slow: MoneroTransactionPriority.slow.raw!, // ); // } // // Future _refreshTransactions() async { // await walletBase!.updateTransactions(); // final transactions = walletBase?.transactionHistory!.transactions; // // // final cachedTransactions = // // DB.instance.get(boxName: walletId, key: 'latest_tx_model') // // as TransactionData?; // // int latestTxnBlockHeight = // // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") // // as int? ?? // // 0; // // // final txidsList = DB.instance // // .get(boxName: walletId, key: "cachedTxids") as List? ?? // // []; // // // // final Set cachedTxids = Set.from(txidsList); // // final List> txnsData = // []; // // if (transactions != null) { // for (var tx in transactions.entries) { // // cachedTxids.add(tx.value.id); // // Logging.instance.log( // // "${tx.value.accountIndex} ${tx.value.addressIndex} ${tx.value.amount} ${tx.value.date} " // // "${tx.value.direction} ${tx.value.fee} ${tx.value.height} ${tx.value.id} ${tx.value.isPending} ${tx.value.key} " // // "${tx.value.recipientAddress}, ${tx.value.additionalInfo} con:${tx.value.confirmations}" // // " ${tx.value.keyIndex}", // // level: LogLevel.Info); // // isar_models.Address? address; // isar_models.TransactionType type; // if (tx.value.direction == TransactionDirection.incoming) { // final addressInfo = tx.value.additionalInfo; // // final addressString = walletBase?.getTransactionAddress( // addressInfo!['accountIndex'] as int, // addressInfo['addressIndex'] as int, // ); // // if (addressString != null) { // address = await db // .getAddresses(walletId) // .filter() // .valueEqualTo(addressString) // .findFirst(); // } // // type = isar_models.TransactionType.incoming; // } else { // // txn.address = ""; // type = isar_models.TransactionType.outgoing; // } // // final txn = isar_models.Transaction( // walletId: walletId, // txid: tx.value.id, // timestamp: (tx.value.date.millisecondsSinceEpoch ~/ 1000), // type: type, // subType: isar_models.TransactionSubType.none, // amount: tx.value.amount ?? 0, // amountString: Amount( // rawValue: BigInt.from(tx.value.amount ?? 0), // fractionDigits: coin.decimals, // ).toJsonString(), // fee: tx.value.fee ?? 0, // height: tx.value.height, // isCancelled: false, // isLelantus: false, // slateId: null, // otherData: null, // nonce: null, // inputs: [], // outputs: [], // numberOfMessages: null, // ); // // txnsData.add(Tuple2(txn, address)); // } // } // // await db.addNewTransactionData(txnsData, walletId); // // // quick hack to notify manager to call notifyListeners if // // transactions changed // if (txnsData.isNotEmpty) { // GlobalEventBus.instance.fire( // UpdatedInBackgroundEvent( // "Transactions updated/added for: $walletId $walletName ", // walletId, // ), // ); // } // } // // Future _pathForWalletDir({ // required String name, // required WalletType type, // }) async { // Directory root = await StackFileSystem.applicationRootDirectory(); // // final prefix = walletTypeToString(type).toLowerCase(); // final walletsDir = Directory('${root.path}/wallets'); // final walletDire = Directory('${walletsDir.path}/$prefix/$name'); // // if (!walletDire.existsSync()) { // walletDire.createSync(recursive: true); // } // // return walletDire.path; // } // // Future _pathForWallet({ // required String name, // required WalletType type, // }) async => // await _pathForWalletDir(name: name, type: type) // .then((path) => '$path/$name'); // // Future _getCurrentNode() async { // return NodeService(secureStorageInterface: _secureStorage) // .getPrimaryNodeFor(coin: coin) ?? // DefaultNodes.getNodeFor(coin); // } // // void onNewBlock({required int height, required int blocksLeft}) { // // // print("============================="); // print("New Block! :: $walletName"); // print("============================="); // updateCachedChainHeight(height); // _refreshTxDataHelper(); // } // // void onNewTransaction() { // // // print("============================="); // print("New Transaction! :: $walletName"); // print("============================="); // // // call this here? // GlobalEventBus.instance.fire( // UpdatedInBackgroundEvent( // "New data found in $walletId $walletName in background!", // walletId, // ), // ); // } // // bool _txRefreshLock = false; // int _lastCheckedHeight = -1; // int _txCount = 0; // // Future _refreshTxDataHelper() async { // if (_txRefreshLock) return; // _txRefreshLock = true; // // final syncStatus = walletBase?.syncStatus; // // if (syncStatus != null && syncStatus is SyncingSyncStatus) { // final int blocksLeft = syncStatus.blocksLeft; // final tenKChange = blocksLeft ~/ 10000; // // // only refresh transactions periodically during a sync // if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { // _lastCheckedHeight = tenKChange; // await _refreshTxData(); // } // } else { // await _refreshTxData(); // } // // _txRefreshLock = false; // } // // Future _refreshTxData() async { // await _refreshTransactions(); // final count = await db.getTransactions(walletId).count(); // // if (count > _txCount) { // _txCount = count; // await _updateBalance(); // GlobalEventBus.instance.fire( // UpdatedInBackgroundEvent( // "New transaction data found in $walletId $walletName!", // walletId, // ), // ); // } // } // // void syncStatusChanged() async { // final syncStatus = walletBase?.syncStatus; // if (syncStatus != null) { // if (syncStatus.progress() == 1) { // refreshMutex = false; // } // // WalletSyncStatus? status; // _isConnected = true; // // if (syncStatus is SyncingSyncStatus) { // final int blocksLeft = syncStatus.blocksLeft; // // // ensure at least 1 to prevent math errors // final int height = max(1, syncStatus.height); // // final nodeHeight = height + blocksLeft; // // final percent = height / nodeHeight; // // final highest = max(highestPercentCached, percent); // // // update cached // if (highestPercentCached < percent) { // highestPercentCached = percent; // } // await updateCachedChainHeight(height); // // GlobalEventBus.instance.fire( // RefreshPercentChangedEvent( // highest, // walletId, // ), // ); // GlobalEventBus.instance.fire( // BlocksRemainingEvent( // blocksLeft, // walletId, // ), // ); // } else if (syncStatus is SyncedSyncStatus) { // status = WalletSyncStatus.synced; // } else if (syncStatus is NotConnectedSyncStatus) { // status = WalletSyncStatus.unableToSync; // _isConnected = false; // } else if (syncStatus is StartingSyncStatus) { // status = WalletSyncStatus.syncing; // GlobalEventBus.instance.fire( // RefreshPercentChangedEvent( // highestPercentCached, // walletId, // ), // ); // } else if (syncStatus is FailedSyncStatus) { // status = WalletSyncStatus.unableToSync; // _isConnected = false; // } else if (syncStatus is ConnectingSyncStatus) { // status = WalletSyncStatus.syncing; // GlobalEventBus.instance.fire( // RefreshPercentChangedEvent( // highestPercentCached, // walletId, // ), // ); // } else if (syncStatus is ConnectedSyncStatus) { // status = WalletSyncStatus.syncing; // GlobalEventBus.instance.fire( // RefreshPercentChangedEvent( // highestPercentCached, // walletId, // ), // ); // } else if (syncStatus is LostConnectionSyncStatus) { // status = WalletSyncStatus.unableToSync; // _isConnected = false; // } // // if (status != null) { // GlobalEventBus.instance.fire( // WalletSyncStatusChangedEvent( // status, // walletId, // coin, // ), // ); // } // } // } // // Future _checkCurrentReceivingAddressesForTransactions() async { // try { // await _checkReceivingAddressForTransactions(); // } catch (e, s) { // Logging.instance.log( // "Exception rethrown from _checkCurrentReceivingAddressesForTransactions(): $e\n$s", // level: LogLevel.Error); // rethrow; // } // } // // Future _checkReceivingAddressForTransactions() async { // try { // int highestIndex = -1; // for (var element // in walletBase!.transactionHistory!.transactions!.entries) { // if (element.value.direction == TransactionDirection.incoming) { // int curAddressIndex = // element.value.additionalInfo!['addressIndex'] as int; // if (curAddressIndex > highestIndex) { // highestIndex = curAddressIndex; // } // } // } // // // Check the new receiving index // final currentReceiving = await _currentReceivingAddress; // final curIndex = currentReceiving?.derivationIndex ?? -1; // // if (highestIndex >= curIndex) { // // First increment the receiving index // final newReceivingIndex = curIndex + 1; // // // Use new index to derive a new receiving address // final newReceivingAddress = // await _generateAddressForChain(0, newReceivingIndex); // // final existing = await db // .getAddresses(walletId) // .filter() // .valueEqualTo(newReceivingAddress.value) // .findFirst(); // if (existing == null) { // // Add that new change address // await db.putAddress(newReceivingAddress); // } else { // // we need to update the address // await db.updateAddress(existing, newReceivingAddress); // // // since we updated an existing address there is a chance it has // // some tx history. To prevent address reuse we will call check again // // recursively // await _checkReceivingAddressForTransactions(); // } // } // } on SocketException catch (se, s) { // Logging.instance.log( // "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", // level: LogLevel.Error); // return; // } catch (e, s) { // Logging.instance.log( // "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", // level: LogLevel.Error); // rethrow; // } // } // // double get highestPercentCached => // DB.instance.get(boxName: walletId, key: "highestPercentCached") // as double? ?? // 0; // // set highestPercentCached(double value) => DB.instance.put( // boxName: walletId, // key: "highestPercentCached", // value: value, // ); // // @override // int get storedChainHeight => getCachedChainHeight(); // // @override // Balance get balance => _balance ??= getCachedBalance(); // Balance? _balance; // // @override // Future> get transactions => // db.getTransactions(walletId).sortByTimestampDesc().findAll(); // // @override // // TODO: implement utxos // Future> get utxos => throw UnimplementedError(); // }