Fixes for exchanges. CAKE-159.

This commit is contained in:
M 2020-11-10 16:58:40 +02:00
parent d7c33b0ab5
commit 93049b4aa1
7 changed files with 113 additions and 76 deletions

View file

@ -354,7 +354,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -371,7 +371,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
MARKETING_VERSION = 4.0.2; MARKETING_VERSION = 4.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -494,7 +494,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -511,7 +511,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
MARKETING_VERSION = 4.0.2; MARKETING_VERSION = 4.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -528,7 +528,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -545,7 +545,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
MARKETING_VERSION = 4.0.2; MARKETING_VERSION = 4.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View file

@ -18,19 +18,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
ChangeNowExchangeProvider() ChangeNowExchangeProvider()
: super( : super(
pairList: CryptoCurrency.all pairList: CryptoCurrency.all
.map((i) { .map((i) => CryptoCurrency.all
return CryptoCurrency.all.map((k) { .map((k) => ExchangePair(from: i, to: k, reverse: true))
if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) { .where((c) => c != null))
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);
})
.expand((i) => i) .expand((i) => i)
.toList()); .toList());
@ -43,10 +33,16 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
@override @override
String get title => 'ChangeNOW'; String get title => 'ChangeNOW';
@override
bool get isAvailable => true;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.changeNow; ExchangeProviderDescription.changeNow;
@override
Future<bool> checkIsAvailable() async => true;
@override @override
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
final symbol = from.toString() + '_' + to.toString(); final symbol = from.toString() + '_' + to.toString();
@ -146,8 +142,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
@override @override
Future<double> calculateAmount( Future<double> calculateAmount(
{CryptoCurrency from, CryptoCurrency to, double amount, {CryptoCurrency from,
bool isReceiveAmount}) async { CryptoCurrency to,
double amount,
bool isReceiveAmount}) async {
final url = apiUri + final url = apiUri +
_exchangeAmountUriSufix + _exchangeAmountUriSufix +
amount.toString() + amount.toString() +

View file

@ -12,6 +12,7 @@ abstract class ExchangeProvider {
String get title; String get title;
List<ExchangePair> pairList; List<ExchangePair> pairList;
ExchangeProviderDescription description; ExchangeProviderDescription description;
bool get isAvailable;
@override @override
String toString() => title; String toString() => title;
@ -21,4 +22,5 @@ abstract class ExchangeProvider {
Future<Trade> findTradeById({@required String id}); Future<Trade> findTradeById({@required String id});
Future<double> calculateAmount( Future<double> calculateAmount(
{CryptoCurrency from, CryptoCurrency to, double amount, bool isReceiveAmount}); {CryptoCurrency from, CryptoCurrency to, double amount, bool isReceiveAmount});
Future<bool> checkIsAvailable();
} }

View file

@ -60,10 +60,16 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
@override @override
String get title => 'MorphToken'; String get title => 'MorphToken';
@override
bool get isAvailable => true;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.morphToken; ExchangeProviderDescription.morphToken;
@override
Future<bool> checkIsAvailable() async => true;
@override @override
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
final url = apiUri + _limitsURISuffix; final url = apiUri + _limitsURISuffix;

View file

@ -15,44 +15,47 @@ import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
class XMRTOExchangeProvider extends ExchangeProvider { class XMRTOExchangeProvider extends ExchangeProvider {
XMRTOExchangeProvider() XMRTOExchangeProvider()
: super(pairList: [ : _isAvailable = false,
super(pairList: [
ExchangePair( ExchangePair(
from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false) from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false)
]); ]);
static const userAgent = 'CakeWallet/XMR iOS'; static const userAgent = 'CakeWallet/XMR iOS';
static const originalApiUri = 'https://xmr.to/api/v3/xmr2btc'; static const originalApiUri = 'https://xmr.to/api/v3/xmr2btc';
static const proxyApiUri = 'https://xmrproxy.net/api/v3/xmr2btc'; static const _orderParameterUriSuffix = '/order_parameter_query';
static const _orderParameterUriSufix = '/order_parameter_query'; static const _orderStatusUriSuffix = '/order_status_query/';
static const _orderStatusUriSufix = '/order_status_query/'; static const _orderCreateUriSuffix = '/order_create/';
static const _orderCreateUriSufix = '/order_create/';
static String _apiUri = '';
static Future<String> getApiUri() async { static Future<bool> _checkIsAvailable() async {
if (_apiUri != null && _apiUri.isNotEmpty) { const url = originalApiUri + _orderParameterUriSuffix;
return _apiUri;
}
const url = originalApiUri + _orderParameterUriSufix;
final response = final response =
await get(url, headers: {'Content-Type': 'application/json'}); await get(url, headers: {'Content-Type': 'application/json'});
_apiUri = response.statusCode == 403 ? proxyApiUri : originalApiUri; return !(response.statusCode == 403);
return _apiUri;
} }
@override @override
String get title => 'XMR.TO'; String get title => 'XMR.TO';
@override
bool get isAvailable => _isAvailable;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.xmrto; ExchangeProviderDescription.xmrto;
double _rate = 0; double _rate = 0;
bool _isAvailable;
@override
Future<bool> checkIsAvailable() async {
_isAvailable = await _checkIsAvailable();
return isAvailable;
}
@override @override
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
final url = await getApiUri() + _orderParameterUriSufix; final url = originalApiUri + _orderParameterUriSuffix;
final response = await get(url); final response = await get(url);
final correction = 0.001; final correction = 0.001;
@ -67,9 +70,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
if (price > 0) { if (price > 0) {
try { try {
min = min/price + correction; min = min / price + correction;
min = _limitsFormat(min); min = _limitsFormat(min);
max = max/price - correction; max = max / price - correction;
max = _limitsFormat(max); max = _limitsFormat(max);
} catch (e) { } catch (e) {
min = 0; min = 0;
@ -86,11 +89,10 @@ class XMRTOExchangeProvider extends ExchangeProvider {
@override @override
Future<Trade> createTrade({TradeRequest request}) async { Future<Trade> createTrade({TradeRequest request}) async {
final _request = request as XMRTOTradeRequest; final _request = request as XMRTOTradeRequest;
final url = await getApiUri() + _orderCreateUriSufix; final url = originalApiUri + _orderCreateUriSuffix;
final body = { final body = {
'amount': _request.isBTCRequest 'amount':
? _request.receiveAmount _request.isBTCRequest ? _request.receiveAmount : _request.amount,
: _request.amount,
'amount_currency': _request.isBTCRequest 'amount_currency': _request.isBTCRequest
? _request.to.toString() ? _request.to.toString()
: _request.from.toString(), : _request.from.toString(),
@ -129,7 +131,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'User-Agent': userAgent 'User-Agent': userAgent
}; };
final url = await getApiUri() + _orderStatusUriSufix; final url = originalApiUri + _orderStatusUriSuffix;
final body = {'uuid': id}; final body = {'uuid': id};
final response = await post(url, headers: headers, body: json.encode(body)); final response = await post(url, headers: headers, body: json.encode(body));
@ -170,8 +172,10 @@ class XMRTOExchangeProvider extends ExchangeProvider {
@override @override
Future<double> calculateAmount( Future<double> calculateAmount(
{CryptoCurrency from, CryptoCurrency to, double amount, {CryptoCurrency from,
bool isReceiveAmount}) async { CryptoCurrency to,
double amount,
bool isReceiveAmount}) async {
if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) { if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) {
return 0; return 0;
} }
@ -181,7 +185,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
} }
final double result = isReceiveAmount final double result = isReceiveAmount
? _rate == 0 ? 0 : amount / _rate ? _rate == 0
? 0
: amount / _rate
: _rate * amount; : _rate * amount;
return double.parse(result.toStringAsFixed(12)); return double.parse(result.toStringAsFixed(12));
@ -189,7 +195,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
Future<double> _fetchRates() async { Future<double> _fetchRates() async {
try { try {
final url = await getApiUri() + _orderParameterUriSufix; final url = originalApiUri + _orderParameterUriSuffix;
final response = final response =
await get(url, headers: {'Content-Type': 'application/json'}); await get(url, headers: {'Content-Type': 'application/json'});
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;

View file

@ -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:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
@ -14,8 +15,8 @@ class PresentProviderPicker extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrowBottom = final arrowBottom = Image.asset(
Image.asset('assets/images/arrow_bottom_purple_icon.png', 'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white, color: Colors.white,
height: 6); height: 6);
@ -50,8 +51,7 @@ class PresentProviderPicker extends StatelessWidget {
child: arrowBottom, child: arrowBottom,
) )
], ],
) ));
);
} }
void _presentProviderPicker(BuildContext context) { void _presentProviderPicker(BuildContext context) {
@ -64,7 +64,6 @@ class PresentProviderPicker extends StatelessWidget {
switch (provider.description) { switch (provider.description) {
case ExchangeProviderDescription.xmrto: case ExchangeProviderDescription.xmrto:
images.add(Image.asset('assets/images/xmr_btc.png')); images.add(Image.asset('assets/images/xmr_btc.png'));
description = S.of(context).picker_description;
break; break;
case ExchangeProviderDescription.changeNow: case ExchangeProviderDescription.changeNow:
images.add(Image.asset('assets/images/change_now.png')); images.add(Image.asset('assets/images/change_now.png'));
@ -76,14 +75,25 @@ class PresentProviderPicker extends StatelessWidget {
} }
showPopUp<void>( showPopUp<void>(
builder: (_) => Picker( builder: (BuildContext popUpContext) => Picker(
items: items, items: items,
images: images, images: images,
selectedAtIndex: selectedItem, selectedAtIndex: selectedItem,
title: S.of(context).change_exchange_provider, title: S.of(context).change_exchange_provider,
description: description, description: description,
onItemSelected: (ExchangeProvider provider) => onItemSelected: (ExchangeProvider provider) {
exchangeViewModel.changeProvider(provider: provider)), if (!provider.isAvailable) {
showPopUp<void>(
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); context: context);
} }
} }

View file

@ -28,10 +28,7 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
abstract class ExchangeViewModelBase with Store { abstract class ExchangeViewModelBase with Store {
ExchangeViewModelBase( ExchangeViewModelBase(
this.wallet, this.wallet, this.trades, this._exchangeTemplateStore, this.tradesStore) {
this.trades,
this._exchangeTemplateStore,
this.tradesStore) {
providerList = [ providerList = [
XMRTOExchangeProvider(), XMRTOExchangeProvider(),
ChangeNowExchangeProvider(), ChangeNowExchangeProvider(),
@ -43,12 +40,22 @@ abstract class ExchangeViewModelBase with Store {
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
depositAmount = ''; depositAmount = '';
receiveAmount = ''; receiveAmount = '';
depositAddress = '';
receiveAddress = ''; receiveAddress = '';
depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
limitsState = LimitsInitialState(); limitsState = LimitsInitialState();
tradeState = ExchangeTradeStateInitial(); tradeState = ExchangeTradeStateInitial();
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
provider = providersForCurrentPair().first; 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; isReceiveAmountEntered = false;
loadLimits(); loadLimits();
} }
@ -146,7 +153,9 @@ abstract class ExchangeViewModelBase with Store {
provider provider
.calculateAmount( .calculateAmount(
from: depositCurrency, to: receiveCurrency, amount: _amount, from: depositCurrency,
to: receiveCurrency,
amount: _amount,
isReceiveAmount: true) isReceiveAmount: true)
.then((amount) => _cryptoNumberFormat .then((amount) => _cryptoNumberFormat
.format(amount) .format(amount)
@ -168,7 +177,9 @@ abstract class ExchangeViewModelBase with Store {
final _amount = double.parse(amount.replaceAll(',', '.')); final _amount = double.parse(amount.replaceAll(',', '.'));
provider provider
.calculateAmount( .calculateAmount(
from: depositCurrency, to: receiveCurrency, amount: _amount, from: depositCurrency,
to: receiveCurrency,
amount: _amount,
isReceiveAmount: false) isReceiveAmount: false)
.then((amount) => _cryptoNumberFormat .then((amount) => _cryptoNumberFormat
.format(amount) .format(amount)
@ -277,19 +288,23 @@ abstract class ExchangeViewModelBase with Store {
void updateTemplate() => _exchangeTemplateStore.update(); void updateTemplate() => _exchangeTemplateStore.update();
void addTemplate({String amount, String depositCurrency, String receiveCurrency, void addTemplate(
String provider, String depositAddress, String receiveAddress}) => {String amount,
_exchangeTemplateStore.addTemplate( String depositCurrency,
amount: amount, String receiveCurrency,
depositCurrency: depositCurrency, String provider,
receiveCurrency: receiveCurrency, String depositAddress,
provider: provider, String receiveAddress}) =>
depositAddress: depositAddress, _exchangeTemplateStore.addTemplate(
receiveAddress: receiveAddress amount: amount,
); depositCurrency: depositCurrency,
receiveCurrency: receiveCurrency,
provider: provider,
depositAddress: depositAddress,
receiveAddress: receiveAddress);
void removeTemplate({ExchangeTemplate template}) => void removeTemplate({ExchangeTemplate template}) =>
_exchangeTemplateStore.remove(template: template); _exchangeTemplateStore.remove(template: template);
List<ExchangeProvider> providersForCurrentPair() { List<ExchangeProvider> providersForCurrentPair() {
return _providersForPair(from: depositCurrency, to: receiveCurrency); return _providersForPair(from: depositCurrency, to: receiveCurrency);
@ -322,9 +337,9 @@ abstract class ExchangeViewModelBase with Store {
} }
} }
depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
depositAmount = ''; depositAmount = '';
receiveAmount = ''; receiveAmount = '';
loadLimits(); loadLimits();
} }