Estimated fee rates.

This commit is contained in:
M
2021-02-13 00:38:34 +02:00
parent b1047fb3cf
commit 35aabcd248
12 changed files with 252 additions and 168 deletions
+8 -10
View File
@@ -2,32 +2,30 @@ import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/generated/i18n.dart';
class BitcoinTransactionPriority extends TransactionPriority {
const BitcoinTransactionPriority(this.rate, {String title, int raw})
const BitcoinTransactionPriority({String title, int raw})
: super(title: title, raw: raw);
static const List<BitcoinTransactionPriority> all = [slow, medium, fast];
static const List<BitcoinTransactionPriority> all = [fast, medium, slow];
static const BitcoinTransactionPriority slow =
BitcoinTransactionPriority(11, title: 'Slow', raw: 0);
BitcoinTransactionPriority(title: 'Slow', raw: 0);
static const BitcoinTransactionPriority medium =
BitcoinTransactionPriority(90, title: 'Medium', raw: 1);
BitcoinTransactionPriority(title: 'Medium', raw: 1);
static const BitcoinTransactionPriority fast =
BitcoinTransactionPriority(98, title: 'Fast', raw: 2);
BitcoinTransactionPriority(title: 'Fast', raw: 2);
static BitcoinTransactionPriority deserialize({int raw}) {
switch (raw) {
case 0:
return slow;
case 2:
case 1:
return medium;
case 3:
case 2:
return fast;
default:
return null;
}
}
final int rate;
@override
String toString() {
var label = '';
@@ -46,6 +44,6 @@ class BitcoinTransactionPriority extends TransactionPriority {
break;
}
return '$label ($rate sat/byte)';
return label;
}
}
+20 -5
View File
@@ -53,6 +53,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
syncStatus = NotConnectedSyncStatus(),
_password = password,
_accountIndex = accountIndex,
_feeRates = <int>[],
super(walletInfo) {
_unspent = [];
_scripthashesUpdateSubject = {};
@@ -118,10 +119,6 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
walletInfo: walletInfo);
}
static int feeAmountForPriority(BitcoinTransactionPriority priority,
int inputsCount, int outputsCount) =>
priority.rate * estimatedTransactionSize(inputsCount, outputsCount);
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
inputsCount * 146 + outputsCounts * 33 + 8;
@@ -161,6 +158,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey);
final String _password;
List<int> _feeRates;
int _accountIndex;
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
@@ -233,6 +231,11 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
_subscribeForUpdates();
await _updateBalance();
await _updateUnspent();
_feeRates = await eclient.feeRates();
Timer.periodic(const Duration(minutes: 1),
(timer) async => _feeRates = await eclient.feeRates());
syncStatus = SyncedSyncStatus();
} catch (e) {
print(e.toString());
@@ -332,7 +335,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
addressToOutputScript(transactionCredentials.address), amount);
final estimatedSize = estimatedTransactionSize(inputs.length, 2);
final feeAmount = transactionCredentials.priority.rate * estimatedSize;
final feeAmount = feeRate(transactionCredentials.priority) * estimatedSize;
final changeValue = totalInputAmount - amount - feeAmount;
if (changeValue > minAmount) {
@@ -362,6 +365,18 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
'balance': balance?.toJSON()
});
int feeRate(TransactionPriority priority) {
if (priority is BitcoinTransactionPriority) {
return _feeRates[priority.raw];
}
return 0;
}
int feeAmountForPriority(BitcoinTransactionPriority priority, int inputsCount,
int outputsCount) =>
feeRate(priority) * estimatedTransactionSize(inputsCount, outputsCount);
@override
int calculateEstimatedFee(TransactionPriority priority, int amount) {
if (priority is BitcoinTransactionPriority) {
+46 -20
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
import 'package:cake_wallet/bitcoin/script_hash.dart';
import 'package:flutter/foundation.dart';
import 'package:rxdart/rxdart.dart';
@@ -22,9 +23,8 @@ String jsonrpcparams(List<Object> params) {
}
String jsonrpc(
{String method, List<Object> params, int id, double version = 2.0}) =>
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json
.encode(params)}}\n';
{String method, List<Object> params, int id, double version = 2.0}) =>
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
class SocketTask {
SocketTask({this.completer, this.isSubscription, this.subject});
@@ -77,7 +77,7 @@ class ElectrumClient {
socket.listen((Uint8List event) {
try {
final response =
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
_handleResponse(response);
} on FormatException catch (e) {
final msg = e.message.toLowerCase();
@@ -93,7 +93,7 @@ class ElectrumClient {
if (isJSONStringCorrect(unterminatedString)) {
final response =
json.decode(unterminatedString) as Map<String, Object>;
json.decode(unterminatedString) as Map<String, Object>;
_handleResponse(response);
unterminatedString = '';
}
@@ -107,7 +107,7 @@ class ElectrumClient {
if (isJSONStringCorrect(unterminatedString)) {
final response =
json.decode(unterminatedString) as Map<String, Object>;
json.decode(unterminatedString) as Map<String, Object>;
_handleResponse(response);
unterminatedString = null;
}
@@ -173,7 +173,7 @@ class ElectrumClient {
});
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
String address) =>
String address) =>
call(
method: 'blockchain.scripthash.listunspent',
params: [scriptHash(address)]).then((dynamic result) {
@@ -253,7 +253,7 @@ class ElectrumClient {
}
Future<String> broadcastTransaction(
{@required String transactionRaw}) async =>
{@required String transactionRaw}) async =>
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
.then((dynamic result) {
if (result is String) {
@@ -264,14 +264,14 @@ class ElectrumClient {
});
Future<Map<String, dynamic>> getMerkle(
{@required String hash, @required int height}) async =>
{@required String hash, @required int height}) async =>
await call(
method: 'blockchain.transaction.get_merkle',
params: [hash, height]) as Map<String, dynamic>;
Future<Map<String, dynamic>> getHeader({@required int height}) async =>
await call(method: 'blockchain.block.get_header', params: [height])
as Map<String, dynamic>;
as Map<String, dynamic>;
Future<double> estimatefee({@required int p}) =>
call(method: 'blockchain.estimatefee', params: [p])
@@ -287,6 +287,32 @@ class ElectrumClient {
return 0;
});
Future<List<List<int>>> feeHistogram() =>
call(method: 'mempool.get_fee_histogram').then((dynamic result) {
if (result is List) {
return result.map((dynamic e) {
if (e is List) {
return e.map((dynamic ee) => ee is int ? ee : null).toList();
}
return null;
}).toList();
}
return [];
});
Future<List<int>> feeRates() async {
final topDoubleString = await estimatefee(p: 1);
final middleDoubleString = await estimatefee(p: 20);
final bottomDoubleString = await estimatefee(p: 150);
final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000).round();
final middle = (stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000).round();
final bottom = (stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000).round();
return [bottom, middle, top];
}
BehaviorSubject<Object> scripthashUpdate(String scripthash) {
_id += 1;
return subscribe<Object>(
@@ -295,9 +321,10 @@ class ElectrumClient {
params: [scripthash]);
}
BehaviorSubject<T> subscribe<T>({@required String id,
@required String method,
List<Object> params = const []}) {
BehaviorSubject<T> subscribe<T>(
{@required String id,
@required String method,
List<Object> params = const []}) {
final subscription = BehaviorSubject<T>();
_regisrySubscription(id, subscription);
socket.write(jsonrpc(method: method, id: _id, params: params));
@@ -315,9 +342,10 @@ class ElectrumClient {
return completer.future;
}
Future<dynamic> callWithTimeout({String method,
List<Object> params = const [],
int timeout = 2000}) async {
Future<dynamic> callWithTimeout(
{String method,
List<Object> params = const [],
int timeout = 2000}) async {
final completer = Completer<dynamic>();
_id += 1;
final id = _id;
@@ -329,7 +357,6 @@ class ElectrumClient {
}
});
return completer.future;
}
@@ -339,9 +366,8 @@ class ElectrumClient {
onConnectionStatusChange = null;
}
void _registryTask(int id, Completer completer) =>
_tasks[id.toString()] =
SocketTask(completer: completer, isSubscription: false);
void _registryTask(int id, Completer completer) => _tasks[id.toString()] =
SocketTask(completer: completer, isSubscription: false);
void _regisrySubscription(String id, BehaviorSubject subject) =>
_tasks[id] = SocketTask(subject: subject, isSubscription: true);