diff --git a/lib/main.dart b/lib/main.dart index 45ee30411..e6d92eff2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,8 +31,9 @@ import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.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/exchange_form_provider.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/providers/global/auto_swb_service_provider.dart'; @@ -41,7 +42,6 @@ import 'package:stackwallet/providers/global/base_currencies_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/route_generator.dart'; -import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/services/debug_service.dart'; import 'package:stackwallet/services/locale_service.dart'; import 'package:stackwallet/services/node_service.dart'; @@ -79,7 +79,7 @@ void main() async { await DebugService.instance.init(isar); // clear out all info logs on startup. No need to await and block - DebugService.instance.purgeInfoLogs(); + unawaited(DebugService.instance.purgeInfoLogs()); // Registering Transaction Model Adapters Hive.registerAdapter(TransactionDataAdapter()); @@ -193,20 +193,21 @@ class _MaterialAppWithThemeState extends ConsumerState NotificationApi.prefs = _prefs; NotificationApi.notificationsService = _notificationsService; - ref.read(baseCurrenciesProvider).update(); + unawaited(ref.read(baseCurrenciesProvider).update()); await _nodeService.updateDefaults(); await _notificationsService.init( nodeService: _nodeService, tradesService: _tradesService, prefs: _prefs, + changeNow: ref.read(changeNowProvider), ); await _prefs.init(); ref.read(priceAnd24hChangeNotifierProvider).start(true); await _wallets.load(_prefs); loadingCompleter.complete(); - // TODO: this currently hangs for a long time - await _nodeService.updateCommunityNodes(); + // TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet + // unawaited(_nodeService.updateCommunityNodes()); if (_prefs.isAutoBackupEnabled) { switch (_prefs.backupFrequencyType) { @@ -216,7 +217,7 @@ class _MaterialAppWithThemeState extends ConsumerState .startPeriodicBackupTimer(duration: const Duration(minutes: 10)); break; case BackupFrequencyType.everyAppStart: - ref.read(autoSWBServiceProvider).doBackup(); + unawaited(ref.read(autoSWBServiceProvider).doBackup()); break; case BackupFrequencyType.afterClosingAWallet: // ignore this case here @@ -236,8 +237,9 @@ class _MaterialAppWithThemeState extends ConsumerState .isNotEmpty) { return; } - final response = await ChangeNow.getAvailableCurrencies(); - final response2 = await ChangeNow.getAvailableFloatingRatePairs(); + 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!; @@ -248,13 +250,13 @@ class _MaterialAppWithThemeState extends ConsumerState if (response.value!.length > 1) { if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { - ref.read(estimatedRateExchangeFormProvider).updateFrom( + 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) { - ref.read(estimatedRateExchangeFormProvider).updateTo( + await ref.read(estimatedRateExchangeFormProvider).updateTo( response.value!.firstWhere((e) => e.ticker == "doge"), false); } } @@ -288,7 +290,8 @@ class _MaterialAppWithThemeState extends ConsumerState return; } - final response3 = await ChangeNow.getAvailableFixedRateMarkets(); + final response3 = + await ref.read(changeNowProvider).getAvailableFixedRateMarkets(); if (response3.value != null) { ref.read(fixedRateMarketPairsStateProvider.state).state = response3.value!; @@ -297,7 +300,7 @@ class _MaterialAppWithThemeState extends ConsumerState final matchingMarkets = response3.value!.where((e) => e.to == "doge" && e.from == "btc"); if (matchingMarkets.isNotEmpty) { - ref + await ref .read(fixedRateExchangeFormProvider) .updateMarket(matchingMarkets.first, true); } @@ -443,7 +446,7 @@ class _MaterialAppWithThemeState extends ConsumerState } }); } else { - Navigator.push( + unawaited(Navigator.push( navigatorKey.currentContext!, RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, @@ -458,7 +461,7 @@ class _MaterialAppWithThemeState extends ConsumerState ), settings: const RouteSettings(name: "/swbrestorelockscreen"), ), - ); + )); } } diff --git a/lib/models/exchange/exchange_form_state.dart b/lib/models/exchange/exchange_form_state.dart index b02a2806c..210df8c95 100644 --- a/lib/models/exchange/exchange_form_state.dart +++ b/lib/models/exchange/exchange_form_state.dart @@ -2,8 +2,9 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/cupertino.dart'; import 'package:stackwallet/models/exchange/change_now/currency.dart'; import 'package:stackwallet/services/change_now/change_now.dart'; +import 'package:stackwallet/utilities/logger.dart'; -class ExchangeFormState extends ChangeNotifier { +class EstimatedRateExchangeFormState extends ChangeNotifier { Decimal? _fromAmount; Decimal? _toAmount; @@ -23,6 +24,18 @@ class ExchangeFormState extends ChangeNotifier { _to = to; } + void clearAmounts(bool shouldNotifyListeners) { + _fromAmount = null; + _toAmount = null; + _minFromAmount = null; + _minToAmount = null; + rate = null; + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + Future swap() async { final Decimal? newToAmount = _fromAmount; final Decimal? newFromAmount = _toAmount; @@ -30,9 +43,9 @@ class ExchangeFormState extends ChangeNotifier { final Decimal? newMinFromAmount = _minToAmount; final Decimal? newMinToAmount = _minFromAmount; - final Decimal? newRate = rate == null - ? rate - : (Decimal.one / rate!).toDecimal(scaleOnInfinitePrecision: 12); + // final Decimal? newRate = rate == null + // ? rate + // : (Decimal.one / rate!).toDecimal(scaleOnInfinitePrecision: 12); final Currency? newTo = from; final Currency? newFrom = to; @@ -43,125 +56,171 @@ class ExchangeFormState extends ChangeNotifier { _minToAmount = newMinToAmount; _minFromAmount = newMinFromAmount; - rate = newRate; + // rate = newRate; _to = newTo; _from = newFrom; + await _updateMinFromAmount(shouldNotifyListeners: false); + + rate = null; + + if (_fromAmount != null) { + Decimal? amt; + if (_minFromAmount != null) { + if (_minFromAmount! > _fromAmount!) { + amt = await getStandardEstimatedToAmount( + fromAmount: _minFromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = + (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); + } + } else { + amt = await getStandardEstimatedToAmount( + fromAmount: _fromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = (amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12); + } + } + } + if (rate != null) { + _toAmount = (_fromAmount! * rate!); + } + } else { + if (_minFromAmount != null) { + Decimal? amt = await getStandardEstimatedToAmount( + fromAmount: _minFromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = + (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); + } + } + } + notifyListeners(); } String get fromAmountString => - _fromAmount == null ? "-" : _fromAmount!.toStringAsFixed(8); + _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); String get toAmountString => - _toAmount == null ? "-" : _toAmount!.toStringAsFixed(8); + _toAmount == null ? "" : _toAmount!.toStringAsFixed(8); Future updateTo(Currency to, bool shouldNotifyListeners) async { - _to = to; - if (_from == null) { + try { + _to = to; + if (_from == null) { + rate = null; + notifyListeners(); + return; + } + + await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); + // await _updateMinToAmount(shouldNotifyListeners: shouldNotifyListeners); + rate = null; - notifyListeners(); - return; - } - await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); - // await _updateMinToAmount(shouldNotifyListeners: shouldNotifyListeners); - - rate = null; - - if (_fromAmount != null) { - Decimal? amt; - if (_minFromAmount != null) { - if (_minFromAmount! > _fromAmount!) { - amt = await getStandardEstimatedToAmount( + if (_fromAmount != null) { + Decimal? amt; + if (_minFromAmount != null) { + if (_minFromAmount! > _fromAmount!) { + amt = await getStandardEstimatedToAmount( + fromAmount: _minFromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = (amt / _minFromAmount!) + .toDecimal(scaleOnInfinitePrecision: 12); + } + debugPrint("A"); + } else { + amt = await getStandardEstimatedToAmount( + fromAmount: _fromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = + (amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12); + } + debugPrint("B"); + } + } + if (rate != null) { + _toAmount = (_fromAmount! * rate!); + } + debugPrint("C"); + } else { + if (_minFromAmount != null) { + Decimal? amt = await getStandardEstimatedToAmount( fromAmount: _minFromAmount!, from: _from!, to: _to!); if (amt != null) { rate = (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); } - debugPrint("A"); - } else { - amt = await getStandardEstimatedToAmount( - fromAmount: _fromAmount!, from: _from!, to: _to!); - if (amt != null) { - rate = (amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12); - } - debugPrint("B"); + debugPrint("D"); } } - if (rate != null) { - _toAmount = (_fromAmount! * rate!); - } - debugPrint("C"); - } else { - if (_minFromAmount != null) { - Decimal? amt = await getStandardEstimatedToAmount( - fromAmount: _minFromAmount!, from: _from!, to: _to!); - if (amt != null) { - rate = - (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); - } - debugPrint("D"); - } - } - debugPrint( - "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); + debugPrint( + "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); - if (shouldNotifyListeners) { - notifyListeners(); + if (shouldNotifyListeners) { + notifyListeners(); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Fatal); } } Future updateFrom(Currency from, bool shouldNotifyListeners) async { - _from = from; + try { + _from = from; + + if (_to == null) { + rate = null; + notifyListeners(); + return; + } + + await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); - if (_to == null) { rate = null; - notifyListeners(); - return; - } - await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); - - rate = null; - - if (_fromAmount != null) { - Decimal? amt; - if (_minFromAmount != null) { - if (_minFromAmount! > _fromAmount!) { - amt = await getStandardEstimatedToAmount( + if (_fromAmount != null) { + Decimal? amt; + if (_minFromAmount != null) { + if (_minFromAmount! > _fromAmount!) { + amt = await getStandardEstimatedToAmount( + fromAmount: _minFromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = (amt / _minFromAmount!) + .toDecimal(scaleOnInfinitePrecision: 12); + } + } else { + amt = await getStandardEstimatedToAmount( + fromAmount: _fromAmount!, from: _from!, to: _to!); + if (amt != null) { + rate = + (amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12); + } + } + } + if (rate != null) { + _toAmount = (_fromAmount! * rate!); + } + } else { + if (_minFromAmount != null) { + Decimal? amt = await getStandardEstimatedToAmount( fromAmount: _minFromAmount!, from: _from!, to: _to!); if (amt != null) { rate = (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); } - } else { - amt = await getStandardEstimatedToAmount( - fromAmount: _fromAmount!, from: _from!, to: _to!); - if (amt != null) { - rate = (amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12); - } } } - if (rate != null) { - _toAmount = (_fromAmount! * rate!); - } - } else { - if (_minFromAmount != null) { - Decimal? amt = await getStandardEstimatedToAmount( - fromAmount: _minFromAmount!, from: _from!, to: _to!); - if (amt != null) { - rate = - (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); - } - } - } - debugPrint( - "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); - if (shouldNotifyListeners) { - notifyListeners(); + debugPrint( + "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); + if (shouldNotifyListeners) { + notifyListeners(); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Fatal); } } @@ -270,7 +329,7 @@ class ExchangeFormState extends ChangeNotifier { required Currency from, required Currency to, }) async { - final response = await ChangeNow.getEstimatedExchangeAmount( + final response = await ChangeNow.instance.getEstimatedExchangeAmount( fromTicker: from.ticker, toTicker: to.ticker, fromAmount: fromAmount); if (response.value != null) { @@ -286,8 +345,8 @@ class ExchangeFormState extends ChangeNotifier { required Currency from, required Currency to, }) async { - final response = await ChangeNow.getMinimalExchangeAmount( - fromTicker: from.ticker, toTicker: to.ticker); + final response = await ChangeNow.instance + .getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker); if (response.value != null) { return response.value!; diff --git a/lib/pages/exchange_view/exchange_loading_overlay.dart b/lib/pages/exchange_view/exchange_loading_overlay.dart index 8285f9a82..9b8e07d7c 100644 --- a/lib/pages/exchange_view/exchange_loading_overlay.dart +++ b/lib/pages/exchange_view/exchange_loading_overlay.dart @@ -2,11 +2,11 @@ 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/exchange_form_provider.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/services/change_now/change_now.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -38,7 +38,8 @@ class _ExchangeLoadingOverlayViewState ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = ChangeNowLoadStatus.loading; - final response3 = await ChangeNow.getAvailableFixedRateMarkets(); + final response3 = + await ref.read(changeNowProvider).getAvailableFixedRateMarkets(); if (response3.value != null) { ref.read(fixedRateMarketPairsStateProvider.state).state = response3.value!; @@ -47,7 +48,7 @@ class _ExchangeLoadingOverlayViewState final matchingMarkets = response3.value!.where((e) => e.to == "doge" && e.from == "btc"); if (matchingMarkets.isNotEmpty) { - ref + await ref .read(fixedRateExchangeFormProvider) .updateMarket(matchingMarkets.first, true); } @@ -78,8 +79,9 @@ class _ExchangeLoadingOverlayViewState ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = ChangeNowLoadStatus.loading; - final response = await ChangeNow.getAvailableCurrencies(); - final response2 = await ChangeNow.getAvailableFloatingRatePairs(); + 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!; @@ -90,13 +92,13 @@ class _ExchangeLoadingOverlayViewState if (response.value!.length > 1) { if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { - ref.read(estimatedRateExchangeFormProvider).updateFrom( + 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) { - ref.read(estimatedRateExchangeFormProvider).updateTo( + await ref.read(estimatedRateExchangeFormProvider).updateTo( response.value!.firstWhere((e) => e.ticker == "doge"), false); } } diff --git a/lib/pages/exchange_view/exchange_step_views/step_3_view.dart b/lib/pages/exchange_view/exchange_step_views/step_3_view.dart index 160f9598a..a0c79343f 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_3_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_3_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/exchange/change_now/change_now_response.dart'; @@ -6,8 +8,8 @@ import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_4_view.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart'; +import 'package:stackwallet/providers/exchange/change_now_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart'; -import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; @@ -219,35 +221,39 @@ class _Step3ViewState extends ConsumerState { response; if (model.rateType == ExchangeRateType.estimated) { - response = await ChangeNow + response = await ref + .read(changeNowProvider) .createStandardExchangeTransaction( - fromTicker: model.sendTicker, - toTicker: model.receiveTicker, - receivingAddress: model.recipientAddress!, - amount: model.sendAmount, - refundAddress: model.refundAddress!, - ); + fromTicker: model.sendTicker, + toTicker: model.receiveTicker, + receivingAddress: + model.recipientAddress!, + amount: model.sendAmount, + refundAddress: model.refundAddress!, + ); } else { - response = await ChangeNow + response = await ref + .read(changeNowProvider) .createFixedRateExchangeTransaction( - fromTicker: model.sendTicker, - toTicker: model.receiveTicker, - receivingAddress: model.recipientAddress!, - amount: model.sendAmount, - refundAddress: model.refundAddress!, - rateId: model.rateId!, - ); + fromTicker: model.sendTicker, + toTicker: model.receiveTicker, + receivingAddress: + model.recipientAddress!, + amount: model.sendAmount, + refundAddress: model.refundAddress!, + rateId: model.rateId!, + ); } if (response.value == null) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( title: "Failed to create trade", message: response.exception?.toString(), ), - ); + )); return; } @@ -257,8 +263,9 @@ class _Step3ViewState extends ConsumerState { shouldNotifyListeners: true, ); - final statusResponse = - await ChangeNow.getTransactionStatus( + final statusResponse = await ref + .read(changeNowProvider) + .getTransactionStatus( id: response.value!.id); debugPrint("WTF: $statusResponse"); @@ -278,7 +285,7 @@ class _Step3ViewState extends ConsumerState { status += " for deposit"; } - NotificationApi.showNotification( + unawaited(NotificationApi.showNotification( changeNowId: model.trade!.id, title: status, body: "Trade ID ${model.trade!.id}", @@ -287,13 +294,13 @@ class _Step3ViewState extends ConsumerState { date: model.trade!.date, shouldWatchForUpdates: true, coinName: "coinName", - ); + )); if (mounted) { - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( Step4View.routeName, arguments: model, - ); + )); } }, style: Theme.of(context) diff --git a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart index c80bba8f1..7ce46630d 100644 --- a/lib/pages/exchange_view/exchange_step_views/step_4_view.dart +++ b/lib/pages/exchange_view/exchange_step_views/step_4_view.dart @@ -13,10 +13,10 @@ import 'package:stackwallet/pages/exchange_view/send_from_view.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart'; +import 'package:stackwallet/providers/exchange/change_now_provider.dart'; import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/route_generator.dart'; -import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; @@ -69,8 +69,9 @@ class _Step4ViewState extends ConsumerState { } Future _updateStatus() async { - final statusResponse = - await ChangeNow.getTransactionStatus(id: model.trade!.id); + final statusResponse = await ref + .read(changeNowProvider) + .getTransactionStatus(id: model.trade!.id); String status = "Waiting"; if (statusResponse.value != null) { _status = statusResponse.value!.status; @@ -214,11 +215,11 @@ class _Step4ViewState extends ConsumerState { final data = ClipboardData( text: model.sendAmount.toString()); await clipboard.setData(data); - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.info, message: "Copied to clipboard", context: context, - ); + )); }, child: Row( children: [ @@ -271,11 +272,11 @@ class _Step4ViewState extends ConsumerState { final data = ClipboardData( text: model.trade!.payinAddress); await clipboard.setData(data); - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.info, message: "Copied to clipboard", context: context, - ); + )); }, child: Row( children: [ @@ -331,11 +332,11 @@ class _Step4ViewState extends ConsumerState { final data = ClipboardData(text: model.trade!.id); await clipboard.setData(data); - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.info, message: "Copied to clipboard", context: context, - ); + )); }, child: SvgPicture.asset( Assets.svg.copy, @@ -487,7 +488,7 @@ class _Step4ViewState extends ConsumerState { try { bool wasCancelled = false; - showDialog( + unawaited(showDialog( context: context, useSafeArea: false, barrierDismissible: false, @@ -500,7 +501,7 @@ class _Step4ViewState extends ConsumerState { }, ); }, - ); + )); final txData = await manager.prepareSend( @@ -524,7 +525,8 @@ class _Step4ViewState extends ConsumerState { txData["address"] = address; if (mounted) { - Navigator.of(context).push( + unawaited( + Navigator.of(context).push( RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator @@ -543,7 +545,7 @@ class _Step4ViewState extends ConsumerState { .routeName, ), ), - ); + )); } } } catch (e) { @@ -551,7 +553,7 @@ class _Step4ViewState extends ConsumerState { // pop building dialog Navigator.of(context).pop(); - showDialog( + unawaited(showDialog( context: context, useSafeArea: false, barrierDismissible: true, @@ -584,7 +586,7 @@ class _Step4ViewState extends ConsumerState { ), ); }, - ); + )); // } } } diff --git a/lib/pages/exchange_view/exchange_view.dart b/lib/pages/exchange_view/exchange_view.dart index 85f2a1f12..df659616f 100644 --- a/lib/pages/exchange_view/exchange_view.dart +++ b/lib/pages/exchange_view/exchange_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -16,20 +18,21 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet. import 'package:stackwallet/pages/exchange_view/trade_details_view.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/exchange_form_provider.dart'; +import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_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/providers/exchange/trade_sent_from_stack_lookup_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; -import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_loading_overlay.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/trade_card.dart'; @@ -55,6 +58,23 @@ class _ExchangeViewState extends ConsumerState { _sendFocusNode.unfocus(); _receiveFocusNode.unfocus(); + unawaited( + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => WillPopScope( + onWillPop: () async => false, + child: Container( + color: CFColors.stackAccent.withOpacity(0.8), + child: const CustomLoadingOverlay( + message: "Updating exchange rate", + eventBus: null, + ), + ), + ), + ), + ); + if (ref.watch(prefsChangeNotifierProvider .select((pref) => pref.exchangeRateType)) == ExchangeRateType.estimated) { @@ -74,6 +94,9 @@ class _ExchangeViewState extends ConsumerState { } } } + if (mounted) { + Navigator.of(context).pop(); + } _swapLock = false; } @@ -197,6 +220,17 @@ class _ExchangeViewState extends ConsumerState { void initState() { _sendController = TextEditingController(); _receiveController = TextEditingController(); + + final isEstimated = + ref.read(prefsChangeNotifierProvider).exchangeRateType == + ExchangeRateType.estimated; + _sendController.text = isEstimated + ? ref.read(estimatedRateExchangeFormProvider).fromAmountString + : ref.read(fixedRateExchangeFormProvider).fromAmountString; + _receiveController.text = isEstimated + ? ref.read(estimatedRateExchangeFormProvider).toAmountString + : ref.read(fixedRateExchangeFormProvider).toAmountString; + super.initState(); } @@ -407,14 +441,14 @@ class _ExchangeViewState extends ConsumerState { .read(fixedRateExchangeFormProvider) .updateMarket(market, true); } catch (e) { - showDialog( + unawaited(showDialog( context: context, builder: (_) => const StackDialog( title: "Fixed rate market error", message: "Could not find the specified fixed rate trade pair", ), - ); + )); return; } }, @@ -716,14 +750,14 @@ class _ExchangeViewState extends ConsumerState { .read(fixedRateExchangeFormProvider) .updateMarket(market, true); } catch (e) { - showDialog( + unawaited(showDialog( context: context, builder: (_) => const StackDialog( title: "Fixed rate market error", message: "Could not find the specified fixed rate trade pair", ), - ); + )); return; } }, @@ -924,12 +958,12 @@ class _ExchangeViewState extends ConsumerState { } } } - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.", context: context, - ); + )); break; case ExchangeRateType.fixed: final fromTicker = ref @@ -970,12 +1004,12 @@ class _ExchangeViewState extends ConsumerState { ); return; } - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Fixed rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last fixed rate pair.", context: context, - ); + )); break; } }, @@ -1047,7 +1081,7 @@ class _ExchangeViewState extends ConsumerState { } if (!isAvailable) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -1056,7 +1090,7 @@ class _ExchangeViewState extends ConsumerState { message: "The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades", ), - ); + )); return; } @@ -1068,15 +1102,16 @@ class _ExchangeViewState extends ConsumerState { .read(prefsChangeNotifierProvider) .exchangeRateType; - final response = await ChangeNow + final response = await ref + .read(changeNowProvider) .getEstimatedExchangeAmount( - fromTicker: fromTicker, - toTicker: toTicker, - fromAmount: sendAmount, - ); + fromTicker: fromTicker, + toTicker: toTicker, + fromAmount: sendAmount, + ); if (response.value == null) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -1084,7 +1119,7 @@ class _ExchangeViewState extends ConsumerState { "Failed to update trade estimate", message: response.exception?.toString(), ), - ); + )); return; } @@ -1108,10 +1143,10 @@ class _ExchangeViewState extends ConsumerState { exchangeSendFromWalletIdStateProvider .state) .state = null; - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( Step1View.routeName, arguments: model, - ); + )); } } else { final fromTicker = ref @@ -1133,18 +1168,19 @@ class _ExchangeViewState extends ConsumerState { .read(prefsChangeNotifierProvider) .exchangeRateType; - final response = await ChangeNow + final response = await ref + .read(changeNowProvider) .getEstimatedFixedRateExchangeAmount( - fromTicker: fromTicker, - toTicker: toTicker, - fromAmount: sendAmount, - useRateId: true, - ); + fromTicker: fromTicker, + toTicker: toTicker, + fromAmount: sendAmount, + useRateId: true, + ); bool? shouldCancel; if (response.value == null) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -1152,7 +1188,7 @@ class _ExchangeViewState extends ConsumerState { "Failed to update trade estimate", message: response.exception?.toString(), ), - ); + )); return; } else if (response.value!.warningMessage != null && @@ -1234,10 +1270,10 @@ class _ExchangeViewState extends ConsumerState { exchangeSendFromWalletIdStateProvider .state) .state = null; - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( Step1View.routeName, arguments: model, - ); + )); } } } @@ -1341,18 +1377,18 @@ class _ExchangeViewState extends ConsumerState { final tx = txData.getAllTransactions()[txid]; if (mounted) { - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( TradeDetailsView.routeName, arguments: Tuple4(tradeId, tx, walletIds.first, manager.walletName), - ); + )); } } else { - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( TradeDetailsView.routeName, arguments: Tuple4( tradeId, null, walletIds?.first, null), - ); + )); } }, ), @@ -1454,7 +1490,7 @@ class RateInfo extends ConsumerWidget { } } - showModalBottomSheet( + unawaited(showModalBottomSheet( backgroundColor: Colors.transparent, context: context, shape: const RoundedRectangleBorder( @@ -1467,7 +1503,7 @@ class RateInfo extends ConsumerWidget { if (value is ExchangeRateType && value != type) { onChanged(value); } - }); + })); } : null, style: Theme.of(context).textButtonTheme.style?.copyWith( diff --git a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart index f44b38991..152826345 100644 --- a/lib/pages/exchange_view/wallet_initiated_exchange_view.dart +++ b/lib/pages/exchange_view/wallet_initiated_exchange_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -16,13 +18,12 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet. import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.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/changenow_initial_load_status.dart'; -import 'package:stackwallet/providers/exchange/exchange_form_provider.dart'; +import 'package:stackwallet/providers/exchange/change_now_provider.dart'; +import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_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/providers/providers.dart'; -import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -30,6 +31,7 @@ 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'; import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:tuple/tuple.dart'; @@ -72,6 +74,23 @@ class _WalletInitiatedExchangeViewState _sendFocusNode.unfocus(); _receiveFocusNode.unfocus(); + unawaited( + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => WillPopScope( + onWillPop: () async => false, + child: Container( + color: CFColors.stackAccent.withOpacity(0.8), + child: const CustomLoadingOverlay( + message: "Updating exchange rate", + eventBus: null, + ), + ), + ), + ), + ); + if (ref.watch(prefsChangeNotifierProvider .select((pref) => pref.exchangeRateType)) == ExchangeRateType.estimated) { @@ -91,6 +110,9 @@ class _WalletInitiatedExchangeViewState } } } + if (mounted) { + Navigator.of(context).pop(); + } _swapLock = false; } @@ -216,6 +238,11 @@ class _WalletInitiatedExchangeViewState coin = widget.coin; _sendController = TextEditingController(); _receiveController = TextEditingController(); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + ref.read(estimatedRateExchangeFormProvider).clearAmounts(true); + // ref.read(fixedRateExchangeFormProvider); + }); super.initState(); } @@ -230,41 +257,6 @@ class _WalletInitiatedExchangeViewState Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - if (ref - .watch(changeNowEstimatedInitialLoadStatusStateProvider.state) - .state == - false) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Center( - child: Text( - "Loading ChangeNOW data", - style: STextStyles.pageTitleH2, - ), - ), - const SizedBox( - height: 16, - ), - Center( - child: Text( - "This shouldn't take long", - style: STextStyles.smallMed14, - ), - ), - const SizedBox( - height: 16, - ), - const SizedBox( - height: 100, - width: 100, - child: LoadingIndicator(), - ), - ], - ); - } - final isEstimated = ref.watch(prefsChangeNotifierProvider .select((pref) => pref.exchangeRateType)) == ExchangeRateType.estimated; @@ -512,14 +504,14 @@ class _WalletInitiatedExchangeViewState fixedRateExchangeFormProvider) .updateMarket(market, true); } catch (e) { - showDialog( + unawaited(showDialog( context: context, builder: (_) => const StackDialog( title: "Fixed rate market error", message: "Could not find the specified fixed rate trade pair", ), - ); + )); return; } }, @@ -867,14 +859,14 @@ class _WalletInitiatedExchangeViewState fixedRateExchangeFormProvider) .updateMarket(market, true); } catch (e) { - showDialog( + unawaited(showDialog( context: context, builder: (_) => const StackDialog( title: "Fixed rate market error", message: "Could not find the specified fixed rate trade pair", ), - ); + )); return; } }, @@ -1099,12 +1091,12 @@ class _WalletInitiatedExchangeViewState } } } - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.", context: context, - ); + )); break; case ExchangeRateType.fixed: final fromTicker = ref @@ -1146,12 +1138,12 @@ class _WalletInitiatedExchangeViewState ); return; } - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Fixed rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last fixed rate pair.", context: context, - ); + )); break; } }, @@ -1219,6 +1211,24 @@ class _WalletInitiatedExchangeViewState 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; @@ -1228,17 +1238,23 @@ class _WalletInitiatedExchangeViewState Format.decimalAmountToSatoshis( sendAmount), feeObject.medium); + + shouldPop = true; + if (!wasPopped && mounted) { + Navigator.of(context).pop(); + } + if (availableBalance < sendAmount + Format.satoshisToAmount(fee)) { - showDialog( + unawaited(showDialog( context: context, builder: (_) => StackOkDialog( title: "Insufficient balance", message: "Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade", ), - ); + )); return; } } @@ -1272,7 +1288,7 @@ class _WalletInitiatedExchangeViewState } if (!isAvailable) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -1281,7 +1297,7 @@ class _WalletInitiatedExchangeViewState message: "The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades", ), - ); + )); return; } @@ -1289,15 +1305,16 @@ class _WalletInitiatedExchangeViewState .read(prefsChangeNotifierProvider) .exchangeRateType; - final response = await ChangeNow + final response = await ref + .read(changeNowProvider) .getEstimatedExchangeAmount( - fromTicker: fromTicker, - toTicker: toTicker, - fromAmount: sendAmount, - ); + fromTicker: fromTicker, + toTicker: toTicker, + fromAmount: sendAmount, + ); if (response.value == null) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -1306,7 +1323,7 @@ class _WalletInitiatedExchangeViewState message: response.exception?.toString(), ), - ); + )); return; } @@ -1330,10 +1347,10 @@ class _WalletInitiatedExchangeViewState exchangeSendFromWalletIdStateProvider .state) .state = Tuple2(walletId, coin); - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( Step2View.routeName, arguments: model, - ); + )); } } else { final fromTicker = ref @@ -1355,18 +1372,19 @@ class _WalletInitiatedExchangeViewState .read(prefsChangeNotifierProvider) .exchangeRateType; - final response = await ChangeNow + final response = await ref + .read(changeNowProvider) .getEstimatedFixedRateExchangeAmount( - fromTicker: fromTicker, - toTicker: toTicker, - fromAmount: sendAmount, - useRateId: true, - ); + fromTicker: fromTicker, + toTicker: toTicker, + fromAmount: sendAmount, + useRateId: true, + ); bool? shouldCancel; if (response.value == null) { - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -1375,7 +1393,7 @@ class _WalletInitiatedExchangeViewState message: response.exception?.toString(), ), - ); + )); return; } else if (response.value!.warningMessage != null && @@ -1457,10 +1475,10 @@ class _WalletInitiatedExchangeViewState exchangeSendFromWalletIdStateProvider .state) .state = Tuple2(walletId, coin); - Navigator.of(context).pushNamed( + unawaited(Navigator.of(context).pushNamed( Step2View.routeName, arguments: model, - ); + )); } } } diff --git a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart index 22766731d..ca7133a0f 100644 --- a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart +++ b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart @@ -1,13 +1,15 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/pages/exchange_view/exchange_view.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/exchange_form_provider.dart'; +import 'package:stackwallet/providers/exchange/change_now_provider.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/providers/providers.dart'; -import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -41,8 +43,9 @@ class _HomeViewButtonBarState extends ConsumerState { BuildContext context, WidgetRef ref, ) async { - final response = await ChangeNow.getAvailableCurrencies(); - final response2 = await ChangeNow.getAvailableFloatingRatePairs(); + final response = await ref.read(changeNowProvider).getAvailableCurrencies(); + final response2 = + await ref.read(changeNowProvider).getAvailableFloatingRatePairs(); if (response.value != null && response2.value != null) { ref.read(availableChangeNowCurrenciesStateProvider.state).state = response.value!; @@ -52,13 +55,13 @@ class _HomeViewButtonBarState extends ConsumerState { if (response.value!.length > 1) { if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { - ref.read(estimatedRateExchangeFormProvider).updateFrom( + await ref.read(estimatedRateExchangeFormProvider).updateFrom( response.value!.firstWhere((e) => e.ticker == "btc"), true); } } if (ref.read(estimatedRateExchangeFormProvider).to == null) { if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { - ref.read(estimatedRateExchangeFormProvider).updateTo( + await ref.read(estimatedRateExchangeFormProvider).updateTo( response.value!.firstWhere((e) => e.ticker == "doge"), true); } } @@ -70,7 +73,7 @@ class _HomeViewButtonBarState extends ConsumerState { Logging.instance.log( "Failed to load changeNOW floating rate market data: \n${response.exception?.errorMessage}\n${response2.exception?.toString()}", level: LogLevel.Error); - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( @@ -78,7 +81,7 @@ class _HomeViewButtonBarState extends ConsumerState { message: "${response.exception?.toString()}\n\n${response2.exception?.toString()}", ), - ); + )); } } @@ -86,7 +89,8 @@ class _HomeViewButtonBarState extends ConsumerState { BuildContext context, WidgetRef ref, ) async { - final response3 = await ChangeNow.getAvailableFixedRateMarkets(); + final response3 = + await ref.read(changeNowProvider).getAvailableFixedRateMarkets(); if (response3.value != null) { ref.read(fixedRateMarketPairsStateProvider.state).state = @@ -97,7 +101,7 @@ class _HomeViewButtonBarState extends ConsumerState { response3.value!.where((e) => e.to == "doge" && e.from == "btc"); if (matchingMarkets.isNotEmpty) { - ref + await ref .read(fixedRateExchangeFormProvider) .updateMarket(matchingMarkets.first, true); } @@ -108,14 +112,14 @@ class _HomeViewButtonBarState extends ConsumerState { Logging.instance.log( "Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}", level: LogLevel.Error); - showDialog( + unawaited(showDialog( context: context, barrierDismissible: true, builder: (_) => StackDialog( title: "ChangeNOW API call failed", message: "${response3.exception?.toString()}", ), - ); + )); } } diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index fa5b19219..3f097ddf3 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -17,7 +17,7 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_navigation_bar. import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart'; import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; -import 'package:stackwallet/providers/exchange/exchange_form_provider.dart'; +import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; 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'; diff --git a/lib/providers/exchange/change_now_provider.dart b/lib/providers/exchange/change_now_provider.dart new file mode 100644 index 000000000..759692c13 --- /dev/null +++ b/lib/providers/exchange/change_now_provider.dart @@ -0,0 +1,4 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/services/change_now/change_now.dart'; + +final changeNowProvider = Provider((ref) => ChangeNow.instance); diff --git a/lib/providers/exchange/exchange_form_provider.dart b/lib/providers/exchange/estimate_rate_exchange_form_provider.dart similarity index 70% rename from lib/providers/exchange/exchange_form_provider.dart rename to lib/providers/exchange/estimate_rate_exchange_form_provider.dart index 18d2e37a0..e6f0a92b2 100644 --- a/lib/providers/exchange/exchange_form_provider.dart +++ b/lib/providers/exchange/estimate_rate_exchange_form_provider.dart @@ -2,4 +2,4 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/exchange/exchange_form_state.dart'; final estimatedRateExchangeFormProvider = - ChangeNotifierProvider((ref) => ExchangeFormState()); + ChangeNotifierProvider((ref) => EstimatedRateExchangeFormState()); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 9e4a8dd5e..329018d9a 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -21,6 +21,7 @@ import 'package:stackwallet/pages/address_book_views/subviews/contact_details_vi import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart'; import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart'; +import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart'; import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_1_view.dart'; import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart'; import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart'; @@ -721,9 +722,14 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => WalletInitiatedExchangeView( - walletId: args.item1, - coin: args.item2, + builder: (_) => Stack( + children: [ + WalletInitiatedExchangeView( + walletId: args.item1, + coin: args.item2, + ), + const ExchangeLoadingOverlayView(), + ], ), settings: RouteSettings( name: settings.name, diff --git a/lib/services/change_now/change_now.dart b/lib/services/change_now/change_now.dart index f67fb8969..af70b83e4 100644 --- a/lib/services/change_now/change_now.dart +++ b/lib/services/change_now/change_now.dart @@ -13,20 +13,24 @@ import 'package:stackwallet/models/exchange/change_now/exchange_transaction_stat import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart'; import 'package:stackwallet/utilities/logger.dart'; -abstract class ChangeNow { +class ChangeNow { static const String scheme = "https"; static const String authority = "api.changenow.io"; static const String apiVersion = "/v1"; - /// set this to override using standard http client. Useful for testing - static http.Client? client; + ChangeNow._(); + static final ChangeNow _instance = ChangeNow._(); + static ChangeNow get instance => _instance; - static Uri _buildUri(String path, Map? params) { + /// set this to override using standard http client. Useful for testing + http.Client? client; + + Uri _buildUri(String path, Map? params) { return Uri.https(authority, apiVersion + path, params); } - static Future _makeGetRequest(Uri uri) async { - final client = ChangeNow.client ?? http.Client(); + Future _makeGetRequest(Uri uri) async { + final client = this.client ?? http.Client(); try { final response = await client.get( uri, @@ -43,11 +47,11 @@ abstract class ChangeNow { } } - static Future _makePostRequest( + Future _makePostRequest( Uri uri, Map body, ) async { - final client = ChangeNow.client ?? http.Client(); + final client = this.client ?? http.Client(); try { final response = await client.post( uri, @@ -69,7 +73,7 @@ abstract class ChangeNow { /// /// Set [active] to true to return only active currencies. /// Set [fixedRate] to true to return only currencies available on a fixed-rate flow. - static Future>> getAvailableCurrencies({ + Future>> getAvailableCurrencies({ bool? fixedRate, bool? active, }) async { @@ -117,7 +121,7 @@ abstract class ChangeNow { } } - static ChangeNowResponse> _parseAvailableCurrenciesJson( + ChangeNowResponse> _parseAvailableCurrenciesJson( List jsonArray) { try { List currencies = []; @@ -144,7 +148,7 @@ abstract class ChangeNow { /// /// Required [ticker] to fetch paired currencies for. /// Set [fixedRate] to true to return only currencies available on a fixed-rate flow. - static Future>> getPairedCurrencies({ + Future>> getPairedCurrencies({ required String ticker, bool? fixedRate, }) async { @@ -199,7 +203,7 @@ abstract class ChangeNow { /// The API endpoint returns minimal payment amount required to make /// an exchange of [fromTicker] to [toTicker]. /// If you try to exchange less, the transaction will most likely fail. - static Future> getMinimalExchangeAmount({ + Future> getMinimalExchangeAmount({ required String fromTicker, required String toTicker, String? apiKey, @@ -237,7 +241,7 @@ abstract class ChangeNow { /// Get estimated amount of [toTicker] cryptocurrency to receive /// for [fromAmount] of [fromTicker] - static Future> + Future> getEstimatedExchangeAmount({ required String fromTicker, required String toTicker, @@ -281,7 +285,7 @@ abstract class ChangeNow { /// This API endpoint returns fixed-rate estimated exchange amount of /// [toTicker] cryptocurrency to receive for [fromAmount] of [fromTicker] - static Future> + Future> getEstimatedFixedRateExchangeAmount({ required String fromTicker, required String toTicker, @@ -336,7 +340,7 @@ abstract class ChangeNow { /// fixed-rate flow. Some currencies get enabled or disabled from time to /// time and the market info gets updates, so make sure to refresh the list /// occasionally. One time per minute is sufficient. - static Future>> + Future>> getAvailableFixedRateMarkets({ String? apiKey, }) async { @@ -373,7 +377,7 @@ abstract class ChangeNow { } } - static ChangeNowResponse> _parseFixedRateMarketsJson( + ChangeNowResponse> _parseFixedRateMarketsJson( List jsonArray) { try { List markets = []; @@ -395,7 +399,7 @@ abstract class ChangeNow { /// The API endpoint creates a transaction, generates an address for /// sending funds and returns transaction attributes. - static Future> + Future> createStandardExchangeTransaction({ required String fromTicker, required String toTicker, @@ -457,7 +461,7 @@ abstract class ChangeNow { /// The API endpoint creates a transaction, generates an address for /// sending funds and returns transaction attributes. - static Future> + Future> createFixedRateExchangeTransaction({ required String fromTicker, required String toTicker, @@ -520,8 +524,7 @@ abstract class ChangeNow { } } - static Future> - getTransactionStatus({ + Future> getTransactionStatus({ required String id, String? apiKey, }) async { @@ -556,7 +559,7 @@ abstract class ChangeNow { } } - static Future>> + Future>> getAvailableFloatingRatePairs({ bool includePartners = false, }) async { @@ -593,7 +596,7 @@ abstract class ChangeNow { } } - static ChangeNowResponse> + ChangeNowResponse> _parseAvailableFloatingRatePairsJson(List jsonArray) { try { List pairs = []; diff --git a/lib/services/coins/bitcoin/bitcoin_wallet.dart b/lib/services/coins/bitcoin/bitcoin_wallet.dart index 7118da1fa..0750e9b93 100644 --- a/lib/services/coins/bitcoin/bitcoin_wallet.dart +++ b/lib/services/coins/bitcoin/bitcoin_wallet.dart @@ -1063,8 +1063,8 @@ class BitcoinWallet extends CoinServiceAPI { @override Future refresh() async { if (refreshMutex) { - Logging.instance - .log("$walletName refreshMutex denied", level: LogLevel.Info); + Logging.instance.log("$walletId $walletName refreshMutex denied", + level: LogLevel.Info); return; } else { refreshMutex = true; @@ -1139,14 +1139,15 @@ class BitcoinWallet extends CoinServiceAPI { if (shouldAutoSync) { timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async { Logging.instance.log( - "Periodic refresh check for $walletName in object instance: $hashCode", + "Periodic refresh check for $walletId $walletName in object instance: $hashCode", level: LogLevel.Info); // chain height check currently broken // if ((await chainHeight) != (await storedChainHeight)) { if (await refreshIfThereIsNewData()) { await refresh(); GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( - "New data found in $walletName in background!", walletId)); + "New data found in $walletId $walletName in background!", + walletId)); } // } }); @@ -3375,7 +3376,7 @@ class BitcoinWallet extends CoinServiceAPI { ); // clear cache - _cachedElectrumXClient.clearSharedTransactionCache(coin: coin); + await _cachedElectrumXClient.clearSharedTransactionCache(coin: coin); // back up data await _rescanBackup(); diff --git a/lib/services/coins/dogecoin/dogecoin_wallet.dart b/lib/services/coins/dogecoin/dogecoin_wallet.dart index f51a139ef..7a3a70a96 100644 --- a/lib/services/coins/dogecoin/dogecoin_wallet.dart +++ b/lib/services/coins/dogecoin/dogecoin_wallet.dart @@ -741,7 +741,8 @@ class DogecoinWallet extends CoinServiceAPI { @override Future refresh() async { if (refreshMutex) { - Logging.instance.log("refreshMutex denied", level: LogLevel.Info); + Logging.instance.log("$walletId $walletName refreshMutex denied", + level: LogLevel.Info); return; } else { refreshMutex = true; @@ -820,7 +821,8 @@ class DogecoinWallet extends CoinServiceAPI { if (await refreshIfThereIsNewData()) { await refresh(); GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( - "New data found in $walletName in background!", walletId)); + "New data found in $walletId $walletName in background!", + walletId)); } // } }); @@ -1761,7 +1763,8 @@ class DogecoinWallet extends CoinServiceAPI { await _getCurrentAddressForChain(1, derivePathType); final int txCount = await getTxCount(address: currentExternalAddr); Logging.instance.log( - 'Number of txs for current change address $currentExternalAddr: $txCount', level: LogLevel.Info); + 'Number of txs for current change address $currentExternalAddr: $txCount', + level: LogLevel.Info); if (txCount >= 1) { // First increment the change index @@ -1786,7 +1789,8 @@ class DogecoinWallet extends CoinServiceAPI { } } catch (e, s) { Logging.instance.log( - "Exception rethrown from _checkChangeAddressForTransactions($derivePathType): $e\n$s", level: LogLevel.Error); + "Exception rethrown from _checkChangeAddressForTransactions($derivePathType): $e\n$s", + level: LogLevel.Error); rethrow; } } @@ -1798,7 +1802,8 @@ class DogecoinWallet extends CoinServiceAPI { } } catch (e, s) { Logging.instance.log( - "Exception rethrown from _checkCurrentReceivingAddressesForTransactions(): $e\n$s", level: LogLevel.Info); + "Exception rethrown from _checkCurrentReceivingAddressesForTransactions(): $e\n$s", + level: LogLevel.Info); rethrow; } } @@ -1821,7 +1826,8 @@ class DogecoinWallet extends CoinServiceAPI { } } catch (e, s) { Logging.instance.log( - "Exception rethrown from _checkCurrentChangeAddressesForTransactions(): $e\n$s", level: LogLevel.Error); + "Exception rethrown from _checkCurrentChangeAddressesForTransactions(): $e\n$s", + level: LogLevel.Error); rethrow; } } @@ -1969,7 +1975,8 @@ class DogecoinWallet extends CoinServiceAPI { Logging.instance.log("addAddresses: $allAddresses", level: LogLevel.Info); Logging.instance.log("allTxHashes: $allTxHashes", level: LogLevel.Info); - Logging.instance.log("allTransactions length: ${allTransactions.length}", level: LogLevel.Info); + Logging.instance.log("allTransactions length: ${allTransactions.length}", + level: LogLevel.Info); final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); @@ -2015,11 +2022,13 @@ class DogecoinWallet extends CoinServiceAPI { } } - Logging.instance.log("recipientsArray: $recipientsArray", level: LogLevel.Info); + Logging.instance + .log("recipientsArray: $recipientsArray", level: LogLevel.Info); final foundInSenders = allAddresses.any((element) => sendersArray.contains(element)); - Logging.instance.log("foundInSenders: $foundInSenders", level: LogLevel.Info); + Logging.instance + .log("foundInSenders: $foundInSenders", level: LogLevel.Info); // If txType = Sent, then calculate inputAmtSentFromWallet if (foundInSenders) { @@ -2228,7 +2237,8 @@ class DogecoinWallet extends CoinServiceAPI { dynamic coinSelection(int satoshiAmountToSend, int selectedTxFeeRate, String _recipientAddress, bool isSendAll, {int additionalOutputs = 0, List? utxos}) async { - Logging.instance.log("Starting coinSelection ----------", level: LogLevel.Info); + Logging.instance + .log("Starting coinSelection ----------", level: LogLevel.Info); final List availableOutputs = utxos ?? outputsList; final List spendableOutputs = []; int spendableSatoshiValue = 0; @@ -2246,10 +2256,14 @@ class DogecoinWallet extends CoinServiceAPI { spendableOutputs.sort( (a, b) => b.status.confirmations.compareTo(a.status.confirmations)); - Logging.instance.log("spendableOutputs.length: ${spendableOutputs.length}", level: LogLevel.Info); - Logging.instance.log("spendableOutputs: $spendableOutputs", level: LogLevel.Info); - Logging.instance.log("spendableSatoshiValue: $spendableSatoshiValue", level: LogLevel.Info); - Logging.instance.log("satoshiAmountToSend: $satoshiAmountToSend", level: LogLevel.Info); + Logging.instance.log("spendableOutputs.length: ${spendableOutputs.length}", + level: LogLevel.Info); + Logging.instance + .log("spendableOutputs: $spendableOutputs", level: LogLevel.Info); + Logging.instance.log("spendableSatoshiValue: $spendableSatoshiValue", + level: LogLevel.Info); + Logging.instance + .log("satoshiAmountToSend: $satoshiAmountToSend", level: LogLevel.Info); // If the amount the user is trying to send is smaller than the amount that they have spendable, // then return 1, which indicates that they have an insufficient balance. if (spendableSatoshiValue < satoshiAmountToSend) { @@ -2284,10 +2298,14 @@ class DogecoinWallet extends CoinServiceAPI { inputsBeingConsumed += 1; } - Logging.instance.log("satoshisBeingUsed: $satoshisBeingUsed", level: LogLevel.Info); - Logging.instance.log("inputsBeingConsumed: $inputsBeingConsumed", level: LogLevel.Info); - Logging.instance.log('utxoObjectsToUse: $utxoObjectsToUse', level: LogLevel.Info); - Logging.instance.log('satoshiAmountToSend $satoshiAmountToSend', level: LogLevel.Info); + Logging.instance + .log("satoshisBeingUsed: $satoshisBeingUsed", level: LogLevel.Info); + Logging.instance + .log("inputsBeingConsumed: $inputsBeingConsumed", level: LogLevel.Info); + Logging.instance + .log('utxoObjectsToUse: $utxoObjectsToUse', level: LogLevel.Info); + Logging.instance + .log('satoshiAmountToSend $satoshiAmountToSend', level: LogLevel.Info); // numberOfOutputs' length must always be equal to that of recipientsArray and recipientsAmtArray List recipientsArray = [_recipientAddress]; @@ -2297,7 +2315,8 @@ class DogecoinWallet extends CoinServiceAPI { final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse); if (isSendAll) { - Logging.instance.log("Attempting to send all $coin", level: LogLevel.Info); + Logging.instance + .log("Attempting to send all $coin", level: LogLevel.Info); final int vSizeForOneOutput = (await buildTransaction( utxosToUse: utxoObjectsToUse, @@ -2362,8 +2381,10 @@ class DogecoinWallet extends CoinServiceAPI { feeRatePerKB: selectedTxFeeRate, ); - Logging.instance.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); - Logging.instance.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); + Logging.instance + .log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); + Logging.instance + .log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) { feeForOneOutput = (vSizeForOneOutput + 1) * 1000; } @@ -2371,8 +2392,10 @@ class DogecoinWallet extends CoinServiceAPI { feeForTwoOutputs = ((vSizeForTwoOutPuts + 1) * 1000); } - Logging.instance.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); - Logging.instance.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); + Logging.instance + .log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); + Logging.instance + .log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) { if (satoshisBeingUsed - satoshiAmountToSend > @@ -2400,12 +2423,17 @@ class DogecoinWallet extends CoinServiceAPI { // At this point, we have the outputs we're going to use, the amounts to send along with which addresses // we intend to send these amounts to. We have enough to send instructions to build the transaction. Logging.instance.log('2 outputs in tx', level: LogLevel.Info); - Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); - Logging.instance.log('Change Output Size: $changeOutputSize', level: LogLevel.Info); Logging.instance - .log('Difference (fee being paid): $feeBeingPaid sats', level: LogLevel.Info); - Logging.instance.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); + .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); + Logging.instance.log('Recipient output size: $satoshiAmountToSend', + level: LogLevel.Info); + Logging.instance.log('Change Output Size: $changeOutputSize', + level: LogLevel.Info); + Logging.instance.log( + 'Difference (fee being paid): $feeBeingPaid sats', + level: LogLevel.Info); + Logging.instance + .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); dynamic txn = await buildTransaction( utxosToUse: utxoObjectsToUse, utxoSigningData: utxoSigningData, @@ -2421,14 +2449,19 @@ class DogecoinWallet extends CoinServiceAPI { satoshisBeingUsed - satoshiAmountToSend - changeOutputSize; recipientsAmtArray.removeLast(); recipientsAmtArray.add(changeOutputSize); - Logging.instance.log('Adjusted Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance - .log('Adjusted Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); - Logging.instance - .log('Adjusted Change Output Size: $changeOutputSize', level: LogLevel.Info); + Logging.instance.log('Adjusted Input size: $satoshisBeingUsed', + level: LogLevel.Info); Logging.instance.log( - 'Adjusted Difference (fee being paid): $feeBeingPaid sats', level: LogLevel.Info); - Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); + 'Adjusted Recipient output size: $satoshiAmountToSend', + level: LogLevel.Info); + Logging.instance.log( + 'Adjusted Change Output Size: $changeOutputSize', + level: LogLevel.Info); + Logging.instance.log( + 'Adjusted Difference (fee being paid): $feeBeingPaid sats', + level: LogLevel.Info); + Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', + level: LogLevel.Info); txn = await buildTransaction( utxosToUse: utxoObjectsToUse, utxoSigningData: utxoSigningData, @@ -2449,11 +2482,15 @@ class DogecoinWallet extends CoinServiceAPI { // Something went wrong here. It either overshot or undershot the estimated fee amount or the changeOutputSize // is smaller than or equal to [DUST_LIMIT]. Revert to single output transaction. Logging.instance.log('1 output in tx', level: LogLevel.Info); - Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); + Logging.instance + .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); + Logging.instance.log('Recipient output size: $satoshiAmountToSend', + level: LogLevel.Info); Logging.instance.log( - 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', level: LogLevel.Info); - Logging.instance.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); + 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', + level: LogLevel.Info); + Logging.instance + .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); dynamic txn = await buildTransaction( utxosToUse: utxoObjectsToUse, utxoSigningData: utxoSigningData, @@ -2474,11 +2511,15 @@ class DogecoinWallet extends CoinServiceAPI { // which makes it uneconomical to add to the transaction. Here, we pass data directly to instruct // the wallet to begin crafting the transaction that the user requested. Logging.instance.log('1 output in tx', level: LogLevel.Info); - Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); + Logging.instance + .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); + Logging.instance.log('Recipient output size: $satoshiAmountToSend', + level: LogLevel.Info); Logging.instance.log( - 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', level: LogLevel.Info); - Logging.instance.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); + 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', + level: LogLevel.Info); + Logging.instance + .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); dynamic txn = await buildTransaction( utxosToUse: utxoObjectsToUse, utxoSigningData: utxoSigningData, @@ -2499,11 +2540,15 @@ class DogecoinWallet extends CoinServiceAPI { // what we need to pay for fees. Here, we pass data directly to instruct the wallet to begin // crafting the transaction that the user requested. Logging.instance.log('1 output in tx', level: LogLevel.Info); - Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); - Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); + Logging.instance + .log('Input size: $satoshisBeingUsed', level: LogLevel.Info); + Logging.instance.log('Recipient output size: $satoshiAmountToSend', + level: LogLevel.Info); Logging.instance.log( - 'Fee being paid: ${satoshisBeingUsed - satoshiAmountToSend} sats', level: LogLevel.Info); - Logging.instance.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); + 'Fee being paid: ${satoshisBeingUsed - satoshiAmountToSend} sats', + level: LogLevel.Info); + Logging.instance + .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); dynamic txn = await buildTransaction( utxosToUse: utxoObjectsToUse, utxoSigningData: utxoSigningData, @@ -2523,7 +2568,8 @@ class DogecoinWallet extends CoinServiceAPI { // pay for the transaction fee. Ideally, at this stage, we should check if the user has any // additional outputs they're able to spend and then recalculate fees. Logging.instance.log( - 'Cannot pay tx fee - checking for more outputs and trying again', level: LogLevel.Warning); + 'Cannot pay tx fee - checking for more outputs and trying again', + level: LogLevel.Warning); // try adding more outputs if (spendableOutputs.length > inputsBeingConsumed) { return coinSelection(satoshiAmountToSend, selectedTxFeeRate, @@ -2630,7 +2676,8 @@ class DogecoinWallet extends CoinServiceAPI { return results; } catch (e, s) { - Logging.instance.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); + Logging.instance + .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); rethrow; } } @@ -2642,7 +2689,8 @@ class DogecoinWallet extends CoinServiceAPI { required List recipients, required List satoshiAmounts, }) async { - Logging.instance.log("Starting buildTransaction ----------", level: LogLevel.Info); + Logging.instance + .log("Starting buildTransaction ----------", level: LogLevel.Info); final txb = TransactionBuilder(network: _network); txb.setVersion(1); @@ -2671,8 +2719,8 @@ class DogecoinWallet extends CoinServiceAPI { ); } } catch (e, s) { - Logging.instance - .log("Caught exception while signing transaction: $e\n$s", level: LogLevel.Error); + Logging.instance.log("Caught exception while signing transaction: $e\n$s", + level: LogLevel.Error); rethrow; } @@ -2733,7 +2781,8 @@ class DogecoinWallet extends CoinServiceAPI { await _rescanRestore(); longMutex = false; - Logging.instance.log("Exception rethrown from fullRescan(): $e\n$s", level: LogLevel.Error); + Logging.instance.log("Exception rethrown from fullRescan(): $e\n$s", + level: LogLevel.Error); rethrow; } } @@ -2884,7 +2933,8 @@ class DogecoinWallet extends CoinServiceAPI { return DB.instance.get(boxName: walletId, key: "isFavorite") as bool; } catch (e, s) { - Logging.instance.log("isFavorite fetch failed: $e\n$s", level: LogLevel.Error); + Logging.instance + .log("isFavorite fetch failed: $e\n$s", level: LogLevel.Error); rethrow; } } diff --git a/lib/services/coins/epiccash/epiccash_wallet.dart b/lib/services/coins/epiccash/epiccash_wallet.dart index c0cb9e4e9..64f775c49 100644 --- a/lib/services/coins/epiccash/epiccash_wallet.dart +++ b/lib/services/coins/epiccash/epiccash_wallet.dart @@ -1827,9 +1827,11 @@ class EpicCashWallet extends CoinServiceAPI { /// Refreshes display data for the wallet @override Future refresh() async { - Logging.instance.log("Calling refresh", level: LogLevel.Info); + Logging.instance + .log("$walletId $walletName Calling refresh", level: LogLevel.Info); if (refreshMutex) { - Logging.instance.log("refreshMutex denied", level: LogLevel.Info); + Logging.instance.log("$walletId $walletName refreshMutex denied", + level: LogLevel.Info); return; } else { refreshMutex = true; @@ -1920,14 +1922,15 @@ class EpicCashWallet extends CoinServiceAPI { if (shouldAutoSync) { timer ??= Timer.periodic(const Duration(seconds: 60), (timer) async { Logging.instance.log( - "Periodic refresh check for $walletId in object instance: $hashCode", + "Periodic refresh check for $walletId $walletName in object instance: $hashCode", level: LogLevel.Info); // chain height check currently broken // if ((await chainHeight) != (await storedChainHeight)) { if (await refreshIfThereIsNewData()) { await refresh(); GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( - "New data found in $walletName in background!", walletId)); + "New data found in $walletId $walletName in background!", + walletId)); } // } }); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 6cc8d9566..c8bd9be24 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1470,8 +1470,8 @@ class FiroWallet extends CoinServiceAPI { @override Future refresh() async { if (refreshMutex) { - Logging.instance - .log("$walletName refreshMutex denied", level: LogLevel.Info); + Logging.instance.log("$walletId $walletName refreshMutex denied", + level: LogLevel.Info); return; } else { refreshMutex = true; @@ -1559,7 +1559,8 @@ class FiroWallet extends CoinServiceAPI { if (shouldNotify) { await refresh(); GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( - "New data found in $walletName in background!", walletId)); + "New data found in $walletId $walletName in background!", + walletId)); } }); } diff --git a/lib/services/coins/monero/monero_wallet.dart b/lib/services/coins/monero/monero_wallet.dart index 7c3d950e1..95794a356 100644 --- a/lib/services/coins/monero/monero_wallet.dart +++ b/lib/services/coins/monero/monero_wallet.dart @@ -390,8 +390,8 @@ class MoneroWallet extends CoinServiceAPI { @override Future refresh() async { if (refreshMutex) { - Logging.instance - .log("$walletId refreshMutex denied", level: LogLevel.Info); + Logging.instance.log("$walletId $walletName refreshMutex denied", + level: LogLevel.Info); return; } else { refreshMutex = true; @@ -465,7 +465,8 @@ class MoneroWallet extends CoinServiceAPI { // if (await refreshIfThereIsNewData()) { await refresh(); GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( - "New data found in $walletName in background!", walletId)); + "New data found in $walletId $walletName in background!", + walletId)); // } // } }); @@ -475,7 +476,8 @@ class MoneroWallet extends CoinServiceAPI { if (isActive) { await walletBase?.save(); GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( - "New data found in $walletName in background!", walletId)); + "New data found in $walletId $walletName in background!", + walletId)); } }); } @@ -866,7 +868,7 @@ class MoneroWallet extends CoinServiceAPI { // Use new index to derive a new receiving address final newReceivingAddress = await _generateAddressForChain(0, curIndex); Logging.instance.log("xmr address in init existing: $newReceivingAddress", - level: LogLevel.Fatal); + level: LogLevel.Info); _currentReceivingAddress = Future(() => newReceivingAddress); } diff --git a/lib/services/notifications_service.dart b/lib/services/notifications_service.dart index 89f7673e5..31d8b664c 100644 --- a/lib/services/notifications_service.dart +++ b/lib/services/notifications_service.dart @@ -17,6 +17,7 @@ class NotificationsService extends ChangeNotifier { late NodeService nodeService; late TradesService tradesService; late Prefs prefs; + late ChangeNow changeNow; NotificationsService._(); static final NotificationsService _instance = NotificationsService._(); @@ -26,10 +27,12 @@ class NotificationsService extends ChangeNotifier { required NodeService nodeService, required TradesService tradesService, required Prefs prefs, + required ChangeNow changeNow, }) async { this.nodeService = nodeService; this.tradesService = tradesService; this.prefs = prefs; + this.changeNow = changeNow; } // watched transactions @@ -177,7 +180,7 @@ class NotificationsService extends ChangeNotifier { for (final notification in _watchedChangeNowTradeNotifications) { final id = notification.changeNowId!; - final result = await ChangeNow.getTransactionStatus(id: id); + final result = await changeNow.getTransactionStatus(id: id); ChangeNowTransactionStatus? status = result.value?.status; diff --git a/test/screen_tests/exchange/exchange_view_test.dart b/test/screen_tests/exchange/exchange_view_test.dart new file mode 100644 index 000000000..1db2ac5a8 --- /dev/null +++ b/test/screen_tests/exchange/exchange_view_test.dart @@ -0,0 +1,109 @@ +import 'package:mockito/annotations.dart'; +import 'package:stackwallet/services/change_now/change_now.dart'; +import 'package:stackwallet/services/trade_notes_service.dart'; +import 'package:stackwallet/services/trade_service.dart'; +import 'package:stackwallet/utilities/prefs.dart'; + +@GenerateMocks([Prefs, TradesService, TradeNotesService, ChangeNow]) +void main() { + // testWidgets("ExchangeView builds correctly with no trade history", + // (widgetTester) async { + // final prefs = MockPrefs(); + // final tradeService = MockTradesService(); + // + // when(prefs.exchangeRateType) + // .thenAnswer((realInvocation) => ExchangeRateType.estimated); + // + // when(tradeService.trades).thenAnswer((realInvocation) => []); + // + // await widgetTester.pumpWidget( + // ProviderScope( + // overrides: [ + // prefsChangeNotifierProvider + // .overrideWithProvider(ChangeNotifierProvider((ref) => prefs)), + // tradesServiceProvider.overrideWithProvider( + // ChangeNotifierProvider((ref) => tradeService)), + // ], + // child: const MaterialApp( + // home: Material(child: ExchangeView()), + // ), + // ), + // ); + // + // await widgetTester.pumpAndSettle(); + // + // expect(find.byType(TextFormField), findsNWidgets(2)); + // expect(find.byType(SvgPicture), findsNWidgets(6)); + // + // expect(find.text("You will send"), findsOneWidget); + // expect(find.text("You will receive"), findsOneWidget); + // expect(find.text("Exchange"), findsOneWidget); + // expect(find.text("Estimated rate"), findsOneWidget); + // expect(find.text("Trades"), findsOneWidget); + // expect(find.text("-"), findsNWidgets(2)); + // + // expect(find.text("Trades will appear here"), findsOneWidget); + // + // expect(find.byType(TextButton), findsNWidgets(2)); + // expect(find.byType(TradeCard), findsNothing); + // }); + // + // testWidgets("ExchangeView builds correctly with one trade history", + // (widgetTester) async { + // final prefs = MockPrefs(); + // final tradeService = MockTradesService(); + // + // when(prefs.exchangeRateType) + // .thenAnswer((realInvocation) => ExchangeRateType.estimated); + // + // when(tradeService.trades).thenAnswer((realInvocation) => [ + // ExchangeTransaction( + // id: "some id", + // payinAddress: "adr", + // payoutAddress: "adr2", + // payinExtraId: "", + // payoutExtraId: "", + // fromCurrency: "btc", + // toCurrency: "xmr", + // amount: "42", + // refundAddress: "", + // refundExtraId: "refundExtraId", + // payoutExtraIdName: "", + // uuid: "dhjkfg872tr8yugsd", + // date: DateTime(1999), + // statusString: "Waiting", + // statusObject: null) + // ]); + // + // await widgetTester.pumpWidget( + // ProviderScope( + // overrides: [ + // prefsChangeNotifierProvider + // .overrideWithProvider(ChangeNotifierProvider((ref) => prefs)), + // tradesServiceProvider.overrideWithProvider( + // ChangeNotifierProvider((ref) => tradeService)), + // ], + // child: const MaterialApp( + // home: Material(child: ExchangeView()), + // ), + // ), + // ); + // + // await widgetTester.pumpAndSettle(); + // + // expect(find.byType(TextFormField), findsNWidgets(2)); + // expect(find.byType(SvgPicture), findsNWidgets(7)); + // + // expect(find.text("You will send"), findsOneWidget); + // expect(find.text("You will receive"), findsOneWidget); + // expect(find.text("Exchange"), findsOneWidget); + // expect(find.text("Estimated rate"), findsOneWidget); + // expect(find.text("Trades"), findsOneWidget); + // expect(find.text("-"), findsNWidgets(2)); + // + // expect(find.text("Trades will appear here"), findsNothing); + // + // expect(find.byType(TextButton), findsNWidgets(2)); + // expect(find.byType(TradeCard), findsOneWidget); + // }); +} diff --git a/test/screen_tests/exchange/exchange_view_test.mocks.dart b/test/screen_tests/exchange/exchange_view_test.mocks.dart new file mode 100644 index 000000000..f659e3104 --- /dev/null +++ b/test/screen_tests/exchange/exchange_view_test.mocks.dart @@ -0,0 +1,317 @@ +// Mocks generated by Mockito 5.2.0 from annotations +// in stackwallet/test/screen_tests/exchange/exchange_view_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i6; +import 'dart:ui' as _i7; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart' + as _i9; +import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart' + as _i4; +import 'package:stackwallet/services/change_now/change_now.dart' as _i11; +import 'package:stackwallet/services/trade_notes_service.dart' as _i10; +import 'package:stackwallet/services/trade_service.dart' as _i8; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i5; +import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i3; +import 'package:stackwallet/utilities/prefs.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +/// A class which mocks [Prefs]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPrefs extends _i1.Mock implements _i2.Prefs { + MockPrefs() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isInitialized => + (super.noSuchMethod(Invocation.getter(#isInitialized), returnValue: false) + as bool); + @override + int get lastUnlockedTimeout => (super + .noSuchMethod(Invocation.getter(#lastUnlockedTimeout), returnValue: 0) + as int); + @override + set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( + Invocation.setter(#lastUnlockedTimeout, lastUnlockedTimeout), + returnValueForMissingStub: null); + @override + int get lastUnlocked => + (super.noSuchMethod(Invocation.getter(#lastUnlocked), returnValue: 0) + as int); + @override + set lastUnlocked(int? lastUnlocked) => + super.noSuchMethod(Invocation.setter(#lastUnlocked, lastUnlocked), + returnValueForMissingStub: null); + @override + int get currentNotificationId => + (super.noSuchMethod(Invocation.getter(#currentNotificationId), + returnValue: 0) as int); + @override + List get walletIdsSyncOnStartup => + (super.noSuchMethod(Invocation.getter(#walletIdsSyncOnStartup), + returnValue: []) as List); + @override + set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => + super.noSuchMethod( + Invocation.setter(#walletIdsSyncOnStartup, walletIdsSyncOnStartup), + returnValueForMissingStub: null); + @override + _i3.SyncingType get syncType => + (super.noSuchMethod(Invocation.getter(#syncType), + returnValue: _i3.SyncingType.currentWalletOnly) as _i3.SyncingType); + @override + set syncType(_i3.SyncingType? syncType) => + super.noSuchMethod(Invocation.setter(#syncType, syncType), + returnValueForMissingStub: null); + @override + bool get wifiOnly => + (super.noSuchMethod(Invocation.getter(#wifiOnly), returnValue: false) + as bool); + @override + set wifiOnly(bool? wifiOnly) => + super.noSuchMethod(Invocation.setter(#wifiOnly, wifiOnly), + returnValueForMissingStub: null); + @override + bool get showFavoriteWallets => + (super.noSuchMethod(Invocation.getter(#showFavoriteWallets), + returnValue: false) as bool); + @override + set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( + Invocation.setter(#showFavoriteWallets, showFavoriteWallets), + returnValueForMissingStub: null); + @override + String get language => + (super.noSuchMethod(Invocation.getter(#language), returnValue: '') + as String); + @override + set language(String? newLanguage) => + super.noSuchMethod(Invocation.setter(#language, newLanguage), + returnValueForMissingStub: null); + @override + String get currency => + (super.noSuchMethod(Invocation.getter(#currency), returnValue: '') + as String); + @override + set currency(String? newCurrency) => + super.noSuchMethod(Invocation.setter(#currency, newCurrency), + returnValueForMissingStub: null); + @override + _i4.ExchangeRateType get exchangeRateType => + (super.noSuchMethod(Invocation.getter(#exchangeRateType), + returnValue: _i4.ExchangeRateType.estimated) as _i4.ExchangeRateType); + @override + set exchangeRateType(_i4.ExchangeRateType? exchangeRateType) => + super.noSuchMethod(Invocation.setter(#exchangeRateType, exchangeRateType), + returnValueForMissingStub: null); + @override + bool get useBiometrics => + (super.noSuchMethod(Invocation.getter(#useBiometrics), returnValue: false) + as bool); + @override + set useBiometrics(bool? useBiometrics) => + super.noSuchMethod(Invocation.setter(#useBiometrics, useBiometrics), + returnValueForMissingStub: null); + @override + bool get hasPin => + (super.noSuchMethod(Invocation.getter(#hasPin), returnValue: false) + as bool); + @override + set hasPin(bool? hasPin) => + super.noSuchMethod(Invocation.setter(#hasPin, hasPin), + returnValueForMissingStub: null); + @override + bool get showTestNetCoins => + (super.noSuchMethod(Invocation.getter(#showTestNetCoins), + returnValue: false) as bool); + @override + set showTestNetCoins(bool? showTestNetCoins) => + super.noSuchMethod(Invocation.setter(#showTestNetCoins, showTestNetCoins), + returnValueForMissingStub: null); + @override + bool get isAutoBackupEnabled => + (super.noSuchMethod(Invocation.getter(#isAutoBackupEnabled), + returnValue: false) as bool); + @override + set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( + Invocation.setter(#isAutoBackupEnabled, isAutoBackupEnabled), + returnValueForMissingStub: null); + @override + set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( + Invocation.setter(#autoBackupLocation, autoBackupLocation), + returnValueForMissingStub: null); + @override + _i5.BackupFrequencyType get backupFrequencyType => + (super.noSuchMethod(Invocation.getter(#backupFrequencyType), + returnValue: _i5.BackupFrequencyType.everyTenMinutes) + as _i5.BackupFrequencyType); + @override + set backupFrequencyType(_i5.BackupFrequencyType? backupFrequencyType) => + super.noSuchMethod( + Invocation.setter(#backupFrequencyType, backupFrequencyType), + returnValueForMissingStub: null); + @override + set lastAutoBackup(DateTime? lastAutoBackup) => + super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup), + returnValueForMissingStub: null); + @override + bool get hasListeners => + (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) + as bool); + @override + _i6.Future init() => (super.noSuchMethod(Invocation.method(#init, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( + Invocation.method(#incrementCurrentNotificationIndex, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + void addListener(_i7.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#addListener, [listener]), + returnValueForMissingStub: null); + @override + void removeListener(_i7.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#removeListener, [listener]), + returnValueForMissingStub: null); + @override + void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), + returnValueForMissingStub: null); + @override + void notifyListeners() => + super.noSuchMethod(Invocation.method(#notifyListeners, []), + returnValueForMissingStub: null); +} + +/// A class which mocks [TradesService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTradesService extends _i1.Mock implements _i8.TradesService { + MockTradesService() { + _i1.throwOnMissingStub(this); + } + + @override + List<_i9.ExchangeTransaction> get trades => + (super.noSuchMethod(Invocation.getter(#trades), + returnValue: <_i9.ExchangeTransaction>[]) + as List<_i9.ExchangeTransaction>); + @override + bool get hasListeners => + (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) + as bool); + @override + _i6.Future add( + {_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) => + (super.noSuchMethod( + Invocation.method(#add, [], + {#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future edit( + {_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) => + (super.noSuchMethod( + Invocation.method(#edit, [], + {#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future delete( + {_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) => + (super.noSuchMethod( + Invocation.method(#delete, [], + {#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future deleteByUuid({String? uuid, bool? shouldNotifyListeners}) => + (super.noSuchMethod( + Invocation.method(#deleteByUuid, [], + {#uuid: uuid, #shouldNotifyListeners: shouldNotifyListeners}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + void addListener(_i7.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#addListener, [listener]), + returnValueForMissingStub: null); + @override + void removeListener(_i7.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#removeListener, [listener]), + returnValueForMissingStub: null); + @override + void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), + returnValueForMissingStub: null); + @override + void notifyListeners() => + super.noSuchMethod(Invocation.method(#notifyListeners, []), + returnValueForMissingStub: null); +} + +/// A class which mocks [TradeNotesService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTradeNotesService extends _i1.Mock implements _i10.TradeNotesService { + MockTradeNotesService() { + _i1.throwOnMissingStub(this); + } + + @override + Map get all => (super.noSuchMethod(Invocation.getter(#all), + returnValue: {}) as Map); + @override + bool get hasListeners => + (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) + as bool); + @override + String getNote({String? tradeId}) => + (super.noSuchMethod(Invocation.method(#getNote, [], {#tradeId: tradeId}), + returnValue: '') as String); + @override + _i6.Future set({String? tradeId, String? note}) => (super.noSuchMethod( + Invocation.method(#set, [], {#tradeId: tradeId, #note: note}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future delete({String? tradeId}) => + (super.noSuchMethod(Invocation.method(#delete, [], {#tradeId: tradeId}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + void addListener(_i7.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#addListener, [listener]), + returnValueForMissingStub: null); + @override + void removeListener(_i7.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#removeListener, [listener]), + returnValueForMissingStub: null); + @override + void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), + returnValueForMissingStub: null); + @override + void notifyListeners() => + super.noSuchMethod(Invocation.method(#notifyListeners, []), + returnValueForMissingStub: null); +} + +/// A class which mocks [ChangeNow]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockChangeNow extends _i1.Mock implements _i11.ChangeNow { + MockChangeNow() { + _i1.throwOnMissingStub(this); + } +} diff --git a/test/services/change_now/change_now_test.dart b/test/services/change_now/change_now_test.dart index 8792e4807..7a923907b 100644 --- a/test/services/change_now/change_now_test.dart +++ b/test/services/change_now/change_now_test.dart @@ -20,15 +20,15 @@ void main() { group("getAvailableCurrencies", () { test("getAvailableCurrencies succeeds without options", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies"), + Uri.parse("https://api.ChangeNow.io/v1/currencies"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(availableCurrenciesJSON), 200)); - final result = await ChangeNow.getAvailableCurrencies(); + final result = await ChangeNow.instance.getAvailableCurrencies(); expect(result.exception, null); expect(result.value == null, false); @@ -37,15 +37,16 @@ void main() { test("getAvailableCurrencies succeeds with active option", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies?active=true"), + Uri.parse("https://api.ChangeNow.io/v1/currencies?active=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(availableCurrenciesJSONActive), 200)); - final result = await ChangeNow.getAvailableCurrencies(active: true); + final result = + await ChangeNow.instance.getAvailableCurrencies(active: true); expect(result.exception, null); expect(result.value == null, false); @@ -54,15 +55,16 @@ void main() { test("getAvailableCurrencies succeeds with fixedRate option", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies?fixedRate=true"), + Uri.parse("https://api.ChangeNow.io/v1/currencies?fixedRate=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(availableCurrenciesJSONFixedRate), 200)); - final result = await ChangeNow.getAvailableCurrencies(fixedRate: true); + final result = + await ChangeNow.instance.getAvailableCurrencies(fixedRate: true); expect(result.exception, null); expect(result.value == null, false); @@ -72,17 +74,17 @@ void main() { test("getAvailableCurrencies succeeds with fixedRate and active options", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/currencies?fixedRate=true&active=true"), + "https://api.ChangeNow.io/v1/currencies?fixedRate=true&active=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(availableCurrenciesJSONActiveFixedRate), 200)); - final result = - await ChangeNow.getAvailableCurrencies(active: true, fixedRate: true); + final result = await ChangeNow.instance + .getAvailableCurrencies(active: true, fixedRate: true); expect(result.exception, null); expect(result.value == null, false); @@ -93,15 +95,15 @@ void main() { "getAvailableCurrencies fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies"), + Uri.parse("https://api.ChangeNow.io/v1/currencies"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"some unexpected": "but valid json data"}', 200)); - final result = await ChangeNow.getAvailableCurrencies(); + final result = await ChangeNow.instance.getAvailableCurrencies(); expect(result.exception!.type, ChangeNowExceptionType.serializeResponseError); @@ -110,14 +112,14 @@ void main() { test("getAvailableCurrencies fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies"), + Uri.parse("https://api.ChangeNow.io/v1/currencies"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response("", 400)); - final result = await ChangeNow.getAvailableCurrencies(); + final result = await ChangeNow.instance.getAvailableCurrencies(); expect(result.exception!.type, ChangeNowExceptionType.generic); expect(result.value == null, true); @@ -127,15 +129,16 @@ void main() { group("getPairedCurrencies", () { test("getPairedCurrencies succeeds without fixedRate option", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies-to/XMR"), + Uri.parse("https://api.ChangeNow.io/v1/currencies-to/XMR"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(getPairedCurrenciesJSON), 200)); - final result = await ChangeNow.getPairedCurrencies(ticker: "XMR"); + final result = + await ChangeNow.instance.getPairedCurrencies(ticker: "XMR"); expect(result.exception, null); expect(result.value == null, false); @@ -144,17 +147,17 @@ void main() { test("getPairedCurrencies succeeds with fixedRate option", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/currencies-to/XMR?fixedRate=true"), + "https://api.ChangeNow.io/v1/currencies-to/XMR?fixedRate=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(getPairedCurrenciesJSONFixedRate), 200)); - final result = - await ChangeNow.getPairedCurrencies(ticker: "XMR", fixedRate: true); + final result = await ChangeNow.instance + .getPairedCurrencies(ticker: "XMR", fixedRate: true); expect(result.exception, null); expect(result.value == null, false); @@ -165,15 +168,16 @@ void main() { "getPairedCurrencies fails with ChangeNowExceptionType.serializeResponseError A", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies-to/XMR"), + Uri.parse("https://api.ChangeNow.io/v1/currencies-to/XMR"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('[{"some unexpected": "but valid json data"}]', 200)); - final result = await ChangeNow.getPairedCurrencies(ticker: "XMR"); + final result = + await ChangeNow.instance.getPairedCurrencies(ticker: "XMR"); expect(result.exception!.type, ChangeNowExceptionType.serializeResponseError); @@ -182,15 +186,15 @@ void main() { test("getPairedCurrencies fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( - Uri.parse("https://api.changenow.io/v1/currencies"), + Uri.parse("https://api.ChangeNow.io/v1/currencies"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response("", 400)); - final result = - await ChangeNow.getPairedCurrencies(ticker: "XMR", fixedRate: true); + final result = await ChangeNow.instance + .getPairedCurrencies(ticker: "XMR", fixedRate: true); expect(result.exception!.type, ChangeNowExceptionType.generic); expect(result.value == null, true); @@ -200,16 +204,16 @@ void main() { group("getMinimalExchangeAmount", () { test("getMinimalExchangeAmount succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"), + "https://api.ChangeNow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer( (realInvocation) async => Response('{"minAmount": 42}', 200)); - final result = await ChangeNow.getMinimalExchangeAmount( + final result = await ChangeNow.instance.getMinimalExchangeAmount( fromTicker: "xmr", toTicker: "btc", apiKey: "testAPIKEY", @@ -224,15 +228,15 @@ void main() { "getMinimalExchangeAmount fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"), + "https://api.ChangeNow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.getMinimalExchangeAmount( + final result = await ChangeNow.instance.getMinimalExchangeAmount( fromTicker: "xmr", toTicker: "btc", apiKey: "testAPIKEY", @@ -245,15 +249,15 @@ void main() { test("getMinimalExchangeAmount fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"), + "https://api.ChangeNow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.getMinimalExchangeAmount( + final result = await ChangeNow.instance.getMinimalExchangeAmount( fromTicker: "xmr", toTicker: "btc", apiKey: "testAPIKEY", @@ -267,17 +271,17 @@ void main() { group("getEstimatedExchangeAmount", () { test("getEstimatedExchangeAmount succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"), + "https://api.ChangeNow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response( '{"estimatedAmount": 58.4142873, "transactionSpeedForecast": "10-60", "warningMessage": null}', 200)); - final result = await ChangeNow.getEstimatedExchangeAmount( + final result = await ChangeNow.instance.getEstimatedExchangeAmount( fromTicker: "xmr", toTicker: "btc", fromAmount: Decimal.fromInt(42), @@ -293,15 +297,15 @@ void main() { "getEstimatedExchangeAmount fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"), + "https://api.ChangeNow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.getEstimatedExchangeAmount( + final result = await ChangeNow.instance.getEstimatedExchangeAmount( fromTicker: "xmr", toTicker: "btc", fromAmount: Decimal.fromInt(42), @@ -315,15 +319,15 @@ void main() { test("getEstimatedExchangeAmount fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"), + "https://api.ChangeNow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.getEstimatedExchangeAmount( + final result = await ChangeNow.instance.getEstimatedExchangeAmount( fromTicker: "xmr", toTicker: "btc", fromAmount: Decimal.fromInt(42), @@ -338,16 +342,17 @@ void main() { group("getEstimatedFixedRateExchangeAmount", () { test("getEstimatedFixedRateExchangeAmount succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"), + "https://api.ChangeNow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(estFixedRateExchangeAmountJSON), 200)); - final result = await ChangeNow.getEstimatedFixedRateExchangeAmount( + final result = + await ChangeNow.instance.getEstimatedFixedRateExchangeAmount( fromTicker: "xmr", toTicker: "btc", fromAmount: Decimal.fromInt(10), @@ -364,15 +369,16 @@ void main() { "getEstimatedFixedRateExchangeAmount fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"), + "https://api.ChangeNow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.getEstimatedFixedRateExchangeAmount( + final result = + await ChangeNow.instance.getEstimatedFixedRateExchangeAmount( fromTicker: "xmr", toTicker: "btc", fromAmount: Decimal.fromInt(10), @@ -387,15 +393,16 @@ void main() { test("getEstimatedFixedRateExchangeAmount fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"), + "https://api.ChangeNow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.getEstimatedFixedRateExchangeAmount( + final result = + await ChangeNow.instance.getEstimatedFixedRateExchangeAmount( fromTicker: "xmr", toTicker: "btc", fromAmount: Decimal.fromInt(10), @@ -410,16 +417,16 @@ void main() { group("getAvailableFixedRateMarkets", () { test("getAvailableFixedRateMarkets succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/market-info/fixed-rate/testAPIKEY"), + "https://api.ChangeNow.io/v1/market-info/fixed-rate/testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response(jsonEncode(fixedRateMarketsJSON), 200)); - final result = await ChangeNow.getAvailableFixedRateMarkets( + final result = await ChangeNow.instance.getAvailableFixedRateMarkets( apiKey: "testAPIKEY", ); @@ -432,15 +439,15 @@ void main() { "getAvailableFixedRateMarkets fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/market-info/fixed-rate/testAPIKEY"), + "https://api.ChangeNow.io/v1/market-info/fixed-rate/testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.getAvailableFixedRateMarkets( + final result = await ChangeNow.instance.getAvailableFixedRateMarkets( apiKey: "testAPIKEY", ); @@ -451,15 +458,15 @@ void main() { test("getAvailableFixedRateMarkets fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/market-info/fixed-rate/testAPIKEY"), + "https://api.ChangeNow.io/v1/market-info/fixed-rate/testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.getAvailableFixedRateMarkets( + final result = await ChangeNow.instance.getAvailableFixedRateMarkets( apiKey: "testAPIKEY", ); @@ -471,10 +478,10 @@ void main() { group("createStandardExchangeTransaction", () { test("createStandardExchangeTransaction succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.post( - Uri.parse("https://api.changenow.io/v1/transactions/testAPIKEY"), + Uri.parse("https://api.ChangeNow.io/v1/transactions/testAPIKEY"), headers: {'Content-Type': 'application/json'}, body: '{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}', @@ -482,7 +489,7 @@ void main() { )).thenAnswer((realInvocation) async => Response(jsonEncode(createStandardTransactionResponse), 200)); - final result = await ChangeNow.createStandardExchangeTransaction( + final result = await ChangeNow.instance.createStandardExchangeTransaction( fromTicker: "xmr", toTicker: "btc", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", @@ -501,17 +508,17 @@ void main() { "createStandardExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.post( - Uri.parse("https://api.changenow.io/v1/transactions/testAPIKEY"), + Uri.parse("https://api.ChangeNow.io/v1/transactions/testAPIKEY"), headers: {'Content-Type': 'application/json'}, body: '{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}', encoding: null, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.createStandardExchangeTransaction( + final result = await ChangeNow.instance.createStandardExchangeTransaction( fromTicker: "xmr", toTicker: "btc", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", @@ -529,17 +536,17 @@ void main() { test("createStandardExchangeTransaction fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.post( - Uri.parse("https://api.changenow.io/v1/transactions/testAPIKEY"), + Uri.parse("https://api.ChangeNow.io/v1/transactions/testAPIKEY"), headers: {'Content-Type': 'application/json'}, body: '{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}', encoding: null, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.createStandardExchangeTransaction( + final result = await ChangeNow.instance.createStandardExchangeTransaction( fromTicker: "xmr", toTicker: "btc", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", @@ -557,11 +564,11 @@ void main() { group("createFixedRateExchangeTransaction", () { test("createFixedRateExchangeTransaction succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.post( Uri.parse( - "https://api.changenow.io/v1/transactions/fixed-rate/testAPIKEY"), + "https://api.ChangeNow.io/v1/transactions/fixed-rate/testAPIKEY"), headers: {'Content-Type': 'application/json'}, body: '{"from":"btc","to":"eth","address":"0x57f31ad4b64095347F87eDB1675566DAfF5EC886","amount":"0.3","flow":"fixed-rate","extraId":"","userId":"","contactEmail":"","refundAddress":"","refundExtraId":"","rateId":""}', @@ -570,7 +577,8 @@ void main() { '{"payinAddress": "33eFX2jfeWbXMSmRe9ewUUTrmSVSxZi5cj", "payoutAddress": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886","payoutExtraId": "", "fromCurrency": "btc", "toCurrency": "eth", "refundAddress": "","refundExtraId": "","validUntil": "2019-09-09T14:01:04.921Z","id": "a5c73e2603f40d","amount": 62.9737711}', 200)); - final result = await ChangeNow.createFixedRateExchangeTransaction( + final result = + await ChangeNow.instance.createFixedRateExchangeTransaction( fromTicker: "btc", toTicker: "eth", receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", @@ -589,11 +597,11 @@ void main() { "createFixedRateExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.post( Uri.parse( - "https://api.changenow.io/v1/transactions/fixed-rate/testAPIKEY"), + "https://api.ChangeNow.io/v1/transactions/fixed-rate/testAPIKEY"), headers: {'Content-Type': 'application/json'}, body: '{"from":"btc","to":"eth","address":"0x57f31ad4b64095347F87eDB1675566DAfF5EC886","amount":"0.3","flow":"fixed-rate","extraId":"","userId":"","contactEmail":"","refundAddress":"","refundExtraId":"","rateId":""}', @@ -601,7 +609,8 @@ void main() { )).thenAnswer((realInvocation) async => Response('{"id": "a5c73e2603f40d","amount": 62.9737711}', 200)); - final result = await ChangeNow.createFixedRateExchangeTransaction( + final result = + await ChangeNow.instance.createFixedRateExchangeTransaction( fromTicker: "btc", toTicker: "eth", receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", @@ -619,18 +628,19 @@ void main() { test("createFixedRateExchangeTransaction fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.post( Uri.parse( - "https://api.changenow.io/v1/transactions/fixed-rate/testAPIKEY"), + "https://api.ChangeNow.io/v1/transactions/fixed-rate/testAPIKEY"), headers: {'Content-Type': 'application/json'}, body: '{"from": "btc","to": "eth","address": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", "amount": "1.12345","extraId": "", "userId": "","contactEmail": "","refundAddress": "", "refundExtraId": "", "rateId": "" }', encoding: null, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.createFixedRateExchangeTransaction( + final result = + await ChangeNow.instance.createFixedRateExchangeTransaction( fromTicker: "xmr", toTicker: "btc", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", @@ -649,17 +659,17 @@ void main() { group("getTransactionStatus", () { test("getTransactionStatus succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), + "https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response( '{"status": "waiting", "payinAddress": "32Ge2ci26rj1sRGw2NjiQa9L7Xvxtgzhrj", "payoutAddress": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", "fromCurrency": "btc", "toCurrency": "eth", "id": "50727663e5d9a4", "updatedAt": "2019-08-22T14:47:49.943Z", "expectedSendAmount": 1, "expectedReceiveAmount": 52.31667, "createdAt": "2019-08-22T14:47:49.943Z", "isPartner": false}', 200)); - final result = await ChangeNow.getTransactionStatus( + final result = await ChangeNow.instance.getTransactionStatus( id: "47F87eDB1675566DAfF5EC886", apiKey: "testAPIKEY", ); @@ -673,15 +683,15 @@ void main() { "getTransactionStatus fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), + "https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.getTransactionStatus( + final result = await ChangeNow.instance.getTransactionStatus( id: "47F87eDB1675566DAfF5EC886", apiKey: "testAPIKEY", ); @@ -693,15 +703,15 @@ void main() { test("getTransactionStatus fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), + "https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.getTransactionStatus( + final result = await ChangeNow.instance.getTransactionStatus( id: "47F87eDB1675566DAfF5EC886", apiKey: "testAPIKEY", ); @@ -714,16 +724,16 @@ void main() { group("getAvailableFloatingRatePairs", () { test("getAvailableFloatingRatePairs succeeds", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/market-info/available-pairs?includePartners=false"), + "https://api.ChangeNow.io/v1/market-info/available-pairs?includePartners=false"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('["btc_xmr","btc_firo","btc_doge","eth_ltc"]', 200)); - final result = await ChangeNow.getAvailableFloatingRatePairs(); + final result = await ChangeNow.instance.getAvailableFloatingRatePairs(); expect(result.exception, null); expect(result.value == null, false); @@ -734,15 +744,15 @@ void main() { "getAvailableFloatingRatePairs fails with ChangeNowExceptionType.serializeResponseError", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/market-info/available-pairs?includePartners=false"), + "https://api.ChangeNow.io/v1/market-info/available-pairs?includePartners=false"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); - final result = await ChangeNow.getAvailableFloatingRatePairs(); + final result = await ChangeNow.instance.getAvailableFloatingRatePairs(); expect(result.exception!.type, ChangeNowExceptionType.serializeResponseError); @@ -751,15 +761,15 @@ void main() { test("getAvailableFloatingRatePairs fails for any other reason", () async { final client = MockClient(); - ChangeNow.client = client; + ChangeNow.instance.client = client; when(client.get( Uri.parse( - "https://api.changenow.io/v1/market-info/available-pairs?includePartners=false"), + "https://api.ChangeNow.io/v1/market-info/available-pairs?includePartners=false"), headers: {'Content-Type': 'application/json'}, )).thenAnswer((realInvocation) async => Response('', 400)); - final result = await ChangeNow.getAvailableFloatingRatePairs(); + final result = await ChangeNow.instance.getAvailableFloatingRatePairs(); expect(result.exception!.type, ChangeNowExceptionType.generic); expect(result.value == null, true); diff --git a/test/services/change_now/change_now_test.mocks.dart b/test/services/change_now/change_now_test.mocks.dart index d47a9e356..355c1904a 100644 --- a/test/services/change_now/change_now_test.mocks.dart +++ b/test/services/change_now/change_now_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in stackwallet/test/services/change_now_test.dart. +// in stackwallet/test/services/change_now/change_now_test.dart. // Do not manually edit this file. import 'dart:async' as _i5;