Merge pull request #176 from cake-tech/CAKE-234

CAKE-234
This commit is contained in:
M 2021-01-27 18:28:30 +02:00
commit c4a973e9c5
21 changed files with 427 additions and 236 deletions

View file

@ -357,7 +357,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 19; CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -498,7 +498,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 19; CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -533,7 +533,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 19; CURRENT_PROJECT_VERSION = 20;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (

View file

@ -1,9 +1,9 @@
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
class BitcoinTransactionCredentials { class BitcoinTransactionCredentials {
BitcoinTransactionCredentials(this.address, this.amount, this.priority); BitcoinTransactionCredentials(this.address, this.amount, this.priority);
final String address; final String address;
final String amount; final String amount;
TransactionPriority priority; BitcoinTransactionPriority priority;
} }

View file

@ -102,8 +102,12 @@ abstract class BitcoinTransactionHistoryBase
BitcoinTransactionInfo get(String id) => transactions[id]; BitcoinTransactionInfo get(String id) => transactions[id];
Future<void> save() async { Future<void> save() async {
final data = json.encode({'height': _height, 'transactions': transactions}); try {
await writeData(path: path, password: _password, data: data); final data = json.encode({'height': _height, 'transactions': transactions});
await writeData(path: path, password: _password, data: data);
} catch(e) {
print('Error while save bitcoin transaction history: ${e.toString()}');
}
} }
@override @override
@ -170,6 +174,10 @@ abstract class BitcoinTransactionHistoryBase
} }
void _updateOrInsert(BitcoinTransactionInfo transaction) { void _updateOrInsert(BitcoinTransactionInfo transaction) {
if (transaction.id == null) {
return;
}
if (transactions[transaction.id] == null) { if (transactions[transaction.id] == null) {
transactions[transaction.id] = transaction; transactions[transaction.id] = transaction;
} else { } else {

View file

@ -0,0 +1,29 @@
import 'package:cake_wallet/entities/transaction_priority.dart';
class BitcoinTransactionPriority extends TransactionPriority {
const BitcoinTransactionPriority(this.rate, {String title, int raw})
: super(title: title, raw: raw);
static const List<BitcoinTransactionPriority> all = [slow, medium, fast];
static const BitcoinTransactionPriority slow = BitcoinTransactionPriority(11, title: 'Slow', raw: 0);
static const BitcoinTransactionPriority medium = BitcoinTransactionPriority(90, title: 'Medium', raw: 1);
static const BitcoinTransactionPriority fast = BitcoinTransactionPriority(98, title: 'Fast', raw: 2);
static BitcoinTransactionPriority deserialize({int raw}) {
switch (raw) {
case 0:
return slow;
case 2:
return medium;
case 3:
return fast;
default:
return null;
}
}
final int rate;
@override
String toString() => '$rate sat/byte';
}

View file

@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:cake_wallet/bitcoin/address_to_output_script.dart'; import 'package:cake_wallet/bitcoin/address_to_output_script.dart';
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@ -17,7 +19,6 @@ import 'package:cake_wallet/bitcoin/script_hash.dart';
import 'package:cake_wallet/bitcoin/utils.dart'; import 'package:cake_wallet/bitcoin/utils.dart';
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_history.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_history.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
@ -53,6 +54,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
_password = password, _password = password,
_accountIndex = accountIndex, _accountIndex = accountIndex,
super(walletInfo) { super(walletInfo) {
_unspent = [];
_scripthashesUpdateSubject = {}; _scripthashesUpdateSubject = {};
} }
@ -116,18 +118,12 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
walletInfo: walletInfo); walletInfo: walletInfo);
} }
static int feeAmountForPriority(TransactionPriority priority) { static int feeAmountForPriority(BitcoinTransactionPriority priority,
switch (priority) { int inputsCount, int outputsCount) =>
case TransactionPriority.slow: priority.rate * estimatedTransactionSize(inputsCount, outputsCount);
return 6000;
case TransactionPriority.regular: static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
return 22080; inputsCount * 146 + outputsCounts * 33 + 8;
case TransactionPriority.fast:
return 24000;
default:
return 0;
}
}
@override @override
final BitcoinTransactionHistory transactionHistory; final BitcoinTransactionHistory transactionHistory;
@ -136,6 +132,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
final ElectrumClient eclient; final ElectrumClient eclient;
final String mnemonic; final String mnemonic;
List<BitcoinUnspent> _unspent;
@override @override
@observable @observable
String address; String address;
@ -234,6 +232,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
}); });
_subscribeForUpdates(); _subscribeForUpdates();
await _updateBalance(); await _updateBalance();
await _updateUnspent();
syncStatus = SyncedSyncStatus(); syncStatus = SyncedSyncStatus();
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
@ -264,37 +263,25 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
Object credentials) async { Object credentials) async {
final transactionCredentials = credentials as BitcoinTransactionCredentials; final transactionCredentials = credentials as BitcoinTransactionCredentials;
final inputs = <BitcoinUnspent>[]; final inputs = <BitcoinUnspent>[];
final fee = feeAmountForPriority(transactionCredentials.priority); final allAmountFee =
calculateEstimatedFee(transactionCredentials.priority, null);
var fee = 0;
final amount = transactionCredentials.amount != null final amount = transactionCredentials.amount != null
? stringDoubleToBitcoinAmount(transactionCredentials.amount) ? stringDoubleToBitcoinAmount(transactionCredentials.amount)
: balance.confirmed - fee; : balance.confirmed - allAmountFee;
final totalAmount = amount + fee;
final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin); final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin);
final changeAddress = address; final changeAddress = address;
var leftAmount = totalAmount; var leftAmount = amount;
var totalInputAmount = 0; var totalInputAmount = 0;
if (totalAmount > balance.confirmed) { if (_unspent.isEmpty) {
throw BitcoinTransactionWrongBalanceException(); await _updateUnspent();
} }
final unspent = addresses.map((address) => eclient for (final utx in _unspent) {
.getListUnspentWithAddress(address.address) leftAmount = leftAmount - utx.value;
.then((unspent) => unspent totalInputAmount += utx.value;
.map((unspent) => BitcoinUnspent.fromJSON(address, unspent)))); inputs.add(utx);
for (final unptsFutures in unspent) {
final utxs = await unptsFutures;
for (final utx in utxs) {
leftAmount = leftAmount - utx.value;
totalInputAmount += utx.value;
inputs.add(utx);
if (leftAmount <= 0) {
break;
}
}
if (leftAmount <= 0) { if (leftAmount <= 0) {
break; break;
@ -305,11 +292,19 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
throw BitcoinTransactionNoInputsException(); throw BitcoinTransactionNoInputsException();
} }
if (amount <= 0 || totalInputAmount < amount) { final totalAmount = amount + fee;
fee = transactionCredentials.amount != null
? feeAmountForPriority(
transactionCredentials.priority, inputs.length, 2)
: allAmountFee;
if (totalAmount > balance.confirmed) {
throw BitcoinTransactionWrongBalanceException(); throw BitcoinTransactionWrongBalanceException();
} }
final changeValue = totalInputAmount - amount - fee; if (amount <= 0 || totalInputAmount < amount) {
throw BitcoinTransactionWrongBalanceException();
}
txb.setVersion(1); txb.setVersion(1);
@ -330,6 +325,10 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
txb.addOutput( txb.addOutput(
addressToOutputScript(transactionCredentials.address), amount); addressToOutputScript(transactionCredentials.address), amount);
final estimatedSize = estimatedTransactionSize(inputs.length, 2);
final feeAmount = transactionCredentials.priority.rate * estimatedSize;
final changeValue = totalInputAmount - amount - feeAmount;
if (changeValue > 0) { if (changeValue > 0) {
txb.addOutput(changeAddress, changeValue); txb.addOutput(changeAddress, changeValue);
} }
@ -358,8 +357,30 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
}); });
@override @override
double calculateEstimatedFee(TransactionPriority priority) => int calculateEstimatedFee(TransactionPriority priority, int amount) {
bitcoinAmountToDouble(amount: feeAmountForPriority(priority)); if (priority is BitcoinTransactionPriority) {
int inputsCount = 0;
if (amount != null) {
int totalValue = 0;
for (final input in _unspent) {
if (totalValue >= amount) {
break;
}
totalValue += input.value;
inputsCount += 1;
}
} else {
inputsCount = _unspent.length;
}
return feeAmountForPriority(priority, inputsCount, 2);
}
return 0;
}
@override @override
Future<void> save() async { Future<void> save() async {
@ -380,13 +401,26 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
await eclient.close(); await eclient.close();
} }
Future<void> _updateUnspent() async {
final unspent = await Future.wait(addresses.map((address) => eclient
.getListUnspentWithAddress(address.address)
.then((unspent) => unspent
.map((unspent) => BitcoinUnspent.fromJSON(address, unspent)))));
_unspent = unspent.expand((e) => e).toList();
}
void _subscribeForUpdates() { void _subscribeForUpdates() {
scriptHashes.forEach((sh) async { scriptHashes.forEach((sh) async {
await _scripthashesUpdateSubject[sh]?.close(); await _scripthashesUpdateSubject[sh]?.close();
_scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh); _scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh);
_scripthashesUpdateSubject[sh].listen((event) async { _scripthashesUpdateSubject[sh].listen((event) async {
await _updateBalance(); try {
transactionHistory.updateAsync(); await _updateBalance();
await _updateUnspent();
transactionHistory.updateAsync();
} catch (e) {
print(e.toString());
}
}); });
}); });
} }

