diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index ceaa2f088..527bef3ea 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -784,6 +784,7 @@ abstract class ElectrumWalletBase Future estimateTxForAmount( int credentialsAmount, List outputs, + List updatedOutputs, int feeRate, { int? inputsCount, String? memo, @@ -812,6 +813,7 @@ abstract class ElectrumWalletBase return estimateTxForAmount( credentialsAmount, outputs, + updatedOutputs, feeRate, inputsCount: utxoDetails.utxos.length + 1, memo: memo, @@ -824,19 +826,30 @@ abstract class ElectrumWalletBase } final changeAddress = await walletAddresses.getChangeAddress( - outputs: outputs, + outputs: updatedOutputs, utxoDetails: utxoDetails, ); final address = RegexUtils.addressTypeFromStr(changeAddress, network); + updatedOutputs.add(BitcoinOutput( + address: address, + value: BigInt.from(amountLeftForChangeAndFee), + isChange: true, + )); outputs.add(BitcoinOutput( address: address, value: BigInt.from(amountLeftForChangeAndFee), isChange: true, )); + // calcFee updates the silent payment outputs to calculate the tx size accounting + // for taproot addresses, but if more inputs are needed to make up for fees, + // the silent payment outputs need to be recalculated for the new inputs + var temp = outputs.map((output) => output).toList(); int fee = await calcFee( utxos: utxoDetails.utxos, - outputs: outputs, + // Always take only not updated bitcoin outputs here so for every estimation + // the SP outputs are re-generated to the proper taproot addresses + outputs: temp, network: network, memo: memo, feeRate: feeRate, @@ -844,18 +857,25 @@ abstract class ElectrumWalletBase vinOutpoints: utxoDetails.vinOutpoints, ); + updatedOutputs.clear(); + updatedOutputs.addAll(temp); + if (fee == 0) { throw BitcoinTransactionNoFeeException(); } int amount = credentialsAmount; - final lastOutput = outputs.last; + final lastOutput = updatedOutputs.last; final amountLeftForChange = amountLeftForChangeAndFee - fee; - print(amountLeftForChangeAndFee); - if (!_isBelowDust(amountLeftForChange)) { // Here, lastOutput already is change, return the amount left without the fee to the user's address. + updatedOutputs[updatedOutputs.length - 1] = BitcoinOutput( + address: lastOutput.address, + value: BigInt.from(amountLeftForChange), + isSilentPayment: lastOutput.isSilentPayment, + isChange: true, + ); outputs[outputs.length - 1] = BitcoinOutput( address: lastOutput.address, value: BigInt.from(amountLeftForChange), @@ -864,6 +884,7 @@ abstract class ElectrumWalletBase ); } else { // If has change that is lower than dust, will end up with tx rejected by network rules, so estimate again without the added change + updatedOutputs.removeLast(); outputs.removeLast(); // Still has inputs to spend before failing @@ -871,16 +892,18 @@ abstract class ElectrumWalletBase return estimateTxForAmount( credentialsAmount, outputs, + updatedOutputs, feeRate, inputsCount: utxoDetails.utxos.length + 1, memo: memo, + hasSilentPayment: hasSilentPayment, useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, coinTypeToSpendFrom: coinTypeToSpendFrom, ); } final estimatedSendAll = await estimateSendAllTx( - outputs, + updatedOutputs, feeRate, memo: memo, coinTypeToSpendFrom: coinTypeToSpendFrom, @@ -913,10 +936,12 @@ abstract class ElectrumWalletBase if (spendingAllCoins) { throw BitcoinTransactionWrongBalanceException(); } else { + updatedOutputs.removeLast(); outputs.removeLast(); return estimateTxForAmount( credentialsAmount, outputs, + updatedOutputs, feeRate, inputsCount: utxoDetails.utxos.length + 1, memo: memo, @@ -1029,6 +1054,9 @@ abstract class ElectrumWalletBase : feeRate(transactionCredentials.priority!); EstimatedTxResult estimatedTx; + final updatedOutputs = + outputs.map((e) => BitcoinOutput(address: e.address, value: e.value)).toList(); + if (sendAll) { estimatedTx = await estimateSendAllTx( outputs, @@ -1042,6 +1070,7 @@ abstract class ElectrumWalletBase estimatedTx = await estimateTxForAmount( credentialsAmount, outputs, + updatedOutputs, feeRateInt, memo: memo, hasSilentPayment: hasSilentPayment, @@ -1052,7 +1081,7 @@ abstract class ElectrumWalletBase if (walletInfo.isHardwareWallet) { final transaction = await buildHardwareWalletTransaction( utxos: estimatedTx.utxos, - outputs: outputs, + outputs: updatedOutputs, publicKeys: estimatedTx.publicKeys, fee: BigInt.from(estimatedTx.fee), network: network, @@ -1092,7 +1121,7 @@ abstract class ElectrumWalletBase } else { txb = BitcoinTransactionBuilder( utxos: estimatedTx.utxos, - outputs: outputs, + outputs: updatedOutputs, fee: BigInt.from(estimatedTx.fee), network: network, memo: estimatedTx.memo,