Merge pull request #23 from cypherstack/ui-testing

UI testing
This commit is contained in:
Marco Salazar 2022-09-01 17:04:40 +08:00 committed by GitHub
commit e02215fa9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1114 additions and 474 deletions

View file

@ -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/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_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_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/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_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/global/auto_swb_service_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/global/trades_service_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.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/debug_service.dart';
import 'package:stackwallet/services/locale_service.dart'; import 'package:stackwallet/services/locale_service.dart';
import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/node_service.dart';
@ -79,7 +79,7 @@ void main() async {
await DebugService.instance.init(isar); await DebugService.instance.init(isar);
// clear out all info logs on startup. No need to await and block // clear out all info logs on startup. No need to await and block
DebugService.instance.purgeInfoLogs(); unawaited(DebugService.instance.purgeInfoLogs());
// Registering Transaction Model Adapters // Registering Transaction Model Adapters
Hive.registerAdapter(TransactionDataAdapter()); Hive.registerAdapter(TransactionDataAdapter());
@ -193,20 +193,21 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
NotificationApi.prefs = _prefs; NotificationApi.prefs = _prefs;
NotificationApi.notificationsService = _notificationsService; NotificationApi.notificationsService = _notificationsService;
ref.read(baseCurrenciesProvider).update(); unawaited(ref.read(baseCurrenciesProvider).update());
await _nodeService.updateDefaults(); await _nodeService.updateDefaults();
await _notificationsService.init( await _notificationsService.init(
nodeService: _nodeService, nodeService: _nodeService,
tradesService: _tradesService, tradesService: _tradesService,
prefs: _prefs, prefs: _prefs,
changeNow: ref.read(changeNowProvider),
); );
await _prefs.init(); await _prefs.init();
ref.read(priceAnd24hChangeNotifierProvider).start(true); ref.read(priceAnd24hChangeNotifierProvider).start(true);
await _wallets.load(_prefs); await _wallets.load(_prefs);
loadingCompleter.complete(); loadingCompleter.complete();
// TODO: this currently hangs for a long time // TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
await _nodeService.updateCommunityNodes(); // unawaited(_nodeService.updateCommunityNodes());
if (_prefs.isAutoBackupEnabled) { if (_prefs.isAutoBackupEnabled) {
switch (_prefs.backupFrequencyType) { switch (_prefs.backupFrequencyType) {
@ -216,7 +217,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
.startPeriodicBackupTimer(duration: const Duration(minutes: 10)); .startPeriodicBackupTimer(duration: const Duration(minutes: 10));
break; break;
case BackupFrequencyType.everyAppStart: case BackupFrequencyType.everyAppStart:
ref.read(autoSWBServiceProvider).doBackup(); unawaited(ref.read(autoSWBServiceProvider).doBackup());
break; break;
case BackupFrequencyType.afterClosingAWallet: case BackupFrequencyType.afterClosingAWallet:
// ignore this case here // ignore this case here
@ -236,8 +237,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
.isNotEmpty) { .isNotEmpty) {
return; return;
} }
final response = await ChangeNow.getAvailableCurrencies(); final response = await ref.read(changeNowProvider).getAvailableCurrencies();
final response2 = await ChangeNow.getAvailableFloatingRatePairs(); final response2 =
await ref.read(changeNowProvider).getAvailableFloatingRatePairs();
if (response.value != null) { if (response.value != null) {
ref.read(availableChangeNowCurrenciesStateProvider.state).state = ref.read(availableChangeNowCurrenciesStateProvider.state).state =
response.value!; response.value!;
@ -248,13 +250,13 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
if (response.value!.length > 1) { if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { 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); response.value!.firstWhere((e) => e.ticker == "btc"), false);
} }
} }
if (ref.read(estimatedRateExchangeFormProvider).to == null) { if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { 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); response.value!.firstWhere((e) => e.ticker == "doge"), false);
} }
} }
@ -288,7 +290,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
return; return;
} }
final response3 = await ChangeNow.getAvailableFixedRateMarkets(); final response3 =
await ref.read(changeNowProvider).getAvailableFixedRateMarkets();
if (response3.value != null) { if (response3.value != null) {
ref.read(fixedRateMarketPairsStateProvider.state).state = ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!; response3.value!;
@ -297,7 +300,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
final matchingMarkets = final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc"); response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) { if (matchingMarkets.isNotEmpty) {
ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.updateMarket(matchingMarkets.first, true); .updateMarket(matchingMarkets.first, true);
} }
@ -443,7 +446,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
} }
}); });
} else { } else {
Navigator.push( unawaited(Navigator.push(
navigatorKey.currentContext!, navigatorKey.currentContext!,
RouteGenerator.getRoute( RouteGenerator.getRoute(
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
@ -458,7 +461,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
), ),
settings: const RouteSettings(name: "/swbrestorelockscreen"), settings: const RouteSettings(name: "/swbrestorelockscreen"),
), ),
); ));
} }
} }

View file

@ -2,8 +2,9 @@ import 'package:decimal/decimal.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:stackwallet/models/exchange/change_now/currency.dart'; import 'package:stackwallet/models/exchange/change_now/currency.dart';
import 'package:stackwallet/services/change_now/change_now.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? _fromAmount;
Decimal? _toAmount; Decimal? _toAmount;
@ -23,6 +24,18 @@ class ExchangeFormState extends ChangeNotifier {
_to = to; _to = to;
} }
void clearAmounts(bool shouldNotifyListeners) {
_fromAmount = null;
_toAmount = null;
_minFromAmount = null;
_minToAmount = null;
rate = null;
if (shouldNotifyListeners) {
notifyListeners();
}
}
Future<void> swap() async { Future<void> swap() async {
final Decimal? newToAmount = _fromAmount; final Decimal? newToAmount = _fromAmount;
final Decimal? newFromAmount = _toAmount; final Decimal? newFromAmount = _toAmount;
@ -30,9 +43,9 @@ class ExchangeFormState extends ChangeNotifier {
final Decimal? newMinFromAmount = _minToAmount; final Decimal? newMinFromAmount = _minToAmount;
final Decimal? newMinToAmount = _minFromAmount; final Decimal? newMinToAmount = _minFromAmount;
final Decimal? newRate = rate == null // final Decimal? newRate = rate == null
? rate // ? rate
: (Decimal.one / rate!).toDecimal(scaleOnInfinitePrecision: 12); // : (Decimal.one / rate!).toDecimal(scaleOnInfinitePrecision: 12);
final Currency? newTo = from; final Currency? newTo = from;
final Currency? newFrom = to; final Currency? newFrom = to;
@ -43,125 +56,171 @@ class ExchangeFormState extends ChangeNotifier {
_minToAmount = newMinToAmount; _minToAmount = newMinToAmount;
_minFromAmount = newMinFromAmount; _minFromAmount = newMinFromAmount;
rate = newRate; // rate = newRate;
_to = newTo; _to = newTo;
_from = newFrom; _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(); notifyListeners();
} }
String get fromAmountString => String get fromAmountString =>
_fromAmount == null ? "-" : _fromAmount!.toStringAsFixed(8); _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8);
String get toAmountString => String get toAmountString =>
_toAmount == null ? "-" : _toAmount!.toStringAsFixed(8); _toAmount == null ? "" : _toAmount!.toStringAsFixed(8);
Future<void> updateTo(Currency to, bool shouldNotifyListeners) async { Future<void> updateTo(Currency to, bool shouldNotifyListeners) async {
_to = to; try {
if (_from == null) { _to = to;
if (_from == null) {
rate = null;
notifyListeners();
return;
}
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners);
// await _updateMinToAmount(shouldNotifyListeners: shouldNotifyListeners);
rate = null; rate = null;
notifyListeners();
return;
}
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); if (_fromAmount != null) {
// await _updateMinToAmount(shouldNotifyListeners: shouldNotifyListeners); Decimal? amt;
if (_minFromAmount != null) {
rate = null; if (_minFromAmount! > _fromAmount!) {
amt = await getStandardEstimatedToAmount(
if (_fromAmount != null) { fromAmount: _minFromAmount!, from: _from!, to: _to!);
Decimal? amt; if (amt != null) {
if (_minFromAmount != null) { rate = (amt / _minFromAmount!)
if (_minFromAmount! > _fromAmount!) { .toDecimal(scaleOnInfinitePrecision: 12);
amt = await getStandardEstimatedToAmount( }
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!); fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) { if (amt != null) {
rate = rate =
(amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); (amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
} }
debugPrint("A"); debugPrint("D");
} 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("D");
}
}
debugPrint( debugPrint(
"_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate");
if (shouldNotifyListeners) { if (shouldNotifyListeners) {
notifyListeners(); notifyListeners();
}
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Fatal);
} }
} }
Future<void> updateFrom(Currency from, bool shouldNotifyListeners) async { Future<void> 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; rate = null;
notifyListeners();
return;
}
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); if (_fromAmount != null) {
Decimal? amt;
rate = null; if (_minFromAmount != null) {
if (_minFromAmount! > _fromAmount!) {
if (_fromAmount != null) { amt = await getStandardEstimatedToAmount(
Decimal? amt; fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (_minFromAmount != null) { if (amt != null) {
if (_minFromAmount! > _fromAmount!) { rate = (amt / _minFromAmount!)
amt = await getStandardEstimatedToAmount( .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!); fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) { if (amt != null) {
rate = rate =
(amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12); (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( debugPrint(
"_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate");
if (shouldNotifyListeners) { if (shouldNotifyListeners) {
notifyListeners(); 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 from,
required Currency to, required Currency to,
}) async { }) async {
final response = await ChangeNow.getEstimatedExchangeAmount( final response = await ChangeNow.instance.getEstimatedExchangeAmount(
fromTicker: from.ticker, toTicker: to.ticker, fromAmount: fromAmount); fromTicker: from.ticker, toTicker: to.ticker, fromAmount: fromAmount);
if (response.value != null) { if (response.value != null) {
@ -286,8 +345,8 @@ class ExchangeFormState extends ChangeNotifier {
required Currency from, required Currency from,
required Currency to, required Currency to,
}) async { }) async {
final response = await ChangeNow.getMinimalExchangeAmount( final response = await ChangeNow.instance
fromTicker: from.ticker, toTicker: to.ticker); .getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker);
if (response.value != null) { if (response.value != null) {
return response.value!; return response.value!;

View file

@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.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/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/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_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_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/cfcolors.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
@ -38,7 +38,8 @@ class _ExchangeLoadingOverlayViewState
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state = ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.loading; ChangeNowLoadStatus.loading;
final response3 = await ChangeNow.getAvailableFixedRateMarkets(); final response3 =
await ref.read(changeNowProvider).getAvailableFixedRateMarkets();
if (response3.value != null) { if (response3.value != null) {
ref.read(fixedRateMarketPairsStateProvider.state).state = ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!; response3.value!;
@ -47,7 +48,7 @@ class _ExchangeLoadingOverlayViewState
final matchingMarkets = final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc"); response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) { if (matchingMarkets.isNotEmpty) {
ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.updateMarket(matchingMarkets.first, true); .updateMarket(matchingMarkets.first, true);
} }
@ -78,8 +79,9 @@ class _ExchangeLoadingOverlayViewState
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state = ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.loading; ChangeNowLoadStatus.loading;
final response = await ChangeNow.getAvailableCurrencies(); final response = await ref.read(changeNowProvider).getAvailableCurrencies();
final response2 = await ChangeNow.getAvailableFloatingRatePairs(); final response2 =
await ref.read(changeNowProvider).getAvailableFloatingRatePairs();
if (response.value != null) { if (response.value != null) {
ref.read(availableChangeNowCurrenciesStateProvider.state).state = ref.read(availableChangeNowCurrenciesStateProvider.state).state =
response.value!; response.value!;
@ -90,13 +92,13 @@ class _ExchangeLoadingOverlayViewState
if (response.value!.length > 1) { if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { 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); response.value!.firstWhere((e) => e.ticker == "btc"), false);
} }
} }
if (ref.read(estimatedRateExchangeFormProvider).to == null) { if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { 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); response.value!.firstWhere((e) => e.ticker == "doge"), false);
} }
} }

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/change_now/change_now_response.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/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/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.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/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/services/notifications_api.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
@ -219,35 +221,39 @@ class _Step3ViewState extends ConsumerState<Step3View> {
response; response;
if (model.rateType == if (model.rateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
response = await ChangeNow response = await ref
.read(changeNowProvider)
.createStandardExchangeTransaction( .createStandardExchangeTransaction(
fromTicker: model.sendTicker, fromTicker: model.sendTicker,
toTicker: model.receiveTicker, toTicker: model.receiveTicker,
receivingAddress: model.recipientAddress!, receivingAddress:
amount: model.sendAmount, model.recipientAddress!,
refundAddress: model.refundAddress!, amount: model.sendAmount,
); refundAddress: model.refundAddress!,
);
} else { } else {
response = await ChangeNow response = await ref
.read(changeNowProvider)
.createFixedRateExchangeTransaction( .createFixedRateExchangeTransaction(
fromTicker: model.sendTicker, fromTicker: model.sendTicker,
toTicker: model.receiveTicker, toTicker: model.receiveTicker,
receivingAddress: model.recipientAddress!, receivingAddress:
amount: model.sendAmount, model.recipientAddress!,
refundAddress: model.refundAddress!, amount: model.sendAmount,
rateId: model.rateId!, refundAddress: model.refundAddress!,
); rateId: model.rateId!,
);
} }
if (response.value == null) { if (response.value == null) {
showDialog<void>( unawaited(showDialog<void>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
title: "Failed to create trade", title: "Failed to create trade",
message: response.exception?.toString(), message: response.exception?.toString(),
), ),
); ));
return; return;
} }
@ -257,8 +263,9 @@ class _Step3ViewState extends ConsumerState<Step3View> {
shouldNotifyListeners: true, shouldNotifyListeners: true,
); );
final statusResponse = final statusResponse = await ref
await ChangeNow.getTransactionStatus( .read(changeNowProvider)
.getTransactionStatus(
id: response.value!.id); id: response.value!.id);
debugPrint("WTF: $statusResponse"); debugPrint("WTF: $statusResponse");
@ -278,7 +285,7 @@ class _Step3ViewState extends ConsumerState<Step3View> {
status += " for deposit"; status += " for deposit";
} }
NotificationApi.showNotification( unawaited(NotificationApi.showNotification(
changeNowId: model.trade!.id, changeNowId: model.trade!.id,
title: status, title: status,
body: "Trade ID ${model.trade!.id}", body: "Trade ID ${model.trade!.id}",
@ -287,13 +294,13 @@ class _Step3ViewState extends ConsumerState<Step3View> {
date: model.trade!.date, date: model.trade!.date,
shouldWatchForUpdates: true, shouldWatchForUpdates: true,
coinName: "coinName", coinName: "coinName",
); ));
if (mounted) { if (mounted) {
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
Step4View.routeName, Step4View.routeName,
arguments: model, arguments: model,
); ));
} }
}, },
style: Theme.of(context) style: Theme.of(context)

