mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
rbf fixes issues sum utxo and fee calculation (#1625)
* total out amount issue * fix empty inputs and outputs addresses for new tx * fix sum value of utxo not spending * Update configure.dart * Update electrum_wallet.dart * receiving address * review fixes
This commit is contained in:
parent
d01199bd04
commit
4c2d061363
6 changed files with 132 additions and 78 deletions
|
@ -132,6 +132,7 @@ abstract class ElectrumWalletBase
|
|||
final String? _mnemonic;
|
||||
|
||||
Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0));
|
||||
|
||||
Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1));
|
||||
|
||||
final EncryptionFileUtils encryptionFileUtils;
|
||||
|
@ -1363,26 +1364,15 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> canReplaceByFee(String hash) async {
|
||||
final verboseTransaction = await electrumClient.getTransactionVerbose(hash: hash);
|
||||
|
||||
final String? transactionHex;
|
||||
int confirmations = 0;
|
||||
|
||||
if (verboseTransaction.isEmpty) {
|
||||
transactionHex = await electrumClient.getTransactionHex(hash: hash);
|
||||
} else {
|
||||
confirmations = verboseTransaction['confirmations'] as int? ?? 0;
|
||||
transactionHex = verboseTransaction['hex'] as String?;
|
||||
}
|
||||
|
||||
if (confirmations > 0) return false;
|
||||
|
||||
if (transactionHex == null || transactionHex.isEmpty) {
|
||||
Future<bool> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
||||
try {
|
||||
final bundle = await getTransactionExpanded(hash: tx.txHash);
|
||||
_updateInputsAndOutputs(tx, bundle);
|
||||
if (bundle.confirmations > 0) return false;
|
||||
return bundle.originalTransaction.canReplaceByFee;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return BtcTransaction.fromRaw(transactionHex).canReplaceByFee;
|
||||
}
|
||||
|
||||
Future<bool> isChangeSufficientForFee(String txId, int newFee) async {
|
||||
|
@ -1458,47 +1448,59 @@ abstract class ElectrumWalletBase
|
|||
);
|
||||
}
|
||||
|
||||
int totalOutAmount = bundle.originalTransaction.outputs
|
||||
.fold<int>(0, (previousValue, element) => previousValue + element.amount.toInt());
|
||||
|
||||
var currentFee = allInputsAmount - totalOutAmount;
|
||||
int remainingFee = newFee - currentFee;
|
||||
|
||||
// Create a list of available outputs
|
||||
final outputs = <BitcoinOutput>[];
|
||||
|
||||
// Add outputs and deduct the fees from it
|
||||
for (int i = bundle.originalTransaction.outputs.length - 1; i >= 0; i--) {
|
||||
final out = bundle.originalTransaction.outputs[i];
|
||||
for (final out in bundle.originalTransaction.outputs) {
|
||||
final address = addressFromOutputScript(out.scriptPubKey, network);
|
||||
final btcAddress = addressTypeFromStr(address, network);
|
||||
outputs.add(BitcoinOutput(address: btcAddress, value: BigInt.from(out.amount.toInt())));
|
||||
}
|
||||
|
||||
int newAmount;
|
||||
if (out.amount.toInt() >= remainingFee) {
|
||||
newAmount = out.amount.toInt() - remainingFee;
|
||||
remainingFee = 0;
|
||||
// Calculate the total amount and fees
|
||||
int totalOutAmount =
|
||||
outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
|
||||
int currentFee = allInputsAmount - totalOutAmount;
|
||||
int remainingFee = newFee - currentFee;
|
||||
|
||||
// if new amount of output is less than dust amount, then don't add this output as well
|
||||
if (newAmount <= _dustAmount) {
|
||||
continue;
|
||||
if (remainingFee <= 0) {
|
||||
throw Exception("New fee must be higher than the current fee.");
|
||||
}
|
||||
|
||||
// Deduct Remaining Fee from Main Outputs
|
||||
if (remainingFee > 0) {
|
||||
for (int i = outputs.length - 1; i >= 0; i--) {
|
||||
int outputAmount = outputs[i].value.toInt();
|
||||
|
||||
if (outputAmount > _dustAmount) {
|
||||
int deduction = (outputAmount - _dustAmount >= remainingFee)
|
||||
? remainingFee
|
||||
: outputAmount - _dustAmount;
|
||||
outputs[i] = BitcoinOutput(
|
||||
address: outputs[i].address, value: BigInt.from(outputAmount - deduction));
|
||||
remainingFee -= deduction;
|
||||
|
||||
if (remainingFee <= 0) break;
|
||||
}
|
||||
} else {
|
||||
remainingFee -= out.amount.toInt();
|
||||
continue;
|
||||
}
|
||||
|
||||
outputs.add(BitcoinOutput(address: btcAddress, value: BigInt.from(newAmount)));
|
||||
}
|
||||
|
||||
// Final check if the remaining fee couldn't be deducted
|
||||
if (remainingFee > 0) {
|
||||
throw Exception("Not enough funds to cover the fee.");
|
||||
}
|
||||
|
||||
// Identify all change outputs
|
||||
final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden);
|
||||
final List<BitcoinOutput> changeOutputs = outputs
|
||||
.where((output) => changeAddresses
|
||||
.any((element) => element.address == output.address.toAddress(network)))
|
||||
.toList();
|
||||
|
||||
// look for a change address in the outputs
|
||||
final changeOutput = outputs.firstWhereOrNull((output) =>
|
||||
changeAddresses.any((element) => element.address == output.address.toAddress(network)));
|
||||
int totalChangeAmount =
|
||||
changeOutputs.fold<int>(0, (sum, output) => sum + output.value.toInt());
|
||||
|
||||
// deduct the change amount from the output amount
|
||||
if (changeOutput != null) {
|
||||
totalOutAmount -= changeOutput.value.toInt();
|
||||
}
|
||||
// The final amount that the receiver will receive
|
||||
int sendingAmount = allInputsAmount - newFee - totalChangeAmount;
|
||||
|
||||
final txb = BitcoinTransactionBuilder(
|
||||
utxos: utxos,
|
||||
|
@ -1527,10 +1529,10 @@ abstract class ElectrumWalletBase
|
|||
transaction,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: totalOutAmount,
|
||||
amount: sendingAmount,
|
||||
fee: newFee,
|
||||
network: network,
|
||||
hasChange: changeOutput != null,
|
||||
hasChange: changeOutputs.isNotEmpty,
|
||||
feeRate: newFee.toString(),
|
||||
)..addListener((transaction) async {
|
||||
transactionHistory.addOne(transaction);
|
||||
|
@ -2026,6 +2028,39 @@ abstract class ElectrumWalletBase
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _updateInputsAndOutputs(ElectrumTransactionInfo tx, ElectrumTransactionBundle bundle) {
|
||||
tx.inputAddresses = tx.inputAddresses?.where((address) => address.isNotEmpty).toList();
|
||||
|
||||
if (tx.inputAddresses == null ||
|
||||
tx.inputAddresses!.isEmpty ||
|
||||
tx.outputAddresses == null ||
|
||||
tx.outputAddresses!.isEmpty) {
|
||||
List<String> inputAddresses = [];
|
||||
List<String> outputAddresses = [];
|
||||
|
||||
for (int i = 0; i < bundle.originalTransaction.inputs.length; i++) {
|
||||
final input = bundle.originalTransaction.inputs[i];
|
||||
final inputTransaction = bundle.ins[i];
|
||||
final vout = input.txIndex;
|
||||
final outTransaction = inputTransaction.outputs[vout];
|
||||
final address = addressFromOutputScript(outTransaction.scriptPubKey, network);
|
||||
|
||||
if (address.isNotEmpty) inputAddresses.add(address);
|
||||
}
|
||||
|
||||
for (int i = 0; i < bundle.originalTransaction.outputs.length; i++) {
|
||||
final out = bundle.originalTransaction.outputs[i];
|
||||
final address = addressFromOutputScript(out.scriptPubKey, network);
|
||||
|
||||
if (address.isNotEmpty) outputAddresses.add(address);
|
||||
}
|
||||
tx.inputAddresses = inputAddresses;
|
||||
tx.outputAddresses = outputAddresses;
|
||||
|
||||
transactionHistory.addOne(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScanNode {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue