Fiat api fix (#1070)

* Cw 474 linux swapping wallets btc then xmr still shows btc receive qr code (#1068)

* feat: improve address page txt field

* fix: switching wallets from the desktop dropdown updates dashboard pages

* Revert "feat: improve address page txt field"

This reverts commit 0a30e6d9e1.

* refactor: rename to WalletChangeListener

* fix: _init also behaves on wallet change

* Fix Fiat conversion API parsing
Cherry pick fix for Desktop switching wallets not updating dashboard screens

* Minor indentations fix [skip ci]

---------

Co-authored-by: Rafael Saes <76502841+saltrafael@users.noreply.github.com>
This commit is contained in:
Omar Hatem 2023-09-01 18:06:18 +03:00 committed by GitHub
parent 710fe82d7a
commit 68a821cc0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 209 additions and 201 deletions

View file

@ -21,7 +21,7 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
'key': secrets.fiatApiKey, 'key': secrets.fiatApiKey,
}; };
double price = 0.0; num price = 0.0;
try { try {
late final Uri uri; late final Uri uri;
@ -41,12 +41,12 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
final results = responseJSON['results'] as Map<String, dynamic>; final results = responseJSON['results'] as Map<String, dynamic>;
if (results.isNotEmpty) { if (results.isNotEmpty) {
price = results.values.first as double; price = results.values.first as num;
} }
return price; return price.toDouble();
} catch (e) { } catch (e) {
return price; return price.toDouble();
} }
} }

View file

@ -0,0 +1,30 @@
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/store/app_store.dart';
part 'wallet_change_listener_view_model.g.dart';
class WalletChangeListenerViewModel = WalletChangeListenerViewModelBase
with _$WalletChangeListenerViewModel;
abstract class WalletChangeListenerViewModelBase with Store {
WalletChangeListenerViewModelBase({
required AppStore appStore,
}) : _wallet = appStore.wallet! {
reaction((_) => appStore.wallet, (WalletBase? wallet) {
_wallet = wallet!;
onWalletChange(wallet);
});
}
void onWalletChange(WalletBase wallet) {}
@observable
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
@computed
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> get wallet =>
_wallet;
}

View file