View file

@ -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/exchange_view/sub_widgets/step_row.dart';
import 'package:stackwallet/pages/home_view/home_view.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/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/exchange/exchange_send_from_wallet_id_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.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/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart';
@ -69,8 +69,9 @@ class _Step4ViewState extends ConsumerState<Step4View> {
} }
Future<void> _updateStatus() async { Future<void> _updateStatus() async {
final statusResponse = final statusResponse = await ref
await ChangeNow.getTransactionStatus(id: model.trade!.id); .read(changeNowProvider)
.getTransactionStatus(id: model.trade!.id);
String status = "Waiting"; String status = "Waiting";
if (statusResponse.value != null) { if (statusResponse.value != null) {
_status = statusResponse.value!.status; _status = statusResponse.value!.status;
@ -214,11 +215,11 @@ class _Step4ViewState extends ConsumerState<Step4View> {
final data = ClipboardData( final data = ClipboardData(
text: model.sendAmount.toString()); text: model.sendAmount.toString());
await clipboard.setData(data); await clipboard.setData(data);
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.info, type: FlushBarType.info,
message: "Copied to clipboard", message: "Copied to clipboard",
context: context, context: context,
); ));
}, },
child: Row( child: Row(
children: [ children: [
@ -271,11 +272,11 @@ class _Step4ViewState extends ConsumerState<Step4View> {
final data = ClipboardData( final data = ClipboardData(
text: model.trade!.payinAddress); text: model.trade!.payinAddress);
await clipboard.setData(data); await clipboard.setData(data);
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.info, type: FlushBarType.info,
message: "Copied to clipboard", message: "Copied to clipboard",
context: context, context: context,
); ));
}, },
child: Row( child: Row(
children: [ children: [
@ -331,11 +332,11 @@ class _Step4ViewState extends ConsumerState<Step4View> {
final data = final data =
ClipboardData(text: model.trade!.id); ClipboardData(text: model.trade!.id);
await clipboard.setData(data); await clipboard.setData(data);
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.info, type: FlushBarType.info,
message: "Copied to clipboard", message: "Copied to clipboard",
context: context, context: context,
); ));
}, },
child: SvgPicture.asset( child: SvgPicture.asset(
Assets.svg.copy, Assets.svg.copy,
@ -487,7 +488,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
try { try {
bool wasCancelled = false; bool wasCancelled = false;
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
useSafeArea: false, useSafeArea: false,
barrierDismissible: false, barrierDismissible: false,
@ -500,7 +501,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
}, },
); );
}, },
); ));
final txData = final txData =
await manager.prepareSend( await manager.prepareSend(
@ -524,7 +525,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
txData["address"] = address; txData["address"] = address;
if (mounted) { if (mounted) {
Navigator.of(context).push( unawaited(
Navigator.of(context).push(
RouteGenerator.getRoute( RouteGenerator.getRoute(
shouldUseMaterialRoute: shouldUseMaterialRoute:
RouteGenerator RouteGenerator
@ -543,7 +545,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
.routeName, .routeName,
), ),
), ),
); ));
} }
} }
} catch (e) { } catch (e) {
@ -551,7 +553,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
// pop building dialog // pop building dialog
Navigator.of(context).pop(); Navigator.of(context).pop();
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
useSafeArea: false, useSafeArea: false,
barrierDismissible: true, barrierDismissible: true,
@ -584,7 +586,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
), ),
); );
}, },
); ));
// } // }
} }
} }

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.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/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/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/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_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_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/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/providers.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/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.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/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/trade_card.dart'; import 'package:stackwallet/widgets/trade_card.dart';
@ -55,6 +58,23 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
_sendFocusNode.unfocus(); _sendFocusNode.unfocus();
_receiveFocusNode.unfocus(); _receiveFocusNode.unfocus();
unawaited(
showDialog<void>(
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 if (ref.watch(prefsChangeNotifierProvider
.select((pref) => pref.exchangeRateType)) == .select((pref) => pref.exchangeRateType)) ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
@ -74,6 +94,9 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
} }
} }
} }
if (mounted) {
Navigator.of(context).pop();
}
_swapLock = false; _swapLock = false;
} }
@ -197,6 +220,17 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
void initState() { void initState() {
_sendController = TextEditingController(); _sendController = TextEditingController();
_receiveController = 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(); super.initState();
} }
@ -407,14 +441,14 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.updateMarket(market, true); .updateMarket(market, true);
} catch (e) { } catch (e) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
builder: (_) => const StackDialog( builder: (_) => const StackDialog(
title: "Fixed rate market error", title: "Fixed rate market error",
message: message:
"Could not find the specified fixed rate trade pair", "Could not find the specified fixed rate trade pair",
), ),
); ));
return; return;
} }
}, },
@ -716,14 +750,14 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.updateMarket(market, true); .updateMarket(market, true);
} catch (e) { } catch (e) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
builder: (_) => const StackDialog( builder: (_) => const StackDialog(
title: "Fixed rate market error", title: "Fixed rate market error",
message: message:
"Could not find the specified fixed rate trade pair", "Could not find the specified fixed rate trade pair",
), ),
); ));
return; return;
} }
}, },
@ -924,12 +958,12 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
} }
} }
} }
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: message:
"Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.", "Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.",
context: context, context: context,
); ));
break; break;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
final fromTicker = ref final fromTicker = ref
@ -970,12 +1004,12 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
); );
return; return;
} }
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: message:
"Fixed rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last fixed rate pair.", "Fixed rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last fixed rate pair.",
context: context, context: context,
); ));
break; break;
} }
}, },
@ -1047,7 +1081,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
} }
if (!isAvailable) { if (!isAvailable) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -1056,7 +1090,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
message: message:
"The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades", "The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades",
), ),
); ));
return; return;
} }
@ -1068,15 +1102,16 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType; .exchangeRateType;
final response = await ChangeNow final response = await ref
.read(changeNowProvider)
.getEstimatedExchangeAmount( .getEstimatedExchangeAmount(
fromTicker: fromTicker, fromTicker: fromTicker,
toTicker: toTicker, toTicker: toTicker,
fromAmount: sendAmount, fromAmount: sendAmount,
); );
if (response.value == null) { if (response.value == null) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -1084,7 +1119,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
"Failed to update trade estimate", "Failed to update trade estimate",
message: response.exception?.toString(), message: response.exception?.toString(),
), ),
); ));
return; return;
} }
@ -1108,10 +1143,10 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
exchangeSendFromWalletIdStateProvider exchangeSendFromWalletIdStateProvider
.state) .state)
.state = null; .state = null;
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
Step1View.routeName, Step1View.routeName,
arguments: model, arguments: model,
); ));
} }
} else { } else {
final fromTicker = ref final fromTicker = ref
@ -1133,18 +1168,19 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType; .exchangeRateType;
final response = await ChangeNow final response = await ref
.read(changeNowProvider)
.getEstimatedFixedRateExchangeAmount( .getEstimatedFixedRateExchangeAmount(
fromTicker: fromTicker, fromTicker: fromTicker,
toTicker: toTicker, toTicker: toTicker,
fromAmount: sendAmount, fromAmount: sendAmount,
useRateId: true, useRateId: true,
); );
bool? shouldCancel; bool? shouldCancel;
if (response.value == null) { if (response.value == null) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -1152,7 +1188,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
"Failed to update trade estimate", "Failed to update trade estimate",
message: response.exception?.toString(), message: response.exception?.toString(),
), ),
); ));
return; return;
} else if (response.value!.warningMessage != } else if (response.value!.warningMessage !=
null && null &&
@ -1234,10 +1270,10 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
exchangeSendFromWalletIdStateProvider exchangeSendFromWalletIdStateProvider
.state) .state)
.state = null; .state = null;
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
Step1View.routeName, Step1View.routeName,
arguments: model, arguments: model,
); ));
} }
} }
} }
@ -1341,18 +1377,18 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
final tx = txData.getAllTransactions()[txid]; final tx = txData.getAllTransactions()[txid];
if (mounted) { if (mounted) {
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
TradeDetailsView.routeName, TradeDetailsView.routeName,
arguments: Tuple4(tradeId, tx, arguments: Tuple4(tradeId, tx,
walletIds.first, manager.walletName), walletIds.first, manager.walletName),
); ));
} }
} else { } else {
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
TradeDetailsView.routeName, TradeDetailsView.routeName,
arguments: Tuple4( arguments: Tuple4(
tradeId, null, walletIds?.first, null), tradeId, null, walletIds?.first, null),
); ));
} }
}, },
), ),
@ -1454,7 +1490,7 @@ class RateInfo extends ConsumerWidget {
} }
} }
showModalBottomSheet<dynamic>( unawaited(showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
context: context, context: context,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
@ -1467,7 +1503,7 @@ class RateInfo extends ConsumerWidget {
if (value is ExchangeRateType && value != type) { if (value is ExchangeRateType && value != type) {
onChanged(value); onChanged(value);
} }
}); }));
} }
: null, : null,
style: Theme.of(context).textButtonTheme.style?.copyWith( style: Theme.of(context).textButtonTheme.style?.copyWith(

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/pages/exchange_view/sub_widgets/step_row.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.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/available_floating_rate_pairs_state_provider.dart';
import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart'; import 'package:stackwallet/providers/exchange/change_now_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/exchange/exchange_send_from_wallet_id_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_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/providers.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/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/enums/coin_enum.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/format.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -72,6 +74,23 @@ class _WalletInitiatedExchangeViewState
_sendFocusNode.unfocus(); _sendFocusNode.unfocus();
_receiveFocusNode.unfocus(); _receiveFocusNode.unfocus();
unawaited(
showDialog<void>(
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 if (ref.watch(prefsChangeNotifierProvider
.select((pref) => pref.exchangeRateType)) == .select((pref) => pref.exchangeRateType)) ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
@ -91,6 +110,9 @@ class _WalletInitiatedExchangeViewState
} }
} }
} }
if (mounted) {
Navigator.of(context).pop();
}
_swapLock = false; _swapLock = false;
} }
@ -216,6 +238,11 @@ class _WalletInitiatedExchangeViewState
coin = widget.coin; coin = widget.coin;
_sendController = TextEditingController(); _sendController = TextEditingController();
_receiveController = TextEditingController(); _receiveController = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ref.read(estimatedRateExchangeFormProvider).clearAmounts(true);
// ref.read(fixedRateExchangeFormProvider);
});
super.initState(); super.initState();
} }
@ -230,41 +257,6 @@ class _WalletInitiatedExchangeViewState
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); 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 final isEstimated = ref.watch(prefsChangeNotifierProvider
.select((pref) => pref.exchangeRateType)) == .select((pref) => pref.exchangeRateType)) ==
ExchangeRateType.estimated; ExchangeRateType.estimated;
@ -512,14 +504,14 @@ class _WalletInitiatedExchangeViewState
fixedRateExchangeFormProvider) fixedRateExchangeFormProvider)
.updateMarket(market, true); .updateMarket(market, true);
} catch (e) { } catch (e) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
builder: (_) => const StackDialog( builder: (_) => const StackDialog(
title: "Fixed rate market error", title: "Fixed rate market error",
message: message:
"Could not find the specified fixed rate trade pair", "Could not find the specified fixed rate trade pair",
), ),
); ));
return; return;
} }
}, },
@ -867,14 +859,14 @@ class _WalletInitiatedExchangeViewState
fixedRateExchangeFormProvider) fixedRateExchangeFormProvider)
.updateMarket(market, true); .updateMarket(market, true);
} catch (e) { } catch (e) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
builder: (_) => const StackDialog( builder: (_) => const StackDialog(
title: "Fixed rate market error", title: "Fixed rate market error",
message: message:
"Could not find the specified fixed rate trade pair", "Could not find the specified fixed rate trade pair",
), ),
); ));
return; return;
} }
}, },
@ -1099,12 +1091,12 @@ class _WalletInitiatedExchangeViewState
} }
} }
} }
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: message:
"Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.", "Estimated rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last estimated rate pair.",
context: context, context: context,
); ));
break; break;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
final fromTicker = ref final fromTicker = ref
@ -1146,12 +1138,12 @@ class _WalletInitiatedExchangeViewState
); );
return; return;
} }
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: message:
"Fixed rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last fixed rate pair.", "Fixed rate trade pair \"$fromTicker-$toTicker\" unavailable. Reverting to last fixed rate pair.",
context: context, context: context,
); ));
break; break;
} }
}, },
@ -1219,6 +1211,24 @@ class _WalletInitiatedExchangeViewState
if (ft.toLowerCase() == if (ft.toLowerCase() ==
coin.ticker.toLowerCase()) { coin.ticker.toLowerCase()) {
bool shouldPop = false;
bool wasPopped = false;
unawaited(showDialog<void>(
context: context,
builder: (_) => WillPopScope(
onWillPop: () async {
if (shouldPop) {
wasPopped = true;
}
return shouldPop;
},
child: const CustomLoadingOverlay(
message: "Checking available balance",
eventBus: null,
),
),
));
final availableBalance = final availableBalance =
await manager.availableBalance; await manager.availableBalance;
@ -1228,17 +1238,23 @@ class _WalletInitiatedExchangeViewState
Format.decimalAmountToSatoshis( Format.decimalAmountToSatoshis(
sendAmount), sendAmount),
feeObject.medium); feeObject.medium);
shouldPop = true;
if (!wasPopped && mounted) {
Navigator.of(context).pop();
}
if (availableBalance < if (availableBalance <
sendAmount + sendAmount +
Format.satoshisToAmount(fee)) { Format.satoshisToAmount(fee)) {
showDialog<void>( unawaited(showDialog<void>(
context: context, context: context,
builder: (_) => StackOkDialog( builder: (_) => StackOkDialog(
title: "Insufficient balance", title: "Insufficient balance",
message: message:
"Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade", "Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade",
), ),
); ));
return; return;
} }
} }
@ -1272,7 +1288,7 @@ class _WalletInitiatedExchangeViewState
} }
if (!isAvailable) { if (!isAvailable) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -1281,7 +1297,7 @@ class _WalletInitiatedExchangeViewState
message: message:
"The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades", "The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades",
), ),
); ));
return; return;
} }
@ -1289,15 +1305,16 @@ class _WalletInitiatedExchangeViewState
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType; .exchangeRateType;
final response = await ChangeNow final response = await ref
.read(changeNowProvider)
.getEstimatedExchangeAmount( .getEstimatedExchangeAmount(
fromTicker: fromTicker, fromTicker: fromTicker,
toTicker: toTicker, toTicker: toTicker,
fromAmount: sendAmount, fromAmount: sendAmount,
); );
if (response.value == null) { if (response.value == null) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -1306,7 +1323,7 @@ class _WalletInitiatedExchangeViewState
message: message:
response.exception?.toString(), response.exception?.toString(),
), ),
); ));
return; return;
} }
@ -1330,10 +1347,10 @@ class _WalletInitiatedExchangeViewState
exchangeSendFromWalletIdStateProvider exchangeSendFromWalletIdStateProvider
.state) .state)
.state = Tuple2(walletId, coin); .state = Tuple2(walletId, coin);
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
Step2View.routeName, Step2View.routeName,
arguments: model, arguments: model,
); ));
} }
} else { } else {
final fromTicker = ref final fromTicker = ref
@ -1355,18 +1372,19 @@ class _WalletInitiatedExchangeViewState
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType; .exchangeRateType;
final response = await ChangeNow final response = await ref
.read(changeNowProvider)
.getEstimatedFixedRateExchangeAmount( .getEstimatedFixedRateExchangeAmount(
fromTicker: fromTicker, fromTicker: fromTicker,
toTicker: toTicker, toTicker: toTicker,
fromAmount: sendAmount, fromAmount: sendAmount,
useRateId: true, useRateId: true,
); );
bool? shouldCancel; bool? shouldCancel;
if (response.value == null) { if (response.value == null) {
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -1375,7 +1393,7 @@ class _WalletInitiatedExchangeViewState
message: message:
response.exception?.toString(), response.exception?.toString(),
), ),
); ));
return; return;
} else if (response.value!.warningMessage != } else if (response.value!.warningMessage !=
null && null &&
@ -1457,10 +1475,10 @@ class _WalletInitiatedExchangeViewState
exchangeSendFromWalletIdStateProvider exchangeSendFromWalletIdStateProvider
.state) .state)
.state = Tuple2(walletId, coin); .state = Tuple2(walletId, coin);
Navigator.of(context).pushNamed( unawaited(Navigator.of(context).pushNamed(
Step2View.routeName, Step2View.routeName,
arguments: model, arguments: model,
); ));
} }
} }
} }

