From 93049b4aa15c1c86a3d54d133d1840e124d5a7aa Mon Sep 17 00:00:00 2001 From: M Date: Tue, 10 Nov 2020 16:58:40 +0200 Subject: [PATCH] Fixes for exchanges. CAKE-159. --- ios/Runner.xcodeproj/project.pbxproj | 12 ++-- .../changenow_exchange_provider.dart | 28 ++++----- lib/exchange/exchange_provider.dart | 2 + .../morphtoken_exchange_provider.dart | 6 ++ .../xmrto/xmrto_exchange_provider.dart | 60 ++++++++++--------- .../widgets/present_provider_picker.dart | 28 ++++++--- .../exchange/exchange_view_model.dart | 53 ++++++++++------ 7 files changed, 113 insertions(+), 76 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b9a43c0aa..ca83bc8ca 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -371,7 +371,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.2; + MARKETING_VERSION = 4.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -494,7 +494,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -511,7 +511,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.2; + MARKETING_VERSION = 4.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -528,7 +528,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -545,7 +545,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.2; + MARKETING_VERSION = 4.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/exchange/changenow/changenow_exchange_provider.dart b/lib/exchange/changenow/changenow_exchange_provider.dart index 2a9b38c64..8f984359f 100644 --- a/lib/exchange/changenow/changenow_exchange_provider.dart +++ b/lib/exchange/changenow/changenow_exchange_provider.dart @@ -18,19 +18,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider { ChangeNowExchangeProvider() : super( pairList: CryptoCurrency.all - .map((i) { - return CryptoCurrency.all.map((k) { - if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) { - return ExchangePair(from: i, to: k, reverse: false); - } - - if (i == CryptoCurrency.xmr && k == CryptoCurrency.btc) { - return null; - } - - return ExchangePair(from: i, to: k, reverse: true); - }).where((c) => c != null); - }) + .map((i) => CryptoCurrency.all + .map((k) => ExchangePair(from: i, to: k, reverse: true)) + .where((c) => c != null)) .expand((i) => i) .toList()); @@ -43,10 +33,16 @@ class ChangeNowExchangeProvider extends ExchangeProvider { @override String get title => 'ChangeNOW'; + @override + bool get isAvailable => true; + @override ExchangeProviderDescription get description => ExchangeProviderDescription.changeNow; + @override + Future checkIsAvailable() async => true; + @override Future fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { final symbol = from.toString() + '_' + to.toString(); @@ -146,8 +142,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider { @override Future calculateAmount( - {CryptoCurrency from, CryptoCurrency to, double amount, - bool isReceiveAmount}) async { + {CryptoCurrency from, + CryptoCurrency to, + double amount, + bool isReceiveAmount}) async { final url = apiUri + _exchangeAmountUriSufix + amount.toString() + diff --git a/lib/exchange/exchange_provider.dart b/lib/exchange/exchange_provider.dart index 65f7ad8ba..276177dac 100644 --- a/lib/exchange/exchange_provider.dart +++ b/lib/exchange/exchange_provider.dart @@ -12,6 +12,7 @@ abstract class ExchangeProvider { String get title; List pairList; ExchangeProviderDescription description; + bool get isAvailable; @override String toString() => title; @@ -21,4 +22,5 @@ abstract class ExchangeProvider { Future findTradeById({@required String id}); Future calculateAmount( {CryptoCurrency from, CryptoCurrency to, double amount, bool isReceiveAmount}); + Future checkIsAvailable(); } diff --git a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart index 195f72ae5..b90eb84e9 100644 --- a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart +++ b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart @@ -60,10 +60,16 @@ class MorphTokenExchangeProvider extends ExchangeProvider { @override String get title => 'MorphToken'; + @override + bool get isAvailable => true; + @override ExchangeProviderDescription get description => ExchangeProviderDescription.morphToken; + @override + Future checkIsAvailable() async => true; + @override Future fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { final url = apiUri + _limitsURISuffix; diff --git a/lib/exchange/xmrto/xmrto_exchange_provider.dart b/lib/exchange/xmrto/xmrto_exchange_provider.dart index c5fe3f1f1..c57529823 100644 --- a/lib/exchange/xmrto/xmrto_exchange_provider.dart +++ b/lib/exchange/xmrto/xmrto_exchange_provider.dart @@ -15,44 +15,47 @@ import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; class XMRTOExchangeProvider extends ExchangeProvider { XMRTOExchangeProvider() - : super(pairList: [ + : _isAvailable = false, + super(pairList: [ ExchangePair( from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false) ]); static const userAgent = 'CakeWallet/XMR iOS'; static const originalApiUri = 'https://xmr.to/api/v3/xmr2btc'; - static const proxyApiUri = 'https://xmrproxy.net/api/v3/xmr2btc'; - static const _orderParameterUriSufix = '/order_parameter_query'; - static const _orderStatusUriSufix = '/order_status_query/'; - static const _orderCreateUriSufix = '/order_create/'; - static String _apiUri = ''; + static const _orderParameterUriSuffix = '/order_parameter_query'; + static const _orderStatusUriSuffix = '/order_status_query/'; + static const _orderCreateUriSuffix = '/order_create/'; - static Future getApiUri() async { - if (_apiUri != null && _apiUri.isNotEmpty) { - return _apiUri; - } - - const url = originalApiUri + _orderParameterUriSufix; + static Future _checkIsAvailable() async { + const url = originalApiUri + _orderParameterUriSuffix; final response = await get(url, headers: {'Content-Type': 'application/json'}); - _apiUri = response.statusCode == 403 ? proxyApiUri : originalApiUri; - - return _apiUri; + return !(response.statusCode == 403); } @override String get title => 'XMR.TO'; + @override + bool get isAvailable => _isAvailable; + @override ExchangeProviderDescription get description => ExchangeProviderDescription.xmrto; double _rate = 0; + bool _isAvailable; + + @override + Future checkIsAvailable() async { + _isAvailable = await _checkIsAvailable(); + return isAvailable; + } @override Future fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { - final url = await getApiUri() + _orderParameterUriSufix; + final url = originalApiUri + _orderParameterUriSuffix; final response = await get(url); final correction = 0.001; @@ -67,9 +70,9 @@ class XMRTOExchangeProvider extends ExchangeProvider { if (price > 0) { try { - min = min/price + correction; + min = min / price + correction; min = _limitsFormat(min); - max = max/price - correction; + max = max / price - correction; max = _limitsFormat(max); } catch (e) { min = 0; @@ -86,11 +89,10 @@ class XMRTOExchangeProvider extends ExchangeProvider { @override Future createTrade({TradeRequest request}) async { final _request = request as XMRTOTradeRequest; - final url = await getApiUri() + _orderCreateUriSufix; + final url = originalApiUri + _orderCreateUriSuffix; final body = { - 'amount': _request.isBTCRequest - ? _request.receiveAmount - : _request.amount, + 'amount': + _request.isBTCRequest ? _request.receiveAmount : _request.amount, 'amount_currency': _request.isBTCRequest ? _request.to.toString() : _request.from.toString(), @@ -129,7 +131,7 @@ class XMRTOExchangeProvider extends ExchangeProvider { 'Content-Type': 'application/json', 'User-Agent': userAgent }; - final url = await getApiUri() + _orderStatusUriSufix; + final url = originalApiUri + _orderStatusUriSuffix; final body = {'uuid': id}; final response = await post(url, headers: headers, body: json.encode(body)); @@ -170,8 +172,10 @@ class XMRTOExchangeProvider extends ExchangeProvider { @override Future calculateAmount( - {CryptoCurrency from, CryptoCurrency to, double amount, - bool isReceiveAmount}) async { + {CryptoCurrency from, + CryptoCurrency to, + double amount, + bool isReceiveAmount}) async { if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) { return 0; } @@ -181,7 +185,9 @@ class XMRTOExchangeProvider extends ExchangeProvider { } final double result = isReceiveAmount - ? _rate == 0 ? 0 : amount / _rate + ? _rate == 0 + ? 0 + : amount / _rate : _rate * amount; return double.parse(result.toStringAsFixed(12)); @@ -189,7 +195,7 @@ class XMRTOExchangeProvider extends ExchangeProvider { Future _fetchRates() async { try { - final url = await getApiUri() + _orderParameterUriSufix; + final url = originalApiUri + _orderParameterUriSuffix; final response = await get(url, headers: {'Content-Type': 'application/json'}); final responseJSON = json.decode(response.body) as Map; diff --git a/lib/src/screens/exchange/widgets/present_provider_picker.dart b/lib/src/screens/exchange/widgets/present_provider_picker.dart index 664dd90ca..4656ba74e 100644 --- a/lib/src/screens/exchange/widgets/present_provider_picker.dart +++ b/lib/src/screens/exchange/widgets/present_provider_picker.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; @@ -14,8 +15,8 @@ class PresentProviderPicker extends StatelessWidget { @override Widget build(BuildContext context) { - final arrowBottom = - Image.asset('assets/images/arrow_bottom_purple_icon.png', + final arrowBottom = Image.asset( + 'assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6); @@ -50,8 +51,7 @@ class PresentProviderPicker extends StatelessWidget { child: arrowBottom, ) ], - ) - ); + )); } void _presentProviderPicker(BuildContext context) { @@ -64,7 +64,6 @@ class PresentProviderPicker extends StatelessWidget { switch (provider.description) { case ExchangeProviderDescription.xmrto: images.add(Image.asset('assets/images/xmr_btc.png')); - description = S.of(context).picker_description; break; case ExchangeProviderDescription.changeNow: images.add(Image.asset('assets/images/change_now.png')); @@ -76,14 +75,25 @@ class PresentProviderPicker extends StatelessWidget { } showPopUp( - builder: (_) => Picker( + builder: (BuildContext popUpContext) => Picker( items: items, images: images, selectedAtIndex: selectedItem, title: S.of(context).change_exchange_provider, description: description, - onItemSelected: (ExchangeProvider provider) => - exchangeViewModel.changeProvider(provider: provider)), + onItemSelected: (ExchangeProvider provider) { + if (!provider.isAvailable) { + showPopUp( + builder: (BuildContext popUpContext) => AlertWithOneAction( + alertTitle: 'Error', + alertContent: 'The exchange is blocked in your region.', + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()), + context: context); + return; + } + exchangeViewModel.changeProvider(provider: provider); + }), context: context); } -} \ No newline at end of file +} diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 14106272c..1b14980dd 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -28,10 +28,7 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { ExchangeViewModelBase( - this.wallet, - this.trades, - this._exchangeTemplateStore, - this.tradesStore) { + this.wallet, this.trades, this._exchangeTemplateStore, this.tradesStore) { providerList = [ XMRTOExchangeProvider(), ChangeNowExchangeProvider(), @@ -43,12 +40,22 @@ abstract class ExchangeViewModelBase with Store { isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); depositAmount = ''; receiveAmount = ''; - depositAddress = ''; receiveAddress = ''; + depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; limitsState = LimitsInitialState(); tradeState = ExchangeTradeStateInitial(); _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; provider = providersForCurrentPair().first; + final initialProvider = provider; + provider.checkIsAvailable().then((bool isAvailable) { + if (!isAvailable && provider == initialProvider) { + provider = providerList.firstWhere( + (provider) => provider is ChangeNowExchangeProvider, + orElse: () => providerList.last); + _onPairChange(); + } + }); + isReceiveAmountEntered = false; loadLimits(); } @@ -146,7 +153,9 @@ abstract class ExchangeViewModelBase with Store { provider .calculateAmount( - from: depositCurrency, to: receiveCurrency, amount: _amount, + from: depositCurrency, + to: receiveCurrency, + amount: _amount, isReceiveAmount: true) .then((amount) => _cryptoNumberFormat .format(amount) @@ -168,7 +177,9 @@ abstract class ExchangeViewModelBase with Store { final _amount = double.parse(amount.replaceAll(',', '.')); provider .calculateAmount( - from: depositCurrency, to: receiveCurrency, amount: _amount, + from: depositCurrency, + to: receiveCurrency, + amount: _amount, isReceiveAmount: false) .then((amount) => _cryptoNumberFormat .format(amount) @@ -277,19 +288,23 @@ abstract class ExchangeViewModelBase with Store { void updateTemplate() => _exchangeTemplateStore.update(); - void addTemplate({String amount, String depositCurrency, String receiveCurrency, - String provider, String depositAddress, String receiveAddress}) => - _exchangeTemplateStore.addTemplate( - amount: amount, - depositCurrency: depositCurrency, - receiveCurrency: receiveCurrency, - provider: provider, - depositAddress: depositAddress, - receiveAddress: receiveAddress - ); + void addTemplate( + {String amount, + String depositCurrency, + String receiveCurrency, + String provider, + String depositAddress, + String receiveAddress}) => + _exchangeTemplateStore.addTemplate( + amount: amount, + depositCurrency: depositCurrency, + receiveCurrency: receiveCurrency, + provider: provider, + depositAddress: depositAddress, + receiveAddress: receiveAddress); void removeTemplate({ExchangeTemplate template}) => - _exchangeTemplateStore.remove(template: template); + _exchangeTemplateStore.remove(template: template); List providersForCurrentPair() { return _providersForPair(from: depositCurrency, to: receiveCurrency); @@ -322,9 +337,9 @@ abstract class ExchangeViewModelBase with Store { } } + depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; depositAmount = ''; receiveAmount = ''; - loadLimits(); }