@ -523,8 +523,7 @@ Future<void> setup({
getIt.registerFactory<SendViewModel>( getIt.registerFactory<SendViewModel>(
() => SendViewModel( () => SendViewModel(
getIt.get<AppStore>().wallet!, getIt.get<AppStore>(),
getIt.get<AppStore>().settingsStore,
getIt.get<SendTemplateViewModel>(), getIt.get<SendTemplateViewModel>(),
getIt.get<FiatConversionStore>(), getIt.get<FiatConversionStore>(),
getIt.get<BalanceViewModel>(), getIt.get<BalanceViewModel>(),
@ -702,7 +701,7 @@ Future<void> setup({
)); ));
getIt.registerFactory(() => ExchangeViewModel( getIt.registerFactory(() => ExchangeViewModel(
getIt.get<AppStore>().wallet!, getIt.get<AppStore>(),
_tradesSource, _tradesSource,
getIt.get<ExchangeTemplateStore>(), getIt.get<ExchangeTemplateStore>(),
getIt.get<TradesStore>(), getIt.get<TradesStore>(),

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:convert'; import 'dart:convert';
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/exchange_api_mode.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/wallet_contact.dart'; import 'package:cake_wallet/entities/wallet_contact.dart';
@ -13,7 +14,7 @@ import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -45,16 +46,22 @@ part 'exchange_view_model.g.dart';
class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
abstract class ExchangeViewModelBase with Store { abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with Store {
@override
void onWalletChange(wallet) {
receiveCurrency = wallet.currency;
depositCurrency = wallet.currency;
}
ExchangeViewModelBase( ExchangeViewModelBase(
this.wallet, AppStore appStore,
this.trades, this.trades,
this._exchangeTemplateStore, this._exchangeTemplateStore,
this.tradesStore, this.tradesStore,
this._settingsStore, this._settingsStore,
this.sharedPreferences, this.sharedPreferences,
this.contactListViewModel) this.contactListViewModel,
: _cryptoNumberFormat = NumberFormat(), ) : _cryptoNumberFormat = NumberFormat(),
isFixedRateMode = false, isFixedRateMode = false,
isReceiveAmountEntered = false, isReceiveAmountEntered = false,
depositAmount = '', depositAmount = '',
@ -70,10 +77,11 @@ abstract class ExchangeViewModelBase with Store {
limits = Limits(min: 0, max: 0), limits = Limits(min: 0, max: 0),
tradeState = ExchangeTradeStateInitial(), tradeState = ExchangeTradeStateInitial(),
limitsState = LimitsInitialState(), limitsState = LimitsInitialState(),
receiveCurrency = wallet.currency, receiveCurrency = appStore.wallet!.currency,
depositCurrency = wallet.currency, depositCurrency = appStore.wallet!.currency,
providerList = [], providerList = [],
selectedProviders = ObservableList<ExchangeProvider>() { selectedProviders = ObservableList<ExchangeProvider>(),
super(appStore: appStore) {
_useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly; _useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly;
_setProviders(); _setProviders();
const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano];
@ -86,10 +94,9 @@ abstract class ExchangeViewModelBase with Store {
]; ];
_initialPairBasedOnWallet(); _initialPairBasedOnWallet();
final Map<String, dynamic> exchangeProvidersSelection = json.decode( final Map<String, dynamic> exchangeProvidersSelection =
sharedPreferences json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
.getString(PreferencesKey.exchangeProvidersSelection) ?? as Map<String, dynamic>;
"{}") as Map<String, dynamic>;
/// if the provider is not in the user settings (user's first time or newly added provider) /// if the provider is not in the user settings (user's first time or newly added provider)
/// then use its default value decided by us /// then use its default value decided by us
@ -102,34 +109,28 @@ abstract class ExchangeViewModelBase with Store {
_setAvailableProviders(); _setAvailableProviders();
_calculateBestRate(); _calculateBestRate();
bestRateSync = bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
isDepositAddressEnabled = !(depositCurrency == wallet.currency); isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
depositAmount = ''; depositAmount = '';
receiveAmount = ''; receiveAmount = '';
receiveAddress = ''; receiveAddress = '';
depositAddress = depositCurrency == wallet.currency depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
? wallet.walletAddresses.address
: '';
provider = providersForCurrentPair().first; provider = providersForCurrentPair().first;
final initialProvider = provider; final initialProvider = provider;
provider!.checkIsAvailable().then((bool isAvailable) { provider!.checkIsAvailable().then((bool isAvailable) {
if (!isAvailable && provider == initialProvider) { if (!isAvailable && provider == initialProvider) {
provider = providerList.firstWhere( provider = providerList.firstWhere((provider) => provider is ChangeNowExchangeProvider,
(provider) => provider is ChangeNowExchangeProvider,
orElse: () => providerList.last); orElse: () => providerList.last);
_onPairChange(); _onPairChange();
} }
}); });
receiveCurrencies = CryptoCurrency.all receiveCurrencies = CryptoCurrency.all
.where((cryptoCurrency) => .where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency))
!excludeReceiveCurrencies.contains(cryptoCurrency))
.toList(); .toList();
depositCurrencies = CryptoCurrency.all depositCurrencies = CryptoCurrency.all
.where((cryptoCurrency) => .where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency))
!excludeDepositCurrencies.contains(cryptoCurrency))
.toList(); .toList();
_defineIsReceiveAmountEditable(); _defineIsReceiveAmountEditable();
loadLimits(); loadLimits();
@ -140,7 +141,6 @@ abstract class ExchangeViewModelBase with Store {
}); });
} }
bool _useTorOnly; bool _useTorOnly;
final WalletBase wallet;
final Box<Trade> trades; final Box<Trade> trades;
final ExchangeTemplateStore _exchangeTemplateStore; final ExchangeTemplateStore _exchangeTemplateStore;
final TradesStore tradesStore; final TradesStore tradesStore;
@ -165,8 +165,7 @@ abstract class ExchangeViewModelBase with Store {
/// initialize with descending comparator /// initialize with descending comparator
/// since we want largest rate first /// since we want largest rate first
final SplayTreeMap<double, ExchangeProvider> _sortedAvailableProviders = final SplayTreeMap<double, ExchangeProvider> _sortedAvailableProviders =
SplayTreeMap<double, ExchangeProvider>( SplayTreeMap<double, ExchangeProvider>((double a, double b) => b.compareTo(a));
(double a, double b) => b.compareTo(a));
final List<ExchangeProvider> _tradeAvailableProviders = []; final List<ExchangeProvider> _tradeAvailableProviders = [];
@ -222,21 +221,17 @@ abstract class ExchangeViewModelBase with Store {
SyncStatus get status => wallet.syncStatus; SyncStatus get status => wallet.syncStatus;
@computed @computed
ObservableList<ExchangeTemplate> get templates => ObservableList<ExchangeTemplate> get templates => _exchangeTemplateStore.templates;
_exchangeTemplateStore.templates;
@computed @computed
List<WalletContact> get walletContactsToShow => List<WalletContact> get walletContactsToShow => contactListViewModel.walletContacts
contactListViewModel.walletContacts .where((element) => receiveCurrency == null || element.type == receiveCurrency)
.where((element) => .toList();
receiveCurrency == null || element.type == receiveCurrency)
.toList();
@action @action
bool checkIfWalletIsAnInternalWallet(String address) { bool checkIfWalletIsAnInternalWallet(String address) {
final walletContactList = walletContactsToShow final walletContactList =
.where((element) => element.address == address) walletContactsToShow.where((element) => element.address == address).toList();
.toList();
return walletContactList.isNotEmpty; return walletContactList.isNotEmpty;
} }
@ -256,7 +251,6 @@ abstract class ExchangeViewModelBase with Store {
return false; return false;
} }
@computed @computed
TransactionPriority get transactionPriority { TransactionPriority get transactionPriority {
final priority = _settingsStore.priority[wallet.type]; final priority = _settingsStore.priority[wallet.type];
@ -269,7 +263,8 @@ abstract class ExchangeViewModelBase with Store {
} }
bool get hasAllAmount => bool get hasAllAmount =>
(wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && depositCurrency == wallet.currency; (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) &&
depositCurrency == wallet.currency;
bool get isMoneroWallet => wallet.type == WalletType.monero; bool get isMoneroWallet => wallet.type == WalletType.monero;
@ -277,14 +272,11 @@ abstract class ExchangeViewModelBase with Store {
switch (wallet.type) { switch (wallet.type) {
case WalletType.monero: case WalletType.monero:
case WalletType.haven: case WalletType.haven:
return transactionPriority == return transactionPriority == monero!.getMoneroTransactionPrioritySlow();
monero!.getMoneroTransactionPrioritySlow();
case WalletType.bitcoin: case WalletType.bitcoin:
return transactionPriority == return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow();
bitcoin!.getBitcoinTransactionPrioritySlow();
case WalletType.litecoin: case WalletType.litecoin:
return transactionPriority == return transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow();
bitcoin!.getLitecoinTransactionPrioritySlow();
default: default:
return false; return false;
} }
@ -390,20 +382,18 @@ abstract class ExchangeViewModelBase with Store {
} }
Future<void> _calculateBestRate() async { Future<void> _calculateBestRate() async {
final amount = final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
final _providers = _tradeAvailableProviders final _providers = _tradeAvailableProviders
.where((element) => !isFixedRateMode || element.supportsFixedRate) .where((element) => !isFixedRateMode || element.supportsFixedRate)
.toList(); .toList();
final result = await Future.wait<double>(_providers.map((element) => final result = await Future.wait<double>(_providers.map((element) => element.fetchRate(
element.fetchRate( from: depositCurrency,
from: depositCurrency, to: receiveCurrency,
to: receiveCurrency, amount: amount,
amount: amount, isFixedRateMode: isFixedRateMode,
isFixedRateMode: isFixedRateMode, isReceiveAmount: isFixedRateMode)));
isReceiveAmount: isFixedRateMode)));
_sortedAvailableProviders.clear(); _sortedAvailableProviders.clear();
@ -445,14 +435,13 @@ abstract class ExchangeViewModelBase with Store {
} }
try { try {
final tempLimits = await provider.fetchLimits( final tempLimits =
from: from, to: to, isFixedRateMode: isFixedRateMode); await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode);
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) { if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) {
lowestMin = tempLimits.min; lowestMin = tempLimits.min;
} }
if (highestMax != null && if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
(tempLimits.max ?? double.maxFinite) > highestMax) {
highestMax = tempLimits.max; highestMax = tempLimits.max;
} }
} catch (e) { } catch (e) {
@ -568,8 +557,8 @@ abstract class ExchangeViewModelBase with Store {
} else { } else {
try { try {
tradeState = TradeIsCreating(); tradeState = TradeIsCreating();
final trade = await provider.createTrade( final trade =
request: request!, isFixedRateMode: isFixedRateMode); await provider.createTrade(request: request!, isFixedRateMode: isFixedRateMode);
trade.walletId = wallet.id; trade.walletId = wallet.id;
tradesStore.setTrade(trade); tradesStore.setTrade(trade);
await trades.add(trade); await trades.add(trade);
@ -604,12 +593,8 @@ abstract class ExchangeViewModelBase with Store {
isReceiveAmountEntered = false; isReceiveAmountEntered = false;
depositAmount = ''; depositAmount = '';
receiveAmount = ''; receiveAmount = '';
depositAddress = depositCurrency == wallet.currency depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
? wallet.walletAddresses.address receiveAddress = receiveCurrency == wallet.currency ? wallet.walletAddresses.address : '';
: '';
receiveAddress = receiveCurrency == wallet.currency
? wallet.walletAddresses.address
: '';
isDepositAddressEnabled = !(depositCurrency == wallet.currency); isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
isFixedRateMode = false; isFixedRateMode = false;
@ -628,8 +613,7 @@ abstract class ExchangeViewModelBase with Store {
} }
final amount = availableBalance - fee; final amount = availableBalance - fee;
changeDepositAmount( changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
} }
} }
@ -664,9 +648,8 @@ abstract class ExchangeViewModelBase with Store {
List<ExchangeProvider> _providersForPair( List<ExchangeProvider> _providersForPair(
{required CryptoCurrency from, required CryptoCurrency to}) { {required CryptoCurrency from, required CryptoCurrency to}) {
final providers = providerList final providers = providerList
.where((provider) => provider.pairList .where((provider) =>
.where((pair) => pair.from == from && pair.to == to) provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
.isNotEmpty)
.toList(); .toList();
return providers; return providers;
@ -746,14 +729,12 @@ abstract class ExchangeViewModelBase with Store {
_bestRate = 0; _bestRate = 0;
_calculateBestRate(); _calculateBestRate();
final Map<String, dynamic> exchangeProvidersSelection = json.decode( final Map<String, dynamic> exchangeProvidersSelection =
sharedPreferences json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
.getString(PreferencesKey.exchangeProvidersSelection) ?? as Map<String, dynamic>;
"{}") as Map<String, dynamic>;
for (var provider in providerList) { for (var provider in providerList) {
exchangeProvidersSelection[provider.title] = exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider);
selectedProviders.contains(provider);
} }
sharedPreferences.setString( sharedPreferences.setString(
@ -764,15 +745,15 @@ abstract class ExchangeViewModelBase with Store {
bool get isAvailableInSelected { bool get isAvailableInSelected {
final providersForPair = providersForCurrentPair(); final providersForPair = providersForCurrentPair();
return selectedProviders.any( return selectedProviders
(element) => element.isAvailable && providersForPair.contains(element)); .any((element) => element.isAvailable && providersForPair.contains(element));
} }
void _setAvailableProviders() { void _setAvailableProviders() {
_tradeAvailableProviders.clear(); _tradeAvailableProviders.clear();
_tradeAvailableProviders.addAll(selectedProviders _tradeAvailableProviders.addAll(
.where((provider) => providersForCurrentPair().contains(provider))); selectedProviders.where((provider) => providersForCurrentPair().contains(provider)));
} }
@action @action
@ -780,16 +761,13 @@ abstract class ExchangeViewModelBase with Store {
switch (wallet.type) { switch (wallet.type) {
case WalletType.monero: case WalletType.monero:
case WalletType.haven: case WalletType.haven:
_settingsStore.priority[wallet.type] = _settingsStore.priority[wallet.type] = monero!.getMoneroTransactionPriorityAutomatic();
monero!.getMoneroTransactionPriorityAutomatic();
break; break;
case WalletType.bitcoin: case WalletType.bitcoin:
_settingsStore.priority[wallet.type] = _settingsStore.priority[wallet.type] = bitcoin!.getBitcoinTransactionPriorityMedium();
bitcoin!.getBitcoinTransactionPriorityMedium();
break; break;
case WalletType.litecoin: case WalletType.litecoin:
_settingsStore.priority[wallet.type] = _settingsStore.priority[wallet.type] = bitcoin!.getLitecoinTransactionPriorityMedium();
bitcoin!.getLitecoinTransactionPriorityMedium();
break; break;
default: default:
break; break;
@ -798,9 +776,7 @@ abstract class ExchangeViewModelBase with Store {
void _setProviders() { void _setProviders() {
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) { if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) {
providerList = _allProviders providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList();
.where((provider) => provider.supportsOnionAddress)
.toList();
} else { } else {
providerList = _allProviders; providerList = _allProviders;
} }

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_description.dart';
@ -15,7 +16,7 @@ import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_validator.dart'; import 'package:cake_wallet/core/amount_validator.dart';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/core/validator.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
@ -34,30 +35,38 @@ part 'send_view_model.g.dart';
class SendViewModel = SendViewModelBase with _$SendViewModel; class SendViewModel = SendViewModelBase with _$SendViewModel;
abstract class SendViewModelBase with Store { abstract class SendViewModelBase extends WalletChangeListenerViewModel with Store {
SendViewModelBase( @override
this._wallet, void onWalletChange(wallet) {
this._settingsStore, currencies = wallet.balance.keys.toList();
this.sendTemplateViewModel, selectedCryptoCurrency = wallet.currency;
this._fiatConversationStore, hasMultipleTokens = wallet.type == WalletType.ethereum;
this.balanceViewModel, }
this.contactListViewModel,
this.transactionDescriptionBox)
: state = InitialExecutionState(),
currencies = _wallet.balance.keys.toList(),
selectedCryptoCurrency = _wallet.currency,
hasMultipleTokens = _wallet.type == WalletType.ethereum,
outputs = ObservableList<Output>(),
fiatFromSettings = _settingsStore.fiatCurrency {
final priority = _settingsStore.priority[_wallet.type];
final priorities = priorityForWalletType(_wallet.type);
if (!priorityForWalletType(_wallet.type).contains(priority)) { SendViewModelBase(
_settingsStore.priority[_wallet.type] = priorities.first; AppStore appStore,
this.sendTemplateViewModel,
this._fiatConversationStore,
this.balanceViewModel,
this.contactListViewModel,
this.transactionDescriptionBox,
) : state = InitialExecutionState(),
currencies = appStore.wallet!.balance.keys.toList(),
selectedCryptoCurrency = appStore.wallet!.currency,
hasMultipleTokens = appStore.wallet!.type == WalletType.ethereum,
outputs = ObservableList<Output>(),
_settingsStore = appStore.settingsStore,
fiatFromSettings = appStore.settingsStore.fiatCurrency,
super(appStore: appStore) {
final priority = _settingsStore.priority[wallet.type];
final priorities = priorityForWalletType(wallet.type);
if (!priorityForWalletType(wallet.type).contains(priority)) {
_settingsStore.priority[wallet.type] = priorities.first;
} }
outputs outputs
.add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); .add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
} }
@observable @observable
@ -68,7 +77,7 @@ abstract class SendViewModelBase with Store {
@action @action
void addOutput() { void addOutput() {
outputs outputs
.add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); .add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
} }
@action @action
@ -107,9 +116,8 @@ abstract class SendViewModelBase with Store {
String get pendingTransactionFeeFiatAmount { String get pendingTransactionFeeFiatAmount {
try { try {
if (pendingTransaction != null) { if (pendingTransaction != null) {
final currency = walletType == WalletType.ethereum final currency =
? _wallet.currency walletType == WalletType.ethereum ? wallet.currency : selectedCryptoCurrency;
: selectedCryptoCurrency;
final fiat = calculateFiatAmount( final fiat = calculateFiatAmount(
price: _fiatConversationStore.prices[currency]!, price: _fiatConversationStore.prices[currency]!,
cryptoAmount: pendingTransaction!.feeFormatted); cryptoAmount: pendingTransaction!.feeFormatted);
@ -125,19 +133,19 @@ abstract class SendViewModelBase with Store {
FiatCurrency get fiat => _settingsStore.fiatCurrency; FiatCurrency get fiat => _settingsStore.fiatCurrency;
TransactionPriority get transactionPriority { TransactionPriority get transactionPriority {
final priority = _settingsStore.priority[_wallet.type]; final priority = _settingsStore.priority[wallet.type];
if (priority == null) { if (priority == null) {
throw Exception('Unexpected type ${_wallet.type}'); throw Exception('Unexpected type ${wallet.type}');
} }
return priority; return priority;
} }
CryptoCurrency get currency => _wallet.currency; CryptoCurrency get currency => wallet.currency;
Validator<String> get amountValidator => Validator<String> get amountValidator =>
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type)); AmountValidator(currency: walletTypeToCryptoCurrency(wallet.type));
Validator<String> get allAmountValidator => AllAmountValidator(); Validator<String> get allAmountValidator => AllAmountValidator();
@ -151,7 +159,7 @@ abstract class SendViewModelBase with Store {
PendingTransaction? pendingTransaction; PendingTransaction? pendingTransaction;
@computed @computed
String get balance => _wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance;
@computed @computed
bool get isFiatDisabled => balanceViewModel.isFiatDisabled; bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
@ -165,7 +173,7 @@ abstract class SendViewModelBase with Store {
isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title; isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title;
@computed @computed
bool get isReadyForSend => _wallet.syncStatus is SyncedSyncStatus; bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus;
@computed @computed
List<Template> get templates => sendTemplateViewModel.templates List<Template> get templates => sendTemplateViewModel.templates
@ -174,39 +182,40 @@ abstract class SendViewModelBase with Store {
@computed @computed
bool get hasCoinControl => bool get hasCoinControl =>
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin || _wallet.type == WalletType.monero; wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin ||
wallet.type == WalletType.monero;
@computed @computed
bool get isElectrumWallet => bool get isElectrumWallet =>
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin; wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin;
@observable @observable
CryptoCurrency selectedCryptoCurrency; CryptoCurrency selectedCryptoCurrency;
List<CryptoCurrency> currencies; List<CryptoCurrency> currencies;
bool get hasYat => outputs.any((out) => bool get hasYat => outputs
out.isParsedAddress && .any((out) => out.isParsedAddress && out.parsedAddress.parseFrom == ParseFrom.yatRecord);
out.parsedAddress.parseFrom == ParseFrom.yatRecord);
WalletType get walletType => _wallet.type; WalletType get walletType => wallet.type;
String? get walletCurrencyName => String? get walletCurrencyName => wallet.currency.fullName?.toLowerCase() ?? wallet.currency.name;
_wallet.currency.fullName?.toLowerCase() ?? _wallet.currency.name;
bool get hasCurrecyChanger => walletType == WalletType.haven; bool get hasCurrecyChanger => walletType == WalletType.haven;
@computed @computed
FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency;
final WalletBase _wallet;
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
final SendTemplateViewModel sendTemplateViewModel; final SendTemplateViewModel sendTemplateViewModel;
final BalanceViewModel balanceViewModel; final BalanceViewModel balanceViewModel;
final ContactListViewModel contactListViewModel; final ContactListViewModel contactListViewModel;
final FiatConversionStore _fiatConversationStore; final FiatConversionStore _fiatConversationStore;
final Box<TransactionDescription> transactionDescriptionBox; final Box<TransactionDescription> transactionDescriptionBox;
final bool hasMultipleTokens;
@observable
bool hasMultipleTokens;
@computed @computed
List<ContactRecord> get contactsToShow => contactListViewModel.contacts List<ContactRecord> get contactsToShow => contactListViewModel.contacts
@ -275,7 +284,7 @@ abstract class SendViewModelBase with Store {
Future<void> createTransaction() async { Future<void> createTransaction() async {
try { try {
state = IsExecutingState(); state = IsExecutingState();
pendingTransaction = await _wallet.createTransaction(_credentials()); pendingTransaction = await wallet.createTransaction(_credentials());
state = ExecutedSuccessfullyState(); state = ExecutedSuccessfullyState();
} catch (e) { } catch (e) {
state = FailureState(e.toString()); state = FailureState(e.toString());
@ -322,61 +331,60 @@ abstract class SendViewModelBase with Store {
@action @action
void setTransactionPriority(TransactionPriority priority) => void setTransactionPriority(TransactionPriority priority) =>
_settingsStore.priority[_wallet.type] = priority; _settingsStore.priority[wallet.type] = priority;
Object _credentials() { Object _credentials() {
switch (_wallet.type) { switch (wallet.type) {
case WalletType.bitcoin: case WalletType.bitcoin:
final priority = _settingsStore.priority[_wallet.type]; final priority = _settingsStore.priority[wallet.type];
if (priority == null) { if (priority == null) {
throw Exception('Priority is null for wallet type: ${_wallet.type}'); throw Exception('Priority is null for wallet type: ${wallet.type}');
} }
return bitcoin!.createBitcoinTransactionCredentials(outputs, priority: priority); return bitcoin!.createBitcoinTransactionCredentials(outputs, priority: priority);
case WalletType.litecoin: case WalletType.litecoin:
final priority = _settingsStore.priority[_wallet.type]; final priority = _settingsStore.priority[wallet.type];
if (priority == null) { if (priority == null) {
throw Exception('Priority is null for wallet type: ${_wallet.type}'); throw Exception('Priority is null for wallet type: ${wallet.type}');
} }
return bitcoin!.createBitcoinTransactionCredentials(outputs, priority: priority); return bitcoin!.createBitcoinTransactionCredentials(outputs, priority: priority);
case WalletType.monero: case WalletType.monero:
final priority = _settingsStore.priority[_wallet.type]; final priority = _settingsStore.priority[wallet.type];
if (priority == null) { if (priority == null) {
throw Exception('Priority is null for wallet type: ${_wallet.type}'); throw Exception('Priority is null for wallet type: ${wallet.type}');
} }
return monero! return monero!
.createMoneroTransactionCreationCredentials(outputs: outputs, priority: priority); .createMoneroTransactionCreationCredentials(outputs: outputs, priority: priority);
case WalletType.haven: case WalletType.haven:
final priority = _settingsStore.priority[_wallet.type]; final priority = _settingsStore.priority[wallet.type];
if (priority == null) { if (priority == null) {
throw Exception('Priority is null for wallet type: ${_wallet.type}'); throw Exception('Priority is null for wallet type: ${wallet.type}');
} }
return haven!.createHavenTransactionCreationCredentials( return haven!.createHavenTransactionCreationCredentials(
outputs: outputs, priority: priority, assetType: selectedCryptoCurrency.title); outputs: outputs, priority: priority, assetType: selectedCryptoCurrency.title);
case WalletType.ethereum: case WalletType.ethereum:
final priority = _settingsStore.priority[_wallet.type]; final priority = _settingsStore.priority[wallet.type];
if (priority == null) { if (priority == null) {
throw Exception('Priority is null for wallet type: ${_wallet.type}'); throw Exception('Priority is null for wallet type: ${wallet.type}');
} }
return ethereum!.createEthereumTransactionCredentials( return ethereum!.createEthereumTransactionCredentials(outputs,
outputs, priority: priority, currency: selectedCryptoCurrency); priority: priority, currency: selectedCryptoCurrency);
default: default:
throw Exception('Unexpected wallet type: ${_wallet.type}'); throw Exception('Unexpected wallet type: ${wallet.type}');
} }
} }
String displayFeeRate(dynamic priority) { String displayFeeRate(dynamic priority) {
final _priority = priority as TransactionPriority; final _priority = priority as TransactionPriority;
final wallet = _wallet;
if (isElectrumWallet) { if (isElectrumWallet) {
final rate = bitcoin!.getFeeRate(wallet, _priority); final rate = bitcoin!.getFeeRate(wallet, _priority);
@ -387,22 +395,21 @@ abstract class SendViewModelBase with Store {
} }
bool _isEqualCurrency(String currency) => bool _isEqualCurrency(String currency) =>
_wallet.balance.keys.any((e) => currency.toLowerCase() == e.title.toLowerCase()); wallet.balance.keys.any((e) => currency.toLowerCase() == e.title.toLowerCase());
@action @action
void onClose() => _settingsStore.fiatCurrency = fiatFromSettings; void onClose() => _settingsStore.fiatCurrency = fiatFromSettings;
@action @action
void setFiatCurrency(FiatCurrency fiat) => void setFiatCurrency(FiatCurrency fiat) => _settingsStore.fiatCurrency = fiat;
_settingsStore.fiatCurrency = fiat;
@action @action
void setSelectedCryptoCurrency(String cryptoCurrency) { void setSelectedCryptoCurrency(String cryptoCurrency) {
try { try {
selectedCryptoCurrency = _wallet.balance.keys selectedCryptoCurrency = wallet.balance.keys
.firstWhere((e) => cryptoCurrency.toLowerCase() == e.title.toLowerCase()); .firstWhere((e) => cryptoCurrency.toLowerCase() == e.title.toLowerCase());
} catch (e) { } catch (e) {
selectedCryptoCurrency = _wallet.currency; selectedCryptoCurrency = wallet.currency;
} }
} }
} }

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
@ -5,16 +6,12 @@ import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cw_core/currency.dart'; import 'package:cw_core/currency.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/utils/list_item.dart'; import 'package:cake_wallet/utils/list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
@ -110,29 +107,36 @@ class EthereumURI extends PaymentURI {
} }
} }
abstract class WalletAddressListViewModelBase with Store { abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
WalletAddressListViewModelBase({ WalletAddressListViewModelBase({
required AppStore appStore, required AppStore appStore,
required this.yatStore, required this.yatStore,
required this.fiatConversionStore, required this.fiatConversionStore,
}) : _appStore = appStore, }) : _baseItems = <ListItem>[],
_baseItems = <ListItem>[],
_wallet = appStore.wallet!,
selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type),
_cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern),
hasAccounts = hasAccounts =
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven, appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
amount = '' { amount = '',
super(appStore: appStore) {
_init(); _init();
} }
@override
void onWalletChange(wallet) {
_init();
selectedCurrency = walletTypeToCryptoCurrency(wallet.type);
hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven;
}
static const String _cryptoNumberPattern = '0.00000000'; static const String _cryptoNumberPattern = '0.00000000';
final NumberFormat _cryptoNumberFormat; final NumberFormat _cryptoNumberFormat;
final FiatConversionStore fiatConversionStore; final FiatConversionStore fiatConversionStore;
List<Currency> get currencies => [walletTypeToCryptoCurrency(_wallet.type), ...FiatCurrency.all]; List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
@observable @observable
Currency selectedCurrency; Currency selectedCurrency;
@ -144,31 +148,31 @@ abstract class WalletAddressListViewModelBase with Store {
String amount; String amount;
@computed @computed
WalletType get type => _wallet.type; WalletType get type => wallet.type;
@computed @computed
WalletAddressListItem get address => WalletAddressListItem get address =>
WalletAddressListItem(address: _wallet.walletAddresses.address, isPrimary: false); WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
@computed @computed
PaymentURI get uri { PaymentURI get uri {
if (_wallet.type == WalletType.monero) { if (wallet.type == WalletType.monero) {
return MoneroURI(amount: amount, address: address.address); return MoneroURI(amount: amount, address: address.address);
} }
if (_wallet.type == WalletType.haven) { if (wallet.type == WalletType.haven) {
return HavenURI(amount: amount, address: address.address); return HavenURI(amount: amount, address: address.address);
} }
if (_wallet.type == WalletType.bitcoin) { if (wallet.type == WalletType.bitcoin) {
return BitcoinURI(amount: amount, address: address.address); return BitcoinURI(amount: amount, address: address.address);
} }
if (_wallet.type == WalletType.litecoin) { if (wallet.type == WalletType.litecoin) {
return LitecoinURI(amount: amount, address: address.address); return LitecoinURI(amount: amount, address: address.address);
} }
if (_wallet.type == WalletType.ethereum) { if (wallet.type == WalletType.ethereum) {
return EthereumURI(amount: amount, address: address.address); return EthereumURI(amount: amount, address: address.address);
} }
@ -182,7 +186,6 @@ abstract class WalletAddressListViewModelBase with Store {
@computed @computed
ObservableList<ListItem> get addressList { ObservableList<ListItem> get addressList {
final wallet = _wallet;
final addressList = ObservableList<ListItem>(); final addressList = ObservableList<ListItem>();
if (wallet.type == WalletType.monero) { if (wallet.type == WalletType.monero) {
@ -237,8 +240,6 @@ abstract class WalletAddressListViewModelBase with Store {
@computed @computed
String get accountLabel { String get accountLabel {
final wallet = _wallet;
if (wallet.type == WalletType.monero) { if (wallet.type == WalletType.monero) {
return monero!.getCurrentAccount(wallet).label; return monero!.getCurrentAccount(wallet).label;
} }
@ -251,29 +252,24 @@ abstract class WalletAddressListViewModelBase with Store {
} }
@computed @computed
bool get hasAddressList => _wallet.type == WalletType.monero || _wallet.type == WalletType.haven; bool get hasAddressList => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
@computed @computed
bool get showElectrumAddressDisclaimer => bool get showElectrumAddressDisclaimer =>
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin; wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin;
@observable
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
List<ListItem> _baseItems; List<ListItem> _baseItems;
AppStore _appStore;
final YatStore yatStore; final YatStore yatStore;
@action @action
void setAddress(WalletAddressListItem address) => void setAddress(WalletAddressListItem address) =>
_wallet.walletAddresses.address = address.address; wallet.walletAddresses.address = address.address;
void _init() { void _init() {
_baseItems = []; _baseItems = [];
if (_wallet.type == WalletType.monero || _wallet.type == WalletType.haven) { if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
_baseItems.add(WalletAccountListHeader()); _baseItems.add(WalletAccountListHeader());
} }
@ -294,7 +290,7 @@ abstract class WalletAddressListViewModelBase with Store {
} }
void _convertAmountToCrypto() { void _convertAmountToCrypto() {
final cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type); final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
try { try {
final crypto = final crypto =
double.parse(amount.replaceAll(',', '.')) / fiatConversionStore.prices[cryptoCurrency]!; double.parse(amount.replaceAll(',', '.')) / fiatConversionStore.prices[cryptoCurrency]!;