Electrum enhancements (#1794)
* Enhance the code for sending/sending-ALL for Electrum * remove prints [skip ci]
Omar Hatem committed
Nov 8, 2024 at 12:50 UTC
389c334f1076779a134cd037b2af5b1b1c11df2b
2 files changed
+63
-100
cw_bitcoin/lib/electrum_wallet.dart
+61
-98
@@ -11,7 +11,6 @@ import 'package:blockchain_utils/blockchain_utils.dart';
11
import 'package:collection/collection.dart';
12
import 'package:cw_bitcoin/address_from_output.dart';
13
import 'package:cw_bitcoin/bitcoin_address_record.dart';
14
-import 'package:cw_bitcoin/bitcoin_amount_format.dart';
14
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
15
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
16
import 'package:cw_bitcoin/bitcoin_unspent.dart';
@@ -597,8 +596,8 @@ abstract class ElectrumWalletBase
596
597
UtxoDetails _createUTXOS({
598
required bool sendAll,
600
- required int credentialsAmount,
599
required bool paysToSilentPayment,
600
+ int credentialsAmount = 0,
601
int? inputsCount,
602
UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any,
603
}) {
@@ -732,13 +731,11 @@ abstract class ElectrumWalletBase
731
List<BitcoinOutput> outputs,
732
int feeRate, {
733
String? memo,
735
- int credentialsAmount = 0,
734
bool hasSilentPayment = false,
735
UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any,
736
}) async {
737
final utxoDetails = _createUTXOS(
738
sendAll: true,
741
- credentialsAmount: credentialsAmount,
739
paysToSilentPayment: hasSilentPayment,
740
coinTypeToSpendFrom: coinTypeToSpendFrom,
741
);
@@ -764,23 +761,11 @@ abstract class ElectrumWalletBase
761
throw BitcoinTransactionWrongBalanceException(amount: utxoDetails.allInputsAmount + fee);
762
}
763
767
- if (amount <= 0) {
768
- throw BitcoinTransactionWrongBalanceException();
769
- }
770
-
764
// Attempting to send less than the dust limit
765
if (_isBelowDust(amount)) {
766
throw BitcoinTransactionNoDustException();
767
}
768
776
- if (credentialsAmount > 0) {
777
- final amountLeftForFee = amount - credentialsAmount;
778
- if (amountLeftForFee > 0 && _isBelowDust(amountLeftForFee)) {
779
- amount -= amountLeftForFee;
780
- fee += amountLeftForFee;
781
- }
782
- }
783
-
769
if (outputs.length == 1) {
770
outputs[0] = BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount));
771
}
@@ -810,6 +795,11 @@ abstract class ElectrumWalletBase
795
bool hasSilentPayment = false,
796
UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any,
797
}) async {
798
+ // Attempting to send less than the dust limit
799
+ if (_isBelowDust(credentialsAmount)) {
800
+ throw BitcoinTransactionNoDustException();
801
+ }
802
+
803
final utxoDetails = _createUTXOS(
804
sendAll: false,
805
credentialsAmount: credentialsAmount,
@@ -894,7 +884,43 @@ abstract class ElectrumWalletBase
884
final lastOutput = updatedOutputs.last;
885
final amountLeftForChange = amountLeftForChangeAndFee - fee;
886
897
- if (!_isBelowDust(amountLeftForChange)) {
887
+ if (_isBelowDust(amountLeftForChange)) {
888
+ // If has change that is lower than dust, will end up with tx rejected by network rules
889
+ // so remove the change amount
890
+ updatedOutputs.removeLast();
891
+ outputs.removeLast();
892
+
893
+ if (amountLeftForChange < 0) {
894
+ if (!spendingAllCoins) {
895
+ return estimateTxForAmount(
896
+ credentialsAmount,
897
+ outputs,
898
+ updatedOutputs,
899
+ feeRate,
900
+ inputsCount: utxoDetails.utxos.length + 1,
901
+ memo: memo,
902
+ useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
903
+ hasSilentPayment: hasSilentPayment,
904
+ coinTypeToSpendFrom: coinTypeToSpendFrom,
905
+ );
906
+ } else {
907
+ throw BitcoinTransactionWrongBalanceException();
908
+ }
909
+ }
910
+
911
+ return EstimatedTxResult(
912
+ utxos: utxoDetails.utxos,
913
+ inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
914
+ publicKeys: utxoDetails.publicKeys,
915
+ fee: fee,
916
+ amount: amount,
917
+ hasChange: false,
918
+ isSendAll: spendingAllCoins,
919
+ memo: memo,
920
+ spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX,
921
+ spendsSilentPayment: utxoDetails.spendsSilentPayment,
922
+ );
923
+ } else {
924
// Here, lastOutput already is change, return the amount left without the fee to the user's address.
925
updatedOutputs[updatedOutputs.length - 1] = BitcoinOutput(
926
address: lastOutput.address,
@@ -908,88 +934,20 @@ abstract class ElectrumWalletBase
934
isSilentPayment: lastOutput.isSilentPayment,
935
isChange: true,
936
);
911
- } else {
912
- // If has change that is lower than dust, will end up with tx rejected by network rules, so estimate again without the added change
913
- updatedOutputs.removeLast();
914
- outputs.removeLast();
915
-
916
- // Still has inputs to spend before failing
917
- if (!spendingAllCoins) {
918
- return estimateTxForAmount(
919
- credentialsAmount,
920
- outputs,
921
- updatedOutputs,
922
- feeRate,
923
- inputsCount: utxoDetails.utxos.length + 1,
924
- memo: memo,
925
- hasSilentPayment: hasSilentPayment,
926
- useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
927
- coinTypeToSpendFrom: coinTypeToSpendFrom,
928
- );
929
- }
937
931
- final estimatedSendAll = await estimateSendAllTx(
932
- updatedOutputs,
933
- feeRate,
938
+ return EstimatedTxResult(
939
+ utxos: utxoDetails.utxos,
940
+ inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
941
+ publicKeys: utxoDetails.publicKeys,
942
+ fee: fee,
943
+ amount: amount,
944
+ hasChange: true,
945
+ isSendAll: spendingAllCoins,
946
memo: memo,
935
- coinTypeToSpendFrom: coinTypeToSpendFrom,
936
- );
937
-
938
- if (estimatedSendAll.amount == credentialsAmount) {
939
- return estimatedSendAll;
940
- }
941
-
942
- // Estimate to user how much is needed to send to cover the fee
943
- final maxAmountWithReturningChange = utxoDetails.allInputsAmount - _dustAmount - fee - 1;
944
- throw BitcoinTransactionNoDustOnChangeException(
945
- bitcoinAmountToString(amount: maxAmountWithReturningChange),
946
- bitcoinAmountToString(amount: estimatedSendAll.amount),
947
+ spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX,
948
+ spendsSilentPayment: utxoDetails.spendsSilentPayment,
949
);
950
}
949
-
950
- // Attempting to send less than the dust limit
951
- if (_isBelowDust(amount)) {
952
- throw BitcoinTransactionNoDustException();
953
- }
954
-
955
- final totalAmount = amount + fee;
956
-
957
- if (totalAmount > (balance[currency]!.confirmed + balance[currency]!.secondConfirmed)) {
958
- throw BitcoinTransactionWrongBalanceException();
959
- }
960
-
961
- if (totalAmount > utxoDetails.allInputsAmount) {
962
- if (spendingAllCoins) {
963
- throw BitcoinTransactionWrongBalanceException();
964
- } else {
965
- updatedOutputs.removeLast();
966
- outputs.removeLast();
967
- return estimateTxForAmount(
968
- credentialsAmount,
969
- outputs,
970
- updatedOutputs,
971
- feeRate,
972
- inputsCount: utxoDetails.utxos.length + 1,
973
- memo: memo,
974
- useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
975
- hasSilentPayment: hasSilentPayment,
976
- coinTypeToSpendFrom: coinTypeToSpendFrom,
977
- );
978
- }
979
- }
980
-
981
- return EstimatedTxResult(
982
- utxos: utxoDetails.utxos,
983
- inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
984
- publicKeys: utxoDetails.publicKeys,
985
- fee: fee,
986
- amount: amount,
987
- hasChange: true,
988
- isSendAll: false,
989
- memo: memo,
990
- spendsUnconfirmedTX: utxoDetails.spendsUnconfirmedTX,
991
- spendsSilentPayment: utxoDetails.spendsSilentPayment,
992
- );
951
}
952
953
Future<int> calcFee({
@@ -1080,15 +1038,20 @@ abstract class ElectrumWalletBase
1038
: feeRate(transactionCredentials.priority!);
1039
1040
EstimatedTxResult estimatedTx;
1083
- final updatedOutputs =
1084
- outputs.map((e) => BitcoinOutput(address: e.address, value: e.value)).toList();
1041
+ final updatedOutputs = outputs
1042
+ .map((e) => BitcoinOutput(
1043
+ address: e.address,
1044
+ value: e.value,
1045
+ isSilentPayment: e.isSilentPayment,
1046
+ isChange: e.isChange,
1047
+ ))
1048
+ .toList();
1049
1050
if (sendAll) {
1051
estimatedTx = await estimateSendAllTx(
1052
updatedOutputs,
1053
feeRateInt,
1054
memo: memo,
1091
- credentialsAmount: credentialsAmount,
1055
hasSilentPayment: hasSilentPayment,
1056
coinTypeToSpendFrom: coinTypeToSpendFrom,
1057
);
cw_bitcoin/pubspec.lock
+2
-2
@@ -568,10 +568,10 @@ packages:
568
dependency: "direct main"
569
description:
570
name: ledger_flutter_plus
571
- sha256: ea3ed586e1697776dacf42ac979095f1ca3bd143bf007cbe5c78e09cb6943f42
571
+ sha256: c7b04008553193dbca7e17b430768eecc372a72b0ff3625b5e7fc5e5c8d3231b
572
url: "https://pub.dev"
573
source: hosted
574
- version: "1.2.5"
574
+ version: "1.4.1"
575
ledger_litecoin:
576
dependency: "direct main"
577
description: