From f319aaf594c03931184809271d8fb54313e30ceb Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 16 Jan 2024 11:58:16 -0600 Subject: [PATCH] various address and SWB fixes, as well as some electrumx_interface unused function cleanup --- lib/pages/pinpad_views/lock_screen_view.dart | 21 +- .../helpers/restore_create_backup.dart | 12 +- .../stack_restore_progress_view.dart | 6 +- .../sub_widgets/favorite_card.dart | 22 +- .../sub_widgets/wallet_list_item.dart | 20 +- .../my_stack_view/coin_wallets_table.dart | 21 +- lib/services/wallets.dart | 44 +-- lib/wallets/wallet/impl/epiccash_wallet.dart | 5 + lib/wallets/wallet/impl/ethereum_wallet.dart | 47 +-- lib/wallets/wallet/impl/stellar_wallet.dart | 5 +- .../impl/sub_wallets/eth_token_wallet.dart | 5 + lib/wallets/wallet/impl/tezos_wallet.dart | 6 +- .../wallet/intermediate/bip39_hd_wallet.dart | 14 + lib/wallets/wallet/wallet.dart | 6 +- .../cw_based_interface.dart | 5 + .../electrumx_interface.dart | 307 +----------------- .../nano_interface.dart | 8 +- lib/widgets/wallet_card.dart | 17 +- 18 files changed, 153 insertions(+), 418 deletions(-) diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index f8b473d5c..e4ecb85a4 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -27,6 +27,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/show_loading.dart'; import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; @@ -98,14 +99,20 @@ class _LockscreenViewState extends ConsumerState { final walletId = widget.routeOnSuccessArguments as String; final wallet = ref.read(pWallets).getWallet(walletId); - if (wallet.info.coin == Coin.monero) { - await showLoading( - opaqueBG: true, - whileFuture: wallet.init(), - context: context, - message: "Loading ${wallet.info.coin.prettyName} wallet...", - ); + final Future loadFuture; + if (wallet is CwBasedInterface) { + loadFuture = + wallet.init().then((value) async => await (wallet).open()); + } else { + loadFuture = wallet.init(); } + + await showLoading( + opaqueBG: true, + whileFuture: loadFuture, + context: context, + message: "Loading ${wallet.info.coin.prettyName} wallet...", + ); } if (mounted) { diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index 275defc05..757868e9a 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -368,7 +368,7 @@ abstract class SWB { return backupJson; } - static Future asyncRestore( + static Future _asyncRestore( Tuple2 tuple, Prefs prefs, NodeService nodeService, @@ -757,7 +757,7 @@ abstract class SWB { )) { return false; } - final bools = await asyncRestore( + final bools = await _asyncRestore( tuple, _prefs, nodeService, @@ -796,10 +796,10 @@ abstract class SWB { } Logging.instance.log("done with SWB restore", level: LogLevel.Warning); - if (Util.isDesktop) { - await Wallets.sharedInstance - .loadAfterStackRestore(_prefs, uiState?.wallets ?? []); - } + + await Wallets.sharedInstance + .loadAfterStackRestore(_prefs, uiState?.wallets ?? [], Util.isDesktop); + return true; } diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart index 3de77e609..147d3a6a6 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart @@ -216,9 +216,9 @@ class _StackRestoreProgressViewState void _addWalletsToHomeView() { ref.read(pWallets).loadAfterStackRestore( - ref.read(prefsChangeNotifierProvider), - ref.read(stackRestoringUIStateProvider).wallets, - ); + ref.read(prefsChangeNotifierProvider), + ref.read(stackRestoringUIStateProvider).wallets, + Util.isDesktop); } @override diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index e3b030fa3..4cbd10140 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -115,17 +115,21 @@ class _FavoriteCardState extends ConsumerState { child: GestureDetector( onTap: () async { final wallet = ref.read(pWallets).getWallet(walletId); - await wallet.init(); + + final Future loadFuture; if (wallet is CwBasedInterface) { - if (mounted) { - await showLoading( - whileFuture: wallet.open(), - context: context, - message: 'Opening ${wallet.info.name}', - isDesktop: Util.isDesktop, - ); - } + loadFuture = + wallet.init().then((value) async => await (wallet).open()); + } else { + loadFuture = wallet.init(); } + await showLoading( + whileFuture: loadFuture, + context: context, + message: 'Opening ${wallet.info.name}', + isDesktop: Util.isDesktop, + ); + if (mounted) { if (Util.isDesktop) { await Navigator.of(context).pushNamed( diff --git a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart index 247694477..b352693ee 100644 --- a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart +++ b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart @@ -63,17 +63,19 @@ class WalletListItem extends ConsumerWidget { .read(pWallets) .wallets .firstWhere((e) => e.info.coin == coin); - await wallet.init(); + final Future loadFuture; if (wallet is CwBasedInterface) { - if (context.mounted) { - await showLoading( - whileFuture: wallet.open(), - context: context, - message: 'Opening ${wallet.info.name}', - isDesktop: Util.isDesktop, - ); - } + loadFuture = + wallet.init().then((value) async => await (wallet).open()); + } else { + loadFuture = wallet.init(); } + await showLoading( + whileFuture: loadFuture, + context: context, + message: 'Opening ${wallet.info.name}', + isDesktop: Util.isDesktop, + ); if (context.mounted) { unawaited( Navigator.of(context).pushNamed( diff --git a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart index 89a9db785..7f138509f 100644 --- a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart +++ b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart @@ -80,17 +80,20 @@ class CoinWalletsTable extends ConsumerWidget { final wallet = ref.read(pWallets).getWallet(walletIds[i]); - await wallet.init(); + final Future loadFuture; if (wallet is CwBasedInterface) { - if (context.mounted) { - await showLoading( - whileFuture: wallet.open(), - context: context, - message: 'Opening ${wallet.info.name}', - isDesktop: Util.isDesktop, - ); - } + loadFuture = wallet + .init() + .then((value) async => await (wallet).open()); + } else { + loadFuture = wallet.init(); } + await showLoading( + whileFuture: loadFuture, + context: context, + message: 'Opening ${wallet.info.name}', + isDesktop: Util.isDesktop, + ); if (context.mounted) { await Navigator.of(context).pushNamed( diff --git a/lib/services/wallets.dart b/lib/services/wallets.dart index c1124b05f..b56a6b090 100644 --- a/lib/services/wallets.dart +++ b/lib/services/wallets.dart @@ -25,6 +25,7 @@ import 'package:stackwallet/utilities/prefs.dart'; import 'package:stackwallet/wallets/isar/models/wallet_info.dart'; import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet.dart'; +import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; class Wallets { Wallets._private(); @@ -192,8 +193,7 @@ class Wallets { final shouldSetAutoSync = shouldAutoSyncAll || walletIdsToEnableAutoSync.contains(walletInfo.walletId); - if (walletInfo.coin == Coin.monero || - walletInfo.coin == Coin.wownero) { + if (wallet is CwBasedInterface) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add(wallet.init().then((_) { @@ -230,6 +230,7 @@ class Wallets { Future loadAfterStackRestore( Prefs prefs, List wallets, + bool isDesktop, ) async { List> walletInitFutures = []; List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = []; @@ -259,15 +260,16 @@ class Wallets { 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; - } - })); + if (isDesktop) { + if (wallet is CwBasedInterface) { + // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); + } else { + walletInitFutures.add(wallet.init().then((value) { + if (shouldSetAutoSync) { + wallet.shouldAutoSync = true; + } + })); + } } _wallets[wallet.walletId] = wallet; @@ -278,15 +280,17 @@ class Wallets { } } - 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); + if (isDesktop) { + 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); + } } } diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart index fba66845c..3ef3e1026 100644 --- a/lib/wallets/wallet/impl/epiccash_wallet.dart +++ b/lib/wallets/wallet/impl/epiccash_wallet.dart @@ -484,6 +484,11 @@ class EpiccashWallet extends Bip39Wallet { FilterOperation? get receivingAddressFilterOperation => FilterGroup.and(standardReceivingAddressFilters); + @override + Future checkSaveInitialReceivingAddress() async { + // epiccash seems ok with nothing here? + } + @override Future init({bool? isRestore}) async { if (isRestore != true) { diff --git a/lib/wallets/wallet/impl/ethereum_wallet.dart b/lib/wallets/wallet/impl/ethereum_wallet.dart index 2089e06e7..0037ff1d5 100644 --- a/lib/wallets/wallet/impl/ethereum_wallet.dart +++ b/lib/wallets/wallet/impl/ethereum_wallet.dart @@ -86,25 +86,6 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { _credentials = web3.EthPrivateKey.fromHex(privateKey); } - Future _generateAndSaveAddress( - String mnemonic, - String mnemonicPassphrase, - ) async { - await _initCredentials(mnemonic, mnemonicPassphrase); - - final address = Address( - walletId: walletId, - value: _credentials.address.hexEip55, - publicKey: [], // maybe store address bytes here? seems a waste of space though - derivationIndex: 0, - derivationPath: DerivationPath()..value = "$hdPathEthereum/0", - type: AddressType.ethereum, - subType: AddressSubType.receiving, - ); - - await mainDB.updateOrPutAddresses([address]); - } - // ==================== Overrides ============================================ @override @@ -127,15 +108,27 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { FilterGroup.and(standardReceivingAddressFilters); @override - Future init({bool? isRestore}) async { + Future checkSaveInitialReceivingAddress() async { final address = await getCurrentReceivingAddress(); if (address == null) { - await _generateAndSaveAddress( + await _initCredentials( await getMnemonic(), await getMnemonicPassphrase(), ); + + final address = Address( + walletId: walletId, + value: _credentials.address.hexEip55, + publicKey: [], + // maybe store address bytes here? seems a waste of space though + derivationIndex: 0, + derivationPath: DerivationPath()..value = "$hdPathEthereum/0", + type: AddressType.ethereum, + subType: AddressSubType.receiving, + ); + + await mainDB.updateOrPutAddresses([address]); } - return super.init(); } @override @@ -475,17 +468,11 @@ class EthereumWallet extends Bip39Wallet with PrivateKeyInterface { await refreshMutex.protect(() async { if (isRescan) { await mainDB.deleteWalletBlockchainData(walletId); - await _generateAndSaveAddress( - await getMnemonic(), - await getMnemonicPassphrase(), - ); + await checkSaveInitialReceivingAddress(); await updateBalance(); await updateTransactions(isRescan: true); } else { - await _generateAndSaveAddress( - await getMnemonic(), - await getMnemonicPassphrase(), - ); + await checkSaveInitialReceivingAddress(); unawaited(updateBalance()); unawaited(updateTransactions()); } diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index 06a2a638a..d078dd98e 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -111,7 +111,7 @@ class StellarWallet extends Bip39Wallet { FilterGroup.and(standardReceivingAddressFilters); @override - Future init() async { + Future checkSaveInitialReceivingAddress() async { try { final address = await getCurrentReceivingAddress(); if (address == null) { @@ -121,11 +121,10 @@ class StellarWallet extends Bip39Wallet { } catch (e, s) { // do nothing, still allow user into wallet Logging.instance.log( - "$runtimeType init() failed: $e\n$s", + "$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s", level: LogLevel.Error, ); } - return super.init(); } @override diff --git a/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart b/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart index 573ef5608..8244c5fd9 100644 --- a/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart +++ b/lib/wallets/wallet/impl/sub_wallets/eth_token_wallet.dart @@ -527,4 +527,9 @@ class EthTokenWallet extends Wallet { value: TransactionSubType.ethToken, ), ]); + + @override + Future checkSaveInitialReceivingAddress() async { + await ethWallet.checkSaveInitialReceivingAddress(); + } } diff --git a/lib/wallets/wallet/impl/tezos_wallet.dart b/lib/wallets/wallet/impl/tezos_wallet.dart index fa48d70af..1bde36803 100644 --- a/lib/wallets/wallet/impl/tezos_wallet.dart +++ b/lib/wallets/wallet/impl/tezos_wallet.dart @@ -145,7 +145,7 @@ class TezosWallet extends Bip39Wallet { // =========================================================================== @override - Future init() async { + Future checkSaveInitialReceivingAddress() async { try { final _address = await getCurrentReceivingAddress(); if (_address == null) { @@ -155,12 +155,10 @@ class TezosWallet extends Bip39Wallet { } catch (e, s) { // do nothing, still allow user into wallet Logging.instance.log( - "$runtimeType init() failed: $e\n$s", + "$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s", level: LogLevel.Error, ); } - - await super.init(); } @override diff --git a/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart b/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart index 7bb795016..8d352811b 100644 --- a/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart +++ b/lib/wallets/wallet/intermediate/bip39_hd_wallet.dart @@ -61,6 +61,20 @@ abstract class Bip39HDWallet extends Bip39Wallet await mainDB.updateOrPutAddresses([address]); } + @override + Future checkSaveInitialReceivingAddress() async { + final current = await getCurrentChangeAddress(); + if (current == null) { + final address = await _generateAddress( + chain: 0, // receiving + index: 0, // initial index + derivePathType: DerivePathTypeExt.primaryFor(info.coin), + ); + + await mainDB.updateOrPutAddresses([address]); + } + } + // ========== Subclasses may override ======================================== /// To be overridden by crypto currencies that do extra address conversions diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 1059b12cf..26f557dba 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -435,6 +435,8 @@ abstract class Wallet { Future pingCheck(); + Future checkSaveInitialReceivingAddress(); + //=========================================== /// add transaction to local db temporarily. Used for quickly updating ui /// before refresh can fetch data from server @@ -600,6 +602,7 @@ abstract class Wallet { @mustCallSuper Future init() async { + await checkSaveInitialReceivingAddress(); final address = await getCurrentReceivingAddress(); if (address != null) { await info.updateReceivingAddress( @@ -607,9 +610,6 @@ abstract class Wallet { isar: mainDB.isar, ); } - - // TODO: make sure subclasses override this if they require some set up - // especially xmr/wow/epiccash } // =========================================================================== diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart index f6bfbdcf2..19faf7465 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart @@ -179,6 +179,11 @@ mixin CwBasedInterface on CryptonoteWallet } } + @override + Future checkSaveInitialReceivingAddress() async { + // this doesn't work without opening the wallet first which takes a while + } + // ============ Interface ==================================================== Future get availableBalance; diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index 17a1b49de..4257c0f7c 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -1,11 +1,9 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:math'; import 'package:bip47/src/util.dart'; import 'package:bitcoindart/bitcoindart.dart' as bitcoindart; import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib; -import 'package:decimal/decimal.dart'; import 'package:isar/isar.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart'; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart'; @@ -831,55 +829,6 @@ mixin ElectrumXInterface on Bip39HDWallet { } } - Future> - fetchTransactionsV1({ - required List
addresses, - required int currentChainHeight, - }) async { - final List<({String txHash, int height, String address})> allTxHashes = - (await fetchHistory(addresses.map((e) => e.value).toList())) - .map( - (e) => ( - txHash: e["tx_hash"] as String, - height: e["height"] as int, - address: e["address"] as String, - ), - ) - .toList(); - - List> allTransactions = []; - - for (final data in allTxHashes) { - final tx = await electrumXCachedClient.getTransaction( - txHash: data.txHash, - verbose: true, - coin: cryptoCurrency.coin, - ); - - // check for duplicates before adding to list - if (allTransactions - .indexWhere((e) => e["txid"] == tx["txid"] as String) == - -1) { - tx["address"] = addresses.firstWhere((e) => e.value == data.address); - tx["height"] = data.height; - allTransactions.add(tx); - } - } - - final List<({Transaction transaction, Address address})> txnsData = []; - - for (final txObject in allTransactions) { - final data = await _parseTransactionV1( - txObject, - addresses, - ); - - txnsData.add(data); - } - - return txnsData; - } - Future getCurrentElectrumXNode() async { final node = getCurrentNode(); @@ -1185,257 +1134,6 @@ mixin ElectrumXInterface on Bip39HDWallet { return utxo; } - Future<({Transaction transaction, Address address})> _parseTransactionV1( - Map txData, - List
myAddresses, - ) async { - Set receivingAddresses = myAddresses - .where((e) => - e.subType == AddressSubType.receiving || - e.subType == AddressSubType.paynymReceive || - e.subType == AddressSubType.paynymNotification) - .map((e) => e.value) - .toSet(); - Set changeAddresses = myAddresses - .where((e) => e.subType == AddressSubType.change) - .map((e) => e.value) - .toSet(); - - Set inputAddresses = {}; - Set outputAddresses = {}; - - Amount totalInputValue = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.coin.decimals, - ); - Amount totalOutputValue = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.coin.decimals, - ); - - Amount amountSentFromWallet = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.coin.decimals, - ); - Amount amountReceivedInWallet = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.coin.decimals, - ); - Amount changeAmount = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.coin.decimals, - ); - - // parse inputs - for (final input in txData["vin"] as List) { - final prevTxid = input["txid"] as String; - final prevOut = input["vout"] as int; - - // fetch input tx to get address - final inputTx = await electrumXCachedClient.getTransaction( - txHash: prevTxid, - coin: cryptoCurrency.coin, - ); - - for (final output in inputTx["vout"] as List) { - // check matching output - if (prevOut == output["n"]) { - // get value - final value = Amount.fromDecimal( - Decimal.parse(output["value"].toString()), - fractionDigits: cryptoCurrency.coin.decimals, - ); - - // add value to total - totalInputValue += value; - - // get input(prevOut) address - final address = output["scriptPubKey"]?["addresses"]?[0] as String? ?? - output["scriptPubKey"]?["address"] as String?; - - if (address != null) { - inputAddresses.add(address); - - // if input was from my wallet, add value to amount sent - if (receivingAddresses.contains(address) || - changeAddresses.contains(address)) { - amountSentFromWallet += value; - } - } - } - } - } - - // parse outputs - for (final output in txData["vout"] as List) { - // get value - final value = Amount.fromDecimal( - Decimal.parse(output["value"].toString()), - fractionDigits: cryptoCurrency.coin.decimals, - ); - - // add value to total - totalOutputValue += value; - - // get output address - final address = output["scriptPubKey"]?["addresses"]?[0] as String? ?? - output["scriptPubKey"]?["address"] as String?; - if (address != null) { - outputAddresses.add(address); - - // if output was to my wallet, add value to amount received - if (receivingAddresses.contains(address)) { - amountReceivedInWallet += value; - } else if (changeAddresses.contains(address)) { - changeAmount += value; - } - } - } - - final mySentFromAddresses = [ - ...receivingAddresses.intersection(inputAddresses), - ...changeAddresses.intersection(inputAddresses) - ]; - final myReceivedOnAddresses = - receivingAddresses.intersection(outputAddresses); - final myChangeReceivedOnAddresses = - changeAddresses.intersection(outputAddresses); - - final fee = totalInputValue - totalOutputValue; - - // this is the address initially used to fetch the txid - Address transactionAddress = txData["address"] as Address; - - TransactionType type; - Amount amount; - if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) { - // tx is sent to self - type = TransactionType.sentToSelf; - - // should be 0 - amount = - amountSentFromWallet - amountReceivedInWallet - fee - changeAmount; - } else if (mySentFromAddresses.isNotEmpty) { - // outgoing tx - type = TransactionType.outgoing; - amount = amountSentFromWallet - changeAmount - fee; - - // non wallet addresses found in tx outputs - final nonWalletOutAddresses = outputAddresses.difference( - myChangeReceivedOnAddresses, - ); - - if (nonWalletOutAddresses.isNotEmpty) { - final possible = nonWalletOutAddresses.first; - - if (transactionAddress.value != possible) { - transactionAddress = Address( - walletId: myAddresses.first.walletId, - value: possible, - derivationIndex: -1, - derivationPath: null, - subType: AddressSubType.nonWallet, - type: AddressType.nonWallet, - publicKey: [], - ); - } - } else { - // some other type of tx where the receiving address is - // one of my change addresses - - type = TransactionType.sentToSelf; - amount = changeAmount; - } - } else { - // incoming tx - type = TransactionType.incoming; - amount = amountReceivedInWallet; - } - - List outs = []; - List ins = []; - - for (final json in txData["vin"] as List) { - bool isCoinBase = json['coinbase'] != null; - String? witness; - if (json['witness'] != null && json['witness'] is String) { - witness = json['witness'] as String; - } else if (json['txinwitness'] != null) { - if (json['txinwitness'] is List) { - witness = jsonEncode(json['txinwitness']); - } - } - final input = Input( - txid: json['txid'] as String, - vout: json['vout'] as int? ?? -1, - scriptSig: json['scriptSig']?['hex'] as String?, - scriptSigAsm: json['scriptSig']?['asm'] as String?, - isCoinbase: isCoinBase ? isCoinBase : json['is_coinbase'] as bool?, - sequence: json['sequence'] as int?, - innerRedeemScriptAsm: json['innerRedeemscriptAsm'] as String?, - witness: witness, - ); - ins.add(input); - } - - for (final json in txData["vout"] as List) { - final output = Output( - scriptPubKey: json['scriptPubKey']?['hex'] as String?, - scriptPubKeyAsm: json['scriptPubKey']?['asm'] as String?, - scriptPubKeyType: json['scriptPubKey']?['type'] as String?, - scriptPubKeyAddress: - json["scriptPubKey"]?["addresses"]?[0] as String? ?? - json['scriptPubKey']?['type'] as String? ?? - "", - value: Amount.fromDecimal( - Decimal.parse(json["value"].toString()), - fractionDigits: cryptoCurrency.coin.decimals, - ).raw.toInt(), - ); - outs.add(output); - } - - TransactionSubType txSubType = TransactionSubType.none; - if (this is PaynymInterface && outs.length > 1 && ins.isNotEmpty) { - for (int i = 0; i < outs.length; i++) { - List? scriptChunks = outs[i].scriptPubKeyAsm?.split(" "); - if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") { - final blindedPaymentCode = scriptChunks![1]; - final bytes = blindedPaymentCode.fromHex; - - // https://en.bitcoin.it/wiki/BIP_0047#Sending - if (bytes.length == 80 && bytes.first == 1) { - txSubType = TransactionSubType.bip47Notification; - } - } - } - } - - final tx = Transaction( - walletId: myAddresses.first.walletId, - txid: txData["txid"] as String, - timestamp: txData["blocktime"] as int? ?? - (DateTime.now().millisecondsSinceEpoch ~/ 1000), - type: type, - subType: txSubType, - // amount may overflow. Deprecated. Use amountString - amount: amount.raw.toInt(), - amountString: amount.toJsonString(), - fee: fee.raw.toInt(), - height: txData["height"] as int?, - isCancelled: false, - isLelantus: false, - slateId: null, - otherData: null, - nonce: null, - inputs: ins, - outputs: outs, - numberOfMessages: null, - ); - - return (transaction: tx, address: transactionAddress); - } - //============================================================================ @override @@ -1993,12 +1691,15 @@ mixin ElectrumXInterface on Bip39HDWallet { } } + @override + Future checkSaveInitialReceivingAddress() async {} + @override Future init() async { try { final features = await electrumXClient .getServerFeatures() - .timeout(const Duration(seconds: 2)); + .timeout(const Duration(seconds: 4)); Logging.instance.log("features: $features", level: LogLevel.Info); diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart index b0cfd72b2..d655b3e97 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart @@ -315,22 +315,20 @@ mixin NanoInterface on Bip39Wallet { } @override - Future init() async { + Future checkSaveInitialReceivingAddress() async { try { _cachedAddress = await getCurrentReceivingAddress(); if (_cachedAddress == null) { _cachedAddress = await _getAddressFromMnemonic(); - await mainDB.putAddress(_cachedAddress!); + await mainDB.updateOrPutAddresses([_cachedAddress!]); } } catch (e, s) { // do nothing, still allow user into wallet Logging.instance.log( - "$runtimeType init() failed: $e\n$s", + "$runtimeType checkSaveInitialReceivingAddress() failed: $e\n$s", level: LogLevel.Error, ); } - - return super.init(); } @override diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index 4225d7c39..bc1c80aa4 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -93,17 +93,20 @@ class SimpleWalletCard extends ConsumerWidget { final nav = Navigator.of(context); final wallet = ref.read(pWallets).getWallet(walletId); - await wallet.init(); if (context.mounted) { + final Future loadFuture; if (wallet is CwBasedInterface) { - await showLoading( - whileFuture: wallet.open(), - context: context, - message: 'Opening ${wallet.info.name}', - isDesktop: Util.isDesktop, - ); + loadFuture = wallet.init().then((value) async => await (wallet).open()); + } else { + loadFuture = wallet.init(); } + await showLoading( + whileFuture: loadFuture, + context: context, + message: 'Opening ${wallet.info.name}', + isDesktop: Util.isDesktop, + ); if (popPrevious) nav.pop(); if (desktopNavigatorState != null) {