View file

@ -1,13 +1,15 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/exchange_view/exchange_view.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_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_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_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/providers.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/cfcolors.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
@ -41,8 +43,9 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
BuildContext context, BuildContext context,
WidgetRef ref, WidgetRef ref,
) async { ) async {
final response = await ChangeNow.getAvailableCurrencies(); final response = await ref.read(changeNowProvider).getAvailableCurrencies();
final response2 = await ChangeNow.getAvailableFloatingRatePairs(); final response2 =
await ref.read(changeNowProvider).getAvailableFloatingRatePairs();
if (response.value != null && response2.value != null) { if (response.value != null && response2.value != null) {
ref.read(availableChangeNowCurrenciesStateProvider.state).state = ref.read(availableChangeNowCurrenciesStateProvider.state).state =
response.value!; response.value!;
@ -52,13 +55,13 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
if (response.value!.length > 1) { if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { 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); response.value!.firstWhere((e) => e.ticker == "btc"), true);
} }
} }
if (ref.read(estimatedRateExchangeFormProvider).to == null) { if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { 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); response.value!.firstWhere((e) => e.ticker == "doge"), true);
} }
} }
@ -70,7 +73,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
Logging.instance.log( Logging.instance.log(
"Failed to load changeNOW floating rate market data: \n${response.exception?.errorMessage}\n${response2.exception?.toString()}", "Failed to load changeNOW floating rate market data: \n${response.exception?.errorMessage}\n${response2.exception?.toString()}",
level: LogLevel.Error); level: LogLevel.Error);
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
@ -78,7 +81,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
message: message:
"${response.exception?.toString()}\n\n${response2.exception?.toString()}", "${response.exception?.toString()}\n\n${response2.exception?.toString()}",
), ),
); ));
} }
} }
@ -86,7 +89,8 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
BuildContext context, BuildContext context,
WidgetRef ref, WidgetRef ref,
) async { ) async {
final response3 = await ChangeNow.getAvailableFixedRateMarkets(); final response3 =
await ref.read(changeNowProvider).getAvailableFixedRateMarkets();
if (response3.value != null) { if (response3.value != null) {
ref.read(fixedRateMarketPairsStateProvider.state).state = ref.read(fixedRateMarketPairsStateProvider.state).state =
@ -97,7 +101,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
response3.value!.where((e) => e.to == "doge" && e.from == "btc"); response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) { if (matchingMarkets.isNotEmpty) {
ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.updateMarket(matchingMarkets.first, true); .updateMarket(matchingMarkets.first, true);
} }
@ -108,14 +112,14 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
Logging.instance.log( Logging.instance.log(
"Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}", "Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}",
level: LogLevel.Error); level: LogLevel.Error);
showDialog<dynamic>( unawaited(showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (_) => StackDialog( builder: (_) => StackDialog(
title: "ChangeNOW API call failed", title: "ChangeNOW API call failed",
message: "${response3.exception?.toString()}", message: "${response3.exception?.toString()}",
), ),
); ));
} }
} }

