fix: sp input calc (#1756)

Rafael committed Oct 18, 2024 at 20:24 UTC 7faca38cfa52bcb3914c8b20cda291e4eefe594a
1 file changed +37 -8
cw_bitcoin/lib/electrum_wallet.dart
+37 -8
@@ -784,6 +784,7 @@ abstract class ElectrumWalletBase
784 Future<EstimatedTxResult> estimateTxForAmount(
785 int credentialsAmount,
786 List<BitcoinOutput> outputs,
787 + List<BitcoinOutput> updatedOutputs,
788 int feeRate, {
789 int? inputsCount,
790 String? memo,
@@ -812,6 +813,7 @@ abstract class ElectrumWalletBase
813 return estimateTxForAmount(
814 credentialsAmount,
815 outputs,
816 + updatedOutputs,
817 feeRate,
818 inputsCount: utxoDetails.utxos.length + 1,
819 memo: memo,
@@ -824,19 +826,30 @@ abstract class ElectrumWalletBase
826 }
827
828 final changeAddress = await walletAddresses.getChangeAddress(
827 - outputs: outputs,
829 + outputs: updatedOutputs,
830 utxoDetails: utxoDetails,
831 );
832 final address = RegexUtils.addressTypeFromStr(changeAddress, network);
833 + updatedOutputs.add(BitcoinOutput(
834 + address: address,
835 + value: BigInt.from(amountLeftForChangeAndFee),
836 + isChange: true,
837 + ));
838 outputs.add(BitcoinOutput(
839 address: address,
840 value: BigInt.from(amountLeftForChangeAndFee),
841 isChange: true,
842 ));
843
844 + // calcFee updates the silent payment outputs to calculate the tx size accounting
845 + // for taproot addresses, but if more inputs are needed to make up for fees,
846 + // the silent payment outputs need to be recalculated for the new inputs
847 + var temp = outputs.map((output) => output).toList();
848 int fee = await calcFee(
849 utxos: utxoDetails.utxos,
839 - outputs: outputs,
850 + // Always take only not updated bitcoin outputs here so for every estimation
851 + // the SP outputs are re-generated to the proper taproot addresses
852 + outputs: temp,
853 network: network,
854 memo: memo,
855 feeRate: feeRate,
@@ -844,18 +857,25 @@ abstract class ElectrumWalletBase
857 vinOutpoints: utxoDetails.vinOutpoints,
858 );
859
860 + updatedOutputs.clear();
861 + updatedOutputs.addAll(temp);
862 +
863 if (fee == 0) {
864 throw BitcoinTransactionNoFeeException();
865 }
866
867 int amount = credentialsAmount;
852 - final lastOutput = outputs.last;
868 + final lastOutput = updatedOutputs.last;
869 final amountLeftForChange = amountLeftForChangeAndFee - fee;
870
855 - print(amountLeftForChangeAndFee);
856 -
871 if (!_isBelowDust(amountLeftForChange)) {
872 // Here, lastOutput already is change, return the amount left without the fee to the user's address.
873 + updatedOutputs[updatedOutputs.length - 1] = BitcoinOutput(
874 + address: lastOutput.address,
875 + value: BigInt.from(amountLeftForChange),
876 + isSilentPayment: lastOutput.isSilentPayment,
877 + isChange: true,
878 + );
879 outputs[outputs.length - 1] = BitcoinOutput(
880 address: lastOutput.address,
881 value: BigInt.from(amountLeftForChange),
@@ -864,6 +884,7 @@ abstract class ElectrumWalletBase
884 );
885 } else {
886 // If has change that is lower than dust, will end up with tx rejected by network rules, so estimate again without the added change
887 + updatedOutputs.removeLast();
888 outputs.removeLast();
889
890 // Still has inputs to spend before failing
@@ -871,16 +892,18 @@ abstract class ElectrumWalletBase
892 return estimateTxForAmount(
893 credentialsAmount,
894 outputs,
895 + updatedOutputs,
896 feeRate,
897 inputsCount: utxoDetails.utxos.length + 1,
898 memo: memo,
899 + hasSilentPayment: hasSilentPayment,
900 useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins,
901 coinTypeToSpendFrom: coinTypeToSpendFrom,
902 );
903 }
904
905 final estimatedSendAll = await estimateSendAllTx(
883 - outputs,
906 + updatedOutputs,
907 feeRate,
908 memo: memo,
909 coinTypeToSpendFrom: coinTypeToSpendFrom,
@@ -913,10 +936,12 @@ abstract class ElectrumWalletBase
936 if (spendingAllCoins) {
937 throw BitcoinTransactionWrongBalanceException();
938 } else {
939 + updatedOutputs.removeLast();
940 outputs.removeLast();
941 return estimateTxForAmount(
942 credentialsAmount,
943 outputs,
944 + updatedOutputs,
945 feeRate,
946 inputsCount: utxoDetails.utxos.length + 1,
947 memo: memo,
@@ -1029,6 +1054,9 @@ abstract class ElectrumWalletBase
1054 : feeRate(transactionCredentials.priority!);
1055
1056 EstimatedTxResult estimatedTx;
1057 + final updatedOutputs =
1058 + outputs.map((e) => BitcoinOutput(address: e.address, value: e.value)).toList();
1059 +
1060 if (sendAll) {
1061 estimatedTx = await estimateSendAllTx(
1062 outputs,
@@ -1042,6 +1070,7 @@ abstract class ElectrumWalletBase
1070 estimatedTx = await estimateTxForAmount(
1071 credentialsAmount,
1072 outputs,
1073 + updatedOutputs,
1074 feeRateInt,
1075 memo: memo,
1076 hasSilentPayment: hasSilentPayment,
@@ -1052,7 +1081,7 @@ abstract class ElectrumWalletBase
1081 if (walletInfo.isHardwareWallet) {
1082 final transaction = await buildHardwareWalletTransaction(
1083 utxos: estimatedTx.utxos,
1055 - outputs: outputs,
1084 + outputs: updatedOutputs,
1085 publicKeys: estimatedTx.publicKeys,
1086 fee: BigInt.from(estimatedTx.fee),
1087 network: network,
@@ -1092,7 +1121,7 @@ abstract class ElectrumWalletBase
1121 } else {
1122 txb = BitcoinTransactionBuilder(
1123 utxos: estimatedTx.utxos,
1095 - outputs: outputs,
1124 + outputs: updatedOutputs,
1125 fee: BigInt.from(estimatedTx.fee),
1126 network: network,
1127 memo: estimatedTx.memo,