Merge branch 'flutter-upgrade' of https://github.com/cake-tech/cake_wallet into CW-182-allow-trusted-nodes

This commit is contained in:
OmarHatem
2022-11-03 21:34:20 +02:00
96 changed files with 1383 additions and 671 deletions
+5 -2
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/view_model/auth_state.dart';
@@ -55,8 +56,10 @@ abstract class AuthViewModelBase with Store {
final isSuccessfulAuthenticated = await _authService.authenticate(password);
if (isSuccessfulAuthenticated) {
state = ExecutedSuccessfullyState();
_failureCounter = 0;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
state = ExecutedSuccessfullyState();
_failureCounter = 0;
});
} else {
_failureCounter += 1;
+16
View File
@@ -8,6 +8,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:mobx/mobx.dart';
import 'package:intl/intl.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:path_provider/path_provider.dart';
part 'backup_view_model.g.dart';
@@ -71,6 +72,21 @@ abstract class BackupViewModelBase with Store {
}
}
Future<String> saveBackupFileLocally(BackupExportFile backup) async {
final appDir = await getApplicationDocumentsDirectory();
final path = '${appDir.path}/${backup.name}';
final backupFile = File(path);
await backupFile.writeAsBytes(backup.content);
return path;
}
Future<void> removeBackupFileLocally(BackupExportFile backup) async {
final appDir = await getApplicationDocumentsDirectory();
final path = '${appDir.path}/${backup.name}';
final backupFile = File(path);
await backupFile.delete();
}
@action
void showMasterPassword() => isBackupPasswordVisible = true;
+1 -2
View File
@@ -6,7 +6,6 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/buy/buy_item.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
@@ -103,7 +102,7 @@ abstract class BuyViewModelBase with Store {
print(e.toString());
}
if (isMoonPayEnabled ?? false) {
if (isMoonPayEnabled) {
_providerList.add(MoonPayBuyProvider(wallet: wallet));
}
+181 -144
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
@@ -5,6 +6,10 @@ import 'package:cake_wallet/entities/preferences_key.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/view_model/settings/settings_view_model.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/crypto_currency.dart';
@@ -39,7 +44,7 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
abstract class ExchangeViewModelBase with Store {
ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore,
this.tradesStore, this._settingsStore, this.sharedPreferences)
this.tradesStore, this._settingsStore, this.sharedPreferences, this._settingsViewModel)
: _cryptoNumberFormat = NumberFormat(),
isReverse = false,
isFixedRateMode = false,
@@ -59,12 +64,12 @@ abstract class ExchangeViewModelBase with Store {
receiveCurrency = wallet.currency,
depositCurrency = wallet.currency,
providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider()],
selectedProviders = ObservableList<ExchangeProvider>(),
currentTradeAvailableProviders = SplayTreeMap<double, ExchangeProvider>() {
selectedProviders = ObservableList<ExchangeProvider>() {
const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano];
const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp,
CryptoCurrency.bnb, CryptoCurrency.btt, CryptoCurrency.nano];
_initialPairBasedOnWallet();
final Map<String, dynamic> exchangeProvidersSelection = json
.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map<String, dynamic>;
@@ -76,6 +81,11 @@ abstract class ExchangeViewModelBase with Store {
: (exchangeProvidersSelection[element.title] as bool))
.toList());
_setAvailableProviders();
_calculateBestRate();
bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
depositAmount = '';
@@ -119,8 +129,15 @@ abstract class ExchangeViewModelBase with Store {
/// Maps in dart are not sorted by default
/// SplayTreeMap is a map sorted by keys
/// will use it to sort available providers
/// depending on the amount they yield for the current trade
SplayTreeMap<double, ExchangeProvider> currentTradeAvailableProviders;
/// based on the rate they yield for the current trade
///
///
/// initialize with descending comparator
/// since we want largest rate first
final SplayTreeMap<double, ExchangeProvider> _sortedAvailableProviders =
SplayTreeMap<double, ExchangeProvider>((double a, double b) => b.compareTo(a));
final List<ExchangeProvider> _tradeAvailableProviders = [];
@observable
ObservableList<ExchangeProvider> selectedProviders;
@@ -179,6 +196,20 @@ abstract class ExchangeViewModelBase with Store {
bool get isMoneroWallet => wallet.type == WalletType.monero;
bool get isLowFee {
switch (wallet.type) {
case WalletType.monero:
case WalletType.haven:
return _settingsViewModel.transactionPriority == MoneroTransactionPriority.slow;
case WalletType.bitcoin:
return _settingsViewModel.transactionPriority == BitcoinTransactionPriority.slow;
case WalletType.litecoin:
return _settingsViewModel.transactionPriority == LitecoinTransactionPriority.slow;
default:
return false;
}
}
List<CryptoCurrency> receiveCurrencies;
List<CryptoCurrency> depositCurrencies;
@@ -191,6 +222,12 @@ abstract class ExchangeViewModelBase with Store {
final SettingsStore _settingsStore;
final SettingsViewModel _settingsViewModel;
double _bestRate = 0.0;
late Timer bestRateSync;
@action
void changeDepositCurrency({required CryptoCurrency currency}) {
depositCurrency = currency;
@@ -210,68 +247,11 @@ abstract class ExchangeViewModelBase with Store {
}
@action
void changeReceiveAmount({required String amount}) {
Future<void> changeReceiveAmount({required String amount}) async {
receiveAmount = amount;
isReverse = true;
if (amount == null || amount.isEmpty) {
depositAmount = '';
receiveAmount = '';
return;
}
final _enteredAmount = double.parse(amount.replaceAll(',', '.')) ?? 0;
currentTradeAvailableProviders.clear();
for (var provider in selectedProviders) {
/// if this provider is not valid for the current pair, skip it
if (!providersForCurrentPair().contains(provider)) {
continue;
}
provider
.calculateAmount(
from: receiveCurrency,
to: depositCurrency,
amount: _enteredAmount,
isFixedRateMode: isFixedRateMode,
isReceiveAmount: true)
.then((amount) {
final from = isFixedRateMode
? receiveCurrency
: depositCurrency;
final to = isFixedRateMode
? depositCurrency
: receiveCurrency;
provider.fetchLimits(
from: from,
to: to,
isFixedRateMode: isFixedRateMode,
).then((limits) {
/// if the entered amount doesn't exceed the limits of this provider
if ((limits?.max ?? double.maxFinite) >= _enteredAmount
&& (limits?.min ?? 0) <= _enteredAmount) {
/// add this provider as its valid for this trade
/// will be sorted ascending already since
/// we seek the least deposit amount
currentTradeAvailableProviders[amount] = provider;
}
return amount;
}).then((amount) => depositAmount = _cryptoNumberFormat
.format(amount)
.toString()
.replaceAll(RegExp('\\,'), ''));
});
}
}
@action
void changeDepositAmount({required String amount}) {
depositAmount = amount;
isReverse = false;
if (amount == null || amount.isEmpty) {
if (amount.isEmpty) {
depositAmount = '';
receiveAmount = '';
return;
@@ -279,93 +259,115 @@ abstract class ExchangeViewModelBase with Store {
final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0;
currentTradeAvailableProviders.clear();
for (var provider in selectedProviders) {
/// if this provider is not valid for the current pair, skip it
if (!providersForCurrentPair().contains(provider)) {
continue;
if (_bestRate == 0) {
depositAmount = S.current.fetching;
await _calculateBestRate();
}
depositAmount = _cryptoNumberFormat
.format(_enteredAmount / _bestRate)
.toString()
.replaceAll(RegExp('\\,'), '');
}
@action
Future<void> changeDepositAmount({required String amount}) async {
depositAmount = amount;
isReverse = false;
if (amount.isEmpty) {
depositAmount = '';
receiveAmount = '';
return;
}
final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0;
/// in case the best rate was not calculated yet
if (_bestRate == 0) {
receiveAmount = S.current.fetching;
await _calculateBestRate();
}
receiveAmount = _cryptoNumberFormat
.format(_bestRate * _enteredAmount)
.toString()
.replaceAll(RegExp('\\,'), '');
}
Future<void> _calculateBestRate() async {
final result = await Future.wait<double>(
_tradeAvailableProviders
.map((element) => element.calculateAmount(
from: depositCurrency,
to: receiveCurrency,
amount: 1,
isFixedRateMode: isFixedRateMode,
isReceiveAmount: false))
);
_sortedAvailableProviders.clear();
for (int i=0;i<result.length;i++) {
if (result[i] != 0) {
/// add this provider as its valid for this trade
_sortedAvailableProviders[result[i]] = _tradeAvailableProviders[i];
}
provider
.calculateAmount(
from: depositCurrency,
to: receiveCurrency,
amount: _enteredAmount,
isFixedRateMode: isFixedRateMode,
isReceiveAmount: false)
.then((amount) {
final from = isFixedRateMode
? receiveCurrency
: depositCurrency;
final to = isFixedRateMode
? depositCurrency
: receiveCurrency;
provider.fetchLimits(
from: from,
to: to,
isFixedRateMode: isFixedRateMode,
).then((limits) {
/// if the entered amount doesn't exceed the limits of this provider
if ((limits?.max ?? double.maxFinite) >= _enteredAmount
&& (limits?.min ?? 0) <= _enteredAmount) {
/// add this provider as its valid for this trade
/// subtract from maxFinite so the provider
/// with the largest amount would be sorted ascending
currentTradeAvailableProviders[double.maxFinite - amount] = provider;
}
return amount;
}).then((amount) => receiveAmount =
receiveAmount = _cryptoNumberFormat
.format(amount)
.toString()
.replaceAll(RegExp('\\,'), ''));
});
}
if (_sortedAvailableProviders.isNotEmpty) {
_bestRate = _sortedAvailableProviders.keys.first;
}
}
@action
Future loadLimits() async {
Future<void> loadLimits() async {
if (selectedProviders.isEmpty) {
return;
}
limitsState = LimitsIsLoading();
try {
final from = isFixedRateMode
final from = isFixedRateMode
? receiveCurrency
: depositCurrency;
final to = isFixedRateMode
final to = isFixedRateMode
? depositCurrency
: receiveCurrency;
limits = await selectedProviders.first.fetchLimits(
from: from,
to: to,
isFixedRateMode: isFixedRateMode);
double lowestMin = double.maxFinite;
double? highestMax = 0.0;
/// if the first provider limits is bounded then check with other providers
/// for the highest maximum limit
if (limits.max != null) {
for (int i = 1;i < selectedProviders.length;i++) {
final Limits tempLimits = await selectedProviders[i].fetchLimits(
from: from,
to: to,
isFixedRateMode: isFixedRateMode);
/// set the limits with the maximum provider limit
/// if there is a provider with null max then it's the maximum limit
if ((tempLimits.max ?? double.maxFinite) > limits.max!) {
limits = tempLimits;
}
}
for (var provider in selectedProviders) {
/// if this provider is not valid for the current pair, skip it
if (!providersForCurrentPair().contains(provider)) {
continue;
}
try {
final tempLimits = await provider.fetchLimits(
from: from,
to: to,
isFixedRateMode: isFixedRateMode);
if (tempLimits.min != null && tempLimits.min! < lowestMin) {
lowestMin = tempLimits.min!;
}
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
highestMax = tempLimits.max;
}
} catch (e) {
continue;
}
}
if (lowestMin < double.maxFinite) {
limits = Limits(min: lowestMin, max: highestMax);
limitsState = LimitsLoadedSuccessfully(limits: limits);
} catch (e) {
limitsState = LimitsLoadedFailure(error: e.toString());
} else {
limitsState = LimitsLoadedFailure(error: 'Limits loading failed');
}
}
@@ -374,7 +376,7 @@ abstract class ExchangeViewModelBase with Store {
TradeRequest? request;
String amount = '';
for (var provider in currentTradeAvailableProviders.values) {
for (var provider in _sortedAvailableProviders.values) {
if (!(await provider.checkIsAvailable())) {
continue;
}
@@ -383,7 +385,7 @@ abstract class ExchangeViewModelBase with Store {
request = SideShiftRequest(
depositMethod: depositCurrency,
settleMethod: receiveCurrency,
depositAmount: depositAmount?.replaceAll(',', '.') ?? '',
depositAmount: depositAmount.replaceAll(',', '.'),
settleAddress: receiveAddress,
refundAddress: depositAddress,
);
@@ -394,7 +396,7 @@ abstract class ExchangeViewModelBase with Store {
request = SimpleSwapRequest(
from: depositCurrency,
to: receiveCurrency,
amount: depositAmount?.replaceAll(',', '.') ?? '',
amount: depositAmount.replaceAll(',', '.'),
address: receiveAddress,
refundAddress: depositAddress,
);
@@ -405,8 +407,8 @@ abstract class ExchangeViewModelBase with Store {
request = XMRTOTradeRequest(
from: depositCurrency,
to: receiveCurrency,
amount: depositAmount?.replaceAll(',', '.') ?? '',
receiveAmount: receiveAmount?.replaceAll(',', '.') ?? '',
amount: depositAmount.replaceAll(',', '.'),
receiveAmount: receiveAmount.replaceAll(',', '.'),
address: receiveAddress,
refundAddress: depositAddress,
isBTCRequest: isReceiveAmountEntered);
@@ -417,8 +419,8 @@ abstract class ExchangeViewModelBase with Store {
request = ChangeNowRequest(
from: depositCurrency,
to: receiveCurrency,
fromAmount: depositAmount?.replaceAll(',', '.') ?? '',
toAmount: receiveAmount?.replaceAll(',', '.') ?? '',
fromAmount: depositAmount.replaceAll(',', '.'),
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress,
isReverse: isReverse);
@@ -429,7 +431,7 @@ abstract class ExchangeViewModelBase with Store {
request = MorphTokenRequest(
from: depositCurrency,
to: receiveCurrency,
amount: depositAmount?.replaceAll(',', '.') ?? '',
amount: depositAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress);
amount = depositAmount;
@@ -437,7 +439,7 @@ abstract class ExchangeViewModelBase with Store {
amount = amount.replaceAll(',', '.');
if (limitsState is LimitsLoadedSuccessfully && amount != null) {
if (limitsState is LimitsLoadedSuccessfully) {
if (double.parse(amount) < limits.min!) {
continue;
} else if (limits.max != null && double.parse(amount) > limits.max!) {
@@ -527,7 +529,7 @@ abstract class ExchangeViewModelBase with Store {
final providers = providerList
.where((provider) => provider.pairList
.where((pair) =>
pair.from == (from ?? depositCurrency) && pair.to == (to ?? receiveCurrency))
pair.from == from && pair.to == to)
.isNotEmpty)
.toList();
@@ -537,6 +539,10 @@ abstract class ExchangeViewModelBase with Store {
void _onPairChange() {
depositAmount = '';
receiveAmount = '';
loadLimits();
_setAvailableProviders();
_bestRate = 0;
_calculateBestRate();
}
void _initialPairBasedOnWallet() {
@@ -579,11 +585,15 @@ abstract class ExchangeViewModelBase with Store {
@action
void addExchangeProvider(ExchangeProvider provider) {
selectedProviders.add(provider);
if (providersForCurrentPair().contains(provider)) {
_tradeAvailableProviders.add(provider);
}
}
@action
void removeExchangeProvider(ExchangeProvider provider) {
selectedProviders.remove(provider);
_tradeAvailableProviders.remove(provider);
}
@action
@@ -593,13 +603,14 @@ abstract class ExchangeViewModelBase with Store {
isFixedRateMode = false;
_defineIsReceiveAmountEditable();
loadLimits();
_bestRate = 0;
_calculateBestRate();
final Map<String, dynamic> exchangeProvidersSelection = json
.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map<String, dynamic>;
exchangeProvidersSelection.updateAll((key, dynamic value) => false);
for (var provider in selectedProviders) {
exchangeProvidersSelection[provider.title] = true;
for (var provider in providerList) {
exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider);
}
sharedPreferences.setString(
@@ -612,4 +623,30 @@ abstract class ExchangeViewModelBase with Store {
final providersForPair = providersForCurrentPair();
return selectedProviders.any((element) => element.isAvailable && providersForPair.contains(element));
}
void _setAvailableProviders() {
_tradeAvailableProviders.clear();
_tradeAvailableProviders.addAll(
selectedProviders
.where((provider) => providersForCurrentPair().contains(provider)));
}
@action
void setDefaultTransactionPriority() {
switch (wallet.type) {
case WalletType.monero:
case WalletType.haven:
_settingsStore.priority[wallet.type] = MoneroTransactionPriority.automatic;
break;
case WalletType.bitcoin:
_settingsStore.priority[wallet.type] = BitcoinTransactionPriority.medium;
break;
case WalletType.litecoin:
_settingsStore.priority[wallet.type] = LitecoinTransactionPriority.medium;
break;
default:
break;
}
}
}
@@ -64,7 +64,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
} catch (e) {
var msg = e.toString();
if (msg == 'Message authentication code (MAC) is invalid') {
if (msg.toLowerCase().contains("message authentication code (mac)")) {
msg = 'Incorrect backup password';
}
+1 -2
View File
@@ -1,7 +1,6 @@
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
@@ -276,7 +275,7 @@ abstract class SendViewModelBase with Store {
if (isElectrumWallet) {
final rate = bitcoin!.getFeeRate(wallet, _priority);
return '${(priority as BitcoinTransactionPriority).labelWithRate(rate)}';
return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate);
}
return priority.toString();
@@ -1,7 +1,6 @@
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:flutter/cupertino.dart';
import 'package:mobx/mobx.dart';
import 'package:package_info/package_info.dart';
@@ -137,7 +136,7 @@ abstract class SettingsViewModelBase with Store {
if (wallet.type == WalletType.bitcoin
|| wallet.type == WalletType.litecoin) {
final rate = bitcoin!.getFeeRate(wallet, _priority);
return '${(priority as BitcoinTransactionPriority).labelWithRate(rate)}';
return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate);
}
return priority.toString();
+10
View File
@@ -53,6 +53,16 @@ abstract class SupportViewModelBase with Store {
icon: 'assets/images/change_now.png',
linkTitle: 'support@changenow.io',
link: 'mailto:support@changenow.io'),
LinkListItem(
title: 'SideShift',
icon: 'assets/images/sideshift.png',
linkTitle: S.current.help,
link: 'https://help.sideshift.ai/en/'),
LinkListItem(
title: 'SimpleSwap',
icon: 'assets/images/simpleSwap.png',
linkTitle: 'support@simpleswap.io',
link: 'mailto:support@simpleswap.io'),
if (!isMoneroOnly) ... [
LinkListItem(
title: 'Wyre',