mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
rbf-fixes-recomended-fee-rate (#1684)
* rbf fixes * Revert "rbf fixes" * fix replaced transactions * re-format electrum_wallet.dart [skip ci] * minor fixes [skip ci] --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
4e2e5e708c
commit
32e119e24f
13 changed files with 105 additions and 45 deletions
|
@ -35,6 +35,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
List<String>? outputAddresses,
|
List<String>? outputAddresses,
|
||||||
required TransactionDirection direction,
|
required TransactionDirection direction,
|
||||||
required bool isPending,
|
required bool isPending,
|
||||||
|
required bool isReplaced,
|
||||||
required DateTime date,
|
required DateTime date,
|
||||||
required int confirmations,
|
required int confirmations,
|
||||||
String? to,
|
String? to,
|
||||||
|
@ -50,6 +51,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.isPending = isPending;
|
this.isPending = isPending;
|
||||||
|
this.isReplaced = isReplaced;
|
||||||
this.confirmations = confirmations;
|
this.confirmations = confirmations;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
id: id,
|
id: id,
|
||||||
height: height,
|
height: height,
|
||||||
isPending: false,
|
isPending: false,
|
||||||
|
isReplaced: false,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
direction: direction,
|
direction: direction,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
|
@ -173,6 +176,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
id: bundle.originalTransaction.txId(),
|
id: bundle.originalTransaction.txId(),
|
||||||
height: height,
|
height: height,
|
||||||
isPending: bundle.confirmations == 0,
|
isPending: bundle.confirmations == 0,
|
||||||
|
isReplaced: false,
|
||||||
inputAddresses: inputAddresses,
|
inputAddresses: inputAddresses,
|
||||||
outputAddresses: outputAddresses,
|
outputAddresses: outputAddresses,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
|
@ -196,6 +200,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||||
isPending: data['isPending'] as bool,
|
isPending: data['isPending'] as bool,
|
||||||
|
isReplaced: data['isReplaced'] as bool? ?? false,
|
||||||
confirmations: data['confirmations'] as int,
|
confirmations: data['confirmations'] as int,
|
||||||
inputAddresses:
|
inputAddresses:
|
||||||
inputAddresses.isEmpty ? [] : inputAddresses.map((e) => e.toString()).toList(),
|
inputAddresses.isEmpty ? [] : inputAddresses.map((e) => e.toString()).toList(),
|
||||||
|
@ -238,6 +243,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
direction: direction,
|
direction: direction,
|
||||||
date: date,
|
date: date,
|
||||||
isPending: isPending,
|
isPending: isPending,
|
||||||
|
isReplaced: isReplaced ?? false,
|
||||||
inputAddresses: inputAddresses,
|
inputAddresses: inputAddresses,
|
||||||
outputAddresses: outputAddresses,
|
outputAddresses: outputAddresses,
|
||||||
confirmations: info.confirmations);
|
confirmations: info.confirmations);
|
||||||
|
@ -251,6 +257,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
m['direction'] = direction.index;
|
m['direction'] = direction.index;
|
||||||
m['date'] = date.millisecondsSinceEpoch;
|
m['date'] = date.millisecondsSinceEpoch;
|
||||||
m['isPending'] = isPending;
|
m['isPending'] = isPending;
|
||||||
|
m['isReplaced'] = isReplaced;
|
||||||
m['confirmations'] = confirmations;
|
m['confirmations'] = confirmations;
|
||||||
m['fee'] = fee;
|
m['fee'] = fee;
|
||||||
m['to'] = to;
|
m['to'] = to;
|
||||||
|
@ -262,6 +269,6 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspents, inputAddresses: $inputAddresses, outputAddresses: $outputAddresses)';
|
return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, isReplaced: $isReplaced, confirmations: $confirmations, to: $to, unspent: $unspents, inputAddresses: $inputAddresses, outputAddresses: $outputAddresses)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1400,14 +1400,16 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
int transactionVSize(String transactionHex) => BtcTransaction.fromRaw(transactionHex).getVSize();
|
||||||
|
|
||||||
|
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
||||||
try {
|
try {
|
||||||
final bundle = await getTransactionExpanded(hash: tx.txHash);
|
final bundle = await getTransactionExpanded(hash: tx.txHash);
|
||||||
_updateInputsAndOutputs(tx, bundle);
|
_updateInputsAndOutputs(tx, bundle);
|
||||||
if (bundle.confirmations > 0) return false;
|
if (bundle.confirmations > 0) return null;
|
||||||
return bundle.originalTransaction.canReplaceByFee;
|
return bundle.originalTransaction.canReplaceByFee ? bundle.originalTransaction.toHex() : null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1589,6 +1591,13 @@ abstract class ElectrumWalletBase
|
||||||
hasChange: changeOutputs.isNotEmpty,
|
hasChange: changeOutputs.isNotEmpty,
|
||||||
feeRate: newFee.toString(),
|
feeRate: newFee.toString(),
|
||||||
)..addListener((transaction) async {
|
)..addListener((transaction) async {
|
||||||
|
transactionHistory.transactions.values.forEach((tx) {
|
||||||
|
if (tx.id == hash) {
|
||||||
|
tx.isReplaced = true;
|
||||||
|
tx.isPending = false;
|
||||||
|
transactionHistory.addOne(tx);
|
||||||
|
}
|
||||||
|
});
|
||||||
transactionHistory.addOne(transaction);
|
transactionHistory.addOne(transaction);
|
||||||
await updateBalance();
|
await updateBalance();
|
||||||
});
|
});
|
||||||
|
@ -2317,6 +2326,7 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
fee: 0,
|
fee: 0,
|
||||||
direction: TransactionDirection.incoming,
|
direction: TransactionDirection.incoming,
|
||||||
isPending: false,
|
isPending: false,
|
||||||
|
isReplaced: false,
|
||||||
date: scanData.network == BitcoinNetwork.mainnet
|
date: scanData.network == BitcoinNetwork.mainnet
|
||||||
? getDateByBitcoinHeight(tweakHeight)
|
? getDateByBitcoinHeight(tweakHeight)
|
||||||
: DateTime.now(),
|
: DateTime.now(),
|
||||||
|
@ -2424,6 +2434,7 @@ class EstimatedTxResult {
|
||||||
final int fee;
|
final int fee;
|
||||||
final int amount;
|
final int amount;
|
||||||
final bool spendsSilentPayment;
|
final bool spendsSilentPayment;
|
||||||
|
|
||||||
// final bool sendsToSilentPayment;
|
// final bool sendsToSilentPayment;
|
||||||
final bool hasChange;
|
final bool hasChange;
|
||||||
final bool isSendAll;
|
final bool isSendAll;
|
||||||
|
|
|
@ -110,6 +110,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||||
direction: TransactionDirection.outgoing,
|
direction: TransactionDirection.outgoing,
|
||||||
date: DateTime.now(),
|
date: DateTime.now(),
|
||||||
isPending: true,
|
isPending: true,
|
||||||
|
isReplaced: false,
|
||||||
confirmations: 0,
|
confirmations: 0,
|
||||||
fee: fee);
|
fee: fee);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ abstract class TransactionInfo extends Object with Keyable {
|
||||||
String? to;
|
String? to;
|
||||||
String? from;
|
String? from;
|
||||||
String? evmSignatureName;
|
String? evmSignatureName;
|
||||||
|
bool? isReplaced;
|
||||||
List<String>? inputAddresses;
|
List<String>? inputAddresses;
|
||||||
List<String>? outputAddresses;
|
List<String>? outputAddresses;
|
||||||
|
|
||||||
|
|
|
@ -411,12 +411,18 @@ class CWBitcoin extends Bitcoin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> canReplaceByFee(Object wallet, Object transactionInfo) async {
|
Future<String?> canReplaceByFee(Object wallet, Object transactionInfo) async {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
final tx = transactionInfo as ElectrumTransactionInfo;
|
final tx = transactionInfo as ElectrumTransactionInfo;
|
||||||
return bitcoinWallet.canReplaceByFee(tx);
|
return bitcoinWallet.canReplaceByFee(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int getTransactionVSize(Object wallet, String transactionHex) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return bitcoinWallet.transactionVSize(transactionHex);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee) async {
|
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee) async {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
|
36
lib/di.dart
36
lib/di.dart
|
@ -1080,21 +1080,40 @@ Future<void> setup({
|
||||||
param1: derivations,
|
param1: derivations,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
|
getIt.registerFactoryParam<TransactionDetailsViewModel, List<dynamic>, void>(
|
||||||
(TransactionInfo transactionInfo, _) {
|
(params, _) {
|
||||||
|
final transactionInfo = params[0] as TransactionInfo;
|
||||||
|
final canReplaceByFee = params[1] as bool? ?? false;
|
||||||
final wallet = getIt.get<AppStore>().wallet!;
|
final wallet = getIt.get<AppStore>().wallet!;
|
||||||
|
|
||||||
return TransactionDetailsViewModel(
|
return TransactionDetailsViewModel(
|
||||||
transactionInfo: transactionInfo,
|
transactionInfo: transactionInfo,
|
||||||
transactionDescriptionBox: _transactionDescriptionBox,
|
transactionDescriptionBox: _transactionDescriptionBox,
|
||||||
wallet: wallet,
|
wallet: wallet,
|
||||||
settingsStore: getIt.get<SettingsStore>(),
|
settingsStore: getIt.get<SettingsStore>(),
|
||||||
sendViewModel: getIt.get<SendViewModel>());
|
sendViewModel: getIt.get<SendViewModel>(),
|
||||||
});
|
canReplaceByFee: canReplaceByFee,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
|
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
|
||||||
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
|
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
|
||||||
transactionDetailsViewModel:
|
transactionDetailsViewModel: getIt.get<TransactionDetailsViewModel>(
|
||||||
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
param1: [transactionInfo, false])));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<RBFDetailsPage, List<dynamic>, void>(
|
||||||
|
(params, _) {
|
||||||
|
final transactionInfo = params[0] as TransactionInfo;
|
||||||
|
final txHex = params[1] as String;
|
||||||
|
return RBFDetailsPage(
|
||||||
|
transactionDetailsViewModel: getIt.get<TransactionDetailsViewModel>(
|
||||||
|
param1: [transactionInfo, true],
|
||||||
|
),
|
||||||
|
rawTransaction: txHex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
getIt.registerFactoryParam<NewWalletTypePage, NewWalletTypeArguments, void>(
|
getIt.registerFactoryParam<NewWalletTypePage, NewWalletTypeArguments, void>(
|
||||||
(newWalletTypeArguments, _) {
|
(newWalletTypeArguments, _) {
|
||||||
|
@ -1265,11 +1284,6 @@ Future<void> setup({
|
||||||
|
|
||||||
getIt.registerFactory(() => CakePayAccountPage(getIt.get<CakePayAccountViewModel>()));
|
getIt.registerFactory(() => CakePayAccountPage(getIt.get<CakePayAccountViewModel>()));
|
||||||
|
|
||||||
getIt.registerFactoryParam<RBFDetailsPage, TransactionInfo, void>(
|
|
||||||
(TransactionInfo transactionInfo, _) => RBFDetailsPage(
|
|
||||||
transactionDetailsViewModel:
|
|
||||||
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
|
||||||
|
|
||||||
getIt.registerFactory(() => AnonPayApi(
|
getIt.registerFactory(() => AnonPayApi(
|
||||||
useTorOnly: getIt.get<SettingsStore>().exchangeStatus == ExchangeApiMode.torOnly,
|
useTorOnly: getIt.get<SettingsStore>().exchangeStatus == ExchangeApiMode.torOnly,
|
||||||
wallet: getIt.get<AppStore>().wallet!));
|
wallet: getIt.get<AppStore>().wallet!));
|
||||||
|
|
|
@ -375,7 +375,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.bumpFeePage:
|
case Routes.bumpFeePage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
|
builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as List<dynamic>));
|
||||||
|
|
||||||
case Routes.newSubaddress:
|
case Routes.newSubaddress:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
|
|
@ -84,10 +84,7 @@ class TransactionsPage extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
final transaction = item.transaction;
|
final transaction = item.transaction;
|
||||||
final transactionType = dashboardViewModel.type == WalletType.ethereum &&
|
final transactionType = dashboardViewModel.getTransactionType(transaction);
|
||||||
transaction.evmSignatureName == 'approval'
|
|
||||||
? ' (${transaction.evmSignatureName})'
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return Observer(
|
return Observer(
|
||||||
builder: (_) => TransactionRow(
|
builder: (_) => TransactionRow(
|
||||||
|
@ -102,7 +99,7 @@ class TransactionsPage extends StatelessWidget {
|
||||||
: item.formattedFiatAmount,
|
: item.formattedFiatAmount,
|
||||||
isPending: transaction.isPending,
|
isPending: transaction.isPending,
|
||||||
title:
|
title:
|
||||||
item.formattedTitle + item.formattedStatus + ' $transactionType',
|
item.formattedTitle + item.formattedStatus + transactionType,
|
||||||
isReceivedSilentPayment:
|
isReceivedSilentPayment:
|
||||||
dashboardViewModel.type == WalletType.bitcoin &&
|
dashboardViewModel.type == WalletType.bitcoin &&
|
||||||
bitcoin!.txIsReceivedSilentPayment(transaction),
|
bitcoin!.txIsReceivedSilentPayment(transaction),
|
||||||
|
|
|
@ -24,19 +24,22 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
class RBFDetailsPage extends BasePage {
|
class RBFDetailsPage extends BasePage {
|
||||||
RBFDetailsPage({required this.transactionDetailsViewModel});
|
RBFDetailsPage({required this.transactionDetailsViewModel, required this.rawTransaction}) {
|
||||||
|
transactionDetailsViewModel.addBumpFeesListItems(
|
||||||
|
transactionDetailsViewModel.transactionInfo, rawTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.bump_fee;
|
String get title => S.current.bump_fee;
|
||||||
|
|
||||||
final TransactionDetailsViewModel transactionDetailsViewModel;
|
final TransactionDetailsViewModel transactionDetailsViewModel;
|
||||||
|
final String rawTransaction;
|
||||||
|
|
||||||
bool _effectsInstalled = false;
|
bool _effectsInstalled = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
_setEffects(context);
|
_setEffects(context);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -166,7 +169,9 @@ class RBFDetailsPage extends BasePage {
|
||||||
actionRightButton: () async {
|
actionRightButton: () async {
|
||||||
Navigator.of(popupContext).pop();
|
Navigator.of(popupContext).pop();
|
||||||
await transactionDetailsViewModel.sendViewModel.commitTransaction();
|
await transactionDetailsViewModel.sendViewModel.commitTransaction();
|
||||||
// transactionStatePopup();
|
try {
|
||||||
|
Navigator.of(popupContext).pop();
|
||||||
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
actionLeftButton: () => Navigator.of(popupContext).pop(),
|
actionLeftButton: () => Navigator.of(popupContext).pop(),
|
||||||
feeFiatAmount:
|
feeFiatAmount:
|
||||||
|
|
|
@ -75,7 +75,7 @@ class TransactionDetailsPage extends BasePage {
|
||||||
text: S.of(context).bump_fee,
|
text: S.of(context).bump_fee,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.of(context).pushNamed(Routes.bumpFeePage,
|
Navigator.of(context).pushNamed(Routes.bumpFeePage,
|
||||||
arguments: transactionDetailsViewModel.transactionInfo);
|
arguments: [transactionDetailsViewModel.transactionInfo, transactionDetailsViewModel.rawTransaction]);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -764,6 +764,16 @@ abstract class DashboardViewModelBase with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getTransactionType(TransactionInfo tx) {
|
||||||
|
if (wallet.type == WalletType.bitcoin) {
|
||||||
|
if (tx.isReplaced == true) return ' (replaced)';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wallet.type == WalletType.ethereum && tx.evmSignatureName == 'approval')
|
||||||
|
return ' (${tx.evmSignatureName})';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> refreshDashboard() async {
|
Future<void> refreshDashboard() async {
|
||||||
reconnect();
|
reconnect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
required this.transactionDescriptionBox,
|
required this.transactionDescriptionBox,
|
||||||
required this.wallet,
|
required this.wallet,
|
||||||
required this.settingsStore,
|
required this.settingsStore,
|
||||||
required this.sendViewModel})
|
required this.sendViewModel,
|
||||||
|
this.canReplaceByFee = false})
|
||||||
: items = [],
|
: items = [],
|
||||||
RBFListItems = [],
|
RBFListItems = [],
|
||||||
newFee = 0,
|
newFee = 0,
|
||||||
|
@ -51,8 +52,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
break;
|
break;
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
_addElectrumListItems(tx, dateFormat);
|
_addElectrumListItems(tx, dateFormat);
|
||||||
_addBumpFeesListItems(tx);
|
if(!canReplaceByFee)_checkForRBF(tx);
|
||||||
_checkForRBF(tx);
|
|
||||||
break;
|
break;
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
case WalletType.bitcoinCash:
|
case WalletType.bitcoinCash:
|
||||||
|
@ -139,13 +139,11 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
bool showRecipientAddress;
|
bool showRecipientAddress;
|
||||||
bool isRecipientAddressShown;
|
bool isRecipientAddressShown;
|
||||||
int newFee;
|
int newFee;
|
||||||
|
String? rawTransaction;
|
||||||
TransactionPriority? transactionPriority;
|
TransactionPriority? transactionPriority;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
bool _canReplaceByFee = false;
|
bool canReplaceByFee;
|
||||||
|
|
||||||
@computed
|
|
||||||
bool get canReplaceByFee => _canReplaceByFee /*&& transactionInfo.confirmations <= 0*/;
|
|
||||||
|
|
||||||
String _explorerUrl(WalletType type, String txId) {
|
String _explorerUrl(WalletType type, String txId) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -347,7 +345,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
items.addAll(_items);
|
items.addAll(_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addBumpFeesListItems(TransactionInfo tx) {
|
void addBumpFeesListItems(TransactionInfo tx, String rawTransaction) {
|
||||||
transactionPriority = bitcoin!.getBitcoinTransactionPriorityMedium();
|
transactionPriority = bitcoin!.getBitcoinTransactionPriorityMedium();
|
||||||
final inputsCount = (transactionInfo.inputAddresses?.isEmpty ?? true)
|
final inputsCount = (transactionInfo.inputAddresses?.isEmpty ?? true)
|
||||||
? 1
|
? 1
|
||||||
|
@ -361,6 +359,14 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
|
|
||||||
RBFListItems.add(StandartListItem(title: S.current.old_fee, value: tx.feeFormatted() ?? '0.0'));
|
RBFListItems.add(StandartListItem(title: S.current.old_fee, value: tx.feeFormatted() ?? '0.0'));
|
||||||
|
|
||||||
|
if (transactionInfo.fee != null && rawTransaction.isNotEmpty) {
|
||||||
|
final size = bitcoin!.getTransactionVSize(wallet, rawTransaction);
|
||||||
|
final recommendedRate = (transactionInfo.fee! / size).round() + 1;
|
||||||
|
|
||||||
|
RBFListItems.add(
|
||||||
|
StandartListItem(title: 'New recommended fee rate', value: '$recommendedRate sat/byte'));
|
||||||
|
}
|
||||||
|
|
||||||
final priorities = priorityForWalletType(wallet.type);
|
final priorities = priorityForWalletType(wallet.type);
|
||||||
final selectedItem = priorities.indexOf(sendViewModel.transactionPriority);
|
final selectedItem = priorities.indexOf(sendViewModel.transactionPriority);
|
||||||
final customItem = priorities
|
final customItem = priorities
|
||||||
|
@ -429,8 +435,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
Future<void> _checkForRBF(TransactionInfo tx) async {
|
Future<void> _checkForRBF(TransactionInfo tx) async {
|
||||||
if (wallet.type == WalletType.bitcoin &&
|
if (wallet.type == WalletType.bitcoin &&
|
||||||
transactionInfo.direction == TransactionDirection.outgoing) {
|
transactionInfo.direction == TransactionDirection.outgoing) {
|
||||||
if (await bitcoin!.canReplaceByFee(wallet, tx)) {
|
rawTransaction = await bitcoin!.canReplaceByFee(wallet, tx);
|
||||||
_canReplaceByFee = true;
|
if (rawTransaction != null) {
|
||||||
|
canReplaceByFee = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,8 @@ abstract class Bitcoin {
|
||||||
bool isTestnet(Object wallet);
|
bool isTestnet(Object wallet);
|
||||||
|
|
||||||
Future<PendingTransaction> replaceByFee(Object wallet, String transactionHash, String fee);
|
Future<PendingTransaction> replaceByFee(Object wallet, String transactionHash, String fee);
|
||||||
Future<bool> canReplaceByFee(Object wallet, Object tx);
|
Future<String?> canReplaceByFee(Object wallet, Object tx);
|
||||||
|
int getTransactionVSize(Object wallet, String txHex);
|
||||||
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee);
|
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee);
|
||||||
int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
|
int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
|
||||||
int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount,
|
int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount,
|
||||||
|
|
Loading…
Reference in a new issue