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