View file

@ -22,8 +22,9 @@ String jsonrpcparams(List<Object> params) {
} }
String jsonrpc( String jsonrpc(
{String method, List<Object> params, int id, double version = 2.0}) => {String method, List<Object> params, int id, double version = 2.0}) =>
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n'; '{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json
.encode(params)}}\n';
class SocketTask { class SocketTask {
SocketTask({this.completer, this.isSubscription, this.subject}); SocketTask({this.completer, this.isSubscription, this.subject});
@ -76,7 +77,7 @@ class ElectrumClient {
socket.listen((Uint8List event) { socket.listen((Uint8List event) {
try { try {
final response = final response =
json.decode(utf8.decode(event.toList())) as Map<String, Object>; json.decode(utf8.decode(event.toList())) as Map<String, Object>;
_handleResponse(response); _handleResponse(response);
} on FormatException catch (e) { } on FormatException catch (e) {
final msg = e.message.toLowerCase(); final msg = e.message.toLowerCase();
@ -92,7 +93,7 @@ class ElectrumClient {
if (isJSONStringCorrect(unterminatedString)) { if (isJSONStringCorrect(unterminatedString)) {
final response = final response =
json.decode(unterminatedString) as Map<String, Object>; json.decode(unterminatedString) as Map<String, Object>;
_handleResponse(response); _handleResponse(response);
unterminatedString = ''; unterminatedString = '';
} }
@ -106,7 +107,7 @@ class ElectrumClient {
if (isJSONStringCorrect(unterminatedString)) { if (isJSONStringCorrect(unterminatedString)) {
final response = final response =
json.decode(unterminatedString) as Map<String, Object>; json.decode(unterminatedString) as Map<String, Object>;
_handleResponse(response); _handleResponse(response);
unterminatedString = null; unterminatedString = null;
} }
@ -172,7 +173,7 @@ class ElectrumClient {
}); });
Future<List<Map<String, dynamic>>> getListUnspentWithAddress( Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
String address) => String address) =>
call( call(
method: 'blockchain.scripthash.listunspent', method: 'blockchain.scripthash.listunspent',
params: [scriptHash(address)]).then((dynamic result) { params: [scriptHash(address)]).then((dynamic result) {
@ -252,7 +253,7 @@ class ElectrumClient {
} }
Future<String> broadcastTransaction( Future<String> broadcastTransaction(
{@required String transactionRaw}) async => {@required String transactionRaw}) async =>
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw]) call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
.then((dynamic result) { .then((dynamic result) {
if (result is String) { if (result is String) {
@ -263,14 +264,14 @@ class ElectrumClient {
}); });
Future<Map<String, dynamic>> getMerkle( Future<Map<String, dynamic>> getMerkle(
{@required String hash, @required int height}) async => {@required String hash, @required int height}) async =>
await call( await call(
method: 'blockchain.transaction.get_merkle', method: 'blockchain.transaction.get_merkle',
params: [hash, height]) as Map<String, dynamic>; params: [hash, height]) as Map<String, dynamic>;
Future<Map<String, dynamic>> getHeader({@required int height}) async => Future<Map<String, dynamic>> getHeader({@required int height}) async =>
await call(method: 'blockchain.block.get_header', params: [height]) await call(method: 'blockchain.block.get_header', params: [height])
as Map<String, dynamic>; as Map<String, dynamic>;
Future<double> estimatefee({@required int p}) => Future<double> estimatefee({@required int p}) =>
call(method: 'blockchain.estimatefee', params: [p]) call(method: 'blockchain.estimatefee', params: [p])
@ -294,10 +295,9 @@ class ElectrumClient {
params: [scripthash]); params: [scripthash]);
} }
BehaviorSubject<T> subscribe<T>( BehaviorSubject<T> subscribe<T>({@required String id,
{@required String id, @required String method,
@required String method, List<Object> params = const []}) {
List<Object> params = const []}) {
final subscription = BehaviorSubject<T>(); final subscription = BehaviorSubject<T>();
_regisrySubscription(id, subscription); _regisrySubscription(id, subscription);
socket.write(jsonrpc(method: method, id: _id, params: params)); socket.write(jsonrpc(method: method, id: _id, params: params));
@ -306,38 +306,31 @@ class ElectrumClient {
} }
Future<dynamic> call({String method, List<Object> params = const []}) async { Future<dynamic> call({String method, List<Object> params = const []}) async {
await Future<void>.delayed(Duration(milliseconds: 100));
final completer = Completer<dynamic>(); final completer = Completer<dynamic>();
_id += 1; _id += 1;
final id = _id; final id = _id;
_regisryTask(id, completer); _registryTask(id, completer);
socket.write(jsonrpc(method: method, id: _id, params: params)); socket.write(jsonrpc(method: method, id: id, params: params));
return completer.future; return completer.future;
} }
Future<dynamic> callWithTimeout( Future<dynamic> callWithTimeout({String method,
{String method, List<Object> params = const [],
List<Object> params = const [], int timeout = 2000}) async {
int timeout = 2000}) async {
final completer = Completer<dynamic>(); final completer = Completer<dynamic>();
_id += 1; _id += 1;
final id = _id; final id = _id;
_regisryTask(id, completer); _registryTask(id, completer);
socket.write(jsonrpc(method: method, id: _id, params: params)); socket.write(jsonrpc(method: method, id: id, params: params));
Timer(Duration(milliseconds: timeout), () { Timer(Duration(milliseconds: timeout), () {
if (!completer.isCompleted) { if (!completer.isCompleted) {
completer.completeError(RequestFailedTimeoutException(method, _id)); completer.completeError(RequestFailedTimeoutException(method, id));
} }
}); });
return completer.future;
}
void request({String method, List<Object> params = const []}) { return completer.future;
_id += 1;
socket.write(jsonrpc(method: method, id: _id, params: params));
} }
Future<void> close() async { Future<void> close() async {
@ -346,8 +339,9 @@ class ElectrumClient {
onConnectionStatusChange = null; onConnectionStatusChange = null;
} }
void _regisryTask(int id, Completer completer) => _tasks[id.toString()] = void _registryTask(int id, Completer completer) =>
SocketTask(completer: completer, isSubscription: false); _tasks[id.toString()] =
SocketTask(completer: completer, isSubscription: false);
void _regisrySubscription(String id, BehaviorSubject subject) => void _regisrySubscription(String id, BehaviorSubject subject) =>
_tasks[id] = SocketTask(subject: subject, isSubscription: true); _tasks[id] = SocketTask(subject: subject, isSubscription: true);

View file

@ -149,8 +149,8 @@ class BackupService {
PreferencesKey.shouldSaveRecipientAddressKey, PreferencesKey.shouldSaveRecipientAddressKey,
data[PreferencesKey.shouldSaveRecipientAddressKey] as bool); data[PreferencesKey.shouldSaveRecipientAddressKey] as bool);
await _sharedPreferences.setInt( await _sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKey, PreferencesKey.currentTransactionPriorityKeyLegacy,
data[PreferencesKey.currentTransactionPriorityKey] as int); data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int);
await _sharedPreferences.setBool( await _sharedPreferences.setBool(
PreferencesKey.allowBiometricalAuthenticationKey, PreferencesKey.allowBiometricalAuthenticationKey,
data[PreferencesKey.allowBiometricalAuthenticationKey] as bool); data[PreferencesKey.allowBiometricalAuthenticationKey] as bool);
@ -257,8 +257,8 @@ class BackupService {
_sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy), _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy),
PreferencesKey.currentPinLength: PreferencesKey.currentPinLength:
_sharedPreferences.getInt(PreferencesKey.currentPinLength), _sharedPreferences.getInt(PreferencesKey.currentPinLength),
PreferencesKey.currentTransactionPriorityKey: _sharedPreferences PreferencesKey.currentTransactionPriorityKeyLegacy: _sharedPreferences
.getInt(PreferencesKey.currentTransactionPriorityKey), .getInt(PreferencesKey.currentTransactionPriorityKeyLegacy),
PreferencesKey.allowBiometricalAuthenticationKey: _sharedPreferences PreferencesKey.allowBiometricalAuthenticationKey: _sharedPreferences
.getBool(PreferencesKey.allowBiometricalAuthenticationKey), .getBool(PreferencesKey.allowBiometricalAuthenticationKey),
PreferencesKey.currentBitcoinElectrumSererIdKey: _sharedPreferences PreferencesKey.currentBitcoinElectrumSererIdKey: _sharedPreferences

View file

@ -1,16 +1,17 @@
import 'package:cake_wallet/entities/balance.dart'; import 'package:cake_wallet/entities/balance.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/pending_transaction.dart';
import 'package:cake_wallet/core/transaction_history.dart'; import 'package:cake_wallet/core/transaction_history.dart';
import 'package:cake_wallet/entities/currency_for_wallet_type.dart'; import 'package:cake_wallet/entities/currency_for_wallet_type.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/wallet_type.dart';
abstract class WalletBase<BalaceType extends Balance> { abstract class WalletBase<BalanceType extends Balance> {
WalletBase(this.walletInfo); WalletBase(this.walletInfo);
static String idFor(String name, WalletType type) => static String idFor(String name, WalletType type) =>
@ -30,7 +31,7 @@ abstract class WalletBase<BalaceType extends Balance> {
set address(String address); set address(String address);
BalaceType get balance; BalanceType get balance;
SyncStatus get syncStatus; SyncStatus get syncStatus;
@ -48,7 +49,7 @@ abstract class WalletBase<BalaceType extends Balance> {
Future<PendingTransaction> createTransaction(Object credentials); Future<PendingTransaction> createTransaction(Object credentials);
double calculateEstimatedFee(TransactionPriority priority); int calculateEstimatedFee(TransactionPriority priority, int amount);
Future<void> save(); Future<void> save();

View file

@ -1,23 +1,23 @@
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
double calculateEstimatedFee({TransactionPriority priority}) { double calculateEstimatedFee({MoneroTransactionPriority priority}) {
if (priority == TransactionPriority.slow) { if (priority == MoneroTransactionPriority.slow) {
return 0.00002459; return 0.00002459;
} }
if (priority == TransactionPriority.regular) { if (priority == MoneroTransactionPriority.regular) {
return 0.00012305; return 0.00012305;
} }
if (priority == TransactionPriority.medium) { if (priority == MoneroTransactionPriority.medium) {
return 0.00024503; return 0.00024503;
} }
if (priority == TransactionPriority.fast) { if (priority == MoneroTransactionPriority.fast) {
return 0.00061453; return 0.00061453;
} }
if (priority == TransactionPriority.fastest) { if (priority == MoneroTransactionPriority.fastest) {
return 0.0260216; return 0.0260216;
} }

View file

@ -1,4 +1,5 @@
import 'dart:io' show File, Platform; import 'dart:io' show File, Platform;
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
@ -15,7 +16,7 @@ import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/node_list.dart'; import 'package:cake_wallet/entities/node_list.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/fs_migration.dart'; import 'package:cake_wallet/entities/fs_migration.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
@ -53,8 +54,8 @@ Future defaultSettingsMigration(
PreferencesKey.currentFiatCurrencyKey, PreferencesKey.currentFiatCurrencyKey,
FiatCurrency.usd.toString()); FiatCurrency.usd.toString());
await sharedPreferences.setInt( await sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKey, PreferencesKey.currentTransactionPriorityKeyLegacy,
TransactionPriority.standard.raw); MoneroTransactionPriority.standard.raw);
await sharedPreferences.setInt( await sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey, PreferencesKey.currentBalanceDisplayModeKey,
BalanceDisplayMode.availableBalance.raw); BalanceDisplayMode.availableBalance.raw);
@ -93,6 +94,13 @@ Future defaultSettingsMigration(
case 9: case 9:
await generateBackupPassword(secureStorage); await generateBackupPassword(secureStorage);
break; break;
case 10:
await changeTransactionPriorityAndFeeRateKeys(sharedPreferences);
break;
case 11:
await changeDefaultMoneroNode(nodes, sharedPreferences);
break;
default: default:
break; break;
@ -253,3 +261,36 @@ Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async {
final password = encrypt.Key.fromSecureRandom(32).base16; final password = encrypt.Key.fromSecureRandom(32).base16;
await secureStorage.write(key: key, value: password); await secureStorage.write(key: key, value: password);
} }
Future<void> changeTransactionPriorityAndFeeRateKeys(
SharedPreferences sharedPreferences) async {
final legacyTransactionPriority = sharedPreferences
.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy);
await sharedPreferences.setInt(
PreferencesKey.moneroTransactionPriority, legacyTransactionPriority);
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
BitcoinTransactionPriority.medium.serialize());
}
Future<void> changeDefaultMoneroNode(
Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
const cakeWalletMoneroNodeUriPattern = '.cakewallet.com';
const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final currentMoneroNode = nodeSource.values.firstWhere((node) => node.key == currentMoneroNodeId);
final needToReplaceCurrentMoneroNode = currentMoneroNode.uri.contains(cakeWalletMoneroNodeUriPattern);
nodeSource.values.forEach((node) async {
if (node.type == WalletType.monero && node.uri.contains(cakeWalletMoneroNodeUriPattern)) {
await node.delete();
}
});
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
await nodeSource.add(newCakeWalletNode);
if (needToReplaceCurrentMoneroNode) {
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, newCakeWalletNode.key as int);
}
}

View file

@ -0,0 +1,74 @@
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/enumerable_item.dart';
class MoneroTransactionPriority extends TransactionPriority {
const MoneroTransactionPriority({String title, int raw})
: super(title: title, raw: raw);
static const all = [
MoneroTransactionPriority.slow,
MoneroTransactionPriority.regular,
MoneroTransactionPriority.medium,
MoneroTransactionPriority.fast,
MoneroTransactionPriority.fastest
];
static const slow = MoneroTransactionPriority(title: 'Slow', raw: 0);
static const regular = MoneroTransactionPriority(title: 'Regular', raw: 1);
static const medium = MoneroTransactionPriority(title: 'Medium', raw: 2);
static const fast = MoneroTransactionPriority(title: 'Fast', raw: 3);
static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4);
static const standard = slow;
static List<MoneroTransactionPriority> forWalletType(WalletType type) {
switch (type) {
case WalletType.monero:
return MoneroTransactionPriority.all;
case WalletType.bitcoin:
return [
MoneroTransactionPriority.slow,
MoneroTransactionPriority.regular,
MoneroTransactionPriority.fast
];
default:
return [];
}
}
static MoneroTransactionPriority deserialize({int raw}) {
switch (raw) {
case 0:
return slow;
case 1:
return regular;
case 2:
return medium;
case 3:
return fast;
case 4:
return fastest;
default:
return null;
}
}
@override
String toString() {
switch (this) {
case MoneroTransactionPriority.slow:
return S.current.transaction_priority_slow;
case MoneroTransactionPriority.regular:
return S.current.transaction_priority_regular;
case MoneroTransactionPriority.medium:
return S.current.transaction_priority_medium;
case MoneroTransactionPriority.fast:
return S.current.transaction_priority_fast;
case MoneroTransactionPriority.fastest:
return S.current.transaction_priority_fastest;
default:
return '';
}
}
}

View file

@ -4,7 +4,7 @@ class PreferencesKey {
static const currentNodeIdKey = 'current_node_id'; static const currentNodeIdKey = 'current_node_id';
static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc'; static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
static const currentFiatCurrencyKey = 'current_fiat_currency'; static const currentFiatCurrencyKey = 'current_fiat_currency';
static const currentTransactionPriorityKey = 'current_fee_priority'; static const currentTransactionPriorityKeyLegacy = 'current_fee_priority';
static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
static const shouldSaveRecipientAddressKey = 'save_recipient_address'; static const shouldSaveRecipientAddressKey = 'save_recipient_address';
static const allowBiometricalAuthenticationKey = static const allowBiometricalAuthenticationKey =
@ -15,4 +15,6 @@ class PreferencesKey {
static const currentPinLength = 'current_pin_length'; static const currentPinLength = 'current_pin_length';
static const currentLanguageCode = 'language_code'; static const currentLanguageCode = 'language_code';
static const currentDefaultSettingsMigrationVersion = 'current_default_settings_migration_version'; static const currentDefaultSettingsMigrationVersion = 'current_default_settings_migration_version';
static const moneroTransactionPriority = 'current_fee_priority_monero';
static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin';
} }

View file

@ -1,73 +1,6 @@
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/enumerable_item.dart'; import 'package:cake_wallet/entities/enumerable_item.dart';
class TransactionPriority extends EnumerableItem<int> with Serializable<int> { abstract class TransactionPriority extends EnumerableItem<int>
const TransactionPriority({String title, int raw}) with Serializable<int> {
: super(title: title, raw: raw); const TransactionPriority({String title, int raw}) : super(title: title, raw: raw);
static const all = [
TransactionPriority.slow,
TransactionPriority.regular,
TransactionPriority.medium,
TransactionPriority.fast,
TransactionPriority.fastest
];
static const slow = TransactionPriority(title: 'Slow', raw: 0);
static const regular = TransactionPriority(title: 'Regular', raw: 1);
static const medium = TransactionPriority(title: 'Medium', raw: 2);
static const fast = TransactionPriority(title: 'Fast', raw: 3);
static const fastest = TransactionPriority(title: 'Fastest', raw: 4);
static const standard = slow;
static List<TransactionPriority> forWalletType(WalletType type) {
switch (type) {
case WalletType.monero:
return TransactionPriority.all;
case WalletType.bitcoin:
return [
TransactionPriority.slow,
TransactionPriority.regular,
TransactionPriority.fast
];
default:
return [];
}
}
static TransactionPriority deserialize({int raw}) {
switch (raw) {
case 0:
return slow;
case 1:
return regular;
case 2:
return medium;
case 3:
return fast;
case 4:
return fastest;
default:
return null;
}
}
@override
String toString() {
switch (this) {
case TransactionPriority.slow:
return S.current.transaction_priority_slow;
case TransactionPriority.regular:
return S.current.transaction_priority_regular;
case TransactionPriority.medium:
return S.current.transaction_priority_medium;
case TransactionPriority.fast:
return S.current.transaction_priority_fast;
case TransactionPriority.fastest:
return S.current.transaction_priority_fastest;
default:
return '';
}
}
} }

View file

@ -96,7 +96,7 @@ Future<void> main() async {
exchangeTemplates: exchangeTemplates, exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions, transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage, secureStorage: secureStorage,
initialMigrationVersion: 9); initialMigrationVersion: 11);
runApp(App()); runApp(App());
} catch (e) { } catch (e) {
runApp(MaterialApp( runApp(MaterialApp(
@ -123,7 +123,7 @@ Future<void> initialSetup(
@required Box<ExchangeTemplate> exchangeTemplates, @required Box<ExchangeTemplate> exchangeTemplates,
@required Box<TransactionDescription> transactionDescriptions, @required Box<TransactionDescription> transactionDescriptions,
FlutterSecureStorage secureStorage, FlutterSecureStorage secureStorage,
int initialMigrationVersion = 9}) async { int initialMigrationVersion = 11}) async {
await defaultSettingsMigration( await defaultSettingsMigration(
secureStorage: secureStorage, secureStorage: secureStorage,
version: initialMigrationVersion, version: initialMigrationVersion,

View file

@ -1,5 +1,5 @@
import 'package:cake_wallet/entities/transaction_creation_credentials.dart'; import 'package:cake_wallet/entities/transaction_creation_credentials.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
class MoneroTransactionCreationCredentials class MoneroTransactionCreationCredentials
extends TransactionCreationCredentials { extends TransactionCreationCredentials {
@ -9,5 +9,5 @@ class MoneroTransactionCreationCredentials
final String address; final String address;
final String paymentId; final String paymentId;
final String amount; final String amount;
final TransactionPriority priority; final MoneroTransactionPriority priority;
} }

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart';
import 'package:cake_wallet/monero/monero_transaction_creation_exception.dart'; import 'package:cake_wallet/monero/monero_transaction_creation_exception.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -21,7 +22,7 @@ import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
part 'monero_wallet.g.dart'; part 'monero_wallet.g.dart';
@ -212,27 +213,22 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
} }
@override @override
double calculateEstimatedFee(TransactionPriority priority) { int calculateEstimatedFee(TransactionPriority priority, int amount) {
// FIXME: hardcoded value; // FIXME: hardcoded value;
if (priority == TransactionPriority.slow) { if (priority is MoneroTransactionPriority) {
return 0.00002459; switch (priority) {
} case MoneroTransactionPriority.slow:
return 24590000;
if (priority == TransactionPriority.regular) { case MoneroTransactionPriority.regular:
return 0.00012305; return 123050000;
} case MoneroTransactionPriority.medium:
return 245029999;
if (priority == TransactionPriority.medium) { case MoneroTransactionPriority.fast:
return 0.00024503; return 614530000;
} case MoneroTransactionPriority.fastest:
return 26021600000;
if (priority == TransactionPriority.fast) { }
return 0.00061453;
}
if (priority == TransactionPriority.fastest) {
return 0.0260216;
} }
return 0; return 0;

View file

@ -1,9 +1,11 @@
import 'dart:ui'; import 'dart:ui';
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -752,7 +754,7 @@ class SendPage extends BasePage {
} }
Future<void> _setTransactionPriority(BuildContext context) async { Future<void> _setTransactionPriority(BuildContext context) async {
final items = TransactionPriority.forWalletType(sendViewModel.walletType); final items = priorityForWalletType(sendViewModel.walletType);
final selectedItem = items.indexOf(sendViewModel.transactionPriority); final selectedItem = items.indexOf(sendViewModel.transactionPriority);
final isShowScrollThumb = items.length > 3; final isShowScrollThumb = items.length > 3;

View file

@ -1,4 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/themes/theme_list.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -14,7 +16,7 @@ import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart'; import 'package:cake_wallet/entities/action_list_display_mode.dart';
part 'settings_store.g.dart'; part 'settings_store.g.dart';
@ -25,7 +27,6 @@ abstract class SettingsStoreBase with Store {
SettingsStoreBase( SettingsStoreBase(
{@required SharedPreferences sharedPreferences, {@required SharedPreferences sharedPreferences,
@required FiatCurrency initialFiatCurrency, @required FiatCurrency initialFiatCurrency,
@required TransactionPriority initialTransactionPriority,
@required BalanceDisplayMode initialBalanceDisplayMode, @required BalanceDisplayMode initialBalanceDisplayMode,
@required bool initialSaveRecipientAddress, @required bool initialSaveRecipientAddress,
@required bool initialAllowBiometricalAuthentication, @required bool initialAllowBiometricalAuthentication,
@ -35,15 +36,20 @@ abstract class SettingsStoreBase with Store {
// @required String initialCurrentLocale, // @required String initialCurrentLocale,
@required this.appVersion, @required this.appVersion,
@required Map<WalletType, Node> nodes, @required Map<WalletType, Node> nodes,
@required TransactionPriority initialBitcoinTransactionPriority,
@required TransactionPriority initialMoneroTransactionPriority,
this.actionlistDisplayMode}) { this.actionlistDisplayMode}) {
fiatCurrency = initialFiatCurrency; fiatCurrency = initialFiatCurrency;
transactionPriority = initialTransactionPriority;
balanceDisplayMode = initialBalanceDisplayMode; balanceDisplayMode = initialBalanceDisplayMode;
shouldSaveRecipientAddress = initialSaveRecipientAddress; shouldSaveRecipientAddress = initialSaveRecipientAddress;
allowBiometricalAuthentication = initialAllowBiometricalAuthentication; allowBiometricalAuthentication = initialAllowBiometricalAuthentication;
currentTheme = initialTheme; currentTheme = initialTheme;
pinCodeLength = initialPinLength; pinCodeLength = initialPinLength;
languageCode = initialLanguageCode; languageCode = initialLanguageCode;
priority = ObservableMap<WalletType, TransactionPriority>.of({
WalletType.monero: initialMoneroTransactionPriority,
WalletType.bitcoin: initialBitcoinTransactionPriority
});
this.nodes = ObservableMap<WalletType, Node>.of(nodes); this.nodes = ObservableMap<WalletType, Node>.of(nodes);
_sharedPreferences = sharedPreferences; _sharedPreferences = sharedPreferences;
@ -52,11 +58,13 @@ abstract class SettingsStoreBase with Store {
(FiatCurrency fiatCurrency) => sharedPreferences.setString( (FiatCurrency fiatCurrency) => sharedPreferences.setString(
PreferencesKey.currentFiatCurrencyKey, fiatCurrency.serialize())); PreferencesKey.currentFiatCurrencyKey, fiatCurrency.serialize()));
reaction( priority.observe((change) {
(_) => transactionPriority, final key = change.key == WalletType.monero
(TransactionPriority priority) => sharedPreferences.setInt( ? PreferencesKey.moneroTransactionPriority
PreferencesKey.currentTransactionPriorityKey, : PreferencesKey.bitcoinTransactionPriority;
priority.serialize()));
sharedPreferences.setInt(key, change.newValue.serialize());
});
reaction( reaction(
(_) => shouldSaveRecipientAddress, (_) => shouldSaveRecipientAddress,
@ -104,9 +112,6 @@ abstract class SettingsStoreBase with Store {
@observable @observable
ObservableList<ActionListDisplayMode> actionlistDisplayMode; ObservableList<ActionListDisplayMode> actionlistDisplayMode;
@observable
TransactionPriority transactionPriority;
@observable @observable
BalanceDisplayMode balanceDisplayMode; BalanceDisplayMode balanceDisplayMode;
@ -128,6 +133,9 @@ abstract class SettingsStoreBase with Store {
@observable @observable
String languageCode; String languageCode;
@observable
ObservableMap<WalletType, TransactionPriority> priority;
String appVersion; String appVersion;
SharedPreferences _sharedPreferences; SharedPreferences _sharedPreferences;
@ -139,16 +147,28 @@ abstract class SettingsStoreBase with Store {
static Future<SettingsStore> load( static Future<SettingsStore> load(
{@required Box<Node> nodeSource, {@required Box<Node> nodeSource,
FiatCurrency initialFiatCurrency = FiatCurrency.usd, FiatCurrency initialFiatCurrency = FiatCurrency.usd,
TransactionPriority initialTransactionPriority = TransactionPriority.slow, MoneroTransactionPriority initialMoneroTransactionPriority =
MoneroTransactionPriority.slow,
BitcoinTransactionPriority initialBitcoinTransactionPriority =
BitcoinTransactionPriority.medium,
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode initialBalanceDisplayMode =
BalanceDisplayMode.availableBalance}) async { BalanceDisplayMode.availableBalance}) async {
final sharedPreferences = await getIt.getAsync<SharedPreferences>(); final sharedPreferences = await getIt.getAsync<SharedPreferences>();
final currentFiatCurrency = FiatCurrency( final currentFiatCurrency = FiatCurrency(
symbol: symbol:
sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)); sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey));
final currentTransactionPriority = TransactionPriority.deserialize( final savedMoneroTransactionPriority =
raw: sharedPreferences MoneroTransactionPriority.deserialize(
.getInt(PreferencesKey.currentTransactionPriorityKey)); raw: sharedPreferences
.getInt(PreferencesKey.moneroTransactionPriority));
final savedBitcoinTransactionPriority =
BitcoinTransactionPriority.deserialize(
raw: sharedPreferences
.getInt(PreferencesKey.bitcoinTransactionPriority));
final moneroTransactionPriority =
savedMoneroTransactionPriority ?? initialMoneroTransactionPriority;
final bitcoinTransactionPriority =
savedBitcoinTransactionPriority ?? initialBitcoinTransactionPriority;
final currentBalanceDisplayMode = BalanceDisplayMode.deserialize( final currentBalanceDisplayMode = BalanceDisplayMode.deserialize(
raw: sharedPreferences raw: sharedPreferences
.getInt(PreferencesKey.currentBalanceDisplayModeKey)); .getInt(PreferencesKey.currentBalanceDisplayModeKey));
@ -193,30 +213,36 @@ abstract class SettingsStoreBase with Store {
}, },
appVersion: packageInfo.version, appVersion: packageInfo.version,
initialFiatCurrency: currentFiatCurrency, initialFiatCurrency: currentFiatCurrency,
initialTransactionPriority: currentTransactionPriority,
initialBalanceDisplayMode: currentBalanceDisplayMode, initialBalanceDisplayMode: currentBalanceDisplayMode,
initialSaveRecipientAddress: shouldSaveRecipientAddress, initialSaveRecipientAddress: shouldSaveRecipientAddress,
initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
initialTheme: savedTheme, initialTheme: savedTheme,
actionlistDisplayMode: actionListDisplayMode, actionlistDisplayMode: actionListDisplayMode,
initialPinLength: pinLength, initialPinLength: pinLength,
initialLanguageCode: savedLanguageCode); initialLanguageCode: savedLanguageCode,
initialMoneroTransactionPriority: moneroTransactionPriority,
initialBitcoinTransactionPriority: bitcoinTransactionPriority);
} }
Future<void> reload( Future<void> reload(
{@required Box<Node> nodeSource, {@required Box<Node> nodeSource,
FiatCurrency initialFiatCurrency = FiatCurrency.usd, FiatCurrency initialFiatCurrency = FiatCurrency.usd,
TransactionPriority initialTransactionPriority = TransactionPriority.slow, MoneroTransactionPriority initialMoneroTransactionPriority =
MoneroTransactionPriority.slow,
BitcoinTransactionPriority initialBitcoinTransactionPriority =
BitcoinTransactionPriority.medium,
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode initialBalanceDisplayMode =
BalanceDisplayMode.availableBalance}) async { BalanceDisplayMode.availableBalance}) async {
final settings = await SettingsStoreBase.load( final settings = await SettingsStoreBase.load(
nodeSource: nodeSource, nodeSource: nodeSource,
initialBalanceDisplayMode: initialBalanceDisplayMode, initialBalanceDisplayMode: initialBalanceDisplayMode,
initialFiatCurrency: initialFiatCurrency, initialFiatCurrency: initialFiatCurrency,
initialTransactionPriority: initialTransactionPriority); initialMoneroTransactionPriority: initialMoneroTransactionPriority,
initialBitcoinTransactionPriority: initialBitcoinTransactionPriority);
fiatCurrency = settings.fiatCurrency; fiatCurrency = settings.fiatCurrency;
actionlistDisplayMode = settings.actionlistDisplayMode; actionlistDisplayMode = settings.actionlistDisplayMode;
transactionPriority = settings.transactionPriority; priority[WalletType.monero] = initialMoneroTransactionPriority;
priority[WalletType.bitcoin] = initialBitcoinTransactionPriority;
balanceDisplayMode = settings.balanceDisplayMode; balanceDisplayMode = settings.balanceDisplayMode;
shouldSaveRecipientAddress = settings.shouldSaveRecipientAddress; shouldSaveRecipientAddress = settings.shouldSaveRecipientAddress;
allowBiometricalAuthentication = settings.allowBiometricalAuthentication; allowBiometricalAuthentication = settings.allowBiometricalAuthentication;

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/crypto_currency.dart';
@ -302,8 +303,8 @@ abstract class ExchangeViewModelBase with Store {
void calculateDepositAllAmount() { void calculateDepositAllAmount() {
if (wallet is BitcoinWallet) { if (wallet is BitcoinWallet) {
final availableBalance = wallet.balance.available; final availableBalance = wallet.balance.available;
final fee = BitcoinWalletBase.feeAmountForPriority( final priority = _settingsStore.priority[wallet.type] as BitcoinTransactionPriority;
_settingsStore.transactionPriority); final fee = wallet.calculateEstimatedFee(priority, null);
if (availableBalance < fee || availableBalance == 0) { if (availableBalance < fee || availableBalance == 0) {
return; return;

View file

@ -1,6 +1,11 @@
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/monero/monero_amount_format.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -21,7 +26,7 @@ import 'package:cake_wallet/monero/monero_transaction_creation_credentials.dart'
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
@ -40,11 +45,11 @@ abstract class SendViewModelBase with Store {
_cryptoNumberFormat = NumberFormat(), _cryptoNumberFormat = NumberFormat(),
note = '', note = '',
sendAll = false { sendAll = false {
final _priority = _settingsStore.transactionPriority; final priority = _settingsStore.priority[_wallet.type];
final priorities = priorityForWalletType(_wallet.type);
if (!TransactionPriority.forWalletType(walletType).contains(_priority)) { if (!priorityForWalletType(_wallet.type).contains(priority)) {
_settingsStore.transactionPriority = _settingsStore.priority[_wallet.type] = priorities.first;
TransactionPriority.forWalletType(walletType).first;
} }
_setCryptoNumMaximumFractionDigits(); _setCryptoNumMaximumFractionDigits();
@ -69,8 +74,39 @@ abstract class SendViewModelBase with Store {
bool sendAll; bool sendAll;
@computed @computed
double get estimatedFee => double get estimatedFee {
_wallet.calculateEstimatedFee(_settingsStore.transactionPriority); int amount;
if (cryptoAmount?.isNotEmpty ?? false) {
int _amount = 0;
switch (walletType) {
case WalletType.monero:
_amount = moneroParseAmount(amount: cryptoAmount);
break;
case WalletType.bitcoin:
_amount = stringDoubleToBitcoinAmount(cryptoAmount);
break;
default:
break;
}
if (_amount > 0) {
amount = _amount;
}
}
final fee = _wallet.calculateEstimatedFee(_settingsStore.priority[_wallet.type], amount);
if (_wallet is BitcoinWallet) {
return bitcoinAmountToDouble(amount: fee);
}
if (_wallet is MoneroWallet) {
return moneroAmountToDouble(amount: fee);
}
return 0;
}
@computed @computed
String get estimatedFeeFiatAmount { String get estimatedFeeFiatAmount {
@ -119,7 +155,7 @@ abstract class SendViewModelBase with Store {
FiatCurrency get fiat => _settingsStore.fiatCurrency; FiatCurrency get fiat => _settingsStore.fiatCurrency;
TransactionPriority get transactionPriority => TransactionPriority get transactionPriority =>
_settingsStore.transactionPriority; _settingsStore.priority[_wallet.type];
CryptoCurrency get currency => _wallet.currency; CryptoCurrency get currency => _wallet.currency;
@ -213,7 +249,7 @@ abstract class SendViewModelBase with Store {
@action @action
void setTransactionPriority(TransactionPriority priority) => void setTransactionPriority(TransactionPriority priority) =>
_settingsStore.transactionPriority = priority; _settingsStore.priority[_wallet.type] = priority;
Future<OpenaliasRecord> decodeOpenaliasRecord(String name) async { Future<OpenaliasRecord> decodeOpenaliasRecord(String name) async {
final record = await OpenaliasRecord.fetchAddressAndName( final record = await OpenaliasRecord.fetchAddressAndName(
@ -257,16 +293,17 @@ abstract class SendViewModelBase with Store {
switch (_wallet.type) { switch (_wallet.type) {
case WalletType.bitcoin: case WalletType.bitcoin:
final amount = !sendAll ? _amount : null; final amount = !sendAll ? _amount : null;
final priority = _settingsStore.priority[_wallet.type];
return BitcoinTransactionCredentials( return BitcoinTransactionCredentials(address, amount, priority as BitcoinTransactionPriority);
address, amount, _settingsStore.transactionPriority);
case WalletType.monero: case WalletType.monero:
final amount = !sendAll ? _amount : null; final amount = !sendAll ? _amount : null;
final priority = _settingsStore.priority[_wallet.type];
return MoneroTransactionCreationCredentials( return MoneroTransactionCreationCredentials(
address: address, address: address,
paymentId: '', paymentId: '',
priority: _settingsStore.transactionPriority, priority: priority as MoneroTransactionPriority,
amount: amount); amount: amount);
default: default:
return null; return null;

View file

@ -1,4 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/entities/balance.dart'; import 'package:cake_wallet/entities/balance.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
@ -14,7 +16,7 @@ import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart'; import 'package:cake_wallet/entities/action_list_display_mode.dart';
import 'package:cake_wallet/view_model/settings/version_list_item.dart'; import 'package:cake_wallet/view_model/settings/version_list_item.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart'; import 'package:cake_wallet/view_model/settings/link_list_item.dart';
@ -28,6 +30,17 @@ part 'settings_view_model.g.dart';
class SettingsViewModel = SettingsViewModelBase with _$SettingsViewModel; class SettingsViewModel = SettingsViewModelBase with _$SettingsViewModel;
List<TransactionPriority> priorityForWalletType(WalletType type) {
switch (type) {
case WalletType.monero:
return MoneroTransactionPriority.all;
case WalletType.bitcoin:
return BitcoinTransactionPriority.all;
default:
return [];
}
}
abstract class SettingsViewModelBase with Store { abstract class SettingsViewModelBase with Store {
SettingsViewModelBase(this._settingsStore, WalletBase<Balance> wallet) SettingsViewModelBase(this._settingsStore, WalletBase<Balance> wallet)
: itemHeaders = {}, : itemHeaders = {},
@ -37,11 +50,11 @@ abstract class SettingsViewModelBase with Store {
PackageInfo.fromPlatform().then( PackageInfo.fromPlatform().then(
(PackageInfo packageInfo) => currentVersion = packageInfo.version); (PackageInfo packageInfo) => currentVersion = packageInfo.version);
final _priority = _settingsStore.transactionPriority; final priority = _settingsStore.priority[wallet.type];
final priorities = priorityForWalletType(wallet.type);
if (!TransactionPriority.forWalletType(_walletType).contains(_priority)) { if (!priorities.contains(priority)) {
_settingsStore.transactionPriority = _settingsStore.priority[wallet.type] = priorities.first;
TransactionPriority.forWalletType(_walletType).first;
} }
sections = [ sections = [
@ -60,10 +73,10 @@ abstract class SettingsViewModelBase with Store {
setFiatCurrency(currency)), setFiatCurrency(currency)),
PickerListItem( PickerListItem(
title: S.current.settings_fee_priority, title: S.current.settings_fee_priority,
items: TransactionPriority.forWalletType(wallet.type), items: priorityForWalletType(wallet.type),
selectedItem: () => transactionPriority, selectedItem: () => transactionPriority,
onItemSelected: (TransactionPriority priority) => onItemSelected: (TransactionPriority priority) =>
_settingsStore.transactionPriority = priority), _settingsStore.priority[wallet.type] = priority),
SwitcherListItem( SwitcherListItem(
title: S.current.settings_save_recipient_address, title: S.current.settings_save_recipient_address,
value: () => shouldSaveRecipientAddress, value: () => shouldSaveRecipientAddress,
@ -182,7 +195,7 @@ abstract class SettingsViewModelBase with Store {
@computed @computed
TransactionPriority get transactionPriority => TransactionPriority get transactionPriority =>
_settingsStore.transactionPriority; _settingsStore.priority[_walletType];
@computed @computed
BalanceDisplayMode get balanceDisplayMode => BalanceDisplayMode get balanceDisplayMode =>