Fixes for exchanges. CAKE-159.

M committed Nov 10, 2020 at 16:58 UTC 93049b4aa15c1c86a3d54d133d1840e124d5a7aa
7 files changed +113 -76
ios/Runner.xcodeproj/project.pbxproj
+6 -6
@@ -354,7 +354,7 @@
354 buildSettings = {
355 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
356 CLANG_ENABLE_MODULES = YES;
357 - CURRENT_PROJECT_VERSION = 3;
357 + CURRENT_PROJECT_VERSION = 1;
358 DEVELOPMENT_TEAM = 32J6BB6VUS;
359 ENABLE_BITCODE = NO;
360 FRAMEWORK_SEARCH_PATHS = (
@@ -371,7 +371,7 @@
371 "$(inherited)",
372 "$(PROJECT_DIR)/Flutter",
373 );
374 - MARKETING_VERSION = 4.0.2;
374 + MARKETING_VERSION = 4.0.3;
375 PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
376 PRODUCT_NAME = "$(TARGET_NAME)";
377 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -494,7 +494,7 @@
494 buildSettings = {
495 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
496 CLANG_ENABLE_MODULES = YES;
497 - CURRENT_PROJECT_VERSION = 3;
497 + CURRENT_PROJECT_VERSION = 1;
498 DEVELOPMENT_TEAM = 32J6BB6VUS;
499 ENABLE_BITCODE = NO;
500 FRAMEWORK_SEARCH_PATHS = (
@@ -511,7 +511,7 @@
511 "$(inherited)",
512 "$(PROJECT_DIR)/Flutter",
513 );
514 - MARKETING_VERSION = 4.0.2;
514 + MARKETING_VERSION = 4.0.3;
515 PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
516 PRODUCT_NAME = "$(TARGET_NAME)";
517 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -528,7 +528,7 @@
528 buildSettings = {
529 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
530 CLANG_ENABLE_MODULES = YES;
531 - CURRENT_PROJECT_VERSION = 3;
531 + CURRENT_PROJECT_VERSION = 1;
532 DEVELOPMENT_TEAM = 32J6BB6VUS;
533 ENABLE_BITCODE = NO;
534 FRAMEWORK_SEARCH_PATHS = (
@@ -545,7 +545,7 @@
545 "$(inherited)",
546 "$(PROJECT_DIR)/Flutter",
547 );
548 - MARKETING_VERSION = 4.0.2;
548 + MARKETING_VERSION = 4.0.3;
549 PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
550 PRODUCT_NAME = "$(TARGET_NAME)";
551 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
lib/exchange/changenow/changenow_exchange_provider.dart
+13 -15
@@ -18,19 +18,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
18 ChangeNowExchangeProvider()
19 : super(
20 pairList: CryptoCurrency.all
21 - .map((i) {
22 - return CryptoCurrency.all.map((k) {
23 - if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) {
24 - return ExchangePair(from: i, to: k, reverse: false);
25 - }
26 -
27 - if (i == CryptoCurrency.xmr && k == CryptoCurrency.btc) {
28 - return null;
29 - }
30 -
31 - return ExchangePair(from: i, to: k, reverse: true);
32 - }).where((c) => c != null);
33 - })
21 + .map((i) => CryptoCurrency.all
22 + .map((k) => ExchangePair(from: i, to: k, reverse: true))
23 + .where((c) => c != null))
24 .expand((i) => i)
25 .toList());
26
@@ -43,10 +33,16 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
33 @override
34 String get title => 'ChangeNOW';
35
36 + @override
37 + bool get isAvailable => true;
38 +
39 @override
40 ExchangeProviderDescription get description =>
41 ExchangeProviderDescription.changeNow;
42
43 + @override
44 + Future<bool> checkIsAvailable() async => true;
45 +
46 @override
47 Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
48 final symbol = from.toString() + '_' + to.toString();
@@ -146,8 +142,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
142
143 @override
144 Future<double> calculateAmount(
149 - {CryptoCurrency from, CryptoCurrency to, double amount,
150 - bool isReceiveAmount}) async {
145 + {CryptoCurrency from,
146 + CryptoCurrency to,
147 + double amount,
148 + bool isReceiveAmount}) async {
149 final url = apiUri +
150 _exchangeAmountUriSufix +
151 amount.toString() +
lib/exchange/exchange_provider.dart
+2
@@ -12,6 +12,7 @@ abstract class ExchangeProvider {
12 String get title;
13 List<ExchangePair> pairList;
14 ExchangeProviderDescription description;
15 + bool get isAvailable;
16
17 @override
18 String toString() => title;
@@ -21,4 +22,5 @@ abstract class ExchangeProvider {
22 Future<Trade> findTradeById({@required String id});
23 Future<double> calculateAmount(
24 {CryptoCurrency from, CryptoCurrency to, double amount, bool isReceiveAmount});
25 + Future<bool> checkIsAvailable();
26 }
lib/exchange/morphtoken/morphtoken_exchange_provider.dart
+6
@@ -60,10 +60,16 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
60 @override
61 String get title => 'MorphToken';
62
63 + @override
64 + bool get isAvailable => true;
65 +
66 @override
67 ExchangeProviderDescription get description =>
68 ExchangeProviderDescription.morphToken;
69
70 + @override
71 + Future<bool> checkIsAvailable() async => true;
72 +
73 @override
74 Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
75 final url = apiUri + _limitsURISuffix;
lib/exchange/xmrto/xmrto_exchange_provider.dart
+33 -27
@@ -15,44 +15,47 @@ import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
15
16 class XMRTOExchangeProvider extends ExchangeProvider {
17 XMRTOExchangeProvider()
18 - : super(pairList: [
18 + : _isAvailable = false,
19 + super(pairList: [
20 ExchangePair(
21 from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false)
22 ]);
23
24 static const userAgent = 'CakeWallet/XMR iOS';
25 static const originalApiUri = 'https://xmr.to/api/v3/xmr2btc';
25 - static const proxyApiUri = 'https://xmrproxy.net/api/v3/xmr2btc';
26 - static const _orderParameterUriSufix = '/order_parameter_query';
27 - static const _orderStatusUriSufix = '/order_status_query/';
28 - static const _orderCreateUriSufix = '/order_create/';
29 - static String _apiUri = '';
30 -
31 - static Future<String> getApiUri() async {
32 - if (_apiUri != null && _apiUri.isNotEmpty) {
33 - return _apiUri;
34 - }
26 + static const _orderParameterUriSuffix = '/order_parameter_query';
27 + static const _orderStatusUriSuffix = '/order_status_query/';
28 + static const _orderCreateUriSuffix = '/order_create/';
29
36 - const url = originalApiUri + _orderParameterUriSufix;
30 + static Future<bool> _checkIsAvailable() async {
31 + const url = originalApiUri + _orderParameterUriSuffix;
32 final response =
33 await get(url, headers: {'Content-Type': 'application/json'});
39 - _apiUri = response.statusCode == 403 ? proxyApiUri : originalApiUri;
40 -
41 - return _apiUri;
34 + return !(response.statusCode == 403);
35 }
36
37 @override
38 String get title => 'XMR.TO';
39
40 + @override
41 + bool get isAvailable => _isAvailable;
42 +
43 @override
44 ExchangeProviderDescription get description =>
45 ExchangeProviderDescription.xmrto;
46
47 double _rate = 0;
48 + bool _isAvailable;
49 +
50 + @override
51 + Future<bool> checkIsAvailable() async {
52 + _isAvailable = await _checkIsAvailable();
53 + return isAvailable;
54 + }
55
56 @override
57 Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
55 - final url = await getApiUri() + _orderParameterUriSufix;
58 + final url = originalApiUri + _orderParameterUriSuffix;
59 final response = await get(url);
60 final correction = 0.001;
61
@@ -67,9 +70,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
70
71 if (price > 0) {
72 try {
70 - min = min/price + correction;
73 + min = min / price + correction;
74 min = _limitsFormat(min);
72 - max = max/price - correction;
75 + max = max / price - correction;
76 max = _limitsFormat(max);
77 } catch (e) {
78 min = 0;
@@ -86,11 +89,10 @@ class XMRTOExchangeProvider extends ExchangeProvider {
89 @override
90 Future<Trade> createTrade({TradeRequest request}) async {
91 final _request = request as XMRTOTradeRequest;
89 - final url = await getApiUri() + _orderCreateUriSufix;
92 + final url = originalApiUri + _orderCreateUriSuffix;
93 final body = {
91 - 'amount': _request.isBTCRequest
92 - ? _request.receiveAmount
93 - : _request.amount,
94 + 'amount':
95 + _request.isBTCRequest ? _request.receiveAmount : _request.amount,
96 'amount_currency': _request.isBTCRequest
97 ? _request.to.toString()
98 : _request.from.toString(),
@@ -129,7 +131,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
131 'Content-Type': 'application/json',
132 'User-Agent': userAgent
133 };
132 - final url = await getApiUri() + _orderStatusUriSufix;
134 + final url = originalApiUri + _orderStatusUriSuffix;
135 final body = {'uuid': id};
136 final response = await post(url, headers: headers, body: json.encode(body));
137
@@ -170,8 +172,10 @@ class XMRTOExchangeProvider extends ExchangeProvider {
172
173 @override
174 Future<double> calculateAmount(
173 - {CryptoCurrency from, CryptoCurrency to, double amount,
174 - bool isReceiveAmount}) async {
175 + {CryptoCurrency from,
176 + CryptoCurrency to,
177 + double amount,
178 + bool isReceiveAmount}) async {
179 if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) {
180 return 0;
181 }
@@ -181,7 +185,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
185 }
186
187 final double result = isReceiveAmount
184 - ? _rate == 0 ? 0 : amount / _rate
188 + ? _rate == 0
189 + ? 0
190 + : amount / _rate
191 : _rate * amount;
192
193 return double.parse(result.toStringAsFixed(12));
@@ -189,7 +195,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
195
196 Future<double> _fetchRates() async {
197 try {
192 - final url = await getApiUri() + _orderParameterUriSufix;
198 + final url = originalApiUri + _orderParameterUriSuffix;
199 final response =
200 await get(url, headers: {'Content-Type': 'application/json'});
201 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
lib/src/screens/exchange/widgets/present_provider_picker.dart
+19 -9
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
2 import 'package:cake_wallet/utils/show_pop_up.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
@@ -14,8 +15,8 @@ class PresentProviderPicker extends StatelessWidget {
15
16 @override
17 Widget build(BuildContext context) {
17 - final arrowBottom =
18 - Image.asset('assets/images/arrow_bottom_purple_icon.png',
18 + final arrowBottom = Image.asset(
19 + 'assets/images/arrow_bottom_purple_icon.png',
20 color: Colors.white,
21 height: 6);
22
@@ -50,8 +51,7 @@ class PresentProviderPicker extends StatelessWidget {
51 child: arrowBottom,
52 )
53 ],
53 - )
54 - );
54 + ));
55 }
56
57 void _presentProviderPicker(BuildContext context) {
@@ -64,7 +64,6 @@ class PresentProviderPicker extends StatelessWidget {
64 switch (provider.description) {
65 case ExchangeProviderDescription.xmrto:
66 images.add(Image.asset('assets/images/xmr_btc.png'));
67 - description = S.of(context).picker_description;
67 break;
68 case ExchangeProviderDescription.changeNow:
69 images.add(Image.asset('assets/images/change_now.png'));
@@ -76,14 +75,25 @@ class PresentProviderPicker extends StatelessWidget {
75 }
76
77 showPopUp<void>(
79 - builder: (_) => Picker(
78 + builder: (BuildContext popUpContext) => Picker(
79 items: items,
80 images: images,
81 selectedAtIndex: selectedItem,
82 title: S.of(context).change_exchange_provider,
83 description: description,
85 - onItemSelected: (ExchangeProvider provider) =>
86 - exchangeViewModel.changeProvider(provider: provider)),
84 + onItemSelected: (ExchangeProvider provider) {
85 + if (!provider.isAvailable) {
86 + showPopUp<void>(
87 + builder: (BuildContext popUpContext) => AlertWithOneAction(
88 + alertTitle: 'Error',
89 + alertContent: 'The exchange is blocked in your region.',
90 + buttonText: S.of(context).ok,
91 + buttonAction: () => Navigator.of(context).pop()),
92 + context: context);
93 + return;
94 + }
95 + exchangeViewModel.changeProvider(provider: provider);
96 + }),
97 context: context);
98 }
89 -}
\ No newline at end of file
99 +}
lib/view_model/exchange/exchange_view_model.dart
+34 -19
@@ -28,10 +28,7 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
28
29 abstract class ExchangeViewModelBase with Store {
30 ExchangeViewModelBase(
31 - this.wallet,
32 - this.trades,
33 - this._exchangeTemplateStore,
34 - this.tradesStore) {
31 + this.wallet, this.trades, this._exchangeTemplateStore, this.tradesStore) {
32 providerList = [
33 XMRTOExchangeProvider(),
34 ChangeNowExchangeProvider(),
@@ -43,12 +40,22 @@ abstract class ExchangeViewModelBase with Store {
40 isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
41 depositAmount = '';
42 receiveAmount = '';
46 - depositAddress = '';
43 receiveAddress = '';
44 + depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
45 limitsState = LimitsInitialState();
46 tradeState = ExchangeTradeStateInitial();
47 _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
48 provider = providersForCurrentPair().first;
49 + final initialProvider = provider;
50 + provider.checkIsAvailable().then((bool isAvailable) {
51 + if (!isAvailable && provider == initialProvider) {
52 + provider = providerList.firstWhere(
53 + (provider) => provider is ChangeNowExchangeProvider,
54 + orElse: () => providerList.last);
55 + _onPairChange();
56 + }
57 + });
58 +
59 isReceiveAmountEntered = false;
60 loadLimits();
61 }
@@ -146,7 +153,9 @@ abstract class ExchangeViewModelBase with Store {
153
154 provider
155 .calculateAmount(
149 - from: depositCurrency, to: receiveCurrency, amount: _amount,
156 + from: depositCurrency,
157 + to: receiveCurrency,
158 + amount: _amount,
159 isReceiveAmount: true)
160 .then((amount) => _cryptoNumberFormat
161 .format(amount)
@@ -168,7 +177,9 @@ abstract class ExchangeViewModelBase with Store {
177 final _amount = double.parse(amount.replaceAll(',', '.'));
178 provider
179 .calculateAmount(
171 - from: depositCurrency, to: receiveCurrency, amount: _amount,
180 + from: depositCurrency,
181 + to: receiveCurrency,
182 + amount: _amount,
183 isReceiveAmount: false)
184 .then((amount) => _cryptoNumberFormat
185 .format(amount)
@@ -277,19 +288,23 @@ abstract class ExchangeViewModelBase with Store {
288
289 void updateTemplate() => _exchangeTemplateStore.update();
290
280 - void addTemplate({String amount, String depositCurrency, String receiveCurrency,
281 - String provider, String depositAddress, String receiveAddress}) =>
282 - _exchangeTemplateStore.addTemplate(
283 - amount: amount,
284 - depositCurrency: depositCurrency,
285 - receiveCurrency: receiveCurrency,
286 - provider: provider,
287 - depositAddress: depositAddress,
288 - receiveAddress: receiveAddress
289 - );
291 + void addTemplate(
292 + {String amount,
293 + String depositCurrency,
294 + String receiveCurrency,
295 + String provider,
296 + String depositAddress,
297 + String receiveAddress}) =>
298 + _exchangeTemplateStore.addTemplate(
299 + amount: amount,
300 + depositCurrency: depositCurrency,
301 + receiveCurrency: receiveCurrency,
302 + provider: provider,
303 + depositAddress: depositAddress,
304 + receiveAddress: receiveAddress);
305
306 void removeTemplate({ExchangeTemplate template}) =>
292 - _exchangeTemplateStore.remove(template: template);
307 + _exchangeTemplateStore.remove(template: template);
308
309 List<ExchangeProvider> providersForCurrentPair() {
310 return _providersForPair(from: depositCurrency, to: receiveCurrency);
@@ -322,9 +337,9 @@ abstract class ExchangeViewModelBase with Store {
337 }
338 }
339
340 + depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
341 depositAmount = '';
342 receiveAmount = '';
327 -
343 loadLimits();
344 }
345