Merge pull request from cake-tech/CW-271-fixed-rate-incorrect-amount

[Cw 271] fixed-rate incorrect amount
This commit is contained in:
Omar Hatem 2022-12-08 21:14:54 +02:00 committed by GitHub
commit 3edba88b96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 70 additions and 49 deletions

View file

@ -11,7 +11,6 @@ import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/changenow/changenow_request.dart'; import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
class ChangeNowExchangeProvider extends ExchangeProvider { class ChangeNowExchangeProvider extends ExchangeProvider {
ChangeNowExchangeProvider() ChangeNowExchangeProvider()
@ -21,8 +20,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
.where((i) => i != CryptoCurrency.xhv) .where((i) => i != CryptoCurrency.xhv)
.map((i) => CryptoCurrency.all .map((i) => CryptoCurrency.all
.where((i) => i != CryptoCurrency.xhv) .where((i) => i != CryptoCurrency.xhv)
.map((k) => ExchangePair(from: i, to: k, reverse: true)) .map((k) => ExchangePair(from: i, to: k, reverse: true)))
.where((c) => c != null))
.expand((i) => i) .expand((i) => i)
.toList()); .toList());
@ -43,6 +41,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
@override @override
bool get isEnabled => true; bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.changeNow; ExchangeProviderDescription.changeNow;
@ -96,19 +97,30 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
apiHeaderKey: apiKey, apiHeaderKey: apiKey,
'Content-Type': 'application/json'}; 'Content-Type': 'application/json'};
final flow = getFlow(isFixedRateMode); final flow = getFlow(isFixedRateMode);
final type = isFixedRateMode ? 'reverse' : 'direct';
final body = <String, String>{ final body = <String, String>{
'fromCurrency': normalizeCryptoCurrency(_request.from), 'fromCurrency': normalizeCryptoCurrency(_request.from),
'toCurrency': normalizeCryptoCurrency(_request.to), 'toCurrency': normalizeCryptoCurrency(_request.to),
'fromNetwork': networkFor(_request.from), 'fromNetwork': networkFor(_request.from),
'toNetwork': networkFor(_request.to), 'toNetwork': networkFor(_request.to),
'fromAmount': _request.fromAmount, if (!isFixedRateMode) 'fromAmount': _request.fromAmount,
'toAmount': _request.toAmount, if (isFixedRateMode) 'toAmount': _request.toAmount,
'address': _request.address, 'address': _request.address,
'flow': flow, 'flow': flow,
'type': type,
'refundAddress': _request.refundAddress 'refundAddress': _request.refundAddress
}; };
if (isFixedRateMode) { if (isFixedRateMode) {
// since we schedule to calculate the rate every 5 seconds we need to ensure that
// we have the latest rate id with the given inputs before creating the trade
await fetchRate(
from: _request.from,
to: _request.to,
amount: double.tryParse(_request.toAmount) ?? 0,
isFixedRateMode: true,
isReceiveAmount: true,
);
body['rateId'] = _lastUsedRateId; body['rateId'] = _lastUsedRateId;
} }
@ -141,7 +153,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
refundAddress: refundAddress, refundAddress: refundAddress,
extraId: extraId, extraId: extraId,
createdAt: DateTime.now(), createdAt: DateTime.now(),
amount: _request.fromAmount, amount: responseJSON['fromAmount']?.toString() ?? _request.fromAmount,
state: TradeState.created); state: TradeState.created);
} }
@ -180,9 +192,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final extraId = responseJSON['payinExtraId'] as String; final extraId = responseJSON['payinExtraId'] as String;
final outputTransaction = responseJSON['payoutHash'] as String; final outputTransaction = responseJSON['payoutHash'] as String;
final expiredAtRaw = responseJSON['validUntil'] as String; final expiredAtRaw = responseJSON['validUntil'] as String;
final expiredAt = expiredAtRaw != null final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal();
? DateTime.parse(expiredAtRaw).toLocal()
: null;
return Trade( return Trade(
id: id, id: id,
@ -198,7 +208,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
} }
@override @override
Future<double> calculateAmount( Future<double> fetchRate(
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required double amount, required double amount,
@ -214,10 +224,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final type = isReverse ? 'reverse' : 'direct'; final type = isReverse ? 'reverse' : 'direct';
final flow = getFlow(isFixedRateMode); final flow = getFlow(isFixedRateMode);
final params = <String, String>{ final params = <String, String>{
'fromCurrency': isReverse ? normalizeCryptoCurrency(to) : normalizeCryptoCurrency(from), 'fromCurrency': normalizeCryptoCurrency(from),
'toCurrency': isReverse ? normalizeCryptoCurrency(from) : normalizeCryptoCurrency(to), 'toCurrency': normalizeCryptoCurrency(to),
'fromNetwork': isReverse ? networkFor(to) : networkFor(from), 'fromNetwork': networkFor(from),
'toNetwork': isReverse ? networkFor(from) : networkFor(to), 'toNetwork': networkFor(to),
'type': type, 'type': type,
'flow': flow}; 'flow': flow};
@ -238,7 +248,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
_lastUsedRateId = rateId; _lastUsedRateId = rateId;
} }
return isReverse ? fromAmount : toAmount; return isReverse ? (amount / fromAmount) : (toAmount / amount);
} catch(e) { } catch(e) {
print(e.toString()); print(e.toString());
return 0.0; return 0.0;

View file

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/exchange_pair.dart'; import 'package:cake_wallet/exchange/exchange_pair.dart';
@ -14,6 +13,7 @@ abstract class ExchangeProvider {
ExchangeProviderDescription get description; ExchangeProviderDescription get description;
bool get isAvailable; bool get isAvailable;
bool get isEnabled; bool get isEnabled;
bool get supportsFixedRate;
@override @override
String toString() => title; String toString() => title;
@ -26,7 +26,7 @@ abstract class ExchangeProvider {
required TradeRequest request, required TradeRequest request,
required bool isFixedRateMode}); required bool isFixedRateMode});
Future<Trade> findTradeById({required String id}); Future<Trade> findTradeById({required String id});
Future<double> calculateAmount({ Future<double> fetchRate({
required CryptoCurrency from, required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required double amount, required double amount,

View file

@ -66,6 +66,9 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
@override @override
bool get isEnabled => true; bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.morphToken; ExchangeProviderDescription.morphToken;
@ -200,7 +203,7 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
} }
@override @override
Future<double> calculateAmount( Future<double> fetchRate(
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required double amount, required double amount,

View file

@ -12,7 +12,6 @@ import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/limits.dart'; import 'package:cake_wallet/exchange/limits.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
class SideShiftExchangeProvider extends ExchangeProvider { class SideShiftExchangeProvider extends ExchangeProvider {
@ -48,8 +47,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
return supportedCurrencies return supportedCurrencies
.map((i) => supportedCurrencies .map((i) => supportedCurrencies
.map((k) => ExchangePair(from: i, to: k, reverse: true)) .map((k) => ExchangePair(from: i, to: k, reverse: true)))
.where((c) => c != null))
.expand((i) => i) .expand((i) => i)
.toList(); .toList();
} }
@ -59,7 +57,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
ExchangeProviderDescription.sideShift; ExchangeProviderDescription.sideShift;
@override @override
Future<double> calculateAmount( Future<double> fetchRate(
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required double amount, required double amount,
@ -81,9 +79,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
if (amount > max) return 0.00; if (amount > max) return 0.00;
final estimatedAmount = rate * amount; return rate;
return estimatedAmount;
} catch (_) { } catch (_) {
return 0.00; return 0.00;
} }
@ -257,8 +253,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
state = TradeState.deserialize(raw: status ?? 'created'); state = TradeState.deserialize(raw: status ?? 'created');
final expiredAtRaw = responseJSON['expiresAtISO'] as String; final expiredAtRaw = responseJSON['expiresAtISO'] as String;
final expiredAt = final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal();
expiredAtRaw != null ? DateTime.parse(expiredAtRaw).toLocal() : null;
return Trade( return Trade(
id: id, id: id,
@ -278,6 +273,9 @@ class SideShiftExchangeProvider extends ExchangeProvider {
@override @override
bool get isEnabled => true; bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override @override
String get title => 'SideShift'; String get title => 'SideShift';

View file

@ -20,8 +20,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
.where((i) => i != CryptoCurrency.zaddr) .where((i) => i != CryptoCurrency.zaddr)
.map((i) => CryptoCurrency.all .map((i) => CryptoCurrency.all
.where((i) => i != CryptoCurrency.zaddr) .where((i) => i != CryptoCurrency.zaddr)
.map((k) => ExchangePair(from: i, to: k, reverse: true)) .map((k) => ExchangePair(from: i, to: k, reverse: true)))
.where((c) => c != null))
.expand((i) => i) .expand((i) => i)
.toList()); .toList());
@ -37,7 +36,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
ExchangeProviderDescription.simpleSwap; ExchangeProviderDescription.simpleSwap;
@override @override
Future<double> calculateAmount( Future<double> fetchRate(
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required double amount, required double amount,
@ -59,9 +58,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
final uri = Uri.https(apiAuthority, getEstimatePath, params); final uri = Uri.https(apiAuthority, getEstimatePath, params);
final response = await get(uri); final response = await get(uri);
if (response.body == null || response.body == "null") return 0.00; if (response.body == "null") return 0.00;
final data = json.decode(response.body) as String; final data = json.decode(response.body) as String;
return double.parse(data); return double.parse(data) / amount;
} catch (_) { } catch (_) {
return 0.00; return 0.00;
} }
@ -210,6 +209,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
@override @override
bool get isEnabled => true; bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@override @override
String get title => 'SimpleSwap'; String get title => 'SimpleSwap';

View file

@ -48,6 +48,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
@override @override
bool get isEnabled => true; bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.xmrto; ExchangeProviderDescription.xmrto;
@ -191,7 +194,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
} }
@override @override
Future<double> calculateAmount( Future<double> fetchRate(
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required double amount, required double amount,

View file

@ -115,6 +115,11 @@ abstract class SettingsStoreBase with Store {
(BalanceDisplayMode mode) => sharedPreferences.setInt( (BalanceDisplayMode mode) => sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey, mode.serialize())); PreferencesKey.currentBalanceDisplayModeKey, mode.serialize()));
reaction(
(_) => disableExchange,
(bool disableExchange) => sharedPreferences.setBool(
PreferencesKey.disableExchangeKey, disableExchange));
this this
.nodes .nodes
.observe((change) { .observe((change) {

View file

@ -44,7 +44,6 @@ abstract class ExchangeViewModelBase with Store {
ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore, ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore,
this.tradesStore, this._settingsStore, this.sharedPreferences) this.tradesStore, this._settingsStore, this.sharedPreferences)
: _cryptoNumberFormat = NumberFormat(), : _cryptoNumberFormat = NumberFormat(),
isReverse = false,
isFixedRateMode = false, isFixedRateMode = false,
isReceiveAmountEntered = false, isReceiveAmountEntered = false,
depositAmount = '', depositAmount = '',
@ -112,7 +111,11 @@ abstract class ExchangeViewModelBase with Store {
loadLimits(); loadLimits();
reaction( reaction(
(_) => isFixedRateMode, (_) => isFixedRateMode,
(Object _) => loadLimits()); (Object _) {
loadLimits();
_bestRate = 0;
_calculateBestRate();
});
} }
final WalletBase wallet; final WalletBase wallet;
@ -227,8 +230,6 @@ abstract class ExchangeViewModelBase with Store {
Limits limits; Limits limits;
bool isReverse;
NumberFormat _cryptoNumberFormat; NumberFormat _cryptoNumberFormat;
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
@ -258,7 +259,6 @@ abstract class ExchangeViewModelBase with Store {
@action @action
Future<void> changeReceiveAmount({required String amount}) async { Future<void> changeReceiveAmount({required String amount}) async {
receiveAmount = amount; receiveAmount = amount;
isReverse = true;
if (amount.isEmpty) { if (amount.isEmpty) {
depositAmount = ''; depositAmount = '';
@ -283,7 +283,6 @@ abstract class ExchangeViewModelBase with Store {
@action @action
Future<void> changeDepositAmount({required String amount}) async { Future<void> changeDepositAmount({required String amount}) async {
depositAmount = amount; depositAmount = amount;
isReverse = false;
if (amount.isEmpty) { if (amount.isEmpty) {
depositAmount = ''; depositAmount = '';
@ -311,12 +310,13 @@ abstract class ExchangeViewModelBase with Store {
final result = await Future.wait<double>( final result = await Future.wait<double>(
_tradeAvailableProviders _tradeAvailableProviders
.map((element) => element.calculateAmount( .where((element) => !isFixedRateMode || element.supportsFixedRate)
.map((element) => element.fetchRate(
from: depositCurrency, from: depositCurrency,
to: receiveCurrency, to: receiveCurrency,
amount: amount, amount: amount,
isFixedRateMode: isFixedRateMode, isFixedRateMode: isFixedRateMode,
isReceiveAmount: false)) isReceiveAmount: isFixedRateMode))
); );
_sortedAvailableProviders.clear(); _sortedAvailableProviders.clear();
@ -324,7 +324,7 @@ abstract class ExchangeViewModelBase with Store {
for (int i=0;i<result.length;i++) { for (int i=0;i<result.length;i++) {
if (result[i] != 0) { if (result[i] != 0) {
/// add this provider as its valid for this trade /// add this provider as its valid for this trade
_sortedAvailableProviders[result[i] / amount] = _tradeAvailableProviders[i]; _sortedAvailableProviders[result[i]] = _tradeAvailableProviders[i];
} }
} }
if (_sortedAvailableProviders.isNotEmpty) { if (_sortedAvailableProviders.isNotEmpty) {
@ -401,7 +401,7 @@ abstract class ExchangeViewModelBase with Store {
settleAddress: receiveAddress, settleAddress: receiveAddress,
refundAddress: depositAddress, refundAddress: depositAddress,
); );
amount = depositAmount; amount = isFixedRateMode ? receiveAmount : depositAmount;
} }
if (provider is SimpleSwapExchangeProvider) { if (provider is SimpleSwapExchangeProvider) {
@ -412,7 +412,7 @@ abstract class ExchangeViewModelBase with Store {
address: receiveAddress, address: receiveAddress,
refundAddress: depositAddress, refundAddress: depositAddress,
); );
amount = depositAmount; amount = isFixedRateMode ? receiveAmount : depositAmount;
} }
if (provider is XMRTOExchangeProvider) { if (provider is XMRTOExchangeProvider) {
@ -424,7 +424,7 @@ abstract class ExchangeViewModelBase with Store {
address: receiveAddress, address: receiveAddress,
refundAddress: depositAddress, refundAddress: depositAddress,
isBTCRequest: isReceiveAmountEntered); isBTCRequest: isReceiveAmountEntered);
amount = depositAmount; amount = isFixedRateMode ? receiveAmount : depositAmount;
} }
if (provider is ChangeNowExchangeProvider) { if (provider is ChangeNowExchangeProvider) {
@ -435,8 +435,8 @@ abstract class ExchangeViewModelBase with Store {
toAmount: receiveAmount.replaceAll(',', '.'), toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress, refundAddress: depositAddress,
address: receiveAddress, address: receiveAddress,
isReverse: isReverse); isReverse: isFixedRateMode);
amount = isReverse ? receiveAmount : depositAmount; amount = isFixedRateMode ? receiveAmount : depositAmount;
} }
if (provider is MorphTokenExchangeProvider) { if (provider is MorphTokenExchangeProvider) {
@ -446,13 +446,13 @@ abstract class ExchangeViewModelBase with Store {
amount: depositAmount.replaceAll(',', '.'), amount: depositAmount.replaceAll(',', '.'),
refundAddress: depositAddress, refundAddress: depositAddress,
address: receiveAddress); address: receiveAddress);
amount = depositAmount; amount = isFixedRateMode ? receiveAmount : depositAmount;
} }
amount = amount.replaceAll(',', '.'); amount = amount.replaceAll(',', '.');
if (limitsState is LimitsLoadedSuccessfully) { if (limitsState is LimitsLoadedSuccessfully) {
if (double.parse(amount) < limits.min!) { if (limits.max != null && double.parse(amount) < limits.min!) {
continue; continue;
} else if (limits.max != null && double.parse(amount) > limits.max!) { } else if (limits.max != null && double.parse(amount) > limits.max!) {
continue; continue;