Cw 473 exchange refactoring (#1126)

* CW-473 Remove deprecated Exchanges

* CW-473 Clean up Exchange Code

* CW-473 Add Decimals to Crypto Enums

* CW-473 Fix minor merge error

* CW-473 Fix minor merge error

* CW-473 Fix FAQ Locals

* CW-473 Apply requested changes

* CW-473 Show Error if Exchange Provider is not supported anymore

* CW-473 Implement Requested Changes
This commit is contained in:
Konstantin Ullrich
2023-10-25 23:58:25 +03:00
committed by GitHub
parent db7f025b71
commit cef3029f6f
74 changed files with 941 additions and 1783 deletions
@@ -1,22 +1,21 @@
import 'dart:async';
import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
part 'exchange_trade_view_model.g.dart';
@@ -35,16 +34,10 @@ abstract class ExchangeTradeViewModelBase with Store {
tradesStore.trade!.from.tag == CryptoCurrency.eth.title),
items = ObservableList<ExchangeTradeItem>() {
switch (trade.provider) {
case ExchangeProviderDescription.xmrto:
_provider = XMRTOExchangeProvider();
break;
case ExchangeProviderDescription.changeNow:
_provider =
ChangeNowExchangeProvider(settingsStore: sendViewModel.balanceViewModel.settingsStore);
break;
case ExchangeProviderDescription.morphToken:
_provider = MorphTokenExchangeProvider(trades: trades);
break;
case ExchangeProviderDescription.sideShift:
_provider = SideShiftExchangeProvider();
break;
@@ -60,8 +53,11 @@ abstract class ExchangeTradeViewModelBase with Store {
}
_updateItems();
_updateTrade();
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
if (_provider != null) {
_updateTrade();
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
}
}
final WalletBase wallet;
@@ -100,10 +96,8 @@ abstract class ExchangeTradeViewModelBase with Store {
Timer? timer;
@action
Future confirmSending() async {
if (!isSendable) {
return;
}
Future<void> confirmSending() async {
if (!isSendable) return;
sendViewModel.clearOutputs();
final output = sendViewModel.outputs.first;
@@ -118,13 +112,10 @@ abstract class ExchangeTradeViewModelBase with Store {
try {
final updatedTrade = await _provider!.findTradeById(id: trade.id);
if (updatedTrade.createdAt == null && trade.createdAt != null) {
if (updatedTrade.createdAt == null && trade.createdAt != null)
updatedTrade.createdAt = trade.createdAt;
}
if (updatedTrade.amount.isEmpty) {
updatedTrade.amount = trade.amount;
}
if (updatedTrade.amount.isEmpty) updatedTrade.amount = trade.amount;
trade = updatedTrade;
+59 -165
View File
@@ -3,48 +3,39 @@ import 'dart:collection';
import 'dart:convert';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/wallet_contact.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/exolix/exolix_request.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/limits_state.dart';
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_trade_request.dart';
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
import 'package:cake_wallet/exchange/morphtoken/morphtoken_request.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:shared_preferences/shared_preferences.dart';
part 'exchange_view_model.g.dart';
@@ -144,6 +135,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
_calculateBestRate();
});
}
bool _useTorOnly;
final Box<Trade> trades;
final ExchangeTemplateStore _exchangeTemplateStore;
@@ -384,13 +376,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
double minLimit = limits.min ?? 0;
double? maxLimit = limits.max;
if (_enteredAmount < minLimit) {
return false;
}
if (_enteredAmount < minLimit) return false;
if (maxLimit != null && _enteredAmount > maxLimit) {
return false;
}
if (maxLimit != null && _enteredAmount > maxLimit) return false;
return true;
}
@@ -422,16 +410,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
}
}
if (_sortedAvailableProviders.isNotEmpty) {
_bestRate = _sortedAvailableProviders.keys.first;
}
if (_sortedAvailableProviders.isNotEmpty) _bestRate = _sortedAvailableProviders.keys.first;
}
@action
Future<void> loadLimits() async {
if (selectedProviders.isEmpty) {
return;
}
if (selectedProviders.isEmpty) return;
limitsState = LimitsIsLoading();
@@ -444,20 +428,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
try {
for (var provider in selectedProviders) {
/// if this provider is not valid for the current pair, skip it
if (!providersForCurrentPair().contains(provider)) {
continue;
}
if (!providersForCurrentPair().contains(provider)) continue;
try {
final tempLimits =
await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode);
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) {
lowestMin = tempLimits.min;
}
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) lowestMin = tempLimits.min;
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax)
highestMax = tempLimits.max;
}
} catch (e) {
continue;
}
@@ -482,111 +462,34 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
Future<void> createTrade() async {
TradeRequest? request;
String amount = '';
try {
for (var provider in _sortedAvailableProviders.values) {
if (!(await provider.checkIsAvailable())) {
continue;
}
if (!(await provider.checkIsAvailable())) continue;
if (provider is SideShiftExchangeProvider) {
request = SideShiftRequest(
depositMethod: depositCurrency,
settleMethod: receiveCurrency,
depositAmount: isFixedRateMode
? receiveAmount.replaceAll(',', '.')
: depositAmount.replaceAll(',', '.'),
settleAddress: receiveAddress,
final request = TradeRequest(
fromCurrency: depositCurrency,
toCurrency: receiveCurrency,
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is SimpleSwapExchangeProvider) {
request = SimpleSwapRequest(
from: depositCurrency,
to: receiveCurrency,
amount: depositAmount.replaceAll(',', '.'),
address: receiveAddress,
refundAddress: depositAddress,
);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is XMRTOExchangeProvider) {
request = XMRTOTradeRequest(
from: depositCurrency,
to: receiveCurrency,
amount: depositAmount.replaceAll(',', '.'),
receiveAmount: receiveAmount.replaceAll(',', '.'),
address: receiveAddress,
refundAddress: depositAddress,
isBTCRequest: isReceiveAmountEntered);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is ChangeNowExchangeProvider) {
request = ChangeNowRequest(
from: depositCurrency,
to: receiveCurrency,
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress,
isReverse: isFixedRateMode);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is MorphTokenExchangeProvider) {
request = MorphTokenRequest(
from: depositCurrency,
to: receiveCurrency,
amount: depositAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is TrocadorExchangeProvider) {
request = TrocadorRequest(
from: depositCurrency,
to: receiveCurrency,
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress,
isReverse: isFixedRateMode);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is ExolixExchangeProvider) {
request = ExolixRequest(
from: depositCurrency,
to: receiveCurrency,
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
toAddress: receiveAddress,
isFixedRate: isFixedRateMode);
var amount = isFixedRateMode ? receiveAmount : depositAmount;
amount = amount.replaceAll(',', '.');
if (limitsState is LimitsLoadedSuccessfully) {
if (double.tryParse(amount) == null) {
if (double.tryParse(amount) == null) continue;
if (limits.max != null && double.parse(amount) < limits.min!)
continue;
}
if (limits.max != null && double.parse(amount) < limits.min!) {
else if (limits.max != null && double.parse(amount) > limits.max!)
continue;
} else if (limits.max != null && double.parse(amount) > limits.max!) {
continue;
} else {
else {
try {
tradeState = TradeIsCreating();
final trade =
await provider.createTrade(request: request!, isFixedRateMode: isFixedRateMode);
await provider.createTrade(request: request, isFixedRateMode: isFixedRateMode);
trade.walletId = wallet.id;
tradesStore.setTrade(trade);
await trades.add(trade);
@@ -636,9 +539,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final priority = _settingsStore.priority[wallet.type]!;
final fee = wallet.calculateEstimatedFee(priority, null);
if (availableBalance < fee || availableBalance == 0) {
return;
}
if (availableBalance < fee || availableBalance == 0) return;
final amount = availableBalance - fee;
changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
@@ -669,19 +570,15 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
void removeTemplate({required ExchangeTemplate template}) =>
_exchangeTemplateStore.remove(template: template);
List<ExchangeProvider> providersForCurrentPair() {
return _providersForPair(from: depositCurrency, to: receiveCurrency);
}
List<ExchangeProvider> providersForCurrentPair() =>
_providersForPair(from: depositCurrency, to: receiveCurrency);
List<ExchangeProvider> _providersForPair(
{required CryptoCurrency from, required CryptoCurrency to}) {
final providers = providerList
.where((provider) =>
provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
.toList();
return providers;
}
{required CryptoCurrency from, required CryptoCurrency to}) =>
providerList
.where((provider) =>
provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
.toList();
void _onPairChange() {
depositAmount = '';
@@ -744,9 +641,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
void addExchangeProvider(ExchangeProvider provider) {
selectedProviders.add(provider);
if (providersForCurrentPair().contains(provider)) {
_tradeAvailableProviders.add(provider);
}
if (providersForCurrentPair().contains(provider)) _tradeAvailableProviders.add(provider);
}
@action
@@ -817,14 +712,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
void _setProviders() {
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) {
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly)
providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList();
} else {
else
providerList = _allProviders;
}
}
int get depositMaxDigits => depositCurrency == CryptoCurrency.btc ? 8 : 12;
int get depositMaxDigits => depositCurrency.decimals;
int get receiveMaxDigits => receiveCurrency == CryptoCurrency.btc ? 8 : 12;
int get receiveMaxDigits => receiveCurrency.decimals;
}
+25 -26
View File
@@ -1,28 +1,28 @@
import 'dart:async';
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_provider_unsupported_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:collection/collection.dart';
part 'trade_details_view_model.g.dart';
@@ -37,15 +37,9 @@ abstract class TradeDetailsViewModelBase with Store {
trade = trades.values.firstWhereOrNull((element) => element.id == tradeForDetails.id) ??
tradeForDetails {
switch (trade.provider) {
case ExchangeProviderDescription.xmrto:
_provider = XMRTOExchangeProvider();
break;
case ExchangeProviderDescription.changeNow:
_provider = ChangeNowExchangeProvider(settingsStore: settingsStore);
break;
case ExchangeProviderDescription.morphToken:
_provider = MorphTokenExchangeProvider(trades: trades);
break;
case ExchangeProviderDescription.sideShift:
_provider = SideShiftExchangeProvider();
break;
@@ -62,9 +56,10 @@ abstract class TradeDetailsViewModelBase with Store {
_updateItems();
_updateTrade();
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
if (_provider != null) {
_updateTrade();
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
}
}
final Box<Trade> trades;
@@ -86,9 +81,9 @@ abstract class TradeDetailsViewModelBase with Store {
try {
final updatedTrade = await _provider!.findTradeById(id: trade.id);
if (updatedTrade.createdAt == null && trade.createdAt != null) {
if (updatedTrade.createdAt == null && trade.createdAt != null)
updatedTrade.createdAt = trade.createdAt;
}
Trade? foundElement = trades.values.firstWhereOrNull((element) => element.id == trade.id);
if (foundElement != null) {
final editedTrade = trades.get(foundElement.key);
@@ -109,6 +104,10 @@ abstract class TradeDetailsViewModelBase with Store {
items.clear();
if (_provider == null)
items.add(TradeProviderUnsupportedItem(
error: S.current.exchange_provider_unsupported(trade.provider.title)));
items.add(
DetailsListStatusItem(title: S.current.trade_details_state, value: trade.state.toString()));
@@ -165,7 +164,7 @@ abstract class TradeDetailsViewModelBase with Store {
if (trade.provider == ExchangeProviderDescription.exolix) {
final buildURL = 'https://exolix.com/transaction/${trade.id.toString()}';
items.add(
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
}
}