CW-674: Improve Exchange Flow With Timeout For Rates (#1536)

* fix: Improve exchange flow by adding a timeout to the call to fetch rate from providers * fix: Adjust time limit for fetching rate to 7 seconds and add timelimit to fetching limits * Improve loadlimits function [skip ci] --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Adegoke David committed Jul 21, 2024 at 00:26 UTC 341e06196da34df29891fb3ec28b47ca94fee3a5
2 files changed +52 -22
lib/exchange/provider/sideshift_exchange_provider.dart
+15 -1
@@ -1,4 +1,5 @@
1 import 'dart:convert';
2 +import 'dart:developer';
3
4 import 'package:cake_wallet/.secrets.g.dart' as secrets;
5 import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
@@ -29,6 +30,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
30 CryptoCurrency.bttc,
31 CryptoCurrency.usdt,
32 CryptoCurrency.eos,
33 + CryptoCurrency.xmr,
34 ];
35
36 static const affiliateId = secrets.sideShiftAffiliateId;
@@ -137,8 +139,20 @@ class SideShiftExchangeProvider extends ExchangeProvider {
139 final response = await get(uri);
140 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
141
142 + if (response.statusCode == 500) {
143 + final responseJSON = json.decode(response.body) as Map<String, dynamic>;
144 + final error = responseJSON['error']['message'] as String;
145 +
146 + throw Exception('SideShift Internal Server Error: $error');
147 + }
148 +
149 + if (response.statusCode != 200) {
150 + throw Exception('Unexpected http status: ${response.statusCode}');
151 + }
152 +
153 return double.parse(responseJSON['rate'] as String);
141 - } catch (_) {
154 + } catch (e) {
155 + log('Error fetching rate in SideShift Provider: ${e.toString()}');
156 return 0.00;
157 }
158 }
lib/view_model/exchange/exchange_view_model.dart
+37 -21
@@ -405,13 +405,22 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
405 .where((element) => !isFixedRateMode || element.supportsFixedRate)
406 .toList();
407
408 - final result = await Future.wait<double>(_providers.map((element) => element.fetchRate(
409 - from: depositCurrency,
410 - to: receiveCurrency,
411 - amount: amount,
412 - isFixedRateMode: isFixedRateMode,
413 - isReceiveAmount: isFixedRateMode)));
414 -
408 + final result = await Future.wait<double>(
409 + _providers.map(
410 + (element) => element
411 + .fetchRate(
412 + from: depositCurrency,
413 + to: receiveCurrency,
414 + amount: amount,
415 + isFixedRateMode: isFixedRateMode,
416 + isReceiveAmount: isFixedRateMode,
417 + )
418 + .timeout(
419 + Duration(seconds: 7),
420 + onTimeout: () => 0.0,
421 + ),
422 + ),
423 + );
424 _sortedAvailableProviders.clear();
425
426 for (int i = 0; i < result.length; i++) {
@@ -441,22 +450,29 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
450 double? highestMax = 0.0;
451
452 try {
444 - for (var provider in selectedProviders) {
445 - /// if this provider is not valid for the current pair, skip it
446 - if (!providersForCurrentPair().contains(provider)) continue;
447 -
448 - try {
449 - final tempLimits =
450 - await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode);
451 -
452 - if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) lowestMin = tempLimits.min;
453 + final result = await Future.wait(selectedProviders
454 + .where((element) => providersForCurrentPair().contains(provider))
455 + .map((provider) => provider
456 + .fetchLimits(
457 + from: from,
458 + to: to,
459 + isFixedRateMode: isFixedRateMode,
460 + )
461 + .onError((error, stackTrace) => Limits(max: 0.0, min: double.maxFinite))
462 + .timeout(
463 + Duration(seconds: 7),
464 + onTimeout: () => Limits(max: 0.0, min: double.maxFinite),
465 + )));
466 +
467 + result.forEach((tempLimits) {
468 + if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin!) {
469 + lowestMin = tempLimits.min;
470 + }
471
454 - if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax)
455 - highestMax = tempLimits.max;
456 - } catch (e) {
457 - continue;
472 + if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax!) {
473 + highestMax = tempLimits.max;
474 }
459 - }
475 + });
476 } on ConcurrentModificationError {
477 /// if user changed the selected providers while fetching limits
478 /// then delay the fetching limits a bit and try again