import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/limits.dart'; import 'package:cake_wallet/src/domain/exchange/trade.dart'; import 'package:cake_wallet/src/stores/exchange/limits_state.dart'; import 'package:cake_wallet/store/dashboard/trades_store.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/src/stores/exchange/exchange_trade_state.dart'; import 'package:cake_wallet/src/domain/exchange/changenow/changenow_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/changenow/changenow_request.dart'; import 'package:cake_wallet/src/domain/exchange/trade_request.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_trade_request.dart'; import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart'; import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_template.dart'; part 'exchange_view_model.g.dart'; class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { ExchangeViewModelBase({ this.wallet, this.trades, this.exchangeTemplateStore, this.tradesStore}) { providerList = [ XMRTOExchangeProvider(), ChangeNowExchangeProvider(), MorphTokenExchangeProvider(trades: trades) ]; provider = providerList[ 0 ]; depositCurrency = CryptoCurrency.xmr; receiveCurrency = CryptoCurrency.btc; isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); depositAmount = ''; receiveAmount = ''; depositAddress = ''; receiveAddress = ''; limitsState = LimitsInitialState(); tradeState = ExchangeTradeStateInitial(); _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; loadLimits(); } final WalletBase wallet; final Box trades; final ExchangeTemplateStore exchangeTemplateStore; final TradesStore tradesStore; @observable ExchangeProvider provider; @observable List providerList; @observable CryptoCurrency depositCurrency; @observable CryptoCurrency receiveCurrency; @observable LimitsState limitsState; @observable ExchangeTradeState tradeState; @observable String depositAmount; @observable String receiveAmount; @observable String depositAddress; @observable String receiveAddress; @observable bool isDepositAddressEnabled; @observable bool isReceiveAddressEnabled; Limits limits; NumberFormat _cryptoNumberFormat; @computed ObservableList get templates => exchangeTemplateStore.templates; @action void changeProvider({ExchangeProvider provider}) { this.provider = provider; depositAmount = ''; receiveAmount = ''; loadLimits(); } @action void changeDepositCurrency({CryptoCurrency currency}) { depositCurrency = currency; _onPairChange(); isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); } @action void changeReceiveCurrency({CryptoCurrency currency}) { receiveCurrency = currency; _onPairChange(); isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); } @action void changeReceiveAmount({String amount}) { receiveAmount = amount; if (amount == null || amount.isEmpty) { depositAmount = ''; receiveAmount = ''; return; } final _amount = double.parse(amount) ?? 0; provider .calculateAmount( from: depositCurrency, to: receiveCurrency, amount: _amount) .then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), "")) .then((amount) => depositAmount = amount); } @action void changeDepositAmount({String amount}) { depositAmount = amount; if (amount == null || amount.isEmpty) { depositAmount = ''; receiveAmount = ''; return; } final _amount = double.parse(amount); provider .calculateAmount( from: depositCurrency, to: receiveCurrency, amount: _amount) .then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), "")) .then((amount) => receiveAmount = amount); } @action Future loadLimits() async { limitsState = LimitsIsLoading(); try { limits = await provider.fetchLimits( from: depositCurrency, to: receiveCurrency); limitsState = LimitsLoadedSuccessfully(limits: limits); } catch (e) { limitsState = LimitsLoadedFailure(error: e.toString()); } } @action Future createTrade() async { TradeRequest request; String amount; CryptoCurrency currency; if (provider is XMRTOExchangeProvider) { request = XMRTOTradeRequest( from: depositCurrency, to: receiveCurrency, amount: depositAmount, address: receiveAddress, refundAddress: depositAddress); amount = depositAmount; currency = depositCurrency; } if (provider is ChangeNowExchangeProvider) { request = ChangeNowRequest( from: depositCurrency, to: receiveCurrency, amount: depositAmount, refundAddress: depositAddress, address: receiveAddress); amount = depositAmount; currency = depositCurrency; } if (provider is MorphTokenExchangeProvider) { request = MorphTokenRequest( from: depositCurrency, to: receiveCurrency, amount: depositAmount, refundAddress: depositAddress, address: receiveAddress); amount = depositAmount; currency = depositCurrency; } if (limitsState is LimitsLoadedSuccessfully && amount != null) { if (double.parse(amount) < limits.min) { tradeState = TradeIsCreatedFailure(error: S.current.error_text_minimal_limit('${provider.description}', '${limits.min}', currency.toString())); } else if (limits.max != null && double.parse(amount) > limits.max) { tradeState = TradeIsCreatedFailure(error: S.current.error_text_maximum_limit('${provider.description}', '${limits.max}', currency.toString())); } else { try { tradeState = TradeIsCreating(); final trade = await provider.createTrade(request: request); trade.walletId = wallet.id; tradesStore.setTrade(trade); await trades.add(trade); tradeState = TradeIsCreatedSuccessfully(trade: trade); } catch (e) { tradeState = TradeIsCreatedFailure(error: e.toString()); } } } else { tradeState = TradeIsCreatedFailure(error: S.current.error_text_limits_loading_failed('${provider.description}')); } } @action void reset() { depositAmount = ''; receiveAmount = ''; depositCurrency = CryptoCurrency.xmr; receiveCurrency = CryptoCurrency.btc; depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; receiveAddress = receiveCurrency == wallet.currency ? wallet.address : ''; isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); _onPairChange(); } List providersForCurrentPair() { return _providersForPair(from: depositCurrency, to: receiveCurrency); } List _providersForPair( {CryptoCurrency from, CryptoCurrency to}) { final providers = providerList .where((provider) => provider.pairList .where((pair) => pair.from == depositCurrency && pair.to == receiveCurrency) .isNotEmpty) .toList(); return providers; } void _onPairChange() { final isPairExist = provider.pairList .where((pair) => pair.from == depositCurrency && pair.to == receiveCurrency) .isNotEmpty; if (!isPairExist) { final provider = _providerForPair(from: depositCurrency, to: receiveCurrency); if (provider != null) { changeProvider(provider: provider); } } depositAmount = ''; receiveAmount = ''; loadLimits(); } ExchangeProvider _providerForPair({CryptoCurrency from, CryptoCurrency to}) { final providers = _providersForPair(from: from, to: to); return providers.isNotEmpty ? providers[0] : null; } }