diff --git a/crypto_plugins/flutter_libepiccash b/crypto_plugins/flutter_libepiccash index 588281a52..9a150d8cd 160000 --- a/crypto_plugins/flutter_libepiccash +++ b/crypto_plugins/flutter_libepiccash @@ -1 +1 @@ -Subproject commit 588281a5280a62fff624e6a96cbe5dc2e2154245 +Subproject commit 9a150d8cd2c3625424b0059e6b7306f3659fdbe0 diff --git a/crypto_plugins/flutter_liblelantus b/crypto_plugins/flutter_liblelantus index 624204621..b1e0b2062 160000 --- a/crypto_plugins/flutter_liblelantus +++ b/crypto_plugins/flutter_liblelantus @@ -1 +1 @@ -Subproject commit 6242046217abf47b61d9397ae447632b06f853fa +Subproject commit b1e0b20621be3ebb280ab3e3de10afe0c11db073 diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index cd6f9cf62..4bffa40cb 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit cd6f9cf62afcb6c1e55b16a76374a8577d85352f +Subproject commit 4bffa40cb60ad3d98cf0ea5b5d819f3f4895dcd6 diff --git a/lib/main.dart b/lib/main.dart index 10067ac19..5aeb89508 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -589,9 +589,16 @@ class _MaterialAppWithThemeState extends ConsumerState _loadChangeNowData(); } - return const LockscreenView( + String? startupWalletId; + if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) { + startupWalletId = + ref.read(prefsChangeNotifierProvider).startupWalletId; + } + + return LockscreenView( isInitialAppLogin: true, routeOnSuccess: HomeView.routeName, + routeOnSuccessArguments: startupWalletId, biometricsAuthenticationTitle: "Unlock Stack", biometricsLocalizedReason: "Unlock your stack wallet using biometrics", diff --git a/lib/pages/exchange_view/exchange_loading_overlay.dart b/lib/pages/exchange_view/exchange_loading_overlay.dart index 9b8e07d7c..28484022a 100644 --- a/lib/pages/exchange_view/exchange_loading_overlay.dart +++ b/lib/pages/exchange_view/exchange_loading_overlay.dart @@ -1,20 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; -import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart'; -import 'package:stackwallet/providers/exchange/change_now_provider.dart'; import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; -import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; class ExchangeLoadingOverlayView extends ConsumerStatefulWidget { - const ExchangeLoadingOverlayView({Key? key}) : super(key: key); + const ExchangeLoadingOverlayView({ + Key? key, + required this.unawaitedLoad, + }) : super(key: key); + + final VoidCallback unawaitedLoad; @override ConsumerState createState() => @@ -28,103 +26,6 @@ class _ExchangeLoadingOverlayViewState bool userReloaded = false; - Future _loadFixedRateMarkets() async { - if (ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state == - ChangeNowLoadStatus.loading) { - // already in progress so just - return; - } - - ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.loading; - - final response3 = - await ref.read(changeNowProvider).getAvailableFixedRateMarkets(); - if (response3.value != null) { - ref.read(fixedRateMarketPairsStateProvider.state).state = - response3.value!; - - if (ref.read(fixedRateExchangeFormProvider).market == null) { - final matchingMarkets = - response3.value!.where((e) => e.to == "doge" && e.from == "btc"); - if (matchingMarkets.isNotEmpty) { - await ref - .read(fixedRateExchangeFormProvider) - .updateMarket(matchingMarkets.first, true); - } - } - } else { - Logging.instance.log( - "Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}", - level: LogLevel.Error); - - ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.failed; - return; - } - - ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.success; - } - - Future _loadChangeNowStandardCurrencies() async { - if (ref - .read(changeNowEstimatedInitialLoadStatusStateProvider.state) - .state == - ChangeNowLoadStatus.loading) { - // already in progress so just - return; - } - - ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.loading; - - final response = await ref.read(changeNowProvider).getAvailableCurrencies(); - final response2 = - await ref.read(changeNowProvider).getAvailableFloatingRatePairs(); - if (response.value != null) { - ref.read(availableChangeNowCurrenciesStateProvider.state).state = - response.value!; - if (response2.value != null) { - ref.read(availableFloatingRatePairsStateProvider.state).state = - response2.value!; - - if (response.value!.length > 1) { - if (ref.read(estimatedRateExchangeFormProvider).from == null) { - if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateFrom( - response.value!.firstWhere((e) => e.ticker == "btc"), false); - } - } - if (ref.read(estimatedRateExchangeFormProvider).to == null) { - if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateTo( - response.value!.firstWhere((e) => e.ticker == "doge"), false); - } - } - } - } else { - Logging.instance.log( - "Failed to load changeNOW available floating rate pairs: ${response2.exception?.errorMessage}", - level: LogLevel.Error); - ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.failed; - return; - } - } else { - Logging.instance.log( - "Failed to load changeNOW currencies: ${response.exception?.errorMessage}", - level: LogLevel.Error); - await Future.delayed(const Duration(seconds: 3)); - ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.failed; - return; - } - - ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = - ChangeNowLoadStatus.success; - } - @override void initState() { _statusEst = @@ -168,8 +69,10 @@ class _ExchangeLoadingOverlayViewState child: const CustomLoadingOverlay( message: "Loading ChangeNOW data", eventBus: null), ), - if (_statusEst == ChangeNowLoadStatus.failed || - _statusFixed == ChangeNowLoadStatus.failed) + if ((_statusEst == ChangeNowLoadStatus.failed || + _statusFixed == ChangeNowLoadStatus.failed) && + _statusEst != ChangeNowLoadStatus.loading && + _statusFixed != ChangeNowLoadStatus.loading) Container( color: CFColors.stackAccent.withOpacity(0.7), child: Column( @@ -186,13 +89,8 @@ class _ExchangeLoadingOverlayViewState .copyWith(color: CFColors.stackAccent), ), onPressed: () { - if (_statusEst == ChangeNowLoadStatus.failed) { - _loadChangeNowStandardCurrencies(); - } - if (_statusFixed == ChangeNowLoadStatus.failed) { - userReloaded = true; - _loadFixedRateMarkets(); - } + userReloaded = true; + widget.unawaitedLoad(); }, ), ), diff --git a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart index 74519e379..551f8b5d9 100644 --- a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart +++ b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart @@ -29,7 +29,6 @@ import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; -import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; @@ -1272,55 +1271,55 @@ class _WalletInitiatedExchangeViewState .read(estimatedRateExchangeFormProvider) .fromAmountString); - if (ft.toLowerCase() == - coin.ticker.toLowerCase()) { - bool shouldPop = false; - bool wasPopped = false; - unawaited(showDialog( - context: context, - builder: (_) => WillPopScope( - onWillPop: () async { - if (shouldPop) { - wasPopped = true; - } - return shouldPop; - }, - child: const CustomLoadingOverlay( - message: "Checking available balance", - eventBus: null, - ), - ), - )); - - final availableBalance = - await manager.availableBalance; - - final feeObject = await manager.fees; - - final fee = await manager.estimateFeeFor( - Format.decimalAmountToSatoshis( - sendAmount), - feeObject.medium); - - shouldPop = true; - if (!wasPopped && mounted) { - Navigator.of(context).pop(); - } - - if (availableBalance < - sendAmount + - Format.satoshisToAmount(fee)) { - unawaited(showDialog( - context: context, - builder: (_) => StackOkDialog( - title: "Insufficient balance", - message: - "Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade", - ), - )); - return; - } - } + // if (ft.toLowerCase() == + // coin.ticker.toLowerCase()) { + // bool shouldPop = false; + // bool wasPopped = false; + // unawaited(showDialog( + // context: context, + // builder: (_) => WillPopScope( + // onWillPop: () async { + // if (shouldPop) { + // wasPopped = true; + // } + // return shouldPop; + // }, + // child: const CustomLoadingOverlay( + // message: "Checking available balance", + // eventBus: null, + // ), + // ), + // )); + // + // final availableBalance = + // await manager.availableBalance; + // + // final feeObject = await manager.fees; + // + // final fee = await manager.estimateFeeFor( + // Format.decimalAmountToSatoshis( + // sendAmount), + // feeObject.medium); + // + // shouldPop = true; + // if (!wasPopped && mounted) { + // Navigator.of(context).pop(); + // } + // + // if (availableBalance < + // sendAmount + + // Format.satoshisToAmount(fee)) { + // unawaited(showDialog( + // context: context, + // builder: (_) => StackOkDialog( + // title: "Insufficient balance", + // message: + // "Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade", + // ), + // )); + // return; + // } + // } if (isEstimated) { final fromTicker = ref diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index 32388e07c..669561adf 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -13,6 +13,7 @@ import 'package:stackwallet/pages/wallets_view/wallets_view.dart'; import 'package:stackwallet/providers/global/notifications_provider.dart'; import 'package:stackwallet/providers/ui/home_view_index_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; +import 'package:stackwallet/services/change_now/change_now_loading_service.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/constants.dart'; @@ -40,7 +41,16 @@ class _HomeViewState extends ConsumerState { bool _exitEnabled = false; + final _cnLoadingService = ChangeNowLoadingService(); + Future _onWillPop() async { + + // go to home view when tapping back on the main exchange view + if (ref.read(homeViewPageIndexStateProvider.state).state == 1) { + ref.read(homeViewPageIndexStateProvider.state).state = 0; + return false; + } + if (_exitEnabled) { return true; } @@ -70,6 +80,11 @@ class _HomeViewState extends ConsumerState { return _exitEnabled; } + void _loadCNData() { + // unawaited future + _cnLoadingService.loadAll(ref); + } + @override void initState() { _pageController = PageController(); @@ -77,9 +92,11 @@ class _HomeViewState extends ConsumerState { const WalletsView(), if (Constants.enableExchange) Stack( - children: const [ - ExchangeView(), - ExchangeLoadingOverlayView(), + children: [ + const ExchangeView(), + ExchangeLoadingOverlayView( + unawaitedLoad: _loadCNData, + ), ], ), // const BuyView(), diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index b22cebb9b..03dd5bb26 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -1,9 +1,14 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/home_view/home_view.dart'; +import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; // import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; // import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/biometrics.dart'; @@ -15,6 +20,7 @@ import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart'; import 'package:stackwallet/widgets/shake/shake.dart'; +import 'package:tuple/tuple.dart'; class LockscreenView extends ConsumerStatefulWidget { const LockscreenView({ @@ -75,10 +81,20 @@ class _LockscreenViewState extends ConsumerState { if (widget.popOnSuccess) { Navigator.of(context).pop(widget.routeOnSuccessArguments); } else { - Navigator.of(context).pushReplacementNamed( + unawaited(Navigator.of(context).pushReplacementNamed( widget.routeOnSuccess, arguments: widget.routeOnSuccessArguments, - ); + )); + if (widget.routeOnSuccess == HomeView.routeName && + widget.routeOnSuccessArguments is String) { + final walletId = widget.routeOnSuccessArguments as String; + unawaited(Navigator.of(context).pushNamed(WalletView.routeName, + arguments: Tuple2( + walletId, + ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(walletId)))); + } } } @@ -105,7 +121,7 @@ class _LockscreenViewState extends ConsumerState { // await walletsService.getWalletId(currentWalletName)); // } - _onUnlock(); + unawaited(_onUnlock()); } // leave this commented to enable pin fall back should biometrics not work properly // else { @@ -250,10 +266,10 @@ class _LockscreenViewState extends ConsumerState { _timeout = const Duration(minutes: 60); } - Future.delayed(_timeout).then((_) { + unawaited(Future.delayed(_timeout).then((_) { _attemptLock = false; _attempts = 0; - }); + })); } if (_attemptLock) { @@ -264,13 +280,13 @@ class _LockscreenViewState extends ConsumerState { prettyTime += "${_timeout.inSeconds} seconds"; } - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Incorrect PIN entered too many times. Please wait $prettyTime", context: context, iconAsset: Assets.svg.alertCircle, - ); + )); await Future.delayed( const Duration(milliseconds: 100)); @@ -286,15 +302,15 @@ class _LockscreenViewState extends ConsumerState { if (storedPin == pin) { await Future.delayed( const Duration(milliseconds: 200)); - _onUnlock(); + unawaited(_onUnlock()); } else { - _shakeController.shake(); - showFloatingFlushBar( + unawaited(_shakeController.shake()); + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Incorrect PIN. Please try again", context: context, iconAsset: Assets.svg.alertCircle, - ); + )); await Future.delayed( const Duration(milliseconds: 100)); diff --git a/lib/pages/settings_views/global_settings_view/about_view.dart b/lib/pages/settings_views/global_settings_view/about_view.dart index 765145c34..f1043866d 100644 --- a/lib/pages/settings_views/global_settings_view/about_view.dart +++ b/lib/pages/settings_views/global_settings_view/about_view.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -8,6 +11,85 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS; +import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS; +import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS; + +import 'package:stackwallet/utilities/logger.dart'; + +const kGithubAPI = "https://api.github.com"; +const kGithubSearch = "/search/commits"; +const kGithubHead = "/repos"; + +enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded } + +Future doesCommitExist( + String organization, + String project, + String commit, +) async { + Logging.instance.log("doesCommitExist", level: LogLevel.Info); + final Client client = Client(); + try { + final uri = Uri.parse( + "$kGithubAPI$kGithubHead/$organization/$project/commits/$commit"); + + final commitQuery = await client.get( + uri, + headers: {'Content-Type': 'application/json'}, + ); + + final response = jsonDecode(commitQuery.body.toString()); + Logging.instance.log("doesCommitExist $project $commit $response", + level: LogLevel.Info); + bool isThereCommit; + try { + isThereCommit = response['sha'] == commit; + Logging.instance + .log("isThereCommit $isThereCommit", level: LogLevel.Info); + return isThereCommit; + } catch (e, s) { + return false; + } + } catch (e, s) { + Logging.instance.log("$e $s", level: LogLevel.Error); + return false; + } +} + +Future isHeadCommit( + String organization, + String project, + String branch, + String commit, +) async { + Logging.instance.log("doesCommitExist", level: LogLevel.Info); + final Client client = Client(); + try { + final uri = Uri.parse( + "$kGithubAPI$kGithubHead/$organization/$project/commits/$branch"); + + final commitQuery = await client.get( + uri, + headers: {'Content-Type': 'application/json'}, + ); + + final response = jsonDecode(commitQuery.body.toString()); + Logging.instance.log("isHeadCommit $project $commit $branch $response", + level: LogLevel.Info); + bool isHead; + try { + isHead = response['sha'] == commit; + Logging.instance.log("isHead $isHead", level: LogLevel.Info); + return isHead; + } catch (e, s) { + return false; + } + } catch (e, s) { + Logging.instance.log("$e $s", level: LogLevel.Error); + return false; + } +} class AboutView extends ConsumerWidget { const AboutView({Key? key}) : super(key: key); @@ -16,6 +98,26 @@ class AboutView extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + String firoCommit = FIRO_VERSIONS.getPluginVersion(); + String epicCashCommit = EPIC_VERSIONS.getPluginVersion(); + String moneroCommit = MONERO_VERSIONS.getPluginVersion(); + List futureFiroList = [ + doesCommitExist("cypherstack", "flutter_liblelantus", firoCommit), + isHeadCommit("cypherstack", "flutter_liblelantus", "main", firoCommit), + ]; + Future commitFiroFuture = Future.wait(futureFiroList); + List futureEpicList = [ + doesCommitExist("cypherstack", "flutter_libepiccash", epicCashCommit), + isHeadCommit( + "cypherstack", "flutter_libepiccash", "main", epicCashCommit), + ]; + Future commitEpicFuture = Future.wait(futureEpicList); + List futureMoneroList = [ + doesCommitExist("cypherstack", "flutter_libmonero", moneroCommit), + isHeadCommit("cypherstack", "flutter_libmonero", "main", moneroCommit), + ]; + Future commitMoneroFuture = Future.wait(futureMoneroList); + return Scaffold( backgroundColor: CFColors.almostWhite, appBar: AppBar( @@ -142,6 +244,186 @@ class AboutView extends ConsumerWidget { const SizedBox( height: 12, ), + FutureBuilder( + future: commitFiroFuture, + builder: (context, AsyncSnapshot snapshot) { + bool commitExists = false; + bool isHead = false; + CommitStatus stateOfCommit = CommitStatus.notLoaded; + + if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + commitExists = snapshot.data![0] as bool; + isHead = snapshot.data![1] as bool; + if (commitExists && isHead) { + stateOfCommit = CommitStatus.isHead; + } else if (commitExists) { + stateOfCommit = CommitStatus.isOldCommit; + } else { + stateOfCommit = CommitStatus.notACommit; + } + } + TextStyle indicationStyle = + STextStyles.itemSubtitle; + switch (stateOfCommit) { + case CommitStatus.isHead: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackGreen); + break; + case CommitStatus.isOldCommit: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackYellow); + break; + case CommitStatus.notACommit: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackRed); + break; + default: + break; + } + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Firo Build Commit", + style: STextStyles.titleBold12, + ), + const SizedBox( + height: 4, + ), + SelectableText( + firoCommit, + style: indicationStyle, + ), + ], + ), + ); + }), + const SizedBox( + height: 12, + ), + FutureBuilder( + future: commitEpicFuture, + builder: (context, AsyncSnapshot snapshot) { + bool commitExists = false; + bool isHead = false; + CommitStatus stateOfCommit = CommitStatus.notLoaded; + + if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + commitExists = snapshot.data![0] as bool; + isHead = snapshot.data![1] as bool; + if (commitExists && isHead) { + stateOfCommit = CommitStatus.isHead; + } else if (commitExists) { + stateOfCommit = CommitStatus.isOldCommit; + } else { + stateOfCommit = CommitStatus.notACommit; + } + } + TextStyle indicationStyle = + STextStyles.itemSubtitle; + switch (stateOfCommit) { + case CommitStatus.isHead: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackGreen); + break; + case CommitStatus.isOldCommit: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackYellow); + break; + case CommitStatus.notACommit: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackRed); + break; + default: + break; + } + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Epic Cash Build Commit", + style: STextStyles.titleBold12, + ), + const SizedBox( + height: 4, + ), + SelectableText( + epicCashCommit, + style: indicationStyle, + ), + ], + ), + ); + }), + const SizedBox( + height: 12, + ), + FutureBuilder( + future: commitMoneroFuture, + builder: (context, AsyncSnapshot snapshot) { + bool commitExists = false; + bool isHead = false; + CommitStatus stateOfCommit = CommitStatus.notLoaded; + + if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + commitExists = snapshot.data![0] as bool; + isHead = snapshot.data![1] as bool; + if (commitExists && isHead) { + stateOfCommit = CommitStatus.isHead; + } else if (commitExists) { + stateOfCommit = CommitStatus.isOldCommit; + } else { + stateOfCommit = CommitStatus.notACommit; + } + } + TextStyle indicationStyle = + STextStyles.itemSubtitle; + switch (stateOfCommit) { + case CommitStatus.isHead: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackGreen); + break; + case CommitStatus.isOldCommit: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackYellow); + break; + case CommitStatus.notACommit: + indicationStyle = STextStyles.itemSubtitle + .copyWith(color: CFColors.stackRed); + break; + default: + break; + } + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Monero Build Commit", + style: STextStyles.titleBold12, + ), + const SizedBox( + height: 4, + ), + SelectableText( + moneroCommit, + style: indicationStyle, + ), + ], + ), + ); + }), + const SizedBox( + height: 12, + ), RoundedWhiteContainer( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/settings_views/global_settings_view/global_settings_view.dart b/lib/pages/settings_views/global_settings_view/global_settings_view.dart index 8c739ac00..2f52b9c37 100644 --- a/lib/pages/settings_views/global_settings_view/global_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/global_settings_view.dart @@ -9,6 +9,7 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/language_v import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart'; @@ -166,6 +167,18 @@ class GlobalSettingsView extends StatelessWidget { const SizedBox( height: 8, ), + SettingsListButton( + iconAssetName: Assets.svg.arrowUpRight, + iconSize: 16, + title: "Startup", + onPressed: () { + Navigator.of(context).pushNamed( + StartupPreferencesView.routeName); + }, + ), + const SizedBox( + height: 8, + ), SettingsListButton( iconAssetName: Assets.svg.sun, iconSize: 18, diff --git a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart new file mode 100644 index 000000000..861886625 --- /dev/null +++ b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart @@ -0,0 +1,269 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/utilities/cfcolors.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class StartupPreferencesView extends ConsumerStatefulWidget { + const StartupPreferencesView({Key? key}) : super(key: key); + + static const String routeName = "/startupPreferences"; + + @override + ConsumerState createState() => + _StartupPreferencesViewState(); +} + +class _StartupPreferencesViewState + extends ConsumerState { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: CFColors.almostWhite, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Startup preferences", + style: STextStyles.navBarTitle, + ), + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.all(4.0), + child: RawMaterialButton( + // splashColor: CFColors.splashLight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: () { + ref + .read(prefsChangeNotifierProvider) + .gotoWalletOnStartup = false; + }, + child: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: CFColors.link2, + value: false, + groupValue: ref.watch( + prefsChangeNotifierProvider + .select((value) => value + .gotoWalletOnStartup), + ), + onChanged: (value) { + if (value is bool) { + ref + .read( + prefsChangeNotifierProvider) + .gotoWalletOnStartup = value; + } + }, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Home screen", + style: STextStyles.titleBold12, + textAlign: TextAlign.left, + ), + Text( + "Stack Wallet home screen", + style: STextStyles.itemSubtitle, + textAlign: TextAlign.left, + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(4), + child: RawMaterialButton( + // splashColor: CFColors.splashLight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: () { + ref + .read(prefsChangeNotifierProvider) + .gotoWalletOnStartup = true; + }, + child: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: CFColors.link2, + value: true, + groupValue: ref.watch( + prefsChangeNotifierProvider + .select((value) => value + .gotoWalletOnStartup), + ), + onChanged: (value) { + if (value is bool) { + ref + .read( + prefsChangeNotifierProvider) + .gotoWalletOnStartup = value; + } + }, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Specific wallet", + style: STextStyles.titleBold12, + textAlign: TextAlign.left, + ), + Text( + "Select a specific wallet to load into on startup", + style: STextStyles.itemSubtitle, + textAlign: TextAlign.left, + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + if (!ref.watch(prefsChangeNotifierProvider + .select((value) => value.gotoWalletOnStartup))) + const SizedBox( + height: 12, + ), + if (ref.watch(prefsChangeNotifierProvider + .select((value) => value.gotoWalletOnStartup))) + Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.only( + left: 12.0, + right: 12, + bottom: 12, + ), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const SizedBox( + width: 12 + 20, + height: 12, + ), + Flexible( + child: RawMaterialButton( + // splashColor: CFColors.splashLight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants + .size.circularBorderRadius, + ), + ), + onPressed: () { + Navigator.of(context).pushNamed( + StartupWalletSelectionView + .routeName); + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Select wallet...", + style: STextStyles.link2, + textAlign: TextAlign.left, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart new file mode 100644 index 000000000..9569419e5 --- /dev/null +++ b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart @@ -0,0 +1,197 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/cfcolors.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class StartupWalletSelectionView extends ConsumerStatefulWidget { + const StartupWalletSelectionView({Key? key}) : super(key: key); + + static const String routeName = "/startupWalletSelection"; + @override + ConsumerState createState() => + _StartupWalletSelectionViewState(); +} + +class _StartupWalletSelectionViewState + extends ConsumerState { + final Map _controllers = {}; + + @override + Widget build(BuildContext context) { + final managers = ref + .watch(walletsChangeNotifierProvider.select((value) => value.managers)); + + _controllers.clear(); + for (final manager in managers) { + _controllers[manager.walletId] = DSBController(); + } + + return Scaffold( + backgroundColor: CFColors.almostWhite, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + "Select startup wallet", + style: STextStyles.navBarTitle, + ), + ), + ), + body: LayoutBuilder(builder: (context, constraints) { + return Padding( + padding: const EdgeInsets.only( + left: 12, + top: 12, + right: 12, + ), + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 24, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 4, + ), + Text( + "Select a wallet to load into immediately on startup", + style: STextStyles.smallMed12, + ), + const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: Column( + children: [ + ...managers.map( + (manager) => Padding( + padding: const EdgeInsets.all(12), + child: Row( + key: Key( + "startupWalletSelectionGroupKey_${manager.walletId}"), + children: [ + Container( + decoration: BoxDecoration( + color: CFColors.coin + .forCoin(manager.coin) + .withOpacity(0.5), + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: Padding( + padding: const EdgeInsets.all(4), + child: SvgPicture.asset( + Assets.svg + .iconFor(coin: manager.coin), + width: 20, + height: 20, + ), + ), + ), + const SizedBox( + width: 12, + ), + Column( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + manager.walletName, + style: STextStyles.titleBold12, + ), + // const SizedBox( + // height: 2, + // ), + // FutureBuilder( + // future: manager.totalBalance, + // builder: (builderContext, + // AsyncSnapshot snapshot) { + // if (snapshot.connectionState == + // ConnectionState.done && + // snapshot.hasData) { + // return Text( + // "${Format.localizedStringAsFixed( + // value: snapshot.data!, + // locale: ref.watch( + // localeServiceChangeNotifierProvider + // .select((value) => + // value.locale)), + // decimalPlaces: 8, + // )} ${manager.coin.ticker}", + // style: STextStyles.itemSubtitle, + // ); + // } else { + // return AnimatedText( + // stringsToLoopThrough: const [ + // "Loading balance", + // "Loading balance.", + // "Loading balance..", + // "Loading balance..." + // ], + // style: STextStyles.itemSubtitle, + // ); + // } + // }, + // ), + ], + ), + const Spacer(), + SizedBox( + height: 20, + width: 20, + child: Radio( + activeColor: CFColors.link2, + value: manager.walletId, + groupValue: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.startupWalletId), + ), + onChanged: (value) { + if (value is String) { + ref + .read( + prefsChangeNotifierProvider) + .startupWalletId = value; + } + }, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + }), + ); + } +} diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 94228bee7..01b2f4ad8 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -24,6 +24,9 @@ import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; +import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart'; +import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart'; +import 'package:stackwallet/services/change_now/change_now_loading_service.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; @@ -35,6 +38,7 @@ import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; +import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; @@ -42,10 +46,6 @@ import 'package:stackwallet/widgets/custom_loading_overlay.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:tuple/tuple.dart'; -import '../../providers/wallet/public_private_balance_state_provider.dart'; -import '../../providers/wallet/wallet_balance_toggle_state_provider.dart'; -import '../../utilities/enums/wallet_balance_toggle_state.dart'; - /// [eventBus] should only be set during testing class WalletView extends ConsumerStatefulWidget { const WalletView({ @@ -79,6 +79,8 @@ class _WalletViewState extends ConsumerState { late StreamSubscription _syncStatusSubscription; late StreamSubscription _nodeStatusSubscription; + final _cnLoadingService = ChangeNowLoadingService(); + @override void initState() { walletId = widget.walletId; @@ -272,9 +274,10 @@ class _WalletViewState extends ConsumerState { unawaited(Navigator.of(context).pushNamed( WalletInitiatedExchangeView.routeName, - arguments: Tuple2( + arguments: Tuple3( walletId, coin, + _loadCNData, ), )); } @@ -346,6 +349,11 @@ class _WalletViewState extends ConsumerState { } } + void _loadCNData() { + // unawaited future + _cnLoadingService.loadAll(ref, coin: ref.read(managerProvider).coin); + } + @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 001c21b0d..df15f65fe 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -56,6 +56,8 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; @@ -224,6 +226,18 @@ class RouteGenerator { builder: (_) => const SyncingPreferencesView(), settings: RouteSettings(name: settings.name)); + case StartupPreferencesView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const StartupPreferencesView(), + settings: RouteSettings(name: settings.name)); + + case StartupWalletSelectionView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const StartupWalletSelectionView(), + settings: RouteSettings(name: settings.name)); + case ManageNodesView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, @@ -719,7 +733,7 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case WalletInitiatedExchangeView.routeName: - if (args is Tuple2) { + if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => Stack( @@ -728,7 +742,9 @@ class RouteGenerator { walletId: args.item1, coin: args.item2, ), - const ExchangeLoadingOverlayView(), + ExchangeLoadingOverlayView( + unawaitedLoad: args.item3, + ), ], ), settings: RouteSettings( diff --git a/lib/services/change_now/change_now_loading_service.dart b/lib/services/change_now/change_now_loading_service.dart new file mode 100644 index 000000000..51cd12d21 --- /dev/null +++ b/lib/services/change_now/change_now_loading_service.dart @@ -0,0 +1,140 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; +import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart'; +import 'package:stackwallet/providers/exchange/change_now_provider.dart'; +import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart'; +import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; +import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; +import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/logger.dart'; + +class ChangeNowLoadingService { + Future loadAll(WidgetRef ref, {Coin? coin}) async { + try { + await Future.wait([ + _loadFixedRateMarkets(ref, coin: coin), + _loadChangeNowStandardCurrencies(ref, coin: coin), + ]); + } catch (e, s) { + Logging.instance.log("ChangeNowLoadingService.loadAll failed: $e\n$s", + level: LogLevel.Error); + } + } + + Future _loadFixedRateMarkets(WidgetRef ref, {Coin? coin}) async { + if (ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state == + ChangeNowLoadStatus.loading) { + // already in progress so just + return; + } + + ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.loading; + + final response3 = + await ref.read(changeNowProvider).getAvailableFixedRateMarkets(); + if (response3.value != null) { + ref.read(fixedRateMarketPairsStateProvider.state).state = + response3.value!; + + if (ref.read(fixedRateExchangeFormProvider).market == null) { + String fromTicker = "btc"; + String toTicker = "xmr"; + + if (coin != null) { + fromTicker = coin.ticker.toLowerCase(); + } + + final matchingMarkets = response3.value! + .where((e) => e.to == toTicker && e.from == fromTicker); + if (matchingMarkets.isNotEmpty) { + await ref + .read(fixedRateExchangeFormProvider) + .updateMarket(matchingMarkets.first, true); + } + } + } else { + Logging.instance.log( + "Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}", + level: LogLevel.Error); + + ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.failed; + return; + } + + ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.success; + } + + Future _loadChangeNowStandardCurrencies(WidgetRef ref, + {Coin? coin}) async { + if (ref + .read(changeNowEstimatedInitialLoadStatusStateProvider.state) + .state == + ChangeNowLoadStatus.loading) { + // already in progress so just + return; + } + + ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.loading; + + final response = await ref.read(changeNowProvider).getAvailableCurrencies(); + final response2 = + await ref.read(changeNowProvider).getAvailableFloatingRatePairs(); + if (response.value != null) { + ref.read(availableChangeNowCurrenciesStateProvider.state).state = + response.value!; + if (response2.value != null) { + ref.read(availableFloatingRatePairsStateProvider.state).state = + response2.value!; + + String fromTicker = "btc"; + String toTicker = "xmr"; + + if (coin != null) { + fromTicker = coin.ticker.toLowerCase(); + } + + if (response.value!.length > 1) { + if (ref.read(estimatedRateExchangeFormProvider).from == null) { + if (response.value! + .where((e) => e.ticker == fromTicker) + .isNotEmpty) { + await ref.read(estimatedRateExchangeFormProvider).updateFrom( + response.value!.firstWhere((e) => e.ticker == fromTicker), + false); + } + } + if (ref.read(estimatedRateExchangeFormProvider).to == null) { + if (response.value!.where((e) => e.ticker == toTicker).isNotEmpty) { + await ref.read(estimatedRateExchangeFormProvider).updateTo( + response.value!.firstWhere((e) => e.ticker == toTicker), + false); + } + } + } + } else { + Logging.instance.log( + "Failed to load changeNOW available floating rate pairs: ${response2.exception?.errorMessage}", + level: LogLevel.Error); + ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.failed; + return; + } + } else { + Logging.instance.log( + "Failed to load changeNOW currencies: ${response.exception?.errorMessage}", + level: LogLevel.Error); + await Future.delayed(const Duration(seconds: 3)); + ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.failed; + return; + } + + ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = + ChangeNowLoadStatus.success; + } +} diff --git a/lib/services/coins/bitcoin/bitcoin_wallet.dart b/lib/services/coins/bitcoin/bitcoin_wallet.dart index bc40c8f0c..ae6d7b70a 100644 --- a/lib/services/coins/bitcoin/bitcoin_wallet.dart +++ b/lib/services/coins/bitcoin/bitcoin_wallet.dart @@ -399,6 +399,138 @@ class BitcoinWallet extends CoinServiceAPI { level: LogLevel.Info); } + Future> _checkGaps( + int maxNumberOfIndexesToCheck, + int maxUnusedAddressGap, + int txCountBatchSize, + bip32.BIP32 root, + DerivePathType type, + int account) async { + List addressArray = []; + int returningIndex = -1; + Map> derivations = {}; + int gapCounter = 0; + for (int index = 0; + index < maxNumberOfIndexesToCheck && gapCounter < maxUnusedAddressGap; + index += txCountBatchSize) { + List iterationsAddressArray = []; + Logging.instance.log( + "index: $index, \t GapCounter $account ${type.name}: $gapCounter", + level: LogLevel.Info); + + final _id = "k_$index"; + Map txCountCallArgs = {}; + final Map receivingNodes = {}; + + for (int j = 0; j < txCountBatchSize; j++) { + final node = await compute( + getBip32NodeFromRootWrapper, + Tuple4( + account, + index + j, + root, + type, + ), + ); + String? address; + switch (type) { + case DerivePathType.bip44: + address = P2PKH( + data: PaymentData(pubkey: node.publicKey), + network: _network) + .data + .address!; + break; + case DerivePathType.bip49: + address = P2SH( + data: PaymentData( + redeem: P2WPKH( + data: PaymentData(pubkey: node.publicKey), + network: _network) + .data), + network: _network) + .data + .address!; + break; + case DerivePathType.bip84: + address = P2WPKH( + network: _network, + data: PaymentData(pubkey: node.publicKey)) + .data + .address!; + break; + default: + throw Exception("No Path type $type exists"); + } + receivingNodes.addAll({ + "${_id}_$j": { + "node": node, + "address": address, + } + }); + txCountCallArgs.addAll({ + "${_id}_$j": address, + }); + } + + // get address tx counts + final counts = await _getBatchTxCount(addresses: txCountCallArgs); + + // check and add appropriate addresses + for (int k = 0; k < txCountBatchSize; k++) { + int count = counts["${_id}_$k"]!; + if (count > 0) { + final node = receivingNodes["${_id}_$k"]; + // add address to array + addressArray.add(node["address"] as String); + iterationsAddressArray.add(node["address"] as String); + // set current index + returningIndex = index + k; + // reset counter + gapCounter = 0; + // add info to derivations + derivations[node["address"] as String] = { + "pubKey": Format.uint8listToString( + (node["node"] as bip32.BIP32).publicKey), + "wif": (node["node"] as bip32.BIP32).toWIF(), + }; + } + + // increase counter when no tx history found + if (count == 0) { + gapCounter++; + } + } + // cache all the transactions while waiting for the current function to finish. + unawaited(getTransactionCacheEarly(iterationsAddressArray)); + } + return { + "addressArray": addressArray, + "index": returningIndex, + "derivations": derivations + }; + } + + Future getTransactionCacheEarly(List allAddresses) async { + try { + final List> allTxHashes = + await _fetchHistory(allAddresses); + for (final txHash in allTxHashes) { + try { + unawaited(cachedElectrumXClient.getTransaction( + txHash: txHash["tx_hash"] as String, + verbose: true, + coin: coin, + )); + } catch (e) { + continue; + } + } + } catch (e) { + // + } + } + Future _recoverWalletFromBIP32SeedPhrase({ required String mnemonic, int maxUnusedAddressGap = 20, @@ -429,10 +561,6 @@ class BitcoinWallet extends CoinServiceAPI { int p2shChangeIndex = -1; int p2wpkhChangeIndex = -1; - // The gap limit will be capped at [maxUnusedAddressGap] - int receivingGapCounter = 0; - int changeGapCounter = 0; - // actual size is 36 due to p2pkh, p2sh, and p2wpkh so 12x3 const txCountBatchSize = 12; @@ -440,323 +568,71 @@ class BitcoinWallet extends CoinServiceAPI { // receiving addresses Logging.instance .log("checking receiving addresses...", level: LogLevel.Info); - for (int index = 0; - index < maxNumberOfIndexesToCheck && - receivingGapCounter < maxUnusedAddressGap; - index += txCountBatchSize) { - Logging.instance.log( - "index: $index, \t receivingGapCounter: $receivingGapCounter", - level: LogLevel.Info); + final resultReceive44 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 0); - final receivingP2pkhID = "k_$index"; - final receivingP2shID = "s_$index"; - final receivingP2wpkhID = "w_$index"; - Map txCountCallArgs = {}; - final Map receivingNodes = {}; + final resultReceive49 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip49, 0); - for (int j = 0; j < txCountBatchSize; j++) { - // bip44 / P2PKH - final node44 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 0, - index + j, - root, - DerivePathType.bip44, - ), - ); - final p2pkhReceiveAddress = P2PKH( - data: PaymentData(pubkey: node44.publicKey), - network: _network) - .data - .address!; - receivingNodes.addAll({ - "${receivingP2pkhID}_$j": { - "node": node44, - "address": p2pkhReceiveAddress, - } - }); - txCountCallArgs.addAll({ - "${receivingP2pkhID}_$j": p2pkhReceiveAddress, - }); - - // bip49 / P2SH - final node49 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 0, - index + j, - root, - DerivePathType.bip49, - ), - ); - final p2shReceiveAddress = P2SH( - data: PaymentData( - redeem: P2WPKH( - data: PaymentData(pubkey: node49.publicKey), - network: _network) - .data), - network: _network) - .data - .address!; - receivingNodes.addAll({ - "${receivingP2shID}_$j": { - "node": node49, - "address": p2shReceiveAddress, - } - }); - txCountCallArgs.addAll({ - "${receivingP2shID}_$j": p2shReceiveAddress, - }); - - // bip84 / P2WPKH - final node84 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 0, - index + j, - root, - DerivePathType.bip84, - ), - ); - final p2wpkhReceiveAddress = P2WPKH( - network: _network, - data: PaymentData(pubkey: node84.publicKey)) - .data - .address!; - receivingNodes.addAll({ - "${receivingP2wpkhID}_$j": { - "node": node84, - "address": p2wpkhReceiveAddress, - } - }); - txCountCallArgs.addAll({ - "${receivingP2wpkhID}_$j": p2wpkhReceiveAddress, - }); - } - - // get address tx counts - final counts = await _getBatchTxCount(addresses: txCountCallArgs); - - // check and add appropriate addresses - for (int k = 0; k < txCountBatchSize; k++) { - int p2pkhTxCount = counts["${receivingP2pkhID}_$k"]!; - if (p2pkhTxCount > 0) { - final node = receivingNodes["${receivingP2pkhID}_$k"]; - // add address to array - p2pkhReceiveAddressArray.add(node["address"] as String); - // set current index - p2pkhReceiveIndex = index + k; - // reset counter - receivingGapCounter = 0; - // add info to derivations - p2pkhReceiveDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } - - int p2shTxCount = counts["${receivingP2shID}_$k"]!; - if (p2shTxCount > 0) { - final node = receivingNodes["${receivingP2shID}_$k"]; - // add address to array - p2shReceiveAddressArray.add(node["address"] as String); - // set current index - p2shReceiveIndex = index + k; - // reset counter - receivingGapCounter = 0; - // add info to derivations - p2shReceiveDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } - - int p2wpkhTxCount = counts["${receivingP2wpkhID}_$k"]!; - if (p2wpkhTxCount > 0) { - final node = receivingNodes["${receivingP2wpkhID}_$k"]; - // add address to array - p2wpkhReceiveAddressArray.add(node["address"] as String); - // set current index - p2wpkhReceiveIndex = index + k; - // reset counter - receivingGapCounter = 0; - // add info to derivations - p2wpkhReceiveDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } - - // increase counter when no tx history found - if (p2wpkhTxCount == 0 && p2pkhTxCount == 0 && p2shTxCount == 0) { - receivingGapCounter++; - } - } - } + final resultReceive84 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip84, 0); Logging.instance .log("checking change addresses...", level: LogLevel.Info); // change addresses - for (int index = 0; - index < maxNumberOfIndexesToCheck && - changeGapCounter < maxUnusedAddressGap; - index += txCountBatchSize) { - Logging.instance.log( - "index: $index, \t changeGapCounter: $changeGapCounter", - level: LogLevel.Info); - final changeP2pkhID = "k_$index"; - final changeP2shID = "s_$index"; - final changeP2wpkhID = "w_$index"; - Map args = {}; - final Map changeNodes = {}; + final resultChange44 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 1); - for (int j = 0; j < txCountBatchSize; j++) { - // bip44 / P2PKH - final node44 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 1, - index + j, - root, - DerivePathType.bip44, - ), - ); - final p2pkhChangeAddress = P2PKH( - data: PaymentData(pubkey: node44.publicKey), - network: _network) - .data - .address!; - changeNodes.addAll({ - "${changeP2pkhID}_$j": { - "node": node44, - "address": p2pkhChangeAddress, - } - }); - args.addAll({ - "${changeP2pkhID}_$j": p2pkhChangeAddress, - }); + final resultChange49 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip49, 1); - // bip49 / P2SH - final node49 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 1, - index + j, - root, - DerivePathType.bip49, - ), - ); - final p2shChangeAddress = P2SH( - data: PaymentData( - redeem: P2WPKH( - data: PaymentData(pubkey: node49.publicKey), - network: _network) - .data), - network: _network) - .data - .address!; - changeNodes.addAll({ - "${changeP2shID}_$j": { - "node": node49, - "address": p2shChangeAddress, - } - }); - args.addAll({ - "${changeP2shID}_$j": p2shChangeAddress, - }); + final resultChange84 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip84, 1); - // bip84 / P2WPKH - final node84 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 1, - index + j, - root, - DerivePathType.bip84, - ), - ); - final p2wpkhChangeAddress = P2WPKH( - network: _network, - data: PaymentData(pubkey: node84.publicKey)) - .data - .address!; - changeNodes.addAll({ - "${changeP2wpkhID}_$j": { - "node": node84, - "address": p2wpkhChangeAddress, - } - }); - args.addAll({ - "${changeP2wpkhID}_$j": p2wpkhChangeAddress, - }); - } + await Future.wait([ + resultReceive44, + resultReceive49, + resultReceive84, + resultChange44, + resultChange49, + resultChange84 + ]); - // get address tx counts - final counts = await _getBatchTxCount(addresses: args); + p2pkhReceiveAddressArray = + (await resultReceive44)['addressArray'] as List; + p2pkhReceiveIndex = (await resultReceive44)['index'] as int; + p2pkhReceiveDerivations = (await resultReceive44)['derivations'] + as Map>; - // check and add appropriate addresses - for (int k = 0; k < txCountBatchSize; k++) { - int p2pkhTxCount = counts["${changeP2pkhID}_$k"]!; - if (p2pkhTxCount > 0) { - final node = changeNodes["${changeP2pkhID}_$k"]; - // add address to array - p2pkhChangeAddressArray.add(node["address"] as String); - // set current index - p2pkhChangeIndex = index + k; - // reset counter - changeGapCounter = 0; - // add info to derivations - p2pkhChangeDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } + p2shReceiveAddressArray = + (await resultReceive49)['addressArray'] as List; + p2shReceiveIndex = (await resultReceive49)['index'] as int; + p2shReceiveDerivations = (await resultReceive49)['derivations'] + as Map>; - int p2shTxCount = counts["${changeP2shID}_$k"]!; - if (p2shTxCount > 0) { - final node = changeNodes["${changeP2shID}_$k"]; - // add address to array - p2shChangeAddressArray.add(node["address"] as String); - // set current index - p2shChangeIndex = index + k; - // reset counter - changeGapCounter = 0; - // add info to derivations - p2shChangeDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } + p2wpkhReceiveAddressArray = + (await resultReceive84)['addressArray'] as List; + p2wpkhReceiveIndex = (await resultReceive84)['index'] as int; + p2wpkhReceiveDerivations = (await resultReceive84)['derivations'] + as Map>; - int p2wpkhTxCount = counts["${changeP2wpkhID}_$k"]!; - if (p2wpkhTxCount > 0) { - final node = changeNodes["${changeP2wpkhID}_$k"]; - // add address to array - p2wpkhChangeAddressArray.add(node["address"] as String); - // set current index - p2wpkhChangeIndex = index + k; - // reset counter - changeGapCounter = 0; - // add info to derivations - p2wpkhChangeDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } + p2pkhChangeAddressArray = + (await resultChange44)['addressArray'] as List; + p2pkhChangeIndex = (await resultChange44)['index'] as int; + p2pkhChangeDerivations = (await resultChange44)['derivations'] + as Map>; - // increase counter when no tx history found - if (p2wpkhTxCount == 0 && p2pkhTxCount == 0 && p2shTxCount == 0) { - changeGapCounter++; - } - } - } + p2shChangeAddressArray = + (await resultChange49)['addressArray'] as List; + p2shChangeIndex = (await resultChange49)['index'] as int; + p2shChangeDerivations = (await resultChange49)['derivations'] + as Map>; + + p2wpkhChangeAddressArray = + (await resultChange84)['addressArray'] as List; + p2wpkhChangeIndex = (await resultChange84)['index'] as int; + p2wpkhChangeDerivations = (await resultChange84)['derivations'] + as Map>; // save the derivations (if any) if (p2pkhReceiveDerivations.isNotEmpty) { @@ -975,7 +851,7 @@ class BitcoinWallet extends CoinServiceAPI { // notify on unconfirmed transactions for (final tx in unconfirmedTxnsToNotifyPending) { if (tx.txType == "Received") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Incoming transaction", body: walletName, walletId: walletId, @@ -986,10 +862,10 @@ class BitcoinWallet extends CoinServiceAPI { txid: tx.txid, confirmations: tx.confirmations, requiredConfirmations: MINIMUM_CONFIRMATIONS, - ); + )); await txTracker.addNotifiedPending(tx.txid); } else if (tx.txType == "Sent") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Sending transaction", body: walletName, walletId: walletId, @@ -1000,7 +876,7 @@ class BitcoinWallet extends CoinServiceAPI { txid: tx.txid, confirmations: tx.confirmations, requiredConfirmations: MINIMUM_CONFIRMATIONS, - ); + )); await txTracker.addNotifiedPending(tx.txid); } } @@ -1008,7 +884,7 @@ class BitcoinWallet extends CoinServiceAPI { // notify on confirmed for (final tx in unconfirmedTxnsToNotifyConfirmed) { if (tx.txType == "Received") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Incoming transaction confirmed", body: walletName, walletId: walletId, @@ -1016,10 +892,10 @@ class BitcoinWallet extends CoinServiceAPI { date: DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000), shouldWatchForUpdates: false, coinName: coin.name, - ); + )); await txTracker.addNotifiedConfirmed(tx.txid); } else if (tx.txType == "Sent") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Outgoing transaction confirmed", body: walletName, walletId: walletId, @@ -1027,7 +903,7 @@ class BitcoinWallet extends CoinServiceAPI { date: DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000), shouldWatchForUpdates: false, coinName: coin.name, - ); + )); await txTracker.addNotifiedConfirmed(tx.txid); } } @@ -1094,14 +970,16 @@ class BitcoinWallet extends CoinServiceAPI { if (currentHeight != storedHeight) { if (currentHeight != -1) { // -1 failed to fetch current height - updateStoredChainHeight(newHeight: currentHeight); + unawaited(updateStoredChainHeight(newHeight: currentHeight)); } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId)); - await _checkChangeAddressForTransactions(DerivePathType.bip84); + final changeAddressForTransactions = + _checkChangeAddressForTransactions(DerivePathType.bip84); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId)); - await _checkCurrentReceivingAddressesForTransactions(); + final currentReceivingAddressesForTransactions = + _checkCurrentReceivingAddressesForTransactions(); final newTxData = _fetchTransactionData(); GlobalEventBus.instance @@ -1121,7 +999,15 @@ class BitcoinWallet extends CoinServiceAPI { GlobalEventBus.instance .fire(RefreshPercentChangedEvent(0.80, walletId)); - await getAllTxsToWatch(await newTxData); + final allTxsToWatch = getAllTxsToWatch(await newTxData); + await Future.wait([ + newTxData, + changeAddressForTransactions, + currentReceivingAddressesForTransactions, + newUtxoData, + feeObj, + allTxsToWatch, + ]); GlobalEventBus.instance .fire(RefreshPercentChangedEvent(0.90, walletId)); } @@ -1137,7 +1023,7 @@ class BitcoinWallet extends CoinServiceAPI { ); if (shouldAutoSync) { - timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async { + timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async { Logging.instance.log( "Periodic refresh check for $walletId $walletName in object instance: $hashCode", level: LogLevel.Info); @@ -1472,7 +1358,7 @@ class BitcoinWallet extends CoinServiceAPI { ); if (shouldRefresh) { - refresh(); + unawaited(refresh()); } } @@ -1972,7 +1858,7 @@ class BitcoinWallet extends CoinServiceAPI { final fetchedUtxoList = >>[]; final Map>> batches = {}; - const batchSizeMax = 10; + const batchSizeMax = 100; int batchNumber = 0; for (int i = 0; i < allAddresses.length; i++) { if (batches[batchNumber] == null) { @@ -2357,7 +2243,7 @@ class BitcoinWallet extends CoinServiceAPI { final Map>> batches = {}; final Map requestIdToAddressMap = {}; - const batchSizeMax = 10; + const batchSizeMax = 100; int batchNumber = 0; for (int i = 0; i < allAddresses.length; i++) { if (batches[batchNumber] == null) { @@ -2404,6 +2290,43 @@ class BitcoinWallet extends CoinServiceAPI { return false; } + Future>> fastFetch(List allTxHashes) async { + List> allTransactions = []; + + const futureLimit = 30; + List>> transactionFutures = []; + int currentFutureCount = 0; + for (final txHash in allTxHashes) { + Future> transactionFuture = + cachedElectrumXClient.getTransaction( + txHash: txHash, + verbose: true, + coin: coin, + ); + transactionFutures.add(transactionFuture); + currentFutureCount++; + if (currentFutureCount > futureLimit) { + currentFutureCount = 0; + await Future.wait(transactionFutures); + for (final fTx in transactionFutures) { + final tx = await fTx; + + allTransactions.add(tx); + } + } + } + if (currentFutureCount != 0) { + currentFutureCount = 0; + await Future.wait(transactionFutures); + for (final fTx in transactionFutures) { + final tx = await fTx; + + allTransactions.add(tx); + } + } + return allTransactions; + } + Future _fetchTransactionData() async { final List allAddresses = await _fetchAllOwnAddresses(); @@ -2451,6 +2374,11 @@ class BitcoinWallet extends CoinServiceAPI { } } + Set hashes = {}; + for (var element in allTxHashes) { + hashes.add(element['tx_hash'] as String); + } + await fastFetch(hashes.toList()); List> allTransactions = []; for (final txHash in allTxHashes) { @@ -2480,6 +2408,16 @@ class BitcoinWallet extends CoinServiceAPI { Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; final List> midSortedArray = []; + Set vHashes = {}; + for (final txObject in allTransactions) { + for (int i = 0; i < (txObject["vin"] as List).length; i++) { + final input = txObject["vin"]![i] as Map; + final prevTxid = input["txid"] as String; + vHashes.add(prevTxid); + } + } + await fastFetch(vHashes.toList()); + for (final txObject in allTransactions) { List sendersArray = []; List recipientsArray = []; diff --git a/lib/services/coins/dogecoin/dogecoin_wallet.dart b/lib/services/coins/dogecoin/dogecoin_wallet.dart index a7b2132ad..0235a0c02 100644 --- a/lib/services/coins/dogecoin/dogecoin_wallet.dart +++ b/lib/services/coins/dogecoin/dogecoin_wallet.dart @@ -251,7 +251,7 @@ class DogecoinWallet extends CoinServiceAPI { } Future updateStoredChainHeight({required int newHeight}) async { - DB.instance.put( + await DB.instance.put( boxName: walletId, key: "storedChainHeight", value: newHeight); } @@ -346,6 +346,120 @@ class DogecoinWallet extends CoinServiceAPI { level: LogLevel.Info); } + Future> _checkGaps( + int maxNumberOfIndexesToCheck, + int maxUnusedAddressGap, + int txCountBatchSize, + bip32.BIP32 root, + DerivePathType type, + int account) async { + List addressArray = []; + int returningIndex = -1; + Map> derivations = {}; + int gapCounter = 0; + for (int index = 0; + index < maxNumberOfIndexesToCheck && gapCounter < maxUnusedAddressGap; + index += txCountBatchSize) { + List iterationsAddressArray = []; + Logging.instance.log( + "index: $index, \t GapCounter $account ${type.name}: $gapCounter", + level: LogLevel.Info); + + final _id = "k_$index"; + Map txCountCallArgs = {}; + final Map receivingNodes = {}; + + for (int j = 0; j < txCountBatchSize; j++) { + final node = await compute( + getBip32NodeFromRootWrapper, + Tuple4( + account, + index + j, + root, + type, + ), + ); + String? address; + switch (type) { + case DerivePathType.bip44: + address = P2PKH( + data: PaymentData(pubkey: node.publicKey), + network: _network) + .data + .address!; + break; + default: + throw Exception("No Path type $type exists"); + } + receivingNodes.addAll({ + "${_id}_$j": { + "node": node, + "address": address, + } + }); + txCountCallArgs.addAll({ + "${_id}_$j": address, + }); + } + + // get address tx counts + final counts = await _getBatchTxCount(addresses: txCountCallArgs); + + // check and add appropriate addresses + for (int k = 0; k < txCountBatchSize; k++) { + int count = counts["${_id}_$k"]!; + if (count > 0) { + final node = receivingNodes["${_id}_$k"]; + // add address to array + addressArray.add(node["address"] as String); + iterationsAddressArray.add(node["address"] as String); + // set current index + returningIndex = index + k; + // reset counter + gapCounter = 0; + // add info to derivations + derivations[node["address"] as String] = { + "pubKey": Format.uint8listToString( + (node["node"] as bip32.BIP32).publicKey), + "wif": (node["node"] as bip32.BIP32).toWIF(), + }; + } + + // increase counter when no tx history found + if (count == 0) { + gapCounter++; + } + } + // cache all the transactions while waiting for the current function to finish. + unawaited(getTransactionCacheEarly(iterationsAddressArray)); + } + return { + "addressArray": addressArray, + "index": returningIndex, + "derivations": derivations + }; + } + + Future getTransactionCacheEarly(List allAddresses) async { + try { + final List> allTxHashes = + await _fetchHistory(allAddresses); + for (final txHash in allTxHashes) { + try { + unawaited(cachedElectrumXClient.getTransaction( + txHash: txHash["tx_hash"] as String, + verbose: true, + coin: coin, + )); + } catch (e) { + continue; + } + } + } catch (e) { + // + } + } + Future _recoverWalletFromBIP32SeedPhrase({ required String mnemonic, int maxUnusedAddressGap = 20, @@ -364,10 +478,6 @@ class DogecoinWallet extends CoinServiceAPI { List p2pkhChangeAddressArray = []; int p2pkhChangeIndex = -1; - // The gap limit will be capped at [maxUnusedAddressGap] - int receivingGapCounter = 0; - int changeGapCounter = 0; - // actual size is 12 due to p2pkh so 12x1 const txCountBatchSize = 12; @@ -375,143 +485,31 @@ class DogecoinWallet extends CoinServiceAPI { // receiving addresses Logging.instance .log("checking receiving addresses...", level: LogLevel.Info); - for (int index = 0; - index < maxNumberOfIndexesToCheck && - receivingGapCounter < maxUnusedAddressGap; - index += txCountBatchSize) { - Logging.instance.log( - "index: $index, \t receivingGapCounter: $receivingGapCounter", - level: LogLevel.Info); - - final receivingP2pkhID = "k_$index"; - Map txCountCallArgs = {}; - final Map receivingNodes = {}; - - for (int j = 0; j < txCountBatchSize; j++) { - // bip44 / P2PKH - final node44 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 0, - index + j, - root, - DerivePathType.bip44, - ), - ); - final p2pkhReceiveAddress = P2PKH( - data: PaymentData(pubkey: node44.publicKey), - network: _network) - .data - .address!; - receivingNodes.addAll({ - "${receivingP2pkhID}_$j": { - "node": node44, - "address": p2pkhReceiveAddress, - } - }); - txCountCallArgs.addAll({ - "${receivingP2pkhID}_$j": p2pkhReceiveAddress, - }); - } - - // get address tx counts - final counts = await _getBatchTxCount(addresses: txCountCallArgs); - - // check and add appropriate addresses - for (int k = 0; k < txCountBatchSize; k++) { - int p2pkhTxCount = counts["${receivingP2pkhID}_$k"]!; - if (p2pkhTxCount > 0) { - final node = receivingNodes["${receivingP2pkhID}_$k"]; - // add address to array - p2pkhReceiveAddressArray.add(node["address"] as String); - // set current index - p2pkhReceiveIndex = index + k; - // reset counter - receivingGapCounter = 0; - // add info to derivations - p2pkhReceiveDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } - - // increase counter when no tx history found - if (p2pkhTxCount == 0) { - receivingGapCounter++; - } - } - } + final resultReceive44 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 0); Logging.instance .log("checking change addresses...", level: LogLevel.Info); // change addresses - for (int index = 0; - index < maxNumberOfIndexesToCheck && - changeGapCounter < maxUnusedAddressGap; - index += txCountBatchSize) { - Logging.instance.log( - "index: $index, \t changeGapCounter: $changeGapCounter", - level: LogLevel.Info); - final changeP2pkhID = "k_$index"; - Map args = {}; - final Map changeNodes = {}; + final resultChange44 = _checkGaps(maxNumberOfIndexesToCheck, + maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 1); - for (int j = 0; j < txCountBatchSize; j++) { - // bip44 / P2PKH - final node44 = await compute( - getBip32NodeFromRootWrapper, - Tuple4( - 1, - index + j, - root, - DerivePathType.bip44, - ), - ); - final p2pkhChangeAddress = P2PKH( - data: PaymentData(pubkey: node44.publicKey), - network: _network) - .data - .address!; - changeNodes.addAll({ - "${changeP2pkhID}_$j": { - "node": node44, - "address": p2pkhChangeAddress, - } - }); - args.addAll({ - "${changeP2pkhID}_$j": p2pkhChangeAddress, - }); - } + await Future.wait([ + resultReceive44, + resultChange44, + ]); - // get address tx counts - final counts = await _getBatchTxCount(addresses: args); + p2pkhReceiveAddressArray = + (await resultReceive44)['addressArray'] as List; + p2pkhReceiveIndex = (await resultReceive44)['index'] as int; + p2pkhReceiveDerivations = (await resultReceive44)['derivations'] + as Map>; - // check and add appropriate addresses - for (int k = 0; k < txCountBatchSize; k++) { - int p2pkhTxCount = counts["${changeP2pkhID}_$k"]!; - if (p2pkhTxCount > 0) { - final node = changeNodes["${changeP2pkhID}_$k"]; - // add address to array - p2pkhChangeAddressArray.add(node["address"] as String); - // set current index - p2pkhChangeIndex = index + k; - // reset counter - changeGapCounter = 0; - // add info to derivations - p2pkhChangeDerivations[node["address"] as String] = { - "pubKey": Format.uint8listToString( - (node["node"] as bip32.BIP32).publicKey), - "wif": (node["node"] as bip32.BIP32).toWIF(), - }; - } - - // increase counter when no tx history found - if (p2pkhTxCount == 0) { - changeGapCounter++; - } - } - } + p2pkhChangeAddressArray = + (await resultChange44)['addressArray'] as List; + p2pkhChangeIndex = (await resultChange44)['index'] as int; + p2pkhChangeDerivations = (await resultChange44)['derivations'] + as Map>; // save the derivations (if any) if (p2pkhReceiveDerivations.isNotEmpty) { @@ -655,7 +653,7 @@ class DogecoinWallet extends CoinServiceAPI { // notify on new incoming transaction for (final tx in unconfirmedTxnsToNotifyPending) { if (tx.txType == "Received") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Incoming transaction", body: walletName, walletId: walletId, @@ -666,10 +664,10 @@ class DogecoinWallet extends CoinServiceAPI { txid: tx.txid, confirmations: tx.confirmations, requiredConfirmations: MINIMUM_CONFIRMATIONS, - ); + )); await txTracker.addNotifiedPending(tx.txid); } else if (tx.txType == "Sent") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Sending transaction", body: walletName, walletId: walletId, @@ -680,7 +678,7 @@ class DogecoinWallet extends CoinServiceAPI { txid: tx.txid, confirmations: tx.confirmations, requiredConfirmations: MINIMUM_CONFIRMATIONS, - ); + )); await txTracker.addNotifiedPending(tx.txid); } } @@ -688,7 +686,7 @@ class DogecoinWallet extends CoinServiceAPI { // notify on confirmed for (final tx in unconfirmedTxnsToNotifyConfirmed) { if (tx.txType == "Received") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Incoming transaction confirmed", body: walletName, walletId: walletId, @@ -696,11 +694,11 @@ class DogecoinWallet extends CoinServiceAPI { date: DateTime.now(), shouldWatchForUpdates: false, coinName: coin.name, - ); + )); await txTracker.addNotifiedConfirmed(tx.txid); } else if (tx.txType == "Sent") { - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( title: "Outgoing transaction confirmed", body: walletName, walletId: walletId, @@ -708,7 +706,7 @@ class DogecoinWallet extends CoinServiceAPI { date: DateTime.now(), shouldWatchForUpdates: false, coinName: coin.name, - ); + )); await txTracker.addNotifiedConfirmed(tx.txid); } } @@ -772,7 +770,7 @@ class DogecoinWallet extends CoinServiceAPI { if (currentHeight != storedHeight) { if (currentHeight != -1) { // -1 failed to fetch current height - updateStoredChainHeight(newHeight: currentHeight); + unawaited(updateStoredChainHeight(newHeight: currentHeight)); } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId)); @@ -815,7 +813,7 @@ class DogecoinWallet extends CoinServiceAPI { refreshMutex = false; if (shouldAutoSync) { - timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async { + timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async { // chain height check currently broken // if ((await chainHeight) != (await storedChainHeight)) { if (await refreshIfThereIsNewData()) { @@ -1129,7 +1127,7 @@ class DogecoinWallet extends CoinServiceAPI { ); if (shouldRefresh) { - refresh(); + unawaited(refresh()); } } @@ -1496,6 +1494,43 @@ class DogecoinWallet extends CoinServiceAPI { await _secureStore.write(key: key, value: newReceiveDerivationsString); } + Future>> fastFetch(List allTxHashes) async { + List> allTransactions = []; + + const futureLimit = 30; + List>> transactionFutures = []; + int currentFutureCount = 0; + for (final txHash in allTxHashes) { + Future> transactionFuture = + cachedElectrumXClient.getTransaction( + txHash: txHash, + verbose: true, + coin: coin, + ); + transactionFutures.add(transactionFuture); + currentFutureCount++; + if (currentFutureCount > futureLimit) { + currentFutureCount = 0; + await Future.wait(transactionFutures); + for (final fTx in transactionFutures) { + final tx = await fTx; + + allTransactions.add(tx); + } + } + } + if (currentFutureCount != 0) { + currentFutureCount = 0; + await Future.wait(transactionFutures); + for (final fTx in transactionFutures) { + final tx = await fTx; + + allTransactions.add(tx); + } + } + return allTransactions; + } + Future _fetchUtxoData() async { final List allAddresses = await _fetchAllOwnAddresses(); @@ -1503,7 +1538,7 @@ class DogecoinWallet extends CoinServiceAPI { final fetchedUtxoList = >>[]; final Map>> batches = {}; - const batchSizeMax = 10; + const batchSizeMax = 100; int batchNumber = 0; for (int i = 0; i < allAddresses.length; i++) { if (batches[batchNumber] == null) { @@ -1872,14 +1907,14 @@ class DogecoinWallet extends CoinServiceAPI { final Map>> batches = {}; final Map requestIdToAddressMap = {}; - const batchSizeMax = 10; + const batchSizeMax = 100; int batchNumber = 0; for (int i = 0; i < allAddresses.length; i++) { if (batches[batchNumber] == null) { batches[batchNumber] = {}; } final scripthash = _convertToScriptHash(allAddresses[i], _network); - final id = const Uuid().v1(); + final id = Logger.isTestEnv ? "$i" : const Uuid().v1(); requestIdToAddressMap[id] = allAddresses[i]; batches[batchNumber]!.addAll({ id: [scripthash] @@ -1954,6 +1989,11 @@ class DogecoinWallet extends CoinServiceAPI { } } + List hashes = []; + for (var element in allTxHashes) { + hashes.add(element['tx_hash'] as String); + } + await fastFetch(hashes); List> allTransactions = []; for (final txHash in allTxHashes) { @@ -1983,6 +2023,16 @@ class DogecoinWallet extends CoinServiceAPI { Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; final List> midSortedArray = []; + Set vHashes = {}; + for (final txObject in allTransactions) { + for (int i = 0; i < (txObject["vin"] as List).length; i++) { + final input = txObject["vin"]![i] as Map; + final prevTxid = input["txid"] as String; + vHashes.add(prevTxid); + } + } + await fastFetch(vHashes.toList()); + for (final txObject in allTransactions) { List sendersArray = []; List recipientsArray = []; @@ -2746,7 +2796,7 @@ class DogecoinWallet extends CoinServiceAPI { ); // clear cache - _cachedElectrumXClient.clearSharedTransactionCache(coin: coin); + await _cachedElectrumXClient.clearSharedTransactionCache(coin: coin); // back up data await _rescanBackup(); @@ -3012,6 +3062,7 @@ class DogecoinWallet extends CoinServiceAPI { return available - estimatedFee; } + @override Future generateNewAddress() async { try { await _incrementAddressIndexForChain( diff --git a/lib/services/coins/epiccash/epiccash_wallet.dart b/lib/services/coins/epiccash/epiccash_wallet.dart index 514ddb79f..e33428319 100644 --- a/lib/services/coins/epiccash/epiccash_wallet.dart +++ b/lib/services/coins/epiccash/epiccash_wallet.dart @@ -225,7 +225,7 @@ Future executeNative(Map arguments) async { sendPort .send("Error An error was thrown in this isolate $function: $e\n$s"); } finally { - Logging.instance.isar?.close(); + await Logging.instance.isar?.close(); } } @@ -539,7 +539,7 @@ class EpicCashWallet extends CoinServiceAPI { // TODO notify ui/ fire event for node changed? if (shouldRefresh) { - refresh(); + unawaited(refresh()); } } @@ -705,7 +705,7 @@ class EpicCashWallet extends CoinServiceAPI { try { result = await cancelPendingTransaction(tx_slate_id); Logging.instance.log("result?: $result", level: LogLevel.Info); - if (result != null && !(result.toLowerCase().contains("error"))) { + if (!(result.toLowerCase().contains("error"))) { await postCancel( receiveAddress, tx_slate_id, signature, sendersAddress); } @@ -1180,10 +1180,10 @@ class EpicCashWallet extends CoinServiceAPI { transactionFees = message['result'] as String; }); debugPrint(transactionFees); - var decodeData; + dynamic decodeData; try { decodeData = json.decode(transactionFees!); - } catch (e, s) { + } catch (e) { if (ifErrorEstimateFee) { //Error Not enough funds. Required: 0.56500000, Available: 0.56200000 if (transactionFees!.contains("Required")) { @@ -1705,7 +1705,7 @@ class EpicCashWallet extends CoinServiceAPI { try { final String response = message['result'] as String; - if (response == null || response == "") { + if (response == "") { Logging.instance.log("response: ${response.runtimeType}", level: LogLevel.Info); await deleteSlate(currentAddress, @@ -1877,6 +1877,9 @@ class EpicCashWallet extends CoinServiceAPI { boxName: walletId, key: "creationHeight", value: await chainHeight); } + final int curAdd = await setCurrentIndex(); + _currentReceivingAddress = _getCurrentAddressForChain(curAdd); + if (!await startScans()) { refreshMutex = false; GlobalEventBus.instance.fire( @@ -1895,9 +1898,6 @@ class EpicCashWallet extends CoinServiceAPI { ); return; } - final int curAdd = await setCurrentIndex(); - - _currentReceivingAddress = _getCurrentAddressForChain(curAdd); await processAllSlates(); await processAllCancels(); @@ -1921,7 +1921,7 @@ class EpicCashWallet extends CoinServiceAPI { if (currentHeight != storedHeight) { if (currentHeight != -1) { // -1 failed to fetch current height - updateStoredChainHeight(newHeight: currentHeight); + unawaited(updateStoredChainHeight(newHeight: currentHeight)); } final newTxData = _fetchTransactionData(); @@ -2288,7 +2288,7 @@ class EpicCashWallet extends CoinServiceAPI { timer?.cancel(); timer = null; if (isActive) { - startSync(); + unawaited(startSync()); } else { for (final isolate in isolates.values) { isolate.kill(priority: Isolate.immediate); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 006655cf2..a88f37996 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -43,6 +43,7 @@ import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/prefs.dart'; import 'package:tuple/tuple.dart'; +import 'package:uuid/uuid.dart'; const DUST_LIMIT = 1000; const MINIMUM_CONFIRMATIONS = 1; @@ -145,25 +146,17 @@ Future executeNative(Map arguments) async { final setDataMap = arguments['setDataMap'] as Map; final usedSerialNumbers = arguments['usedSerialNumbers'] as List?; final mnemonic = arguments['mnemonic'] as String; - final transactionData = - arguments['transactionData'] as models.TransactionData; - final currency = arguments['currency'] as String; final coin = arguments['coin'] as Coin; final network = arguments['network'] as NetworkType?; - final currentPrice = arguments['currentPrice'] as Decimal; - final locale = arguments['locale'] as String; if (!(usedSerialNumbers == null || network == null)) { var restoreData = await isolateRestore( - mnemonic, - transactionData, - currency, - coin, - latestSetId, - setDataMap, - usedSerialNumbers, - network, - currentPrice, - locale); + mnemonic, + coin, + latestSetId, + setDataMap, + usedSerialNumbers, + network, + ); sendPort.send(restoreData); return; } @@ -240,15 +233,11 @@ Future> isolateDerive( Future> isolateRestore( String mnemonic, - models.TransactionData data, - String currency, Coin coin, int _latestSetId, Map _setDataMap, List _usedSerialNumbers, NetworkType network, - Decimal currentPrice, - String locale, ) async { List jindexes = []; List> lelantusCoins = []; @@ -373,6 +362,20 @@ Future> isolateRestore( result['_lelantus_coins'] = lelantusCoins; result['mintIndex'] = lastFoundIndex + 1; result['jindex'] = jindexes; + result['spendTxIds'] = spendTxIds; + + return result; +} + +Future> staticProcessRestore( + models.TransactionData data, + Map result, +) async { + List? _l = result['_lelantus_coins'] as List?; + final List> lelantusCoins = []; + for (var el in _l ?? []) { + lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); + } // Edit the receive transactions with the mint fees. Map editedTransactions = @@ -437,7 +440,6 @@ Future> isolateRestore( (value.height == -1 && !value.confirmedStatus)); result['newTxMap'] = transactionMap; - result['spendTxIds'] = spendTxIds; return result; } @@ -1125,7 +1127,7 @@ class FiroWallet extends CoinServiceAPI { final balance = Format.decimalAmountToSatoshis(await availablePrivateBalance()); if (satoshiAmount == balance) { - print("is send all"); + // print("is send all"); isSendAll = true; } dynamic txHexOrError = @@ -2193,7 +2195,7 @@ class FiroWallet extends CoinServiceAPI { final newTxData = _fetchTransactionData(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.35, walletId)); - final FeeObject feeObj = await _getFees(); + final feeObj = _getFees(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.50, walletId)); _utxoData = Future(() => newUtxoData); @@ -2217,9 +2219,6 @@ class FiroWallet extends CoinServiceAPI { GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.95, walletId)); - final maxFee = await _fetchMaxFee(); - _maxFee = Future(() => maxFee); - var txData = (await _txnData); var lTxData = (await lelantusTransactionData); await getAllTxsToWatch(txData, lTxData); @@ -2236,7 +2235,7 @@ class FiroWallet extends CoinServiceAPI { refreshMutex = false; if (isActive || shouldAutoSync) { - timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async { + timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async { bool shouldNotify = await refreshIfThereIsNewData(); if (shouldNotify) { await refresh(); @@ -2744,7 +2743,7 @@ class FiroWallet extends CoinServiceAPI { }; } - Future _refreshLelantusData() async { + Future _refreshLelantusData() async { final List> lelantusCoins = getLelantusCoinMap(); final jindexes = DB.instance.get(boxName: walletId, key: 'jindex') as List?; @@ -2797,18 +2796,18 @@ class FiroWallet extends CoinServiceAPI { final listTxData = txData.getAllTransactions(); listTxData.forEach((key, value) { // ignore change addresses - bool hasAtLeastOneReceive = false; + // bool hasAtLeastOneReceive = false; // int howManyReceiveInputs = 0; - for (var element in value.inputs) { - if (listLelantusTxData.containsKey(element.txid) && - listLelantusTxData[element.txid]!.txType == "Received" - // && - // listLelantusTxData[element.txid].subType != "mint" - ) { - hasAtLeastOneReceive = true; - // howManyReceiveInputs++; - } - } + // for (var element in value.inputs) { + // if (listLelantusTxData.containsKey(element.txid) && + // listLelantusTxData[element.txid]!.txType == "Received" + // // && + // // listLelantusTxData[element.txid].subType != "mint" + // ) { + // // hasAtLeastOneReceive = true; + // // howManyReceiveInputs++; + // } + // } if (value.txType == "Received" && value.subType != "mint") { // Every receive other than a mint should be shown. Mints will be collected and shown from the send side @@ -2826,6 +2825,7 @@ class FiroWallet extends CoinServiceAPI { _lelantusTransactionData = Future(() => newTxData); await DB.instance.put( boxName: walletId, key: 'latest_lelantus_tx_model', value: newTxData); + return newTxData; } Future _getMintHex(int amount, int index) async { @@ -3122,24 +3122,44 @@ class FiroWallet extends CoinServiceAPI { List allAddresses) async { try { List> allTxHashes = []; - // int latestTxnBlockHeight = 0; - for (final address in allAddresses) { - final scripthash = AddressUtils.convertToScriptHash(address, _network); - final txs = await electrumXClient.getHistory(scripthash: scripthash); - for (final map in txs) { - if (!allTxHashes.contains(map)) { - map['address'] = address; - allTxHashes.add(map); + final Map>> batches = {}; + final Map requestIdToAddressMap = {}; + const batchSizeMax = 100; + int batchNumber = 0; + for (int i = 0; i < allAddresses.length; i++) { + if (batches[batchNumber] == null) { + batches[batchNumber] = {}; + } + final scripthash = + AddressUtils.convertToScriptHash(allAddresses[i], _network); + final id = Logger.isTestEnv ? "$i" : const Uuid().v1(); + requestIdToAddressMap[id] = allAddresses[i]; + batches[batchNumber]!.addAll({ + id: [scripthash] + }); + if (i % batchSizeMax == batchSizeMax - 1) { + batchNumber++; + } + } + + for (int i = 0; i < batches.length; i++) { + final response = + await _electrumXClient.getBatchHistory(args: batches[i]!); + for (final entry in response.entries) { + for (int j = 0; j < entry.value.length; j++) { + entry.value[j]["address"] = requestIdToAddressMap[entry.key]; + if (!allTxHashes.contains(entry.value[j])) { + allTxHashes.add(entry.value[j]); + } } } } return allTxHashes; } catch (e, s) { - Logging.instance.log("Exception caught in _fetchHistory(): $e\n$s", - level: LogLevel.Error); - return []; + Logging.instance.log("_fetchHistory: $e\n$s", level: LogLevel.Error); + rethrow; } } @@ -3178,20 +3198,11 @@ class FiroWallet extends CoinServiceAPI { } } - List> allTransactions = []; - - for (final txHash in allTxHashes) { - final tx = await cachedElectrumXClient.getTransaction( - txHash: txHash["tx_hash"] as String, - verbose: true, - coin: coin, - ); - // delete unused large parts - tx.remove("hex"); - tx.remove("lelantusData"); - - allTransactions.add(tx); + List hashes = []; + for (var element in allTxHashes) { + hashes.add(element['tx_hash'] as String); } + List> allTransactions = await fastFetch(hashes); Logging.instance.log("allTransactions length: ${allTransactions.length}", level: LogLevel.Info); @@ -3441,81 +3452,83 @@ class FiroWallet extends CoinServiceAPI { } Future _fetchUtxoData() async { - final List allAddresses = []; - final receivingAddresses = - DB.instance.get(boxName: walletId, key: 'receivingAddresses') - as List; - final changeAddresses = - DB.instance.get(boxName: walletId, key: 'changeAddresses') - as List; - - for (var i = 0; i < receivingAddresses.length; i++) { - if (!allAddresses.contains(receivingAddresses[i])) { - allAddresses.add(receivingAddresses[i] as String); - } - } - for (var i = 0; i < changeAddresses.length; i++) { - if (!allAddresses.contains(changeAddresses[i])) { - allAddresses.add(changeAddresses[i] as String); - } - } + final List allAddresses = await _fetchAllOwnAddresses(); try { - final utxoData = >>[]; + final fetchedUtxoList = >>[]; + final Map>> batches = {}; + const batchSizeMax = 100; + int batchNumber = 0; for (int i = 0; i < allAddresses.length; i++) { + if (batches[batchNumber] == null) { + batches[batchNumber] = {}; + } final scripthash = AddressUtils.convertToScriptHash(allAddresses[i], _network); - final utxos = await electrumXClient.getUTXOs(scripthash: scripthash); - if (utxos.isNotEmpty) { - utxoData.add(utxos); + batches[batchNumber]!.addAll({ + scripthash: [scripthash] + }); + if (i % batchSizeMax == batchSizeMax - 1) { + batchNumber++; } } - Decimal currentPrice = await firoPrice; + for (int i = 0; i < batches.length; i++) { + final response = + await _electrumXClient.getBatchUTXOs(args: batches[i]!); + for (final entry in response.entries) { + if (entry.value.isNotEmpty) { + fetchedUtxoList.add(entry.value); + } + } + } + final priceData = + await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); + Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; final List> outputArray = []; int satoshiBalance = 0; int satoshiBalancePending = 0; - for (int i = 0; i < utxoData.length; i++) { - for (int j = 0; j < utxoData[i].length; j++) { - int value = utxoData[i][j]["value"] as int; + for (int i = 0; i < fetchedUtxoList.length; i++) { + for (int j = 0; j < fetchedUtxoList[i].length; j++) { + int value = fetchedUtxoList[i][j]["value"] as int; satoshiBalance += value; final txn = await cachedElectrumXClient.getTransaction( - txHash: utxoData[i][j]["tx_hash"] as String, + txHash: fetchedUtxoList[i][j]["tx_hash"] as String, verbose: true, coin: coin, ); - final Map tx = {}; + final Map utxo = {}; final int confirmations = txn["confirmations"] as int? ?? 0; final bool confirmed = confirmations >= MINIMUM_CONFIRMATIONS; if (!confirmed) { satoshiBalancePending += value; } - tx["txid"] = txn["txid"]; - tx["vout"] = utxoData[i][j]["tx_pos"]; - tx["value"] = value; + utxo["txid"] = txn["txid"]; + utxo["vout"] = fetchedUtxoList[i][j]["tx_pos"]; + utxo["value"] = value; - tx["status"] = {}; - tx["status"]["confirmed"] = confirmed; - tx["status"]["confirmations"] = confirmations; - tx["status"]["confirmed"] = + utxo["status"] = {}; + utxo["status"]["confirmed"] = confirmed; + utxo["status"]["confirmations"] = confirmations; + utxo["status"]["confirmed"] = txn["confirmations"] == null ? false : txn["confirmations"] > 0; - tx["status"]["block_height"] = txn["height"]; - tx["status"]["block_hash"] = txn["blockhash"]; - tx["status"]["block_time"] = txn["blocktime"]; + utxo["status"]["block_height"] = fetchedUtxoList[i][j]["height"]; + utxo["status"]["block_hash"] = txn["blockhash"]; + utxo["status"]["block_time"] = txn["blocktime"]; final fiatValue = ((Decimal.fromInt(value) * currentPrice) / Decimal.fromInt(Constants.satsPerCoin)) .toDecimal(scaleOnInfinitePrecision: 2); - tx["rawWorth"] = fiatValue; - tx["fiatWorth"] = fiatValue.toString(); - tx["is_coinbase"] = txn['vin'][0]['coinbase'] != null; - outputArray.add(tx); + utxo["rawWorth"] = fiatValue; + utxo["fiatWorth"] = fiatValue.toString(); + utxo["is_coinbase"] = txn['vin'][0]['coinbase'] != null; + outputArray.add(utxo); } } @@ -3538,13 +3551,19 @@ class FiroWallet extends CoinServiceAPI { final dataModel = UtxoData.fromJson(result); final List allOutputs = dataModel.unspentOutputArray; - // Logging.instance.log('Outputs fetched: $allOutputs'); + Logging.instance + .log('Outputs fetched: $allOutputs', level: LogLevel.Info); await _sortOutputs(allOutputs); await DB.instance.put( boxName: walletId, key: 'latest_utxo_model', value: dataModel); + // await DB.instance.put( + // boxName: walletId, + // key: 'totalBalance', + // value: dataModel.satoshiBalance); return dataModel; - } catch (e) { - // Logging.instance.log("Output fetch unsuccessful: $e\n$s"); + } catch (e, s) { + Logging.instance + .log("Output fetch unsuccessful: $e\n$s", level: LogLevel.Error); final latestTxModel = DB.instance.get(boxName: walletId, key: 'latest_utxo_model') as models.UtxoData?; @@ -4056,6 +4075,135 @@ class FiroWallet extends CoinServiceAPI { bool longMutex = false; + Future> getSetDataMap(int latestSetId) async { + final Map setDataMap = {}; + final anonymitySets = await fetchAnonymitySets(); + for (int setId = 1; setId <= latestSetId; setId++) { + final setData = anonymitySets + .firstWhere((element) => element["setId"] == setId, orElse: () => {}); + + if (setData.isNotEmpty) { + setDataMap[setId] = setData; + } + } + return setDataMap; + } + + Future _makeDerivations( + String suppliedMnemonic, int maxUnusedAddressGap) async { + List receivingAddressArray = []; + List changeAddressArray = []; + + int receivingIndex = -1; + int changeIndex = -1; + + // The gap limit will be capped at 20 + int receivingGapCounter = 0; + int changeGapCounter = 0; + + await fillAddresses(suppliedMnemonic, + numberOfThreads: Platform.numberOfProcessors - isolates.length - 1); + + final receiveDerivationsString = + await _secureStore.read(key: "${walletId}_receiveDerivations"); + final changeDerivationsString = + await _secureStore.read(key: "${walletId}_changeDerivations"); + + final receiveDerivations = Map.from( + jsonDecode(receiveDerivationsString ?? "{}") as Map); + final changeDerivations = Map.from( + jsonDecode(changeDerivationsString ?? "{}") as Map); + + // log("rcv: $receiveDerivations"); + // log("chg: $changeDerivations"); + + // Deriving and checking for receiving addresses + for (var i = 0; i < receiveDerivations.length; i++) { + // Break out of loop when receivingGapCounter hits maxUnusedAddressGap + // Same gap limit for change as for receiving, breaks when it hits maxUnusedAddressGap + if (receivingGapCounter >= maxUnusedAddressGap && + changeGapCounter >= maxUnusedAddressGap) { + break; + } + + final receiveDerivation = receiveDerivations["$i"]; + final address = receiveDerivation['address'] as String; + + final changeDerivation = changeDerivations["$i"]; + final _address = changeDerivation['address'] as String; + Future? futureNumTxs; + Future? _futureNumTxs; + if (receivingGapCounter < maxUnusedAddressGap) { + futureNumTxs = _getReceivedTxCount(address: address); + } + if (changeGapCounter < maxUnusedAddressGap) { + _futureNumTxs = _getReceivedTxCount(address: _address); + } + try { + if (futureNumTxs != null) { + int numTxs = await futureNumTxs; + if (numTxs >= 1) { + receivingIndex = i; + receivingAddressArray.add(address); + } else if (numTxs == 0) { + receivingGapCounter += 1; + } + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s", + level: LogLevel.Error); + rethrow; + } + + try { + if (_futureNumTxs != null) { + int numTxs = await _futureNumTxs; + if (numTxs >= 1) { + changeIndex = i; + changeAddressArray.add(_address); + } else if (numTxs == 0) { + changeGapCounter += 1; + } + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s", + level: LogLevel.Error); + rethrow; + } + } + + // If restoring a wallet that never received any funds, then set receivingArray manually + // If we didn't do this, it'd store an empty array + if (receivingIndex == -1) { + final String receivingAddress = await _generateAddressForChain(0, 0); + receivingAddressArray.add(receivingAddress); + } + + // If restoring a wallet that never sent any funds with change, then set changeArray + // manually. If we didn't do this, it'd store an empty array. + if (changeIndex == -1) { + final String changeAddress = await _generateAddressForChain(1, 0); + changeAddressArray.add(changeAddress); + } + + await DB.instance.put( + boxName: walletId, + key: 'receivingAddresses', + value: receivingAddressArray); + await DB.instance.put( + boxName: walletId, key: 'changeAddresses', value: changeAddressArray); + await DB.instance.put( + boxName: walletId, + key: 'receivingIndex', + value: receivingIndex == -1 ? 0 : receivingIndex); + await DB.instance.put( + boxName: walletId, + key: 'changeIndex', + value: changeIndex == -1 ? 0 : changeIndex); + } + /// Recovers wallet from [suppliedMnemonic]. Expects a valid mnemonic. Future _recoverWalletFromBIP32SeedPhrase( String suppliedMnemonic, int maxUnusedAddressGap) async { @@ -4063,137 +4211,20 @@ class FiroWallet extends CoinServiceAPI { Logging.instance .log("PROCESSORS ${Platform.numberOfProcessors}", level: LogLevel.Info); try { - final Map setDataMap = {}; final latestSetId = await getLatestSetId(); - final anonymitySets = await fetchAnonymitySets(); - for (int setId = 1; setId <= latestSetId; setId++) { - final setData = anonymitySets.firstWhere( - (element) => element["setId"] == setId, - orElse: () => {}); - - if (setData.isNotEmpty) { - setDataMap[setId] = setData; - } - } + final setDataMap = getSetDataMap(latestSetId); final usedSerialNumbers = getUsedCoinSerials(); + final makeDerivations = + _makeDerivations(suppliedMnemonic, maxUnusedAddressGap); - List receivingAddressArray = []; - List changeAddressArray = []; - - int receivingIndex = -1; - int changeIndex = -1; - - // The gap limit will be capped at 20 - int receivingGapCounter = 0; - int changeGapCounter = 0; - - await fillAddresses(suppliedMnemonic, - numberOfThreads: Platform.numberOfProcessors - isolates.length - 1); - - final receiveDerivationsString = - await _secureStore.read(key: "${walletId}_receiveDerivations"); - final changeDerivationsString = - await _secureStore.read(key: "${walletId}_changeDerivations"); - - final receiveDerivations = Map.from( - jsonDecode(receiveDerivationsString ?? "{}") as Map); - final changeDerivations = Map.from( - jsonDecode(changeDerivationsString ?? "{}") as Map); - - // log("rcv: $receiveDerivations"); - // log("chg: $changeDerivations"); - - // Deriving and checking for receiving addresses - for (var i = 0; i < receiveDerivations.length; i++) { - // Break out of loop when receivingGapCounter hits maxUnusedAddressGap - // Same gap limit for change as for receiving, breaks when it hits maxUnusedAddressGap - if (receivingGapCounter >= maxUnusedAddressGap && - changeGapCounter >= maxUnusedAddressGap) { - break; - } - - final receiveDerivation = receiveDerivations["$i"]; - final address = receiveDerivation['address'] as String; - - final changeDerivation = changeDerivations["$i"]; - final _address = changeDerivation['address'] as String; - Future? futureNumTxs; - Future? _futureNumTxs; - if (receivingGapCounter < maxUnusedAddressGap) { - futureNumTxs = _getReceivedTxCount(address: address); - } - if (changeGapCounter < maxUnusedAddressGap) { - _futureNumTxs = _getReceivedTxCount(address: _address); - } - try { - if (futureNumTxs != null) { - int numTxs = await futureNumTxs; - if (numTxs >= 1) { - receivingIndex = i; - receivingAddressArray.add(address); - } else if (numTxs == 0) { - receivingGapCounter += 1; - } - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s", - level: LogLevel.Error); - rethrow; - } - - try { - if (_futureNumTxs != null) { - int numTxs = await _futureNumTxs; - if (numTxs >= 1) { - changeIndex = i; - changeAddressArray.add(_address); - } else if (numTxs == 0) { - changeGapCounter += 1; - } - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s", - level: LogLevel.Error); - rethrow; - } - } - - // If restoring a wallet that never received any funds, then set receivingArray manually - // If we didn't do this, it'd store an empty array - if (receivingIndex == -1) { - final String receivingAddress = await _generateAddressForChain(0, 0); - receivingAddressArray.add(receivingAddress); - } - - // If restoring a wallet that never sent any funds with change, then set changeArray - // manually. If we didn't do this, it'd store an empty array. - if (changeIndex == -1) { - final String changeAddress = await _generateAddressForChain(1, 0); - changeAddressArray.add(changeAddress); - } - - await DB.instance.put( - boxName: walletId, - key: 'receivingAddresses', - value: receivingAddressArray); - await DB.instance.put( - boxName: walletId, key: 'changeAddresses', value: changeAddressArray); - await DB.instance.put( - boxName: walletId, - key: 'receivingIndex', - value: receivingIndex == -1 ? 0 : receivingIndex); - await DB.instance.put( - boxName: walletId, - key: 'changeIndex', - value: changeIndex == -1 ? 0 : changeIndex); await DB.instance .put(boxName: walletId, key: "id", value: _walletId); await DB.instance .put(boxName: walletId, key: "isFavorite", value: false); - await _restore(latestSetId, setDataMap, await usedSerialNumbers); + await Future.wait([usedSerialNumbers, setDataMap, makeDerivations]); + + await _restore(latestSetId, await setDataMap, await usedSerialNumbers); longMutex = false; } catch (e, s) { longMutex = false; @@ -4207,27 +4238,23 @@ class FiroWallet extends CoinServiceAPI { Future _restore(int latestSetId, Map setDataMap, dynamic usedSerialNumbers) async { final mnemonic = await _secureStore.read(key: '${_walletId}_mnemonic'); - models.TransactionData data = await _txnData; + final dataFuture = _txnData; final String currency = _prefs.currency; final Decimal currentPrice = await firoPrice; - final locale = await Devicelocale.currentLocale; ReceivePort receivePort = await getIsolate({ "function": "restore", "mnemonic": mnemonic, - "transactionData": data, - "currency": currency, "coin": coin, "latestSetId": latestSetId, "setDataMap": setDataMap, "usedSerialNumbers": usedSerialNumbers, "network": _network, - "currentPrice": currentPrice, - "locale": locale, }); - var message = await receivePort.first; - if (message is String) { + await Future.wait([dataFuture]); + var result = await receivePort.first; + if (result is String) { Logging.instance .log("restore() ->> this is a string", level: LogLevel.Error); stop(receivePort); @@ -4235,6 +4262,9 @@ class FiroWallet extends CoinServiceAPI { } stop(receivePort); + final message = await staticProcessRestore( + (await dataFuture), result as Map); + await DB.instance.put( boxName: walletId, key: 'mintIndex', value: message['mintIndex']); await DB.instance.put( @@ -4274,11 +4304,17 @@ class FiroWallet extends CoinServiceAPI { final latestSetId = await getLatestSetId(); final List> sets = []; + List>> anonFutures = []; for (int i = 1; i <= latestSetId; i++) { - Map set = await cachedElectrumXClient.getAnonymitySet( + final set = cachedElectrumXClient.getAnonymitySet( groupId: "$i", coin: coin, ); + anonFutures.add(set); + } + await Future.wait(anonFutures); + for (int i = 1; i <= latestSetId; i++) { + Map set = (await anonFutures[i - 1]); set["setId"] = i; sets.add(set); } @@ -4567,6 +4603,49 @@ class FiroWallet extends CoinServiceAPI { return available - estimatedFee; } + Future>> fastFetch(List allTxHashes) async { + List> allTransactions = []; + + const futureLimit = 30; + List>> transactionFutures = []; + int currentFutureCount = 0; + for (final txHash in allTxHashes) { + Future> transactionFuture = + cachedElectrumXClient.getTransaction( + txHash: txHash, + verbose: true, + coin: coin, + ); + transactionFutures.add(transactionFuture); + currentFutureCount++; + if (currentFutureCount > futureLimit) { + currentFutureCount = 0; + await Future.wait(transactionFutures); + for (final fTx in transactionFutures) { + final tx = await fTx; + // delete unused large parts + tx.remove("hex"); + tx.remove("lelantusData"); + + allTransactions.add(tx); + } + } + } + if (currentFutureCount != 0) { + currentFutureCount = 0; + await Future.wait(transactionFutures); + for (final fTx in transactionFutures) { + final tx = await fTx; + // delete unused large parts + tx.remove("hex"); + tx.remove("lelantusData"); + + allTransactions.add(tx); + } + } + return allTransactions; + } + Future> getJMintTransactions( CachedElectrumX cachedClient, List transactions, @@ -4577,14 +4656,12 @@ class FiroWallet extends CoinServiceAPI { ) async { try { List txs = []; + List> allTransactions = + await fastFetch(transactions); - for (int i = 0; i < transactions.length; i++) { + for (int i = 0; i < allTransactions.length; i++) { try { - final tx = await cachedClient.getTransaction( - txHash: transactions[i], - verbose: true, - coin: coin, - ); + final tx = allTransactions[i]; tx["confirmed_status"] = tx["confirmations"] != null && tx["confirmations"] as int > 0; diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index cd297f121..578cf35a4 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -34,6 +34,8 @@ class Prefs extends ChangeNotifier { _backupFrequencyType = await _getBackupFrequencyType(); _lastAutoBackup = await _getLastAutoBackup(); _hideBlockExplorerWarning = await _getHideBlockExplorerWarning(); + _gotoWalletOnStartup = await _getGotoWalletOnStartup(); + _startupWalletId = await _getStartupWalletId(); _initialized = true; } @@ -468,8 +470,6 @@ class Prefs extends ChangeNotifier { boxName: DB.boxNamePrefs, key: "autoBackupFileUri") as DateTime?; } - - // auto backup bool _hideBlockExplorerWarning = false; @@ -480,9 +480,9 @@ class Prefs extends ChangeNotifier { if (_hideBlockExplorerWarning != hideBlockExplorerWarning) { DB.instance .put( - boxName: DB.boxNamePrefs, - key: "hideBlockExplorerWarning", - value: hideBlockExplorerWarning) + boxName: DB.boxNamePrefs, + key: "hideBlockExplorerWarning", + value: hideBlockExplorerWarning) .then((_) { _hideBlockExplorerWarning = hideBlockExplorerWarning; notifyListeners(); @@ -492,7 +492,56 @@ class Prefs extends ChangeNotifier { Future _getHideBlockExplorerWarning() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, key: "hideBlockExplorerWarning") as bool? ?? + boxName: DB.boxNamePrefs, + key: "hideBlockExplorerWarning") as bool? ?? false; } + + // auto backup + + bool _gotoWalletOnStartup = false; + + bool get gotoWalletOnStartup => _gotoWalletOnStartup; + + set gotoWalletOnStartup(bool gotoWalletOnStartup) { + if (_gotoWalletOnStartup != gotoWalletOnStartup) { + DB.instance + .put( + boxName: DB.boxNamePrefs, + key: "gotoWalletOnStartup", + value: gotoWalletOnStartup) + .then((_) { + _gotoWalletOnStartup = gotoWalletOnStartup; + notifyListeners(); + }); + } + } + + Future _getGotoWalletOnStartup() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, key: "gotoWalletOnStartup") as bool? ?? + false; + } + + // startup wallet id + + String? _startupWalletId; + + String? get startupWalletId => _startupWalletId; + + set startupWalletId(String? startupWalletId) { + if (this.startupWalletId != startupWalletId) { + DB.instance.put( + boxName: DB.boxNamePrefs, + key: "startupWalletId", + value: startupWalletId); + _startupWalletId = startupWalletId; + notifyListeners(); + } + } + + Future _getStartupWalletId() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, key: "startupWalletId") as String?; + } } diff --git a/test/cached_electrumx_test.mocks.dart b/test/cached_electrumx_test.mocks.dart index 1c72d43c2..ce37c93d1 100644 --- a/test/cached_electrumx_test.mocks.dart +++ b/test/cached_electrumx_test.mocks.dart @@ -358,6 +358,28 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs { super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup), returnValueForMissingStub: null); @override + bool get hideBlockExplorerWarning => + (super.noSuchMethod(Invocation.getter(#hideBlockExplorerWarning), + returnValue: false) as bool); + @override + set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => + super.noSuchMethod( + Invocation.setter( + #hideBlockExplorerWarning, hideBlockExplorerWarning), + returnValueForMissingStub: null); + @override + bool get gotoWalletOnStartup => + (super.noSuchMethod(Invocation.getter(#gotoWalletOnStartup), + returnValue: false) as bool); + @override + set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( + Invocation.setter(#gotoWalletOnStartup, gotoWalletOnStartup), + returnValueForMissingStub: null); + @override + set startupWalletId(String? startupWalletId) => + super.noSuchMethod(Invocation.setter(#startupWalletId, startupWalletId), + returnValueForMissingStub: null); + @override bool get hasListeners => (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) as bool); diff --git a/test/electrumx_test.mocks.dart b/test/electrumx_test.mocks.dart index 4ef0b01ec..fb115f9ab 100644 --- a/test/electrumx_test.mocks.dart +++ b/test/electrumx_test.mocks.dart @@ -207,6 +207,28 @@ class MockPrefs extends _i1.Mock implements _i4.Prefs { super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup), returnValueForMissingStub: null); @override + bool get hideBlockExplorerWarning => + (super.noSuchMethod(Invocation.getter(#hideBlockExplorerWarning), + returnValue: false) as bool); + @override + set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => + super.noSuchMethod( + Invocation.setter( + #hideBlockExplorerWarning, hideBlockExplorerWarning), + returnValueForMissingStub: null); + @override + bool get gotoWalletOnStartup => + (super.noSuchMethod(Invocation.getter(#gotoWalletOnStartup), + returnValue: false) as bool); + @override + set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( + Invocation.setter(#gotoWalletOnStartup, gotoWalletOnStartup), + returnValueForMissingStub: null); + @override + set startupWalletId(String? startupWalletId) => + super.noSuchMethod(Invocation.setter(#startupWalletId, startupWalletId), + returnValueForMissingStub: null); + @override bool get hasListeners => (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) as bool); diff --git a/test/models/exchange/estimated_rate_exchange_form_state_test.mocks.dart b/test/models/exchange/estimated_rate_exchange_form_state_test.mocks.dart index 27aa1a772..2e496569f 100644 --- a/test/models/exchange/estimated_rate_exchange_form_state_test.mocks.dart +++ b/test/models/exchange/estimated_rate_exchange_form_state_test.mocks.dart @@ -8,18 +8,20 @@ import 'package:decimal/decimal.dart' as _i7; import 'package:http/http.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart' - as _i12; + as _i13; import 'package:stackwallet/models/exchange/change_now/change_now_response.dart' as _i2; +import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart' + as _i9; import 'package:stackwallet/models/exchange/change_now/currency.dart' as _i6; import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart' as _i8; import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart' - as _i10; -import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart' as _i11; +import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart' + as _i12; import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart' - as _i9; + as _i10; import 'package:stackwallet/services/change_now/change_now.dart' as _i3; // ignore_for_file: type=lint @@ -98,38 +100,42 @@ class MockChangeNow extends _i1.Mock implements _i3.ChangeNow { as _i5 .Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>); @override - _i5.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>> - getEstimatedFixedRateExchangeAmount( + _i5.Future<_i2.ChangeNowResponse<_i9.CNExchangeEstimate>> + getEstimatedExchangeAmountV2( {String? fromTicker, String? toTicker, - _i7.Decimal? fromAmount, - bool? useRateId = true, + _i9.CNEstimateType? fromOrTo, + _i7.Decimal? amount, + String? fromNetwork, + String? toNetwork, + _i9.CNFlowType? flow = _i9.CNFlowType.standard, String? apiKey}) => (super.noSuchMethod( - Invocation.method(#getEstimatedFixedRateExchangeAmount, [], { + Invocation.method(#getEstimatedExchangeAmountV2, [], { #fromTicker: fromTicker, #toTicker: toTicker, - #fromAmount: fromAmount, - #useRateId: useRateId, + #fromOrTo: fromOrTo, + #amount: amount, + #fromNetwork: fromNetwork, + #toNetwork: toNetwork, + #flow: flow, #apiKey: apiKey }), returnValue: Future< - _i2.ChangeNowResponse< - _i8.EstimatedExchangeAmount>>.value( - _FakeChangeNowResponse_0<_i8.EstimatedExchangeAmount>())) - as _i5 - .Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>); + _i2.ChangeNowResponse<_i9.CNExchangeEstimate>>.value( + _FakeChangeNowResponse_0<_i9.CNExchangeEstimate>())) + as _i5.Future<_i2.ChangeNowResponse<_i9.CNExchangeEstimate>>); @override - _i5.Future<_i2.ChangeNowResponse>> + _i5.Future<_i2.ChangeNowResponse>> getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod( Invocation.method( #getAvailableFixedRateMarkets, [], {#apiKey: apiKey}), returnValue: - Future<_i2.ChangeNowResponse>>.value( - _FakeChangeNowResponse_0>())) as _i5 - .Future<_i2.ChangeNowResponse>>); + Future<_i2.ChangeNowResponse>>.value( + _FakeChangeNowResponse_0>())) as _i5 + .Future<_i2.ChangeNowResponse>>); @override - _i5.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>> + _i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>> createStandardExchangeTransaction( {String? fromTicker, String? toTicker, @@ -155,11 +161,11 @@ class MockChangeNow extends _i1.Mock implements _i3.ChangeNow { #apiKey: apiKey }), returnValue: Future< - _i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value( - _FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i5 - .Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>); + _i2.ChangeNowResponse<_i11.ExchangeTransaction>>.value( + _FakeChangeNowResponse_0<_i11.ExchangeTransaction>())) as _i5 + .Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>>); @override - _i5.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>> + _i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>> createFixedRateExchangeTransaction( {String? fromTicker, String? toTicker, @@ -187,26 +193,26 @@ class MockChangeNow extends _i1.Mock implements _i3.ChangeNow { #apiKey: apiKey }), returnValue: Future< - _i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value( - _FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i5 - .Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>); + _i2.ChangeNowResponse<_i11.ExchangeTransaction>>.value( + _FakeChangeNowResponse_0<_i11.ExchangeTransaction>())) as _i5 + .Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>>); @override - _i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>> + _i5.Future<_i2.ChangeNowResponse<_i12.ExchangeTransactionStatus>> getTransactionStatus({String? id, String? apiKey}) => (super.noSuchMethod( Invocation.method( #getTransactionStatus, [], {#id: id, #apiKey: apiKey}), returnValue: - Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>.value( - _FakeChangeNowResponse_0<_i11.ExchangeTransactionStatus>())) as _i5 - .Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>); + Future<_i2.ChangeNowResponse<_i12.ExchangeTransactionStatus>>.value( + _FakeChangeNowResponse_0<_i12.ExchangeTransactionStatus>())) as _i5 + .Future<_i2.ChangeNowResponse<_i12.ExchangeTransactionStatus>>); @override - _i5.Future<_i2.ChangeNowResponse>> + _i5.Future<_i2.ChangeNowResponse>> getAvailableFloatingRatePairs({bool? includePartners = false}) => (super .noSuchMethod( Invocation.method(#getAvailableFloatingRatePairs, [], {#includePartners: includePartners}), returnValue: - Future<_i2.ChangeNowResponse>>.value( - _FakeChangeNowResponse_0>())) as _i5 - .Future<_i2.ChangeNowResponse>>); + Future<_i2.ChangeNowResponse>>.value( + _FakeChangeNowResponse_0>())) as _i5 + .Future<_i2.ChangeNowResponse>>); } diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart index 31130ec28..c28b76d74 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart @@ -334,6 +334,10 @@ class MockManager extends _i1.Mock implements _i11.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i8.Future); @override + _i8.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i8.Future); + @override void addListener(_i10.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart index 618cef5d8..7829cd299 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart @@ -315,6 +315,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart index 034e4edf8..0aa30d54f 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart @@ -313,6 +313,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/exchange/exchange_view_test.mocks.dart b/test/screen_tests/exchange/exchange_view_test.mocks.dart index aa517d09b..0295bfaed 100644 --- a/test/screen_tests/exchange/exchange_view_test.mocks.dart +++ b/test/screen_tests/exchange/exchange_view_test.mocks.dart @@ -9,18 +9,20 @@ import 'package:decimal/decimal.dart' as _i15; import 'package:http/http.dart' as _i13; import 'package:mockito/mockito.dart' as _i1; import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart' - as _i19; + as _i20; import 'package:stackwallet/models/exchange/change_now/change_now_response.dart' as _i2; +import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart' + as _i17; import 'package:stackwallet/models/exchange/change_now/currency.dart' as _i14; import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart' as _i16; import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart' as _i10; import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart' - as _i18; + as _i19; import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart' - as _i17; + as _i18; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart' as _i5; import 'package:stackwallet/services/change_now/change_now.dart' as _i12; @@ -183,6 +185,28 @@ class MockPrefs extends _i1.Mock implements _i3.Prefs { super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup), returnValueForMissingStub: null); @override + bool get hideBlockExplorerWarning => + (super.noSuchMethod(Invocation.getter(#hideBlockExplorerWarning), + returnValue: false) as bool); + @override + set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => + super.noSuchMethod( + Invocation.setter( + #hideBlockExplorerWarning, hideBlockExplorerWarning), + returnValueForMissingStub: null); + @override + bool get gotoWalletOnStartup => + (super.noSuchMethod(Invocation.getter(#gotoWalletOnStartup), + returnValue: false) as bool); + @override + set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( + Invocation.setter(#gotoWalletOnStartup, gotoWalletOnStartup), + returnValueForMissingStub: null); + @override + set startupWalletId(String? startupWalletId) => + super.noSuchMethod(Invocation.setter(#startupWalletId, startupWalletId), + returnValueForMissingStub: null); + @override bool get hasListeners => (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) as bool); @@ -386,36 +410,40 @@ class MockChangeNow extends _i1.Mock implements _i12.ChangeNow { as _i7 .Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>); @override - _i7.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>> - getEstimatedFixedRateExchangeAmount( + _i7.Future<_i2.ChangeNowResponse<_i17.CNExchangeEstimate>> + getEstimatedExchangeAmountV2( {String? fromTicker, String? toTicker, - _i15.Decimal? fromAmount, - bool? useRateId = true, + _i17.CNEstimateType? fromOrTo, + _i15.Decimal? amount, + String? fromNetwork, + String? toNetwork, + _i17.CNFlowType? flow = _i17.CNFlowType.standard, String? apiKey}) => (super.noSuchMethod( - Invocation.method(#getEstimatedFixedRateExchangeAmount, [], { + Invocation.method(#getEstimatedExchangeAmountV2, [], { #fromTicker: fromTicker, #toTicker: toTicker, - #fromAmount: fromAmount, - #useRateId: useRateId, + #fromOrTo: fromOrTo, + #amount: amount, + #fromNetwork: fromNetwork, + #toNetwork: toNetwork, + #flow: flow, #apiKey: apiKey }), returnValue: Future< - _i2.ChangeNowResponse< - _i16.EstimatedExchangeAmount>>.value( - _FakeChangeNowResponse_0<_i16.EstimatedExchangeAmount>())) - as _i7 - .Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>); + _i2.ChangeNowResponse<_i17.CNExchangeEstimate>>.value( + _FakeChangeNowResponse_0<_i17.CNExchangeEstimate>())) + as _i7.Future<_i2.ChangeNowResponse<_i17.CNExchangeEstimate>>); @override - _i7.Future<_i2.ChangeNowResponse>> + _i7.Future<_i2.ChangeNowResponse>> getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod( Invocation.method( #getAvailableFixedRateMarkets, [], {#apiKey: apiKey}), returnValue: - Future<_i2.ChangeNowResponse>>.value( - _FakeChangeNowResponse_0>())) as _i7 - .Future<_i2.ChangeNowResponse>>); + Future<_i2.ChangeNowResponse>>.value( + _FakeChangeNowResponse_0>())) as _i7 + .Future<_i2.ChangeNowResponse>>); @override _i7.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>> createStandardExchangeTransaction( @@ -479,22 +507,22 @@ class MockChangeNow extends _i1.Mock implements _i12.ChangeNow { _FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i7 .Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>); @override - _i7.Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>> + _i7.Future<_i2.ChangeNowResponse<_i19.ExchangeTransactionStatus>> getTransactionStatus({String? id, String? apiKey}) => (super.noSuchMethod( Invocation.method( #getTransactionStatus, [], {#id: id, #apiKey: apiKey}), returnValue: - Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>.value( - _FakeChangeNowResponse_0<_i18.ExchangeTransactionStatus>())) as _i7 - .Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>); + Future<_i2.ChangeNowResponse<_i19.ExchangeTransactionStatus>>.value( + _FakeChangeNowResponse_0<_i19.ExchangeTransactionStatus>())) as _i7 + .Future<_i2.ChangeNowResponse<_i19.ExchangeTransactionStatus>>); @override - _i7.Future<_i2.ChangeNowResponse>> + _i7.Future<_i2.ChangeNowResponse>> getAvailableFloatingRatePairs({bool? includePartners = false}) => (super .noSuchMethod( Invocation.method(#getAvailableFloatingRatePairs, [], {#includePartners: includePartners}), returnValue: - Future<_i2.ChangeNowResponse>>.value( - _FakeChangeNowResponse_0>())) as _i7 - .Future<_i2.ChangeNowResponse>>); + Future<_i2.ChangeNowResponse>>.value( + _FakeChangeNowResponse_0>())) as _i7 + .Future<_i2.ChangeNowResponse>>); } diff --git a/test/screen_tests/lockscreen_view_screen_test.mocks.dart b/test/screen_tests/lockscreen_view_screen_test.mocks.dart index 81369cdf1..d7d0e193c 100644 --- a/test/screen_tests/lockscreen_view_screen_test.mocks.dart +++ b/test/screen_tests/lockscreen_view_screen_test.mocks.dart @@ -483,6 +483,10 @@ class MockManager extends _i1.Mock implements _i12.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i9.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart index 910b5404b..5d1ac1f5d 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart @@ -373,6 +373,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i6.Future); @override + _i6.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i6.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart index 1c298aa7e..c2ac8447a 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart @@ -373,6 +373,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i6.Future); @override + _i6.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i6.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart index 4f00f5f8e..04a0dac53 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart @@ -373,6 +373,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i6.Future); @override + _i6.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i6.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart index bab971459..80c9677f1 100644 --- a/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart @@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart index da239f761..c34ced0de 100644 --- a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart @@ -371,6 +371,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i6.Future); @override + _i6.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i6.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart index 429b5b6b6..f37383e4b 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart @@ -483,6 +483,10 @@ class MockManager extends _i1.Mock implements _i12.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i9.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart index 7f0d56002..48de40a42 100644 --- a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart @@ -399,6 +399,10 @@ class MockManager extends _i1.Mock implements _i12.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i8.Future); @override + _i8.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i8.Future); + @override void addListener(_i11.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart b/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart index 769a9263f..e93ed2ce7 100644 --- a/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart @@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart index ec7ea5959..b95f2cc22 100644 --- a/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart @@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart index b12808703..17fb394ac 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart @@ -360,6 +360,10 @@ class MockManager extends _i1.Mock implements _i11.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i8.Future); @override + _i8.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i8.Future); + @override void addListener(_i10.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart index ca45f0303..5c08dc466 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart @@ -360,6 +360,10 @@ class MockManager extends _i1.Mock implements _i11.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i8.Future); @override + _i8.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i8.Future); + @override void addListener(_i10.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart index 6f6475108..10d20aa58 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart @@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart index 3c9a86351..d5760cd45 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart @@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart index c3301a326..969da34a1 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart @@ -371,6 +371,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i6.Future); @override + _i6.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i6.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart index c5645ad09..336bb7222 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart @@ -527,6 +527,10 @@ class MockManager extends _i1.Mock implements _i15.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i8.Future); @override + _i8.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i8.Future); + @override void addListener(_i14.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart index 379ceacd1..f74cec132 100644 --- a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart @@ -371,6 +371,10 @@ class MockManager extends _i1.Mock implements _i9.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i6.Future); @override + _i6.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i6.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart index d26dfc10c..337f73285 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart @@ -250,6 +250,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart index 623613b53..0fa3baaa5 100644 --- a/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart @@ -249,6 +249,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart index 86b635747..745dab4f3 100644 --- a/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart @@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart index c7e4bbe9a..72eb2f746 100644 --- a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart @@ -270,6 +270,10 @@ class MockManager extends _i1.Mock implements _i8.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i10.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart index 0ca83f21e..8cbf518ee 100644 --- a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart @@ -250,6 +250,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); diff --git a/test/services/coins/bitcoin/bitcoin_history_sample_data.dart b/test/services/coins/bitcoin/bitcoin_history_sample_data.dart index 64ac9f0d0..d2cdcb01e 100644 --- a/test/services/coins/bitcoin/bitcoin_history_sample_data.dart +++ b/test/services/coins/bitcoin/bitcoin_history_sample_data.dart @@ -1,98 +1,98 @@ final Map> historyBatchArgs0 = { + "k_0_0": ["269df6359accfa9b761f22c42bd10dcda6fbc6ad708476d2e1efd4b3aa3f6e2f"], + "k_0_1": ["a328ae88ebce63c0010709ae900c199df2b585cdebce53a6291886dfdcc28c63"], + "k_0_2": ["16a765b755fcd070f5196f5a31d6370a799c5f2adac922bff8768f1c4e710b25"], + "k_0_3": ["f3baba5f725918c2038e7a80539088b2c0c636c6a27afb52b94b908241c7a5f8"], + "k_0_4": ["e72d39bd9e7b455503a2acae33647cb5ce33c1dc99ef9ebed0e7501b5e387f86"], + "k_0_5": ["7d9beb602c191843af7e0aef8d588f01bb9c5e2252a3eb29d92bb05833f924df"], + "k_0_6": ["6ab497d1554279dc0723ba1513ff687991454e6f720a79de2c1403b0de7fef73"], + "k_0_7": ["fb33f2529224ddbb65056dd98296f5b9aad5fd31d2ec4db51520e38809d4ec38"], + "k_0_8": ["e7cb000615b4ecf2d483279fa437f37acd9deab1e9f3f4e94d4419c33e521753"], + "k_0_9": ["d07ba311749923b3d6c2a291a3be9bcaf43e1fb1b97cb1b262c93672eda50823"], + "k_0_10": [ + "cae047098603b4f4dedb5a5f7f3a0e0f6f0233eed8e0c06f47855c11ccf0a664" + ], + "k_0_11": ["6bdefed2a12896dd9840efd83c265e08f9131406c98839d4089377f517cbb977"] +}; +final Map> historyBatchArgs1 = { + "k_0_0": ["8e33ea48a5fabb82e44e9bb11560e853041eacda2b5e88746331d1104b19a4f9"], + "k_0_1": ["2f18558e5d3015cb6578aee1c3e4b645725fa4e1d26ce22cb31c9949f3b4957c"], + "k_0_2": ["0a9d1dd7a5add91ebff4568ce089d2186f5cb6c5a8e5575a7911fa8ecdfbe812"], + "k_0_3": ["764109deac38c18dddd859f5a00858d4a96ff581a6cc53b17690656d7a34f93c"], + "k_0_4": ["da6c2426877e96589922557d776960ba128d5b1cea6c10cde5e13139e9995acb"], + "k_0_5": ["0a97fa18f6b11103b75006601a8f0c172bf20f79b9e81c95c0e8a5209f9ca573"], + "k_0_6": ["3f2403aee4715d46734dc026a6a6a095d417a6f15cb7144eb1ebdc8891ebb462"], + "k_0_7": ["e44745af4355eac1eda621c8fd17668b2d9ab3329af51abf6f5dfb9baf637a07"], + "k_0_8": ["947757dab98934e87c70171b67160777658eeccf9b74f3d6d9ad3f5ef14d0445"], + "k_0_9": ["b8401c59efd8d163fadc70f39cb02abcd8f8682cc3d5bfa8663c967ea1a6b962"], + "k_0_10": [ + "92edcb3e9da95893192481594ee8e9bef02e941bdf6e957731de967d2265fae6" + ], + "k_0_11": ["9dab36382b9ad792fb07816c6196a30a94c33f62814972da06552614ce97eb3a"] +}; +final Map> historyBatchArgs2 = { "k_0_0": ["dc64e048a76a88a721abe8b1e53d5a44bbbc83ff9b29449dffcd176d43e36e84"], - "s_0_0": ["5d2a7a01f09f3e1df3fb44d25cc53cfd4e6df25b9c3e3f710f4dbc3e2be3a8fc"], - "w_0_0": ["8e33ea48a5fabb82e44e9bb11560e853041eacda2b5e88746331d1104b19a4f9"], "k_0_1": ["06593b2d896751e8dda288bb6587b6bb6a1dee71d82a85457f5654f781e37b12"], - "s_0_1": ["26f92666caebb9a17b14f5b573b385348cdc80065472b8961091f3226d2f650f"], - "w_0_1": ["2f18558e5d3015cb6578aee1c3e4b645725fa4e1d26ce22cb31c9949f3b4957c"], "k_0_2": ["1478257da775ac14cc1120d022999fd35a8f51d5037e6fc11a17ca3dff43a28b"], - "s_0_2": ["20eef9f15b0707cb5779f077dadcaf0be96e7212f76557f96e5dc5fb2e5aafa7"], - "w_0_2": ["0a9d1dd7a5add91ebff4568ce089d2186f5cb6c5a8e5575a7911fa8ecdfbe812"], "k_0_3": ["69e435cb8ba991d2aced5b62271dcd40f0d3b19c38f19d5bc837231b3aef8475"], - "s_0_3": ["eca08355027cb9f27500ea2588ee532b87b1061f1931b00b57244efad2081b99"], - "w_0_3": ["764109deac38c18dddd859f5a00858d4a96ff581a6cc53b17690656d7a34f93c"], "k_0_4": ["a91771ce1cde7467c044c99165c3bc3239f402d18fe4e0eaf69ddf954f9545b4"], - "s_0_4": ["f85fb2cfd89ca882aa93546ce5c6aaed7eb13bd60417822dbfe8514eaa32260d"], - "w_0_4": ["da6c2426877e96589922557d776960ba128d5b1cea6c10cde5e13139e9995acb"], "k_0_5": ["d9d5e8d9b3090e310a40fb7ef3a398fbab300df4c4a3f729f7134caae2027a20"], - "s_0_5": ["4e87132185225b4c79d69ca74a1d1ea329b0864af2b2b216c126fda4dfe44bc3"], - "w_0_5": ["0a97fa18f6b11103b75006601a8f0c172bf20f79b9e81c95c0e8a5209f9ca573"], "k_0_6": ["29099731133ee2536a4f180e618e39669b7ae97867840736e02929d642ae05c9"], - "s_0_6": ["46ade50d7c161264887d93ea77aa86d01c74ce296ce4d6f44a97c82b4023bf5d"], - "w_0_6": ["3f2403aee4715d46734dc026a6a6a095d417a6f15cb7144eb1ebdc8891ebb462"], "k_0_7": ["9c898b834aeb60230a0c13ce1eec9efad0f6045a5ca4243287ab4c2d8c5484fe"], - "s_0_7": ["22ff3049e6ef43f20289b0ac8c8480c61b5502b32e07d5bb57fa8029d6943596"], - "w_0_7": ["e44745af4355eac1eda621c8fd17668b2d9ab3329af51abf6f5dfb9baf637a07"], "k_0_8": ["c1d5dfc51ae2bd98c3d7f61ca5187ba414e5aee7f11bbdb20685e66dfa758a63"], - "s_0_8": ["01c387618378a58c221b51ab65a7f8e95a6932cdb0a8634acfe62acee1cb6cdc"], - "w_0_8": ["947757dab98934e87c70171b67160777658eeccf9b74f3d6d9ad3f5ef14d0445"], "k_0_9": ["1335353bad1e12a2954ffdcc74d734d062a43c9750b7aee7eaa60bfd315fe0a4"], - "s_0_9": ["51928f94d8c56318810e604dfb7b4862755ce5b005ec4242ea46a485e356d24f"], - "w_0_9": ["b8401c59efd8d163fadc70f39cb02abcd8f8682cc3d5bfa8663c967ea1a6b962"], "k_0_10": [ "77a9b30ea64e6ea73566b850f39c792cc7e31e3134831860b3a7582372036029" ], - "s_0_10": [ - "8ea9acdeaa65f2d99ed93a3b0691601e768f6695fb46cd9b1edc0bca10406767" - ], - "w_0_10": [ - "92edcb3e9da95893192481594ee8e9bef02e941bdf6e957731de967d2265fae6" - ], - "k_0_11": [ - "f9692ce7c5edf102c891a980c732e2c058694cc9a5bcc92994175f8c21eed6ae" - ], - "s_0_11": [ - "7cda59a23e03a997b852df04c8368a0e7dba6583b87855c6dbe857254e28805e" - ], - "w_0_11": ["9dab36382b9ad792fb07816c6196a30a94c33f62814972da06552614ce97eb3a"] + "k_0_11": ["f9692ce7c5edf102c891a980c732e2c058694cc9a5bcc92994175f8c21eed6ae"] }; -final Map> historyBatchArgs1 = { +final Map> historyBatchArgs3 = { "k_0_0": ["edf1976568f1aa676482c1882d44617a60476a1ceb92dd553143d43b9d44b32a"], - "s_0_0": ["02f31c1a078f4c8c18321538fe8213b4289f9a24e7718096b6e92969438e110f"], - "w_0_0": ["269df6359accfa9b761f22c42bd10dcda6fbc6ad708476d2e1efd4b3aa3f6e2f"], "k_0_1": ["bf5a6c56814e80eed11e1e459801515f8c2b83da812568aa9dc26e6356f6965b"], - "s_0_1": ["11663d093cb17dfbed4a96d148b22d3e094b31d23c639c2814beb79f2ab0ca75"], - "w_0_1": ["a328ae88ebce63c0010709ae900c199df2b585cdebce53a6291886dfdcc28c63"], "k_0_2": ["c10be67f5a01256e4c84fc9ed3f2b9f98b2ef20cf27b4ea908bd7aafb1d1f023"], - "s_0_2": ["11f1b4a8d257d865abd2f2a48e779f699c7db528f83ad85d9182156580b5b198"], - "w_0_2": ["16a765b755fcd070f5196f5a31d6370a799c5f2adac922bff8768f1c4e710b25"], "k_0_3": ["ce24ba1237e2306f7348f5f02e33c66c73c36c09b026ab4f08ef22f9b8a1e984"], - "s_0_3": ["4fed678c085df3073ac86ea88d70f01d6d2a27d48230935b0514c07400b9aa57"], - "w_0_3": ["f3baba5f725918c2038e7a80539088b2c0c636c6a27afb52b94b908241c7a5f8"], "k_0_4": ["f040d5195d547fc75660ef85417728ffbab6911dea95c49e28c56c589a2d7d9e"], - "s_0_4": ["08598648eaa4193eeedfccb5b98fd291927d762481e637d3fc3633b826f1d11b"], - "w_0_4": ["e72d39bd9e7b455503a2acae33647cb5ce33c1dc99ef9ebed0e7501b5e387f86"], "k_0_5": ["9766272abb45b0de891ad0f8492a5131e0e5ebc0072b9c1efb23303046bd05a5"], - "s_0_5": ["b5d735762cc4caec5a9445b62e9cdb2219a4d34be9fdf021c7831635e74b22ca"], - "w_0_5": ["7d9beb602c191843af7e0aef8d588f01bb9c5e2252a3eb29d92bb05833f924df"], "k_0_6": ["e6844bc07ae8834056698ad30d0ada23bec769fdd0b5ffbe20626c7d8693000a"], - "s_0_6": ["8ad2f479c64645d16e6a67f0f8532f621b43c7be8b9ba3cf9a52b36572761900"], - "w_0_6": ["6ab497d1554279dc0723ba1513ff687991454e6f720a79de2c1403b0de7fef73"], "k_0_7": ["ebff62957000f362d807db28b21af89a5bc9347346fc6434b2fe4e31e1b8dd7c"], - "s_0_7": ["90cf90aef62db7572cb000fe718141bb46086610bbe72779393999f427bef381"], - "w_0_7": ["fb33f2529224ddbb65056dd98296f5b9aad5fd31d2ec4db51520e38809d4ec38"], "k_0_8": ["51c818cf59c11676df096f5759d0d0c098baefe4e6fd240587cf7d751fe5ef8c"], - "s_0_8": ["21090d0c982699238e4261ae6caa45785e18f0c389e0aa36f708ba88edc0655c"], - "w_0_8": ["e7cb000615b4ecf2d483279fa437f37acd9deab1e9f3f4e94d4419c33e521753"], "k_0_9": ["3c942fe69ce42a2f59c94d2365fb805988e6e3e72376ff658a6aa0a926e1ff79"], - "s_0_9": ["e446fec7b44710407d1bdd531578916c28333c75553aa6384c74f1474eff758a"], - "w_0_9": ["d07ba311749923b3d6c2a291a3be9bcaf43e1fb1b97cb1b262c93672eda50823"], "k_0_10": [ "fb29e1b177589820ad4685cc2fa555f170d885366f5046fd043b1891a43fa8ec" ], - "s_0_10": [ + "k_0_11": ["e0b955a5d5bde05cb45e0c7ba95afa8e3e09b04a2cab70e8f1383258471d089a"] +}; +final Map> historyBatchArgs4 = { + "k_0_0": ["02f31c1a078f4c8c18321538fe8213b4289f9a24e7718096b6e92969438e110f"], + "k_0_1": ["11663d093cb17dfbed4a96d148b22d3e094b31d23c639c2814beb79f2ab0ca75"], + "k_0_2": ["11f1b4a8d257d865abd2f2a48e779f699c7db528f83ad85d9182156580b5b198"], + "k_0_3": ["4fed678c085df3073ac86ea88d70f01d6d2a27d48230935b0514c07400b9aa57"], + "k_0_4": ["08598648eaa4193eeedfccb5b98fd291927d762481e637d3fc3633b826f1d11b"], + "k_0_5": ["b5d735762cc4caec5a9445b62e9cdb2219a4d34be9fdf021c7831635e74b22ca"], + "k_0_6": ["8ad2f479c64645d16e6a67f0f8532f621b43c7be8b9ba3cf9a52b36572761900"], + "k_0_7": ["90cf90aef62db7572cb000fe718141bb46086610bbe72779393999f427bef381"], + "k_0_8": ["21090d0c982699238e4261ae6caa45785e18f0c389e0aa36f708ba88edc0655c"], + "k_0_9": ["e446fec7b44710407d1bdd531578916c28333c75553aa6384c74f1474eff758a"], + "k_0_10": [ "fd1564d56943bd604d0c246c1e5ab1edc8d3c8152ffea3c519aec8c300731517" ], - "w_0_10": [ - "cae047098603b4f4dedb5a5f7f3a0e0f6f0233eed8e0c06f47855c11ccf0a664" + "k_0_11": ["b67bc093b3b10d3aad39ef94e161a5fb4ad72bec0a3423d33c7fbc063e1016cc"] +}; +final Map> historyBatchArgs5 = { + "k_0_0": ["5d2a7a01f09f3e1df3fb44d25cc53cfd4e6df25b9c3e3f710f4dbc3e2be3a8fc"], + "k_0_1": ["26f92666caebb9a17b14f5b573b385348cdc80065472b8961091f3226d2f650f"], + "k_0_2": ["20eef9f15b0707cb5779f077dadcaf0be96e7212f76557f96e5dc5fb2e5aafa7"], + "k_0_3": ["eca08355027cb9f27500ea2588ee532b87b1061f1931b00b57244efad2081b99"], + "k_0_4": ["f85fb2cfd89ca882aa93546ce5c6aaed7eb13bd60417822dbfe8514eaa32260d"], + "k_0_5": ["4e87132185225b4c79d69ca74a1d1ea329b0864af2b2b216c126fda4dfe44bc3"], + "k_0_6": ["46ade50d7c161264887d93ea77aa86d01c74ce296ce4d6f44a97c82b4023bf5d"], + "k_0_7": ["22ff3049e6ef43f20289b0ac8c8480c61b5502b32e07d5bb57fa8029d6943596"], + "k_0_8": ["01c387618378a58c221b51ab65a7f8e95a6932cdb0a8634acfe62acee1cb6cdc"], + "k_0_9": ["51928f94d8c56318810e604dfb7b4862755ce5b005ec4242ea46a485e356d24f"], + "k_0_10": [ + "8ea9acdeaa65f2d99ed93a3b0691601e768f6695fb46cd9b1edc0bca10406767" ], - "k_0_11": [ - "e0b955a5d5bde05cb45e0c7ba95afa8e3e09b04a2cab70e8f1383258471d089a" - ], - "s_0_11": [ - "b67bc093b3b10d3aad39ef94e161a5fb4ad72bec0a3423d33c7fbc063e1016cc" - ], - "w_0_11": ["6bdefed2a12896dd9840efd83c265e08f9131406c98839d4089377f517cbb977"] + "k_0_11": ["7cda59a23e03a997b852df04c8368a0e7dba6583b87855c6dbe857254e28805e"] }; final Map>> historyBatchResponse = { @@ -172,3 +172,12 @@ final Map>> emptyHistoryBatchResponse = { "s_0_11": [], "w_0_11": [] }; + +final List activeScriptHashes = [ + "11663d093cb17dfbed4a96d148b22d3e094b31d23c639c2814beb79f2ab0ca75", + "06593b2d896751e8dda288bb6587b6bb6a1dee71d82a85457f5654f781e37b12", + "a328ae88ebce63c0010709ae900c199df2b585cdebce53a6291886dfdcc28c63", + "26f92666caebb9a17b14f5b573b385348cdc80065472b8961091f3226d2f650f", + "2f18558e5d3015cb6578aee1c3e4b645725fa4e1d26ce22cb31c9949f3b4957c", + "bf5a6c56814e80eed11e1e459801515f8c2b83da812568aa9dc26e6356f6965b", +]; diff --git a/test/services/coins/bitcoin/bitcoin_wallet_test.dart b/test/services/coins/bitcoin/bitcoin_wallet_test.dart index fce994961..dac980155 100644 --- a/test/services/coins/bitcoin/bitcoin_wallet_test.dart +++ b/test/services/coins/bitcoin/bitcoin_wallet_test.dart @@ -15,6 +15,7 @@ import 'package:stackwallet/services/price.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; +import 'package:tuple/tuple.dart'; import 'bitcoin_history_sample_data.dart'; import 'bitcoin_transaction_data_samples.dart'; @@ -2233,6 +2234,14 @@ void main() { .thenAnswer((_) async => emptyHistoryBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => emptyHistoryBatchResponse); // await DB.instance.init(); final wallet = await Hive.openBox(testWalletId); bool hasThrown = false; @@ -2250,6 +2259,10 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); expect(secureStore?.interactions, 20); expect(secureStore?.writes, 7); @@ -2276,6 +2289,14 @@ void main() { .thenAnswer((_) async => emptyHistoryBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => emptyHistoryBatchResponse); final wallet = await Hive.openBox(testWalletId); @@ -2290,6 +2311,10 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); verifyNoMoreInteractions(client); verifyNoMoreInteractions(cachedClient); @@ -2312,8 +2337,27 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); - final wallet = await Hive.openBox(testWalletId); + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); bool hasThrown = false; try { @@ -2330,6 +2374,18 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } expect(secureStore?.interactions, 14); expect(secureStore?.writes, 7); @@ -2356,10 +2412,29 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin)) .thenAnswer((realInvocation) async {}); - final wallet = await Hive.openBox(testWalletId); + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + final wallet = await Hive.openBox(testWalletId); // restore so we have something to rescan await btc?.recoverFromMnemonic( @@ -2491,9 +2566,21 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(2); verify(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin)) .called(1); + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } + expect(secureStore?.writes, 25); expect(secureStore?.reads, 32); expect(secureStore?.deletes, 6); @@ -2519,10 +2606,29 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin)) .thenAnswer((realInvocation) async {}); - final wallet = await Hive.openBox(testWalletId); + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + final wallet = await Hive.openBox(testWalletId); // restore so we have something to rescan await btc?.recoverFromMnemonic( @@ -2624,10 +2730,22 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); - verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(2); verify(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin)) .called(1); + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } + expect(secureStore?.writes, 19); expect(secureStore?.reads, 32); expect(secureStore?.deletes, 12); @@ -3911,8 +4029,27 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); - final wallet = await Hive.openBox(testWalletId); + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); when(cachedClient?.getTransaction( txHash: @@ -3995,6 +4132,18 @@ void main() { // .called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } expect(secureStore?.interactions, 20); expect(secureStore?.writes, 10); @@ -4169,8 +4318,27 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); - final wallet = await Hive.openBox(testWalletId); + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); // recover to fill data await btc?.recoverFromMnemonic( @@ -4186,6 +4354,18 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } expect(secureStore?.interactions, 14); expect(secureStore?.writes, 7); @@ -4198,8 +4378,9 @@ void main() { verifyNoMoreInteractions(priceAPI); }); - test("refresh wallet throws", () async { - when(client?.getBlockHeadTip()).thenThrow(Exception("some exception")); + test("refresh wallet normally", () async { + when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async => + {"height": 520481, "hex": "some block hex"}); when(client?.getServerFeatures()).thenAnswer((_) async => { "hosts": {}, "pruning": null, @@ -4210,14 +4391,23 @@ void main() { "hash_function": "sha256", "services": [] }); - when(client?.getBatchHistory(args: historyBatchArgs0)) - .thenAnswer((_) async => historyBatchResponse); - when(client?.getBatchHistory(args: historyBatchArgs1)) - .thenAnswer((_) async => historyBatchResponse); when(client?.getHistory(scripthash: anyNamed("scripthash"))) - .thenThrow(Exception("some exception")); + .thenAnswer((_) async => []); + when(client?.estimateFee(blocks: anyNamed("blocks"))) + .thenAnswer((_) async => Decimal.one); - final wallet = await Hive.openBox(testWalletId); + when(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD")) + .thenAnswer((_) async => {Coin.bitcoin: Tuple2(Decimal.one, 0.3)}); + + final List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); // recover to fill data await btc?.recoverFromMnemonic( @@ -4226,83 +4416,35 @@ void main() { maxNumberOfIndexesToCheck: 1000, height: 4000); + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((_) async => {}); + when(client?.getBatchUTXOs(args: anyNamed("args"))) + .thenAnswer((_) async => emptyHistoryBatchResponse); + await btc?.refresh(); verify(client?.getServerFeatures()).called(1); - verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); - verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(4); + verify(client?.estimateFee(blocks: anyNamed("blocks"))).called(3); verify(client?.getBlockHeadTip()).called(1); - verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1); + verify(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD")).called(2); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + } expect(secureStore?.interactions, 14); expect(secureStore?.writes, 7); expect(secureStore?.reads, 7); expect(secureStore?.deletes, 0); - verifyNoMoreInteractions(client); + // verifyNoMoreInteractions(client); verifyNoMoreInteractions(cachedClient); - verifyNoMoreInteractions(tracker); verifyNoMoreInteractions(priceAPI); }); - // test("refresh wallet normally", () async { - // when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async => - // {"height": 520481, "hex": "some block hex"}); - // when(client?.getServerFeatures()).thenAnswer((_) async => { - // "hosts": {}, - // "pruning": null, - // "server_version": "Unit tests", - // "protocol_min": "1.4", - // "protocol_max": "1.4.2", - // "genesis_hash": GENESIS_HASH_MAINNET, - // "hash_function": "sha256", - // "services": [] - // }); - // when(client?.getBatchHistory(args: historyBatchArgs0)) - // .thenAnswer((_) async => historyBatchResponse); - // when(client?.getBatchHistory(args: historyBatchArgs1)) - // .thenAnswer((_) async => historyBatchResponse); - // when(client?.getHistory(scripthash: anyNamed("scripthash"))) - // .thenAnswer((_) async => []); - // when(client?.estimateFee(blocks: anyNamed("blocks"))) - // .thenAnswer((_) async => Decimal.one); - // // when(priceAPI.getBitcoinPrice(baseCurrency: "USD")) - // // .thenAnswer((_) async => Decimal.one); - // - // // recover to fill data - // await btc?.recoverFromMnemonic( - // mnemonic: TEST_MNEMONIC, - // maxUnusedAddressGap: 2, - // maxNumberOfIndexesToCheck: 1000, - // height: 4000); - // - // when(client?.getBatchHistory(args: anyNamed("args"))) - // .thenAnswer((_) async => {}); - // when(client?.getBatchUTXOs(args: anyNamed("args"))) - // .thenAnswer((_) async => emptyHistoryBatchResponse); - // - // await btc?.refresh(); - // - // verify(client?.getServerFeatures()).called(1); - // verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); - // verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); - // verify(client?.getBatchHistory(args: anyNamed("args"))).called(1); - // verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(1); - // verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(4); - // verify(client?.estimateFee(blocks: anyNamed("blocks"))).called(3); - // verify(client?.getBlockHeadTip()).called(1); - // // verify(priceAPI.getBitcoinPrice(baseCurrency: "USD")).called(2); - // - // expect(secureStore?.interactions, 14); - // expect(secureStore?.writes, 7); - // expect(secureStore?.reads, 7); - // expect(secureStore?.deletes, 0); - // - // verifyNoMoreInteractions(client); - // verifyNoMoreInteractions(cachedClient); - // verifyNoMoreInteractions(priceAPI); - // }); - tearDown(() async { await tearDownTestHive(); }); diff --git a/test/services/coins/dogecoin/dogecoin_wallet_test.dart b/test/services/coins/dogecoin/dogecoin_wallet_test.dart index e2eb90a7b..7fcb1cdbd 100644 --- a/test/services/coins/dogecoin/dogecoin_wallet_test.dart +++ b/test/services/coins/dogecoin/dogecoin_wallet_test.dart @@ -20,8 +20,6 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'dogecoin_history_sample_data.dart'; -import 'dogecoin_transaction_data_samples.dart'; -import 'dogecoin_utxo_sample_data.dart'; import 'dogecoin_wallet_test.mocks.dart'; import 'dogecoin_wallet_test_parameters.dart'; @@ -1853,102 +1851,6 @@ void main() { // // verifyNoMoreInteractions(priceAPI); // // }); - test("fullRescan succeeds", () async { - when(client?.getServerFeatures()).thenAnswer((_) async => { - "hosts": {}, - "pruning": null, - "server_version": "Unit tests", - "protocol_min": "1.4", - "protocol_max": "1.4.2", - "genesis_hash": GENESIS_HASH_MAINNET, - "hash_function": "sha256", - "services": [] - }); - when(client?.getBatchHistory(args: historyBatchArgs0)) - .thenAnswer((_) async => historyBatchResponse); - when(client?.getBatchHistory(args: historyBatchArgs1)) - .thenAnswer((_) async => historyBatchResponse); - when(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin)) - .thenAnswer((realInvocation) async {}); - - final wallet = await Hive.openBox(testWalletId); - - // restore so we have something to rescan - await doge?.recoverFromMnemonic( - mnemonic: TEST_MNEMONIC, - maxUnusedAddressGap: 2, - maxNumberOfIndexesToCheck: 1000, - height: 4000); - - // fetch valid wallet data - final preReceivingAddressesP2PKH = - await wallet.get('receivingAddressesP2PKH'); - final preChangeAddressesP2PKH = await wallet.get('changeAddressesP2PKH'); - final preReceivingIndexP2PKH = await wallet.get('receivingIndexP2PKH'); - final preChangeIndexP2PKH = await wallet.get('changeIndexP2PKH'); - final preUtxoData = await wallet.get('latest_utxo_model'); - final preReceiveDerivationsStringP2PKH = await secureStore?.read( - key: "${testWalletId}_receiveDerivationsP2PKH"); - final preChangeDerivationsStringP2PKH = await secureStore?.read( - key: "${testWalletId}_changeDerivationsP2PKH"); - - // destroy the data that the rescan will fix - await wallet.put( - 'receivingAddressesP2PKH', ["some address", "some other address"]); - await wallet - .put('changeAddressesP2PKH', ["some address", "some other address"]); - - await wallet.put('receivingIndexP2PKH', 123); - await wallet.put('changeIndexP2PKH', 123); - await secureStore?.write( - key: "${testWalletId}_receiveDerivationsP2PKH", value: "{}"); - await secureStore?.write( - key: "${testWalletId}_changeDerivationsP2PKH", value: "{}"); - - bool hasThrown = false; - try { - await doge?.fullRescan(2, 1000); - } catch (_) { - hasThrown = true; - } - expect(hasThrown, false); - - // fetch wallet data again - final receivingAddressesP2PKH = - await wallet.get('receivingAddressesP2PKH'); - final changeAddressesP2PKH = await wallet.get('changeAddressesP2PKH'); - final receivingIndexP2PKH = await wallet.get('receivingIndexP2PKH'); - final changeIndexP2PKH = await wallet.get('changeIndexP2PKH'); - final utxoData = await wallet.get('latest_utxo_model'); - final receiveDerivationsStringP2PKH = await secureStore?.read( - key: "${testWalletId}_receiveDerivationsP2PKH"); - final changeDerivationsStringP2PKH = await secureStore?.read( - key: "${testWalletId}_changeDerivationsP2PKH"); - - expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH); - expect(preChangeAddressesP2PKH, changeAddressesP2PKH); - expect(preReceivingIndexP2PKH, receivingIndexP2PKH); - expect(preChangeIndexP2PKH, changeIndexP2PKH); - expect(preUtxoData, utxoData); - expect(preReceiveDerivationsStringP2PKH, receiveDerivationsStringP2PKH); - expect(preChangeDerivationsStringP2PKH, changeDerivationsStringP2PKH); - - verify(client?.getServerFeatures()).called(1); - verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); - verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); - verify(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin)) - .called(1); - - expect(secureStore?.writes, 9); - expect(secureStore?.reads, 12); - expect(secureStore?.deletes, 2); - - verifyNoMoreInteractions(client); - verifyNoMoreInteractions(cachedClient); - verifyNoMoreInteractions(tracker); - verifyNoMoreInteractions(priceAPI); - }); - test("get mnemonic list", () async { when(client?.getServerFeatures()).thenAnswer((_) async => { "hosts": {}, @@ -2118,7 +2020,19 @@ void main() { when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); - final wallet = await Hive.openBox(testWalletId); + when(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + final wallet = await Hive.openBox(testWalletId); bool hasThrown = false; try { @@ -2135,6 +2049,16 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).called(1); expect(secureStore?.interactions, 6); expect(secureStore?.writes, 3); @@ -2165,7 +2089,19 @@ void main() { when(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin)) .thenAnswer((realInvocation) async {}); - final wallet = await Hive.openBox(testWalletId); + when(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + final wallet = await Hive.openBox(testWalletId); // restore so we have something to rescan await doge?.recoverFromMnemonic( @@ -2230,6 +2166,16 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).called(2); verify(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin)) .called(1); @@ -2259,6 +2205,18 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + + when(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); when(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin)) .thenAnswer((realInvocation) async {}); @@ -2318,7 +2276,17 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); - verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).called(1); verify(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin)) .called(1); @@ -2814,6 +2782,17 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); final wallet = await Hive.openBox(testWalletId); @@ -2831,6 +2810,16 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).called(1); expect(secureStore?.interactions, 6); expect(secureStore?.writes, 3); @@ -2859,6 +2848,17 @@ void main() { .thenAnswer((_) async => historyBatchResponse); when(client?.getBatchHistory(args: historyBatchArgs1)) .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); when(client?.getHistory(scripthash: anyNamed("scripthash"))) .thenThrow(Exception("some exception")); @@ -2876,6 +2876,16 @@ void main() { verify(client?.getServerFeatures()).called(1); verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc" + ] + })).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c" + ] + })).called(1); verify(client?.getBlockHeadTip()).called(1); verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1); diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index e6d9f6e74..b309044b8 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -26,6 +26,7 @@ import 'firo_wallet_test.mocks.dart'; import 'firo_wallet_test_parameters.dart'; import 'sample_data/get_anonymity_set_sample_data.dart'; import 'sample_data/get_used_serials_sample_data.dart'; +import 'sample_data/get_utxos_sample_data.dart'; import 'sample_data/gethistory_samples.dart'; import 'sample_data/transaction_data_samples.dart'; @@ -66,18 +67,15 @@ void main() { return SampleGetTransactionData.txData7; }); - final result = await isolateRestore( + final message = await isolateRestore( TEST_MNEMONIC, - txData, - "USD", Coin.firo, 1, setData, usedSerials, firoNetwork, - Decimal.ten, - "en_US", ); + final result = await staticProcessRestore(txData, message); expect(result, isA>()); expect(result["mintIndex"], 8); @@ -88,22 +86,17 @@ void main() { }); test("isolateRestore throws", () async { - final txData = TransactionData(); final Map setData = {}; final usedSerials = []; expect( () => isolateRestore( TEST_MNEMONIC, - txData, - "USD", Coin.firo, 1, setData, usedSerials, firoNetwork, - Decimal.ten, - "en_US", ), throwsA(isA())); }); @@ -1018,15 +1011,8 @@ void main() { "f4217364cbe6a81ef7ecaaeba0a6d6b576a9850b3e891fa7b88ed4927c505218")) .thenAnswer((realInvocation) => true); - // mock history calls - when(client.getHistory(scripthash: SampleGetHistoryData.scripthash0)) - .thenAnswer((_) async => SampleGetHistoryData.data0); - when(client.getHistory(scripthash: SampleGetHistoryData.scripthash1)) - .thenAnswer((_) async => SampleGetHistoryData.data1); - when(client.getHistory(scripthash: SampleGetHistoryData.scripthash2)) - .thenAnswer((_) async => SampleGetHistoryData.data2); - when(client.getHistory(scripthash: SampleGetHistoryData.scripthash3)) - .thenAnswer((_) async => SampleGetHistoryData.data3); + when(client.getBatchHistory(args: batchHistoryRequest0)) + .thenAnswer((realInvocation) async => batchHistoryResponse0); // mock transaction calls when(cachedClient.getTransaction( @@ -1321,6 +1307,48 @@ void main() { (_) async => GetAnonymitySetSampleData.data, ); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash0, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData0; + }); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash1, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData1; + }); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash2, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData2; + }); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash3, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData3; + }); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash4, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData4; + }); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash5, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData5; + }); + when(cachedClient.getTransaction( + txHash: SampleGetTransactionData.txHash6, + coin: Coin.firo, + )).thenAnswer((_) async { + return SampleGetTransactionData.txData6; + }); when(cachedClient.getTransaction( txHash: SampleGetTransactionData.txHash8, coin: Coin.firo, @@ -1415,6 +1443,14 @@ void main() { .thenAnswer((_) async => data); } + when(client.getBatchHistory(args: { + "0": [SampleGetHistoryData.scripthash0], + "1": [SampleGetHistoryData.scripthash3] + })).thenAnswer((realInvocation) async => { + "0": SampleGetHistoryData.data0, + "1": SampleGetHistoryData.data3, + }); + await firo.recoverFromMnemonic( mnemonic: TEST_MNEMONIC, maxUnusedAddressGap: 20, @@ -1473,8 +1509,8 @@ void main() { expect(jIndex, [2, 4, 6]); final lelantusTxModel = await wallet.get('latest_lelantus_tx_model'); - expect(lelantusTxModel.getAllTransactions().length, 3); - }, timeout: const Timeout(Duration(minutes: 3))); + expect(lelantusTxModel.getAllTransactions().length, 5); + }, timeout: const Timeout(Duration(minutes: 5))); test("fullRescan succeeds", () async { TestWidgetsFlutterBinding.ensureInitialized(); @@ -1580,6 +1616,14 @@ void main() { .thenAnswer((_) async => data); } + when(client.getBatchHistory(args: { + "0": [SampleGetHistoryData.scripthash0], + "1": [SampleGetHistoryData.scripthash3] + })).thenAnswer((realInvocation) async => { + "0": SampleGetHistoryData.data0, + "1": SampleGetHistoryData.data3, + }); + // mock transaction calls when(cachedClient.getTransaction( txHash: SampleGetTransactionData.txHash0, @@ -1680,8 +1724,8 @@ void main() { expect(jIndex, [2, 4, 6]); final lelantusTxModel = await wallet.get('latest_lelantus_tx_model'); - expect(lelantusTxModel.getAllTransactions().length, 3); - }, timeout: Timeout(Duration(minutes: 3))); + expect(lelantusTxModel.getAllTransactions().length, 5); + }, timeout: const Timeout(Duration(minutes: 3))); test("fullRescan fails", () async { TestWidgetsFlutterBinding.ensureInitialized(); @@ -1939,6 +1983,14 @@ void main() { .thenAnswer((_) async => data); } + when(client.getBatchHistory(args: { + "0": [SampleGetHistoryData.scripthash0], + "1": [SampleGetHistoryData.scripthash3] + })).thenAnswer((realInvocation) async => { + "0": SampleGetHistoryData.data0, + "1": SampleGetHistoryData.data3, + }); + // mock transaction calls when(cachedClient.getTransaction( txHash: SampleGetTransactionData.txHash0, @@ -2043,7 +2095,7 @@ void main() { expect(jIndex, [2, 4, 6]); final lelantusTxModel = await wallet.get('latest_lelantus_tx_model'); - expect(lelantusTxModel.getAllTransactions().length, 3); + expect(lelantusTxModel.getAllTransactions().length, 5); await firo.fullRescan(20, 1000); @@ -2099,7 +2151,7 @@ void main() { expect(_jIndex, [2, 4, 6]); final _lelantusTxModel = await wallet.get('latest_lelantus_tx_model'); - expect(_lelantusTxModel.getAllTransactions().length, 3); + expect(_lelantusTxModel.getAllTransactions().length, 5); }, timeout: const Timeout(Duration(minutes: 6))); test("recoverFromMnemonic fails testnet", () async { @@ -2397,7 +2449,11 @@ void main() { expect(serials, GetUsedSerialsSampleData.serials['serials']); }); - test("refresh", () async { + test("firo refresh", () async { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel('uk.spiralarm.flutter/devicelocale') + .setMockMethodCallHandler((methodCall) async => 'en_US'); + final client = MockElectrumX(); final cachedClient = MockCachedElectrumX(); final secureStore = FakeSecureStorage(); @@ -2408,6 +2464,21 @@ void main() { key: "${testWalletId}refresh_mnemonic", value: RefreshTestParams.mnemonic); + when(client.getBatchUTXOs(args: batchUtxoRequest)) + .thenAnswer((realInvocation) async => {}); + + when(client.getBatchHistory(args: { + "0": [SampleGetHistoryData.scripthash1], + "1": [SampleGetHistoryData.scripthash0], + "2": [SampleGetHistoryData.scripthash2], + "3": [SampleGetHistoryData.scripthash3], + })).thenAnswer((realInvocation) async => { + "0": SampleGetHistoryData.data1, + "1": SampleGetHistoryData.data0, + "2": SampleGetHistoryData.data2, + "3": SampleGetHistoryData.data3, + }); + // mock electrumx client calls when(client.getServerFeatures()).thenAnswer((_) async => { "hosts": {}, @@ -2688,6 +2759,10 @@ void main() { // }, timeout: const Timeout(Duration(minutes: 3))); test("send fails due to insufficient balance", () async { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel('uk.spiralarm.flutter/devicelocale') + .setMockMethodCallHandler((methodCall) async => 'en_US'); + final client = MockElectrumX(); final cachedClient = MockCachedElectrumX(); final secureStore = FakeSecureStorage(); @@ -2711,6 +2786,8 @@ void main() { final txid = Format.uint8listToString(reversedBytes); return txid; }); + when(client.getBatchHistory(args: batchHistoryRequest0)) + .thenAnswer((realInvocation) async => batchHistoryResponse0); when(cachedClient.getAnonymitySet( groupId: "1", @@ -2859,9 +2936,13 @@ void main() { () async => await firo.send( toAddress: "aHZJsucDrhr4Uzzx6XXrKnaTgLxsEAokvV", amount: 100), throwsA(isA())); - }, timeout: Timeout(const Duration(minutes: 3))); + }, timeout: const Timeout(Duration(minutes: 3))); test("send fails due to bad transaction created", () async { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel('uk.spiralarm.flutter/devicelocale') + .setMockMethodCallHandler((methodCall) async => 'en_US'); + final client = MockElectrumX(); final cachedClient = MockCachedElectrumX(); final secureStore = FakeSecureStorage(); @@ -2875,6 +2956,9 @@ void main() { return "some bad txid"; }); + when(client.getBatchHistory(args: batchHistoryRequest0)) + .thenAnswer((realInvocation) async => batchHistoryResponse0); + when(cachedClient.getAnonymitySet( groupId: "1", coin: Coin.firo, @@ -3022,9 +3106,13 @@ void main() { () async => await firo.send( toAddress: "aHZJsucDrhr4Uzzx6XXrKnaTgLxsEAokvV", amount: 100), throwsA(isA())); - }, timeout: Timeout(const Duration(minutes: 3))); + }, timeout: const Timeout(Duration(minutes: 3))); test("wallet balances", () async { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel('uk.spiralarm.flutter/devicelocale') + .setMockMethodCallHandler((methodCall) async => 'en_US'); + final client = MockElectrumX(); final cachedClient = MockCachedElectrumX(); final priceAPI = MockPriceAPI(); @@ -3042,6 +3130,12 @@ void main() { when(client.getHistory(scripthash: SampleGetHistoryData.scripthash3)) .thenAnswer((_) async => SampleGetHistoryData.data3); + when(client.getBatchHistory(args: batchHistoryRequest0)) + .thenAnswer((realInvocation) async => batchHistoryResponse0); + + when(client.getBatchUTXOs(args: batchUtxoRequest)) + .thenAnswer((realInvocation) async => {}); + // mock transaction calls when(cachedClient.getTransaction( txHash: SampleGetTransactionData.txHash0, @@ -3112,6 +3206,12 @@ void main() { final priceAPI = MockPriceAPI(); final secureStore = FakeSecureStorage(); + when(client.getBatchHistory(args: batchHistoryRequest0)) + .thenAnswer((realInvocation) async => batchHistoryResponse0); + + when(client.getBatchUTXOs(args: batchUtxoRequest)) + .thenAnswer((realInvocation) async => {}); + // mock price calls when(priceAPI.getPricesAnd24hChange(baseCurrency: "USD")).thenAnswer( (_) async => {Coin.firo: Tuple2(Decimal.fromInt(10), 1.0)}); @@ -3189,6 +3289,10 @@ void main() { }); test("wallet balance minus maxfee - wallet balance is not zero", () async { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel('uk.spiralarm.flutter/devicelocale') + .setMockMethodCallHandler((methodCall) async => 'en_US'); + final client = MockElectrumX(); final cachedClient = MockCachedElectrumX(); final priceAPI = MockPriceAPI(); @@ -3208,6 +3312,12 @@ void main() { when(client.getHistory(scripthash: SampleGetHistoryData.scripthash3)) .thenAnswer((_) async => SampleGetHistoryData.data3); + when(client.getBatchHistory(args: batchHistoryRequest0)) + .thenAnswer((realInvocation) async => batchHistoryResponse0); + + when(client.getBatchUTXOs(args: batchUtxoRequest)) + .thenAnswer((realInvocation) async => {}); + // mock transaction calls when(cachedClient.getTransaction( txHash: SampleGetTransactionData.txHash0, diff --git a/test/services/coins/firo/sample_data/get_utxos_sample_data.dart b/test/services/coins/firo/sample_data/get_utxos_sample_data.dart index 398cca14d..f6c67b756 100644 --- a/test/services/coins/firo/sample_data/get_utxos_sample_data.dart +++ b/test/services/coins/firo/sample_data/get_utxos_sample_data.dart @@ -22,3 +22,18 @@ abstract class GetUtxoSampleData { } ]; } + +final batchUtxoRequest = { + "843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8": [ + "843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8" + ], + "477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8": [ + "477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8" + ], + "c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2": [ + "c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2" + ], + "0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38": [ + "0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38" + ] +}; diff --git a/test/services/coins/firo/sample_data/gethistory_samples.dart b/test/services/coins/firo/sample_data/gethistory_samples.dart index 7e87439fc..518826fbe 100644 --- a/test/services/coins/firo/sample_data/gethistory_samples.dart +++ b/test/services/coins/firo/sample_data/gethistory_samples.dart @@ -52,3 +52,54 @@ abstract class SampleGetHistoryData { "0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38"; static List> data3 = []; } + +final batchHistoryRequest0 = { + "0": ["477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8"], + "1": ["843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8"], + "2": ["c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2"], + "3": ["0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38"], +}; + +final Map>> batchHistoryResponse0 = { + "0": [ + { + "tx_hash": + "76032a2408e7fefd62a0c7e793f93a2984621e37a625cc1f9e8febadbe583a40", + "height": 455866 + }, + { + "tx_hash": + "36c92daa4005d368e28cea917fdb2c1e7069319a4a79fb2ff45c089100680232", + "height": 455876 + } + ], + "1": [ + { + "tx_hash": + "9201d7a58185f000c312a8b0c19d8e5c61c1ce1b69201c1a4dc2bce289794280", + "height": 457339 + }, + { + "tx_hash": + "ea77e74edecd8c14ff5a8ddeb54e9e5e9c7c301c6f76f0ac1ac8119c6cc15e35", + "height": 457341 + }, + { + "tx_hash": + "ac0322cfdd008fa2a79bec525468fd05cf51a5a4e2c2e9c15598b659ec71ac68", + "height": 457373 + }, + { + "tx_hash": + "e8e4bfc080bd6133d38263d2ac7ef6f60dfd73eb29b464e34766ebb5a0d27dd8", + "height": 457376 + }, + { + "tx_hash": + "51576e2230c2911a508aabb85bb50045f04b8dc958790ce2372986c3ebbe7d3e", + "height": 457379 + }, + ], + "2": [], + "3": [], +}; diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 600707e90..8baae2d1b 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -232,6 +232,22 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { super.noSuchMethod(Invocation.method(#stopNetworkAlivePinging, []), returnValueForMissingStub: null); @override + _i8.Future> prepareSendPublic( + {String? address, int? satoshiAmount, Map? args}) => + (super.noSuchMethod( + Invocation.method(#prepareSendPublic, [], { + #address: address, + #satoshiAmount: satoshiAmount, + #args: args + }), + returnValue: + Future>.value({})) + as _i8.Future>); + @override + _i8.Future confirmSendPublic({dynamic txData}) => (super.noSuchMethod( + Invocation.method(#confirmSendPublic, [], {#txData: txData}), + returnValue: Future.value('')) as _i8.Future); + @override _i8.Future> prepareSend( {String? address, int? satoshiAmount, Map? args}) => (super.noSuchMethod( @@ -257,6 +273,47 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { #send, [], {#toAddress: toAddress, #amount: amount, #args: args}), returnValue: Future.value('')) as _i8.Future); @override + int estimateTxFee({int? vSize, int? feeRatePerKB}) => (super.noSuchMethod( + Invocation.method( + #estimateTxFee, [], {#vSize: vSize, #feeRatePerKB: feeRatePerKB}), + returnValue: 0) as int); + @override + dynamic coinSelection(int? satoshiAmountToSend, int? selectedTxFeeRate, + String? _recipientAddress, bool? isSendAll, + {int? additionalOutputs = 0, List<_i4.UtxoObject>? utxos}) => + super.noSuchMethod(Invocation.method(#coinSelection, [ + satoshiAmountToSend, + selectedTxFeeRate, + _recipientAddress, + isSendAll + ], { + #additionalOutputs: additionalOutputs, + #utxos: utxos + })); + @override + _i8.Future> fetchBuildTxData( + List<_i4.UtxoObject>? utxosToUse) => + (super.noSuchMethod(Invocation.method(#fetchBuildTxData, [utxosToUse]), + returnValue: + Future>.value({})) + as _i8.Future>); + @override + _i8.Future> buildTransaction( + {List<_i4.UtxoObject>? utxosToUse, + Map? utxoSigningData, + List? recipients, + List? satoshiAmounts}) => + (super.noSuchMethod( + Invocation.method(#buildTransaction, [], { + #utxosToUse: utxosToUse, + #utxoSigningData: utxoSigningData, + #recipients: recipients, + #satoshiAmounts: satoshiAmounts + }), + returnValue: + Future>.value({})) + as _i8.Future>); + @override _i8.Future updateNode(bool? shouldRefresh) => (super.noSuchMethod(Invocation.method(#updateNode, [shouldRefresh]), returnValue: Future.value(), @@ -293,8 +350,8 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { returnValue: >[]) as List>); @override - _i8.Future autoMint() => - (super.noSuchMethod(Invocation.method(#autoMint, []), + _i8.Future anonymizeAllPublicFunds() => + (super.noSuchMethod(Invocation.method(#anonymizeAllPublicFunds, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i8.Future); @override @@ -325,6 +382,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i8.Future); @override + _i8.Future checkChangeAddressForTransactions() => (super.noSuchMethod( + Invocation.method(#checkChangeAddressForTransactions, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i8.Future); + @override _i8.Future fillAddresses(String? suppliedMnemonic, {int? perBatch = 50, int? numberOfThreads = 4}) => (super.noSuchMethod( @@ -367,6 +429,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i8.Future); @override + _i8.Future> getSetDataMap(int? latestSetId) => + (super.noSuchMethod(Invocation.method(#getSetDataMap, [latestSetId]), + returnValue: Future>.value({})) + as _i8.Future>); + @override _i8.Future>> fetchAnonymitySets() => (super.noSuchMethod(Invocation.method(#fetchAnonymitySets, []), returnValue: Future>>.value( @@ -387,11 +454,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { returnValueForMissingStub: Future.value()) as _i8.Future); @override _i8.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod(Invocation.method(#GetCoinsToJoinSplit, [required]), + (super.noSuchMethod(Invocation.method(#getCoinsToJoinSplit, [required]), returnValue: Future.value()) as _i8.Future); @override _i8.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( - Invocation.method(#EstimateJoinSplitFee, [spendAmount]), + Invocation.method(#estimateJoinSplitFee, [spendAmount]), returnValue: Future.value(0)) as _i8.Future); @override _i8.Future estimateFeeFor(int? satoshiAmount, int? feeRate) => @@ -399,6 +466,27 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i8.Future); @override + _i8.Future estimateFeeForPublic(int? satoshiAmount, int? feeRate) => + (super.noSuchMethod( + Invocation.method(#estimateFeeForPublic, [satoshiAmount, feeRate]), + returnValue: Future.value(0)) as _i8.Future); + @override + int roughFeeEstimate(int? inputCount, int? outputCount, int? feeRatePerKB) => + (super.noSuchMethod( + Invocation.method( + #roughFeeEstimate, [inputCount, outputCount, feeRatePerKB]), + returnValue: 0) as int); + @override + int sweepAllEstimate(int? feeRate) => + (super.noSuchMethod(Invocation.method(#sweepAllEstimate, [feeRate]), + returnValue: 0) as int); + @override + _i8.Future>> fastFetch(List? allTxHashes) => + (super.noSuchMethod(Invocation.method(#fastFetch, [allTxHashes]), + returnValue: Future>>.value( + >[])) + as _i8.Future>>); + @override _i8.Future> getJMintTransactions( _i6.CachedElectrumX? cachedClient, List? transactions, @@ -418,6 +506,20 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet { returnValue: Future>.value(<_i4.Transaction>[])) as _i8.Future>); + @override + _i8.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i8.Future); + @override + _i8.Future<_i3.Decimal> availablePrivateBalance() => + (super.noSuchMethod(Invocation.method(#availablePrivateBalance, []), + returnValue: Future<_i3.Decimal>.value(_FakeDecimal_1())) + as _i8.Future<_i3.Decimal>); + @override + _i8.Future<_i3.Decimal> availablePublicBalance() => + (super.noSuchMethod(Invocation.method(#availablePublicBalance, []), + returnValue: Future<_i3.Decimal>.value(_FakeDecimal_1())) + as _i8.Future<_i3.Decimal>); } /// A class which mocks [ElectrumX]. diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 5757ab5dd..012df8061 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -250,6 +250,10 @@ class MockManager extends _i1.Mock implements _i5.Manager { Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]), returnValue: Future.value(0)) as _i7.Future); @override + _i7.Future generateNewAddress() => + (super.noSuchMethod(Invocation.method(#generateNewAddress, []), + returnValue: Future.value(false)) as _i7.Future); + @override void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null);