View file

@ -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/sub_widgets/wallet_summary.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.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/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/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';

View file

@ -0,0 +1,4 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/services/change_now/change_now.dart';
final changeNowProvider = Provider<ChangeNow>((ref) => ChangeNow.instance);

View file

@ -2,4 +2,4 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/exchange_form_state.dart'; import 'package:stackwallet/models/exchange/exchange_form_state.dart';
final estimatedRateExchangeFormProvider = final estimatedRateExchangeFormProvider =
ChangeNotifierProvider((ref) => ExchangeFormState()); ChangeNotifierProvider((ref) => EstimatedRateExchangeFormState());

View file

@ -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_address_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_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/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_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_2_view.dart';
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_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<String, Coin>) { if (args is Tuple2<String, Coin>) {
return getRoute( return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute, shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => WalletInitiatedExchangeView( builder: (_) => Stack(
walletId: args.item1, children: [
coin: args.item2, WalletInitiatedExchangeView(
walletId: args.item1,
coin: args.item2,
),
const ExchangeLoadingOverlayView(),
],
), ),
settings: RouteSettings( settings: RouteSettings(
name: settings.name, name: settings.name,

View file

@ -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/models/exchange/change_now/fixed_rate_market.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
abstract class ChangeNow { class ChangeNow {
static const String scheme = "https"; static const String scheme = "https";
static const String authority = "api.changenow.io"; static const String authority = "api.changenow.io";
static const String apiVersion = "/v1"; static const String apiVersion = "/v1";
/// set this to override using standard http client. Useful for testing ChangeNow._();
static http.Client? client; static final ChangeNow _instance = ChangeNow._();
static ChangeNow get instance => _instance;
static Uri _buildUri(String path, Map<String, dynamic>? params) { /// set this to override using standard http client. Useful for testing
http.Client? client;
Uri _buildUri(String path, Map<String, dynamic>? params) {
return Uri.https(authority, apiVersion + path, params); return Uri.https(authority, apiVersion + path, params);
} }
static Future<dynamic> _makeGetRequest(Uri uri) async { Future<dynamic> _makeGetRequest(Uri uri) async {
final client = ChangeNow.client ?? http.Client(); final client = this.client ?? http.Client();
try { try {
final response = await client.get( final response = await client.get(
uri, uri,
@ -43,11 +47,11 @@ abstract class ChangeNow {
} }
} }
static Future<dynamic> _makePostRequest( Future<dynamic> _makePostRequest(
Uri uri, Uri uri,
Map<String, String> body, Map<String, String> body,
) async { ) async {
final client = ChangeNow.client ?? http.Client(); final client = this.client ?? http.Client();
try { try {
final response = await client.post( final response = await client.post(
uri, uri,
@ -69,7 +73,7 @@ abstract class ChangeNow {
/// ///
/// Set [active] to true to return only active currencies. /// Set [active] to true to return only active currencies.
/// Set [fixedRate] to true to return only currencies available on a fixed-rate flow. /// Set [fixedRate] to true to return only currencies available on a fixed-rate flow.
static Future<ChangeNowResponse<List<Currency>>> getAvailableCurrencies({ Future<ChangeNowResponse<List<Currency>>> getAvailableCurrencies({
bool? fixedRate, bool? fixedRate,
bool? active, bool? active,
}) async { }) async {
@ -117,7 +121,7 @@ abstract class ChangeNow {
} }
} }
static ChangeNowResponse<List<Currency>> _parseAvailableCurrenciesJson( ChangeNowResponse<List<Currency>> _parseAvailableCurrenciesJson(
List<dynamic> jsonArray) { List<dynamic> jsonArray) {
try { try {
List<Currency> currencies = []; List<Currency> currencies = [];
@ -144,7 +148,7 @@ abstract class ChangeNow {
/// ///
/// Required [ticker] to fetch paired currencies for. /// Required [ticker] to fetch paired currencies for.
/// Set [fixedRate] to true to return only currencies available on a fixed-rate flow. /// Set [fixedRate] to true to return only currencies available on a fixed-rate flow.
static Future<ChangeNowResponse<List<Currency>>> getPairedCurrencies({ Future<ChangeNowResponse<List<Currency>>> getPairedCurrencies({
required String ticker, required String ticker,
bool? fixedRate, bool? fixedRate,
}) async { }) async {
@ -199,7 +203,7 @@ abstract class ChangeNow {
/// The API endpoint returns minimal payment amount required to make /// The API endpoint returns minimal payment amount required to make
/// an exchange of [fromTicker] to [toTicker]. /// an exchange of [fromTicker] to [toTicker].
/// If you try to exchange less, the transaction will most likely fail. /// If you try to exchange less, the transaction will most likely fail.
static Future<ChangeNowResponse<Decimal>> getMinimalExchangeAmount({ Future<ChangeNowResponse<Decimal>> getMinimalExchangeAmount({
required String fromTicker, required String fromTicker,
required String toTicker, required String toTicker,
String? apiKey, String? apiKey,
@ -237,7 +241,7 @@ abstract class ChangeNow {
/// Get estimated amount of [toTicker] cryptocurrency to receive /// Get estimated amount of [toTicker] cryptocurrency to receive
/// for [fromAmount] of [fromTicker] /// for [fromAmount] of [fromTicker]
static Future<ChangeNowResponse<EstimatedExchangeAmount>> Future<ChangeNowResponse<EstimatedExchangeAmount>>
getEstimatedExchangeAmount({ getEstimatedExchangeAmount({
required String fromTicker, required String fromTicker,
required String toTicker, required String toTicker,
@ -281,7 +285,7 @@ abstract class ChangeNow {
/// This API endpoint returns fixed-rate estimated exchange amount of /// This API endpoint returns fixed-rate estimated exchange amount of
/// [toTicker] cryptocurrency to receive for [fromAmount] of [fromTicker] /// [toTicker] cryptocurrency to receive for [fromAmount] of [fromTicker]
static Future<ChangeNowResponse<EstimatedExchangeAmount>> Future<ChangeNowResponse<EstimatedExchangeAmount>>
getEstimatedFixedRateExchangeAmount({ getEstimatedFixedRateExchangeAmount({
required String fromTicker, required String fromTicker,
required String toTicker, required String toTicker,
@ -336,7 +340,7 @@ abstract class ChangeNow {
/// fixed-rate flow. Some currencies get enabled or disabled from time to /// 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 /// time and the market info gets updates, so make sure to refresh the list
/// occasionally. One time per minute is sufficient. /// occasionally. One time per minute is sufficient.
static Future<ChangeNowResponse<List<FixedRateMarket>>> Future<ChangeNowResponse<List<FixedRateMarket>>>
getAvailableFixedRateMarkets({ getAvailableFixedRateMarkets({
String? apiKey, String? apiKey,
}) async { }) async {
@ -373,7 +377,7 @@ abstract class ChangeNow {
} }
} }
static ChangeNowResponse<List<FixedRateMarket>> _parseFixedRateMarketsJson( ChangeNowResponse<List<FixedRateMarket>> _parseFixedRateMarketsJson(
List<dynamic> jsonArray) { List<dynamic> jsonArray) {
try { try {
List<FixedRateMarket> markets = []; List<FixedRateMarket> markets = [];
@ -395,7 +399,7 @@ abstract class ChangeNow {
/// The API endpoint creates a transaction, generates an address for /// The API endpoint creates a transaction, generates an address for
/// sending funds and returns transaction attributes. /// sending funds and returns transaction attributes.
static Future<ChangeNowResponse<ExchangeTransaction>> Future<ChangeNowResponse<ExchangeTransaction>>
createStandardExchangeTransaction({ createStandardExchangeTransaction({
required String fromTicker, required String fromTicker,
required String toTicker, required String toTicker,
@ -457,7 +461,7 @@ abstract class ChangeNow {
/// The API endpoint creates a transaction, generates an address for /// The API endpoint creates a transaction, generates an address for
/// sending funds and returns transaction attributes. /// sending funds and returns transaction attributes.
static Future<ChangeNowResponse<ExchangeTransaction>> Future<ChangeNowResponse<ExchangeTransaction>>
createFixedRateExchangeTransaction({ createFixedRateExchangeTransaction({
required String fromTicker, required String fromTicker,
required String toTicker, required String toTicker,
@ -520,8 +524,7 @@ abstract class ChangeNow {
} }
} }
static Future<ChangeNowResponse<ExchangeTransactionStatus>> Future<ChangeNowResponse<ExchangeTransactionStatus>> getTransactionStatus({
getTransactionStatus({
required String id, required String id,
String? apiKey, String? apiKey,
}) async { }) async {
@ -556,7 +559,7 @@ abstract class ChangeNow {
} }
} }
static Future<ChangeNowResponse<List<AvailableFloatingRatePair>>> Future<ChangeNowResponse<List<AvailableFloatingRatePair>>>
getAvailableFloatingRatePairs({ getAvailableFloatingRatePairs({
bool includePartners = false, bool includePartners = false,
}) async { }) async {
@ -593,7 +596,7 @@ abstract class ChangeNow {
} }
} }
static ChangeNowResponse<List<AvailableFloatingRatePair>> ChangeNowResponse<List<AvailableFloatingRatePair>>
_parseAvailableFloatingRatePairsJson(List<dynamic> jsonArray) { _parseAvailableFloatingRatePairsJson(List<dynamic> jsonArray) {
try { try {
List<AvailableFloatingRatePair> pairs = []; List<AvailableFloatingRatePair> pairs = [];

View file

@ -1063,8 +1063,8 @@ class BitcoinWallet extends CoinServiceAPI {
@override @override
Future<void> refresh() async { Future<void> refresh() async {
if (refreshMutex) { if (refreshMutex) {
Logging.instance Logging.instance.log("$walletId $walletName refreshMutex denied",
.log("$walletName refreshMutex denied", level: LogLevel.Info); level: LogLevel.Info);
return; return;
} else { } else {
refreshMutex = true; refreshMutex = true;
@ -1139,14 +1139,15 @@ class BitcoinWallet extends CoinServiceAPI {
if (shouldAutoSync) { if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async { timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async {
Logging.instance.log( 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); level: LogLevel.Info);
// chain height check currently broken // chain height check currently broken
// if ((await chainHeight) != (await storedChainHeight)) { // if ((await chainHeight) != (await storedChainHeight)) {
if (await refreshIfThereIsNewData()) { if (await refreshIfThereIsNewData()) {
await refresh(); await refresh();
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( 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 // clear cache
_cachedElectrumXClient.clearSharedTransactionCache(coin: coin); await _cachedElectrumXClient.clearSharedTransactionCache(coin: coin);
// back up data // back up data
await _rescanBackup(); await _rescanBackup();

View file

@ -741,7 +741,8 @@ class DogecoinWallet extends CoinServiceAPI {
@override @override
Future<void> refresh() async { Future<void> refresh() async {
if (refreshMutex) { if (refreshMutex) {
Logging.instance.log("refreshMutex denied", level: LogLevel.Info); Logging.instance.log("$walletId $walletName refreshMutex denied",
level: LogLevel.Info);
return; return;
} else { } else {
refreshMutex = true; refreshMutex = true;
@ -820,7 +821,8 @@ class DogecoinWallet extends CoinServiceAPI {
if (await refreshIfThereIsNewData()) { if (await refreshIfThereIsNewData()) {
await refresh(); await refresh();
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( 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); await _getCurrentAddressForChain(1, derivePathType);
final int txCount = await getTxCount(address: currentExternalAddr); final int txCount = await getTxCount(address: currentExternalAddr);
Logging.instance.log( 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) { if (txCount >= 1) {
// First increment the change index // First increment the change index
@ -1786,7 +1789,8 @@ class DogecoinWallet extends CoinServiceAPI {
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log( 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; rethrow;
} }
} }
@ -1798,7 +1802,8 @@ class DogecoinWallet extends CoinServiceAPI {
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log( Logging.instance.log(
"Exception rethrown from _checkCurrentReceivingAddressesForTransactions(): $e\n$s", level: LogLevel.Info); "Exception rethrown from _checkCurrentReceivingAddressesForTransactions(): $e\n$s",
level: LogLevel.Info);
rethrow; rethrow;
} }
} }
@ -1821,7 +1826,8 @@ class DogecoinWallet extends CoinServiceAPI {
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log( Logging.instance.log(
"Exception rethrown from _checkCurrentChangeAddressesForTransactions(): $e\n$s", level: LogLevel.Error); "Exception rethrown from _checkCurrentChangeAddressesForTransactions(): $e\n$s",
level: LogLevel.Error);
rethrow; rethrow;
} }
} }
@ -1969,7 +1975,8 @@ class DogecoinWallet extends CoinServiceAPI {
Logging.instance.log("addAddresses: $allAddresses", level: LogLevel.Info); Logging.instance.log("addAddresses: $allAddresses", level: LogLevel.Info);
Logging.instance.log("allTxHashes: $allTxHashes", 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 = final priceData =
await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); 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 = final foundInSenders =
allAddresses.any((element) => sendersArray.contains(element)); 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 txType = Sent, then calculate inputAmtSentFromWallet
if (foundInSenders) { if (foundInSenders) {
@ -2228,7 +2237,8 @@ class DogecoinWallet extends CoinServiceAPI {
dynamic coinSelection(int satoshiAmountToSend, int selectedTxFeeRate, dynamic coinSelection(int satoshiAmountToSend, int selectedTxFeeRate,
String _recipientAddress, bool isSendAll, String _recipientAddress, bool isSendAll,
{int additionalOutputs = 0, List<UtxoObject>? utxos}) async { {int additionalOutputs = 0, List<UtxoObject>? utxos}) async {
Logging.instance.log("Starting coinSelection ----------", level: LogLevel.Info); Logging.instance
.log("Starting coinSelection ----------", level: LogLevel.Info);
final List<UtxoObject> availableOutputs = utxos ?? outputsList; final List<UtxoObject> availableOutputs = utxos ?? outputsList;
final List<UtxoObject> spendableOutputs = []; final List<UtxoObject> spendableOutputs = [];
int spendableSatoshiValue = 0; int spendableSatoshiValue = 0;
@ -2246,10 +2256,14 @@ class DogecoinWallet extends CoinServiceAPI {
spendableOutputs.sort( spendableOutputs.sort(
(a, b) => b.status.confirmations.compareTo(a.status.confirmations)); (a, b) => b.status.confirmations.compareTo(a.status.confirmations));
Logging.instance.log("spendableOutputs.length: ${spendableOutputs.length}", level: LogLevel.Info); Logging.instance.log("spendableOutputs.length: ${spendableOutputs.length}",
Logging.instance.log("spendableOutputs: $spendableOutputs", level: LogLevel.Info); level: LogLevel.Info);
Logging.instance.log("spendableSatoshiValue: $spendableSatoshiValue", level: LogLevel.Info); Logging.instance
Logging.instance.log("satoshiAmountToSend: $satoshiAmountToSend", level: LogLevel.Info); .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, // 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. // then return 1, which indicates that they have an insufficient balance.
if (spendableSatoshiValue < satoshiAmountToSend) { if (spendableSatoshiValue < satoshiAmountToSend) {
@ -2284,10 +2298,14 @@ class DogecoinWallet extends CoinServiceAPI {
inputsBeingConsumed += 1; inputsBeingConsumed += 1;
} }
Logging.instance.log("satoshisBeingUsed: $satoshisBeingUsed", level: LogLevel.Info); Logging.instance
Logging.instance.log("inputsBeingConsumed: $inputsBeingConsumed", level: LogLevel.Info); .log("satoshisBeingUsed: $satoshisBeingUsed", level: LogLevel.Info);
Logging.instance.log('utxoObjectsToUse: $utxoObjectsToUse', level: LogLevel.Info); Logging.instance
Logging.instance.log('satoshiAmountToSend $satoshiAmountToSend', level: LogLevel.Info); .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 // numberOfOutputs' length must always be equal to that of recipientsArray and recipientsAmtArray
List<String> recipientsArray = [_recipientAddress]; List<String> recipientsArray = [_recipientAddress];
@ -2297,7 +2315,8 @@ class DogecoinWallet extends CoinServiceAPI {
final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse); final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse);
if (isSendAll) { 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( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse, utxosToUse: utxoObjectsToUse,
@ -2362,8 +2381,10 @@ class DogecoinWallet extends CoinServiceAPI {
feeRatePerKB: selectedTxFeeRate, feeRatePerKB: selectedTxFeeRate,
); );
Logging.instance.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); Logging.instance
Logging.instance.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); .log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
Logging.instance
.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info);
if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) { if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) {
feeForOneOutput = (vSizeForOneOutput + 1) * 1000; feeForOneOutput = (vSizeForOneOutput + 1) * 1000;
} }
@ -2371,8 +2392,10 @@ class DogecoinWallet extends CoinServiceAPI {
feeForTwoOutputs = ((vSizeForTwoOutPuts + 1) * 1000); feeForTwoOutputs = ((vSizeForTwoOutPuts + 1) * 1000);
} }
Logging.instance.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info); Logging.instance
Logging.instance.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info); .log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
Logging.instance
.log("feeForOneOutput: $feeForOneOutput", level: LogLevel.Info);
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) { if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend > 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 // 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. // 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('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 Logging.instance
.log('Difference (fee being paid): $feeBeingPaid sats', level: LogLevel.Info); .log('Input size: $satoshisBeingUsed', level: LogLevel.Info);
Logging.instance.log('Estimated fee: $feeForTwoOutputs', 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( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse, utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
@ -2421,14 +2449,19 @@ class DogecoinWallet extends CoinServiceAPI {
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize; satoshisBeingUsed - satoshiAmountToSend - changeOutputSize;
recipientsAmtArray.removeLast(); recipientsAmtArray.removeLast();
recipientsAmtArray.add(changeOutputSize); recipientsAmtArray.add(changeOutputSize);
Logging.instance.log('Adjusted Input size: $satoshisBeingUsed', level: LogLevel.Info); Logging.instance.log('Adjusted Input size: $satoshisBeingUsed',
Logging.instance level: LogLevel.Info);
.log('Adjusted Recipient output size: $satoshiAmountToSend', level: LogLevel.Info);
Logging.instance
.log('Adjusted Change Output Size: $changeOutputSize', level: LogLevel.Info);
Logging.instance.log( Logging.instance.log(
'Adjusted Difference (fee being paid): $feeBeingPaid sats', level: LogLevel.Info); 'Adjusted Recipient output size: $satoshiAmountToSend',
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); 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( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse, utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, 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 // 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. // 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('1 output in tx', level: LogLevel.Info);
Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); Logging.instance
Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); .log('Input size: $satoshisBeingUsed', level: LogLevel.Info);
Logging.instance.log('Recipient output size: $satoshiAmountToSend',
level: LogLevel.Info);
Logging.instance.log( Logging.instance.log(
'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', level: LogLevel.Info); 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats',
Logging.instance.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); level: LogLevel.Info);
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse, utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, 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 // 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. // the wallet to begin crafting the transaction that the user requested.
Logging.instance.log('1 output in tx', level: LogLevel.Info); Logging.instance.log('1 output in tx', level: LogLevel.Info);
Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); Logging.instance
Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); .log('Input size: $satoshisBeingUsed', level: LogLevel.Info);
Logging.instance.log('Recipient output size: $satoshiAmountToSend',
level: LogLevel.Info);
Logging.instance.log( Logging.instance.log(
'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats', level: LogLevel.Info); 'Difference (fee being paid): ${satoshisBeingUsed - satoshiAmountToSend} sats',
Logging.instance.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); level: LogLevel.Info);
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse, utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, 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 // 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. // crafting the transaction that the user requested.
Logging.instance.log('1 output in tx', level: LogLevel.Info); Logging.instance.log('1 output in tx', level: LogLevel.Info);
Logging.instance.log('Input size: $satoshisBeingUsed', level: LogLevel.Info); Logging.instance
Logging.instance.log('Recipient output size: $satoshiAmountToSend', level: LogLevel.Info); .log('Input size: $satoshisBeingUsed', level: LogLevel.Info);
Logging.instance.log('Recipient output size: $satoshiAmountToSend',
level: LogLevel.Info);
Logging.instance.log( Logging.instance.log(
'Fee being paid: ${satoshisBeingUsed - satoshiAmountToSend} sats', level: LogLevel.Info); 'Fee being paid: ${satoshisBeingUsed - satoshiAmountToSend} sats',
Logging.instance.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); level: LogLevel.Info);
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse, utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, 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 // 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. // additional outputs they're able to spend and then recalculate fees.
Logging.instance.log( 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 // try adding more outputs
if (spendableOutputs.length > inputsBeingConsumed) { if (spendableOutputs.length > inputsBeingConsumed) {
return coinSelection(satoshiAmountToSend, selectedTxFeeRate, return coinSelection(satoshiAmountToSend, selectedTxFeeRate,
@ -2630,7 +2676,8 @@ class DogecoinWallet extends CoinServiceAPI {
return results; return results;
} catch (e, s) { } 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; rethrow;
} }
} }
@ -2642,7 +2689,8 @@ class DogecoinWallet extends CoinServiceAPI {
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
Logging.instance.log("Starting buildTransaction ----------", level: LogLevel.Info); Logging.instance
.log("Starting buildTransaction ----------", level: LogLevel.Info);
final txb = TransactionBuilder(network: _network); final txb = TransactionBuilder(network: _network);
txb.setVersion(1); txb.setVersion(1);
@ -2671,8 +2719,8 @@ class DogecoinWallet extends CoinServiceAPI {
); );
} }
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance.log("Caught exception while signing transaction: $e\n$s",
.log("Caught exception while signing transaction: $e\n$s", level: LogLevel.Error); level: LogLevel.Error);
rethrow; rethrow;
} }
@ -2733,7 +2781,8 @@ class DogecoinWallet extends CoinServiceAPI {
await _rescanRestore(); await _rescanRestore();
longMutex = false; 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; rethrow;
} }
} }
@ -2884,7 +2933,8 @@ class DogecoinWallet extends CoinServiceAPI {
return DB.instance.get<dynamic>(boxName: walletId, key: "isFavorite") return DB.instance.get<dynamic>(boxName: walletId, key: "isFavorite")
as bool; as bool;
} catch (e, s) { } 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; rethrow;
} }
} }

View file

@ -1827,9 +1827,11 @@ class EpicCashWallet extends CoinServiceAPI {
/// Refreshes display data for the wallet /// Refreshes display data for the wallet
@override @override
Future<void> refresh() async { Future<void> refresh() async {
Logging.instance.log("Calling refresh", level: LogLevel.Info); Logging.instance
.log("$walletId $walletName Calling refresh", level: LogLevel.Info);
if (refreshMutex) { if (refreshMutex) {
Logging.instance.log("refreshMutex denied", level: LogLevel.Info); Logging.instance.log("$walletId $walletName refreshMutex denied",
level: LogLevel.Info);
return; return;
} else { } else {
refreshMutex = true; refreshMutex = true;
@ -1920,14 +1922,15 @@ class EpicCashWallet extends CoinServiceAPI {
if (shouldAutoSync) { if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 60), (timer) async { timer ??= Timer.periodic(const Duration(seconds: 60), (timer) async {
Logging.instance.log( 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); level: LogLevel.Info);
// chain height check currently broken // chain height check currently broken
// if ((await chainHeight) != (await storedChainHeight)) { // if ((await chainHeight) != (await storedChainHeight)) {
if (await refreshIfThereIsNewData()) { if (await refreshIfThereIsNewData()) {
await refresh(); await refresh();
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( GlobalEventBus.instance.fire(UpdatedInBackgroundEvent(
"New data found in $walletName in background!", walletId)); "New data found in $walletId $walletName in background!",
walletId));
} }
// } // }
}); });

View file

@ -1470,8 +1470,8 @@ class FiroWallet extends CoinServiceAPI {
@override @override
Future<void> refresh() async { Future<void> refresh() async {
if (refreshMutex) { if (refreshMutex) {
Logging.instance Logging.instance.log("$walletId $walletName refreshMutex denied",
.log("$walletName refreshMutex denied", level: LogLevel.Info); level: LogLevel.Info);
return; return;
} else { } else {
refreshMutex = true; refreshMutex = true;
@ -1559,7 +1559,8 @@ class FiroWallet extends CoinServiceAPI {
if (shouldNotify) { if (shouldNotify) {
await refresh(); await refresh();
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( GlobalEventBus.instance.fire(UpdatedInBackgroundEvent(
"New data found in $walletName in background!", walletId)); "New data found in $walletId $walletName in background!",
walletId));
} }
}); });
} }

View file

@ -390,8 +390,8 @@ class MoneroWallet extends CoinServiceAPI {
@override @override
Future<void> refresh() async { Future<void> refresh() async {
if (refreshMutex) { if (refreshMutex) {
Logging.instance Logging.instance.log("$walletId $walletName refreshMutex denied",
.log("$walletId refreshMutex denied", level: LogLevel.Info); level: LogLevel.Info);
return; return;
} else { } else {
refreshMutex = true; refreshMutex = true;
@ -465,7 +465,8 @@ class MoneroWallet extends CoinServiceAPI {
// if (await refreshIfThereIsNewData()) { // if (await refreshIfThereIsNewData()) {
await refresh(); await refresh();
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( 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) { if (isActive) {
await walletBase?.save(); await walletBase?.save();
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( 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 // Use new index to derive a new receiving address
final newReceivingAddress = await _generateAddressForChain(0, curIndex); final newReceivingAddress = await _generateAddressForChain(0, curIndex);
Logging.instance.log("xmr address in init existing: $newReceivingAddress", Logging.instance.log("xmr address in init existing: $newReceivingAddress",
level: LogLevel.Fatal); level: LogLevel.Info);
_currentReceivingAddress = Future(() => newReceivingAddress); _currentReceivingAddress = Future(() => newReceivingAddress);
} }

View file

@ -17,6 +17,7 @@ class NotificationsService extends ChangeNotifier {
late NodeService nodeService; late NodeService nodeService;
late TradesService tradesService; late TradesService tradesService;
late Prefs prefs; late Prefs prefs;
late ChangeNow changeNow;
NotificationsService._(); NotificationsService._();
static final NotificationsService _instance = NotificationsService._(); static final NotificationsService _instance = NotificationsService._();
@ -26,10 +27,12 @@ class NotificationsService extends ChangeNotifier {
required NodeService nodeService, required NodeService nodeService,
required TradesService tradesService, required TradesService tradesService,
required Prefs prefs, required Prefs prefs,
required ChangeNow changeNow,
}) async { }) async {
this.nodeService = nodeService; this.nodeService = nodeService;
this.tradesService = tradesService; this.tradesService = tradesService;
this.prefs = prefs; this.prefs = prefs;
this.changeNow = changeNow;
} }
// watched transactions // watched transactions
@ -177,7 +180,7 @@ class NotificationsService extends ChangeNotifier {
for (final notification in _watchedChangeNowTradeNotifications) { for (final notification in _watchedChangeNowTradeNotifications) {
final id = notification.changeNowId!; final id = notification.changeNowId!;
final result = await ChangeNow.getTransactionStatus(id: id); final result = await changeNow.getTransactionStatus(id: id);
ChangeNowTransactionStatus? status = result.value?.status; ChangeNowTransactionStatus? status = result.value?.status;

View file

@ -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);
// });
}

View file

@ -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<String> get walletIdsSyncOnStartup =>
(super.noSuchMethod(Invocation.getter(#walletIdsSyncOnStartup),
returnValue: <String>[]) as List<String>);
@override
set walletIdsSyncOnStartup(List<String>? 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<void> init() => (super.noSuchMethod(Invocation.method(#init, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@override
_i6.Future<void> incrementCurrentNotificationIndex() => (super.noSuchMethod(
Invocation.method(#incrementCurrentNotificationIndex, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@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<void> add(
{_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) =>
(super.noSuchMethod(
Invocation.method(#add, [],
{#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@override
_i6.Future<void> edit(
{_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) =>
(super.noSuchMethod(
Invocation.method(#edit, [],
{#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@override
_i6.Future<void> delete(
{_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) =>
(super.noSuchMethod(
Invocation.method(#delete, [],
{#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@override
_i6.Future<void> deleteByUuid({String? uuid, bool? shouldNotifyListeners}) =>
(super.noSuchMethod(
Invocation.method(#deleteByUuid, [],
{#uuid: uuid, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@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<String, String> get all => (super.noSuchMethod(Invocation.getter(#all),
returnValue: <String, String>{}) as Map<String, String>);
@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<void> set({String? tradeId, String? note}) => (super.noSuchMethod(
Invocation.method(#set, [], {#tradeId: tradeId, #note: note}),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@override
_i6.Future<void> delete({String? tradeId}) =>
(super.noSuchMethod(Invocation.method(#delete, [], {#tradeId: tradeId}),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>);
@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);
}
}

View file

@ -20,15 +20,15 @@ void main() {
group("getAvailableCurrencies", () { group("getAvailableCurrencies", () {
test("getAvailableCurrencies succeeds without options", () async { test("getAvailableCurrencies succeeds without options", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse("https://api.changenow.io/v1/currencies"), Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSON), 200)); Response(jsonEncode(availableCurrenciesJSON), 200));
final result = await ChangeNow.getAvailableCurrencies(); final result = await ChangeNow.instance.getAvailableCurrencies();
expect(result.exception, null); expect(result.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -37,15 +37,16 @@ void main() {
test("getAvailableCurrencies succeeds with active option", () async { test("getAvailableCurrencies succeeds with active option", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSONActive), 200)); 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.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -54,15 +55,16 @@ void main() {
test("getAvailableCurrencies succeeds with fixedRate option", () async { test("getAvailableCurrencies succeeds with fixedRate option", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSONFixedRate), 200)); 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.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -72,17 +74,17 @@ void main() {
test("getAvailableCurrencies succeeds with fixedRate and active options", test("getAvailableCurrencies succeeds with fixedRate and active options",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSONActiveFixedRate), 200)); Response(jsonEncode(availableCurrenciesJSONActiveFixedRate), 200));
final result = final result = await ChangeNow.instance
await ChangeNow.getAvailableCurrencies(active: true, fixedRate: true); .getAvailableCurrencies(active: true, fixedRate: true);
expect(result.exception, null); expect(result.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -93,15 +95,15 @@ void main() {
"getAvailableCurrencies fails with ChangeNowExceptionType.serializeResponseError", "getAvailableCurrencies fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse("https://api.changenow.io/v1/currencies"), Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response('{"some unexpected": "but valid json data"}', 200)); Response('{"some unexpected": "but valid json data"}', 200));
final result = await ChangeNow.getAvailableCurrencies(); final result = await ChangeNow.instance.getAvailableCurrencies();
expect(result.exception!.type, expect(result.exception!.type,
ChangeNowExceptionType.serializeResponseError); ChangeNowExceptionType.serializeResponseError);
@ -110,14 +112,14 @@ void main() {
test("getAvailableCurrencies fails for any other reason", () async { test("getAvailableCurrencies fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse("https://api.changenow.io/v1/currencies"), Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response("", 400)); )).thenAnswer((realInvocation) async => Response("", 400));
final result = await ChangeNow.getAvailableCurrencies(); final result = await ChangeNow.instance.getAvailableCurrencies();
expect(result.exception!.type, ChangeNowExceptionType.generic); expect(result.exception!.type, ChangeNowExceptionType.generic);
expect(result.value == null, true); expect(result.value == null, true);
@ -127,15 +129,16 @@ void main() {
group("getPairedCurrencies", () { group("getPairedCurrencies", () {
test("getPairedCurrencies succeeds without fixedRate option", () async { test("getPairedCurrencies succeeds without fixedRate option", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(getPairedCurrenciesJSON), 200)); 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.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -144,17 +147,17 @@ void main() {
test("getPairedCurrencies succeeds with fixedRate option", () async { test("getPairedCurrencies succeeds with fixedRate option", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(getPairedCurrenciesJSONFixedRate), 200)); Response(jsonEncode(getPairedCurrenciesJSONFixedRate), 200));
final result = final result = await ChangeNow.instance
await ChangeNow.getPairedCurrencies(ticker: "XMR", fixedRate: true); .getPairedCurrencies(ticker: "XMR", fixedRate: true);
expect(result.exception, null); expect(result.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -165,15 +168,16 @@ void main() {
"getPairedCurrencies fails with ChangeNowExceptionType.serializeResponseError A", "getPairedCurrencies fails with ChangeNowExceptionType.serializeResponseError A",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response('[{"some unexpected": "but valid json data"}]', 200)); 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, expect(result.exception!.type,
ChangeNowExceptionType.serializeResponseError); ChangeNowExceptionType.serializeResponseError);
@ -182,15 +186,15 @@ void main() {
test("getPairedCurrencies fails for any other reason", () async { test("getPairedCurrencies fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse("https://api.changenow.io/v1/currencies"), Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response("", 400)); )).thenAnswer((realInvocation) async => Response("", 400));
final result = final result = await ChangeNow.instance
await ChangeNow.getPairedCurrencies(ticker: "XMR", fixedRate: true); .getPairedCurrencies(ticker: "XMR", fixedRate: true);
expect(result.exception!.type, ChangeNowExceptionType.generic); expect(result.exception!.type, ChangeNowExceptionType.generic);
expect(result.value == null, true); expect(result.value == null, true);
@ -200,16 +204,16 @@ void main() {
group("getMinimalExchangeAmount", () { group("getMinimalExchangeAmount", () {
test("getMinimalExchangeAmount succeeds", () async { test("getMinimalExchangeAmount succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer( )).thenAnswer(
(realInvocation) async => Response('{"minAmount": 42}', 200)); (realInvocation) async => Response('{"minAmount": 42}', 200));
final result = await ChangeNow.getMinimalExchangeAmount( final result = await ChangeNow.instance.getMinimalExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
@ -224,15 +228,15 @@ void main() {
"getMinimalExchangeAmount fails with ChangeNowExceptionType.serializeResponseError", "getMinimalExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.getMinimalExchangeAmount( final result = await ChangeNow.instance.getMinimalExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
@ -245,15 +249,15 @@ void main() {
test("getMinimalExchangeAmount fails for any other reason", () async { test("getMinimalExchangeAmount fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.getMinimalExchangeAmount( final result = await ChangeNow.instance.getMinimalExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
@ -267,17 +271,17 @@ void main() {
group("getEstimatedExchangeAmount", () { group("getEstimatedExchangeAmount", () {
test("getEstimatedExchangeAmount succeeds", () async { test("getEstimatedExchangeAmount succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response( )).thenAnswer((realInvocation) async => Response(
'{"estimatedAmount": 58.4142873, "transactionSpeedForecast": "10-60", "warningMessage": null}', '{"estimatedAmount": 58.4142873, "transactionSpeedForecast": "10-60", "warningMessage": null}',
200)); 200));
final result = await ChangeNow.getEstimatedExchangeAmount( final result = await ChangeNow.instance.getEstimatedExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
fromAmount: Decimal.fromInt(42), fromAmount: Decimal.fromInt(42),
@ -293,15 +297,15 @@ void main() {
"getEstimatedExchangeAmount fails with ChangeNowExceptionType.serializeResponseError", "getEstimatedExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.getEstimatedExchangeAmount( final result = await ChangeNow.instance.getEstimatedExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
fromAmount: Decimal.fromInt(42), fromAmount: Decimal.fromInt(42),
@ -315,15 +319,15 @@ void main() {
test("getEstimatedExchangeAmount fails for any other reason", () async { test("getEstimatedExchangeAmount fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.getEstimatedExchangeAmount( final result = await ChangeNow.instance.getEstimatedExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
fromAmount: Decimal.fromInt(42), fromAmount: Decimal.fromInt(42),
@ -338,16 +342,17 @@ void main() {
group("getEstimatedFixedRateExchangeAmount", () { group("getEstimatedFixedRateExchangeAmount", () {
test("getEstimatedFixedRateExchangeAmount succeeds", () async { test("getEstimatedFixedRateExchangeAmount succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(estFixedRateExchangeAmountJSON), 200)); Response(jsonEncode(estFixedRateExchangeAmountJSON), 200));
final result = await ChangeNow.getEstimatedFixedRateExchangeAmount( final result =
await ChangeNow.instance.getEstimatedFixedRateExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
fromAmount: Decimal.fromInt(10), fromAmount: Decimal.fromInt(10),
@ -364,15 +369,16 @@ void main() {
"getEstimatedFixedRateExchangeAmount fails with ChangeNowExceptionType.serializeResponseError", "getEstimatedFixedRateExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.getEstimatedFixedRateExchangeAmount( final result =
await ChangeNow.instance.getEstimatedFixedRateExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
fromAmount: Decimal.fromInt(10), fromAmount: Decimal.fromInt(10),
@ -387,15 +393,16 @@ void main() {
test("getEstimatedFixedRateExchangeAmount fails for any other reason", test("getEstimatedFixedRateExchangeAmount fails for any other reason",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.getEstimatedFixedRateExchangeAmount( final result =
await ChangeNow.instance.getEstimatedFixedRateExchangeAmount(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
fromAmount: Decimal.fromInt(10), fromAmount: Decimal.fromInt(10),
@ -410,16 +417,16 @@ void main() {
group("getAvailableFixedRateMarkets", () { group("getAvailableFixedRateMarkets", () {
test("getAvailableFixedRateMarkets succeeds", () async { test("getAvailableFixedRateMarkets succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(fixedRateMarketsJSON), 200)); Response(jsonEncode(fixedRateMarketsJSON), 200));
final result = await ChangeNow.getAvailableFixedRateMarkets( final result = await ChangeNow.instance.getAvailableFixedRateMarkets(
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
); );
@ -432,15 +439,15 @@ void main() {
"getAvailableFixedRateMarkets fails with ChangeNowExceptionType.serializeResponseError", "getAvailableFixedRateMarkets fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.getAvailableFixedRateMarkets( final result = await ChangeNow.instance.getAvailableFixedRateMarkets(
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
); );
@ -451,15 +458,15 @@ void main() {
test("getAvailableFixedRateMarkets fails for any other reason", () async { test("getAvailableFixedRateMarkets fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.getAvailableFixedRateMarkets( final result = await ChangeNow.instance.getAvailableFixedRateMarkets(
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
); );
@ -471,10 +478,10 @@ void main() {
group("createStandardExchangeTransaction", () { group("createStandardExchangeTransaction", () {
test("createStandardExchangeTransaction succeeds", () async { test("createStandardExchangeTransaction succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.post( 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'}, headers: {'Content-Type': 'application/json'},
body: body:
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}', '{"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 => )).thenAnswer((realInvocation) async =>
Response(jsonEncode(createStandardTransactionResponse), 200)); Response(jsonEncode(createStandardTransactionResponse), 200));
final result = await ChangeNow.createStandardExchangeTransaction( final result = await ChangeNow.instance.createStandardExchangeTransaction(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -501,17 +508,17 @@ void main() {
"createStandardExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError", "createStandardExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.post( 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'}, headers: {'Content-Type': 'application/json'},
body: body:
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}', '{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
encoding: null, encoding: null,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.createStandardExchangeTransaction( final result = await ChangeNow.instance.createStandardExchangeTransaction(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -529,17 +536,17 @@ void main() {
test("createStandardExchangeTransaction fails for any other reason", test("createStandardExchangeTransaction fails for any other reason",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.post( 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'}, headers: {'Content-Type': 'application/json'},
body: body:
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}', '{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
encoding: null, encoding: null,
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.createStandardExchangeTransaction( final result = await ChangeNow.instance.createStandardExchangeTransaction(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -557,11 +564,11 @@ void main() {
group("createFixedRateExchangeTransaction", () { group("createFixedRateExchangeTransaction", () {
test("createFixedRateExchangeTransaction succeeds", () async { test("createFixedRateExchangeTransaction succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.post( when(client.post(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
body: body:
'{"from":"btc","to":"eth","address":"0x57f31ad4b64095347F87eDB1675566DAfF5EC886","amount":"0.3","flow":"fixed-rate","extraId":"","userId":"","contactEmail":"","refundAddress":"","refundExtraId":"","rateId":""}', '{"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}', '{"payinAddress": "33eFX2jfeWbXMSmRe9ewUUTrmSVSxZi5cj", "payoutAddress": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886","payoutExtraId": "", "fromCurrency": "btc", "toCurrency": "eth", "refundAddress": "","refundExtraId": "","validUntil": "2019-09-09T14:01:04.921Z","id": "a5c73e2603f40d","amount": 62.9737711}',
200)); 200));
final result = await ChangeNow.createFixedRateExchangeTransaction( final result =
await ChangeNow.instance.createFixedRateExchangeTransaction(
fromTicker: "btc", fromTicker: "btc",
toTicker: "eth", toTicker: "eth",
receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886",
@ -589,11 +597,11 @@ void main() {
"createFixedRateExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError", "createFixedRateExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.post( when(client.post(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
body: body:
'{"from":"btc","to":"eth","address":"0x57f31ad4b64095347F87eDB1675566DAfF5EC886","amount":"0.3","flow":"fixed-rate","extraId":"","userId":"","contactEmail":"","refundAddress":"","refundExtraId":"","rateId":""}', '{"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 => )).thenAnswer((realInvocation) async =>
Response('{"id": "a5c73e2603f40d","amount": 62.9737711}', 200)); Response('{"id": "a5c73e2603f40d","amount": 62.9737711}', 200));
final result = await ChangeNow.createFixedRateExchangeTransaction( final result =
await ChangeNow.instance.createFixedRateExchangeTransaction(
fromTicker: "btc", fromTicker: "btc",
toTicker: "eth", toTicker: "eth",
receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886",
@ -619,18 +628,19 @@ void main() {
test("createFixedRateExchangeTransaction fails for any other reason", test("createFixedRateExchangeTransaction fails for any other reason",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.post( when(client.post(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
body: body:
'{"from": "btc","to": "eth","address": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", "amount": "1.12345","extraId": "", "userId": "","contactEmail": "","refundAddress": "", "refundExtraId": "", "rateId": "" }', '{"from": "btc","to": "eth","address": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", "amount": "1.12345","extraId": "", "userId": "","contactEmail": "","refundAddress": "", "refundExtraId": "", "rateId": "" }',
encoding: null, encoding: null,
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.createFixedRateExchangeTransaction( final result =
await ChangeNow.instance.createFixedRateExchangeTransaction(
fromTicker: "xmr", fromTicker: "xmr",
toTicker: "btc", toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5", receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -649,17 +659,17 @@ void main() {
group("getTransactionStatus", () { group("getTransactionStatus", () {
test("getTransactionStatus succeeds", () async { test("getTransactionStatus succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.changenow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), "https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response( )).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}', '{"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)); 200));
final result = await ChangeNow.getTransactionStatus( final result = await ChangeNow.instance.getTransactionStatus(
id: "47F87eDB1675566DAfF5EC886", id: "47F87eDB1675566DAfF5EC886",
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
); );
@ -673,15 +683,15 @@ void main() {
"getTransactionStatus fails with ChangeNowExceptionType.serializeResponseError", "getTransactionStatus fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.changenow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), "https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.getTransactionStatus( final result = await ChangeNow.instance.getTransactionStatus(
id: "47F87eDB1675566DAfF5EC886", id: "47F87eDB1675566DAfF5EC886",
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
); );
@ -693,15 +703,15 @@ void main() {
test("getTransactionStatus fails for any other reason", () async { test("getTransactionStatus fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( Uri.parse(
"https://api.changenow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"), "https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.getTransactionStatus( final result = await ChangeNow.instance.getTransactionStatus(
id: "47F87eDB1675566DAfF5EC886", id: "47F87eDB1675566DAfF5EC886",
apiKey: "testAPIKEY", apiKey: "testAPIKEY",
); );
@ -714,16 +724,16 @@ void main() {
group("getAvailableFloatingRatePairs", () { group("getAvailableFloatingRatePairs", () {
test("getAvailableFloatingRatePairs succeeds", () async { test("getAvailableFloatingRatePairs succeeds", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => )).thenAnswer((realInvocation) async =>
Response('["btc_xmr","btc_firo","btc_doge","eth_ltc"]', 200)); 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.exception, null);
expect(result.value == null, false); expect(result.value == null, false);
@ -734,15 +744,15 @@ void main() {
"getAvailableFloatingRatePairs fails with ChangeNowExceptionType.serializeResponseError", "getAvailableFloatingRatePairs fails with ChangeNowExceptionType.serializeResponseError",
() async { () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200)); )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
final result = await ChangeNow.getAvailableFloatingRatePairs(); final result = await ChangeNow.instance.getAvailableFloatingRatePairs();
expect(result.exception!.type, expect(result.exception!.type,
ChangeNowExceptionType.serializeResponseError); ChangeNowExceptionType.serializeResponseError);
@ -751,15 +761,15 @@ void main() {
test("getAvailableFloatingRatePairs fails for any other reason", () async { test("getAvailableFloatingRatePairs fails for any other reason", () async {
final client = MockClient(); final client = MockClient();
ChangeNow.client = client; ChangeNow.instance.client = client;
when(client.get( when(client.get(
Uri.parse( 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'}, headers: {'Content-Type': 'application/json'},
)).thenAnswer((realInvocation) async => Response('', 400)); )).thenAnswer((realInvocation) async => Response('', 400));
final result = await ChangeNow.getAvailableFloatingRatePairs(); final result = await ChangeNow.instance.getAvailableFloatingRatePairs();
expect(result.exception!.type, ChangeNowExceptionType.generic); expect(result.exception!.type, ChangeNowExceptionType.generic);
expect(result.value == null, true); expect(result.value == null, true);

View file

@ -1,5 +1,5 @@
// Mocks generated by Mockito 5.2.0 from annotations // 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. // Do not manually edit this file.
import 'dart:async' as _i5; import 'dart:async' as _i5;