mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 09:47:37 +00:00
notification tx absurd fees error fix when estimating a tx size
This commit is contained in:
parent
55ed68a89d
commit
6bb133c552
2 changed files with 250 additions and 213 deletions
|
@ -26,6 +26,7 @@ import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||||
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
class PaynymDetailsPopup extends ConsumerStatefulWidget {
|
class PaynymDetailsPopup extends ConsumerStatefulWidget {
|
||||||
|
@ -102,6 +103,20 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
||||||
_showInsufficientFundsInfo = true;
|
_showInsufficientFundsInfo = true;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
canPop = true;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => StackOkDialog(
|
||||||
|
title: "Error",
|
||||||
|
message: e.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|
|
@ -398,135 +398,162 @@ mixin PaynymWalletInterface {
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<UTXO>? utxos,
|
List<UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
final amountToSend = _dustLimitP2PKH;
|
try {
|
||||||
final List<UTXO> availableOutputs =
|
final amountToSend = _dustLimitP2PKH;
|
||||||
utxos ?? await _db.getUTXOs(_walletId).findAll();
|
final List<UTXO> availableOutputs =
|
||||||
final List<UTXO> spendableOutputs = [];
|
utxos ?? await _db.getUTXOs(_walletId).findAll();
|
||||||
int spendableSatoshiValue = 0;
|
final List<UTXO> spendableOutputs = [];
|
||||||
|
int spendableSatoshiValue = 0;
|
||||||
|
|
||||||
// Build list of spendable outputs and totaling their satoshi amount
|
// Build list of spendable outputs and totaling their satoshi amount
|
||||||
for (var i = 0; i < availableOutputs.length; i++) {
|
for (var i = 0; i < availableOutputs.length; i++) {
|
||||||
if (availableOutputs[i].isBlocked == false &&
|
if (availableOutputs[i].isBlocked == false &&
|
||||||
availableOutputs[i]
|
availableOutputs[i]
|
||||||
.isConfirmed(await _getChainHeight(), _minConfirms) ==
|
.isConfirmed(await _getChainHeight(), _minConfirms) ==
|
||||||
true) {
|
true) {
|
||||||
spendableOutputs.add(availableOutputs[i]);
|
spendableOutputs.add(availableOutputs[i]);
|
||||||
spendableSatoshiValue += availableOutputs[i].value;
|
spendableSatoshiValue += availableOutputs[i].value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (spendableSatoshiValue < amountToSend) {
|
if (spendableSatoshiValue < amountToSend) {
|
||||||
// insufficient balance
|
// insufficient balance
|
||||||
throw InsufficientBalanceException(
|
throw InsufficientBalanceException(
|
||||||
"Spendable balance is less than the minimum required for a notification transaction.");
|
"Spendable balance is less than the minimum required for a notification transaction.");
|
||||||
} else if (spendableSatoshiValue == amountToSend) {
|
} else if (spendableSatoshiValue == amountToSend) {
|
||||||
// insufficient balance due to missing amount to cover fee
|
// insufficient balance due to missing amount to cover fee
|
||||||
throw InsufficientBalanceException(
|
throw InsufficientBalanceException(
|
||||||
"Remaining balance does not cover the network fee.");
|
"Remaining balance does not cover the network fee.");
|
||||||
}
|
|
||||||
|
|
||||||
// sort spendable by age (oldest first)
|
|
||||||
spendableOutputs.sort((a, b) => b.blockTime!.compareTo(a.blockTime!));
|
|
||||||
|
|
||||||
int satoshisBeingUsed = 0;
|
|
||||||
int outputsBeingUsed = 0;
|
|
||||||
List<UTXO> utxoObjectsToUse = [];
|
|
||||||
|
|
||||||
for (int i = 0;
|
|
||||||
satoshisBeingUsed < amountToSend && i < spendableOutputs.length;
|
|
||||||
i++) {
|
|
||||||
utxoObjectsToUse.add(spendableOutputs[i]);
|
|
||||||
satoshisBeingUsed += spendableOutputs[i].value;
|
|
||||||
outputsBeingUsed += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add additional outputs if required
|
|
||||||
for (int i = 0;
|
|
||||||
i < additionalOutputs && outputsBeingUsed < spendableOutputs.length;
|
|
||||||
i++) {
|
|
||||||
utxoObjectsToUse.add(spendableOutputs[outputsBeingUsed]);
|
|
||||||
satoshisBeingUsed += spendableOutputs[outputsBeingUsed].value;
|
|
||||||
outputsBeingUsed += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// gather required signing data
|
|
||||||
final utxoSigningData = await _fetchBuildTxData(utxoObjectsToUse);
|
|
||||||
|
|
||||||
final int vSizeForNoChange = (await _createNotificationTx(
|
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
|
||||||
utxosToUse: utxoObjectsToUse,
|
|
||||||
utxoSigningData: utxoSigningData,
|
|
||||||
change: 0))
|
|
||||||
.item2;
|
|
||||||
|
|
||||||
final int vSizeForWithChange = (await _createNotificationTx(
|
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
|
||||||
utxosToUse: utxoObjectsToUse,
|
|
||||||
utxoSigningData: utxoSigningData,
|
|
||||||
change: satoshisBeingUsed - amountToSend))
|
|
||||||
.item2;
|
|
||||||
|
|
||||||
// Assume 2 outputs, for recipient and payment code script
|
|
||||||
int feeForNoChange = _estimateTxFee(
|
|
||||||
vSize: vSizeForNoChange,
|
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assume 3 outputs, for recipient, payment code script, and change
|
|
||||||
int feeForWithChange = _estimateTxFee(
|
|
||||||
vSize: vSizeForWithChange,
|
|
||||||
feeRatePerKB: selectedTxFeeRate,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_coin == Coin.dogecoin || _coin == Coin.dogecoinTestNet) {
|
|
||||||
if (feeForNoChange < vSizeForNoChange * 1000) {
|
|
||||||
feeForNoChange = vSizeForNoChange * 1000;
|
|
||||||
}
|
}
|
||||||
if (feeForWithChange < vSizeForWithChange * 1000) {
|
|
||||||
feeForWithChange = vSizeForWithChange * 1000;
|
// sort spendable by age (oldest first)
|
||||||
|
spendableOutputs.sort((a, b) => b.blockTime!.compareTo(a.blockTime!));
|
||||||
|
|
||||||
|
int satoshisBeingUsed = 0;
|
||||||
|
int outputsBeingUsed = 0;
|
||||||
|
List<UTXO> utxoObjectsToUse = [];
|
||||||
|
|
||||||
|
for (int i = 0;
|
||||||
|
satoshisBeingUsed < amountToSend && i < spendableOutputs.length;
|
||||||
|
i++) {
|
||||||
|
utxoObjectsToUse.add(spendableOutputs[i]);
|
||||||
|
satoshisBeingUsed += spendableOutputs[i].value;
|
||||||
|
outputsBeingUsed += 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (satoshisBeingUsed - amountToSend > feeForNoChange + _dustLimitP2PKH) {
|
// add additional outputs if required
|
||||||
// try to add change output due to "left over" amount being greater than
|
for (int i = 0;
|
||||||
// the estimated fee + the dust limit
|
i < additionalOutputs && outputsBeingUsed < spendableOutputs.length;
|
||||||
int changeAmount = satoshisBeingUsed - amountToSend - feeForWithChange;
|
i++) {
|
||||||
|
utxoObjectsToUse.add(spendableOutputs[outputsBeingUsed]);
|
||||||
|
satoshisBeingUsed += spendableOutputs[outputsBeingUsed].value;
|
||||||
|
outputsBeingUsed += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// check estimates are correct and build notification tx
|
// gather required signing data
|
||||||
if (changeAmount >= _dustLimitP2PKH &&
|
final utxoSigningData = await _fetchBuildTxData(utxoObjectsToUse);
|
||||||
satoshisBeingUsed - amountToSend - changeAmount == feeForWithChange) {
|
|
||||||
var txn = await _createNotificationTx(
|
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
|
||||||
utxosToUse: utxoObjectsToUse,
|
|
||||||
utxoSigningData: utxoSigningData,
|
|
||||||
change: changeAmount,
|
|
||||||
);
|
|
||||||
|
|
||||||
int feeBeingPaid = satoshisBeingUsed - amountToSend - changeAmount;
|
final int vSizeForNoChange = (await _createNotificationTx(
|
||||||
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
|
utxosToUse: utxoObjectsToUse,
|
||||||
|
utxoSigningData: utxoSigningData,
|
||||||
|
change: 0,
|
||||||
|
dustLimit:
|
||||||
|
satoshisBeingUsed, // override amount to get around absurd fees error
|
||||||
|
))
|
||||||
|
.item2;
|
||||||
|
|
||||||
// make sure minimum fee is accurate if that is being used
|
final int vSizeForWithChange = (await _createNotificationTx(
|
||||||
if (txn.item2 - feeBeingPaid == 1) {
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
changeAmount -= 1;
|
utxosToUse: utxoObjectsToUse,
|
||||||
feeBeingPaid += 1;
|
utxoSigningData: utxoSigningData,
|
||||||
txn = await _createNotificationTx(
|
change: satoshisBeingUsed - amountToSend,
|
||||||
|
))
|
||||||
|
.item2;
|
||||||
|
|
||||||
|
// Assume 2 outputs, for recipient and payment code script
|
||||||
|
int feeForNoChange = _estimateTxFee(
|
||||||
|
vSize: vSizeForNoChange,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assume 3 outputs, for recipient, payment code script, and change
|
||||||
|
int feeForWithChange = _estimateTxFee(
|
||||||
|
vSize: vSizeForWithChange,
|
||||||
|
feeRatePerKB: selectedTxFeeRate,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_coin == Coin.dogecoin || _coin == Coin.dogecoinTestNet) {
|
||||||
|
if (feeForNoChange < vSizeForNoChange * 1000) {
|
||||||
|
feeForNoChange = vSizeForNoChange * 1000;
|
||||||
|
}
|
||||||
|
if (feeForWithChange < vSizeForWithChange * 1000) {
|
||||||
|
feeForWithChange = vSizeForWithChange * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (satoshisBeingUsed - amountToSend > feeForNoChange + _dustLimitP2PKH) {
|
||||||
|
// try to add change output due to "left over" amount being greater than
|
||||||
|
// the estimated fee + the dust limit
|
||||||
|
int changeAmount = satoshisBeingUsed - amountToSend - feeForWithChange;
|
||||||
|
|
||||||
|
// check estimates are correct and build notification tx
|
||||||
|
if (changeAmount >= _dustLimitP2PKH &&
|
||||||
|
satoshisBeingUsed - amountToSend - changeAmount ==
|
||||||
|
feeForWithChange) {
|
||||||
|
var txn = await _createNotificationTx(
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
utxosToUse: utxoObjectsToUse,
|
utxosToUse: utxoObjectsToUse,
|
||||||
utxoSigningData: utxoSigningData,
|
utxoSigningData: utxoSigningData,
|
||||||
change: changeAmount,
|
change: changeAmount,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> transactionObject = {
|
int feeBeingPaid = satoshisBeingUsed - amountToSend - changeAmount;
|
||||||
"hex": txn.item1,
|
|
||||||
"recipientPaynym": targetPaymentCodeString,
|
// make sure minimum fee is accurate if that is being used
|
||||||
"amount": amountToSend,
|
if (txn.item2 - feeBeingPaid == 1) {
|
||||||
"fee": feeBeingPaid,
|
changeAmount -= 1;
|
||||||
"vSize": txn.item2,
|
feeBeingPaid += 1;
|
||||||
};
|
txn = await _createNotificationTx(
|
||||||
return transactionObject;
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
} else {
|
utxosToUse: utxoObjectsToUse,
|
||||||
// something broke during fee estimation or the change amount is smaller
|
utxoSigningData: utxoSigningData,
|
||||||
// than the dust limit. Try without change
|
change: changeAmount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> transactionObject = {
|
||||||
|
"hex": txn.item1,
|
||||||
|
"recipientPaynym": targetPaymentCodeString,
|
||||||
|
"amount": amountToSend,
|
||||||
|
"fee": feeBeingPaid,
|
||||||
|
"vSize": txn.item2,
|
||||||
|
};
|
||||||
|
return transactionObject;
|
||||||
|
} else {
|
||||||
|
// something broke during fee estimation or the change amount is smaller
|
||||||
|
// than the dust limit. Try without change
|
||||||
|
final txn = await _createNotificationTx(
|
||||||
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
|
utxosToUse: utxoObjectsToUse,
|
||||||
|
utxoSigningData: utxoSigningData,
|
||||||
|
change: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
int feeBeingPaid = satoshisBeingUsed - amountToSend;
|
||||||
|
|
||||||
|
Map<String, dynamic> transactionObject = {
|
||||||
|
"hex": txn.item1,
|
||||||
|
"recipientPaynym": targetPaymentCodeString,
|
||||||
|
"amount": amountToSend,
|
||||||
|
"fee": feeBeingPaid,
|
||||||
|
"vSize": txn.item2,
|
||||||
|
};
|
||||||
|
return transactionObject;
|
||||||
|
}
|
||||||
|
} else if (satoshisBeingUsed - amountToSend >= feeForNoChange) {
|
||||||
|
// since we already checked if we need to add a change output we can just
|
||||||
|
// build without change here
|
||||||
final txn = await _createNotificationTx(
|
final txn = await _createNotificationTx(
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
utxosToUse: utxoObjectsToUse,
|
utxosToUse: utxoObjectsToUse,
|
||||||
|
@ -544,40 +571,22 @@ mixin PaynymWalletInterface {
|
||||||
"vSize": txn.item2,
|
"vSize": txn.item2,
|
||||||
};
|
};
|
||||||
return transactionObject;
|
return transactionObject;
|
||||||
}
|
|
||||||
} else if (satoshisBeingUsed - amountToSend >= feeForNoChange) {
|
|
||||||
// since we already checked if we need to add a change output we can just
|
|
||||||
// build without change here
|
|
||||||
final txn = await _createNotificationTx(
|
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
|
||||||
utxosToUse: utxoObjectsToUse,
|
|
||||||
utxoSigningData: utxoSigningData,
|
|
||||||
change: 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
int feeBeingPaid = satoshisBeingUsed - amountToSend;
|
|
||||||
|
|
||||||
Map<String, dynamic> transactionObject = {
|
|
||||||
"hex": txn.item1,
|
|
||||||
"recipientPaynym": targetPaymentCodeString,
|
|
||||||
"amount": amountToSend,
|
|
||||||
"fee": feeBeingPaid,
|
|
||||||
"vSize": txn.item2,
|
|
||||||
};
|
|
||||||
return transactionObject;
|
|
||||||
} else {
|
|
||||||
// if we get here we do not have enough funds to cover the tx total so we
|
|
||||||
// check if we have any more available outputs and try again
|
|
||||||
if (spendableOutputs.length > outputsBeingUsed) {
|
|
||||||
return prepareNotificationTx(
|
|
||||||
selectedTxFeeRate: selectedTxFeeRate,
|
|
||||||
targetPaymentCodeString: targetPaymentCodeString,
|
|
||||||
additionalOutputs: additionalOutputs + 1,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
throw InsufficientBalanceException(
|
// if we get here we do not have enough funds to cover the tx total so we
|
||||||
"Remaining balance does not cover the network fee.");
|
// check if we have any more available outputs and try again
|
||||||
|
if (spendableOutputs.length > outputsBeingUsed) {
|
||||||
|
return prepareNotificationTx(
|
||||||
|
selectedTxFeeRate: selectedTxFeeRate,
|
||||||
|
targetPaymentCodeString: targetPaymentCodeString,
|
||||||
|
additionalOutputs: additionalOutputs + 1,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw InsufficientBalanceException(
|
||||||
|
"Remaining balance does not cover the network fee.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,96 +597,109 @@ mixin PaynymWalletInterface {
|
||||||
required List<UTXO> utxosToUse,
|
required List<UTXO> utxosToUse,
|
||||||
required Map<String, dynamic> utxoSigningData,
|
required Map<String, dynamic> utxoSigningData,
|
||||||
required int change,
|
required int change,
|
||||||
|
int? dustLimit,
|
||||||
}) async {
|
}) async {
|
||||||
final targetPaymentCode =
|
try {
|
||||||
PaymentCode.fromPaymentCode(targetPaymentCodeString, _network);
|
final targetPaymentCode =
|
||||||
final myCode = await getPaymentCode(DerivePathType.bip44);
|
PaymentCode.fromPaymentCode(targetPaymentCodeString, _network);
|
||||||
|
final myCode = await getPaymentCode(DerivePathType.bip44);
|
||||||
|
|
||||||
final utxo = utxosToUse.first;
|
final utxo = utxosToUse.first;
|
||||||
final txPoint = utxo.txid.fromHex.toList();
|
final txPoint = utxo.txid.fromHex.toList();
|
||||||
final txPointIndex = utxo.vout;
|
final txPointIndex = utxo.vout;
|
||||||
|
|
||||||
final rev = Uint8List(txPoint.length + 4);
|
final rev = Uint8List(txPoint.length + 4);
|
||||||
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
|
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
|
||||||
final buffer = rev.buffer.asByteData();
|
final buffer = rev.buffer.asByteData();
|
||||||
buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
|
buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
|
||||||
|
|
||||||
final myKeyPair = utxoSigningData[utxo.txid]["keyPair"] as btc_dart.ECPair;
|
final myKeyPair =
|
||||||
|
utxoSigningData[utxo.txid]["keyPair"] as btc_dart.ECPair;
|
||||||
|
|
||||||
final S = SecretPoint(
|
final S = SecretPoint(
|
||||||
myKeyPair.privateKey!,
|
myKeyPair.privateKey!,
|
||||||
targetPaymentCode.notificationPublicKey(),
|
targetPaymentCode.notificationPublicKey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
final blindingMask = PaymentCode.getMask(S.ecdhSecret(), rev);
|
final blindingMask = PaymentCode.getMask(S.ecdhSecret(), rev);
|
||||||
|
|
||||||
final blindedPaymentCode = PaymentCode.blind(
|
final blindedPaymentCode = PaymentCode.blind(
|
||||||
payload: myCode.getPayload(),
|
payload: myCode.getPayload(),
|
||||||
mask: blindingMask,
|
mask: blindingMask,
|
||||||
unBlind: false,
|
unBlind: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
final opReturnScript = bscript.compile([
|
final opReturnScript = bscript.compile([
|
||||||
(op.OPS["OP_RETURN"] as int),
|
(op.OPS["OP_RETURN"] as int),
|
||||||
blindedPaymentCode,
|
blindedPaymentCode,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// build a notification tx
|
// build a notification tx
|
||||||
final txb = btc_dart.TransactionBuilder(network: _network);
|
final txb = btc_dart.TransactionBuilder(network: _network);
|
||||||
txb.setVersion(1);
|
txb.setVersion(1);
|
||||||
|
|
||||||
txb.addInput(
|
|
||||||
utxo.txid,
|
|
||||||
txPointIndex,
|
|
||||||
null,
|
|
||||||
utxoSigningData[utxo.txid]["output"] as Uint8List,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add rest of possible inputs
|
|
||||||
for (var i = 1; i < utxosToUse.length; i++) {
|
|
||||||
final utxo = utxosToUse[i];
|
|
||||||
txb.addInput(
|
txb.addInput(
|
||||||
utxo.txid,
|
utxo.txid,
|
||||||
utxo.vout,
|
txPointIndex,
|
||||||
null,
|
null,
|
||||||
utxoSigningData[utxo.txid]["output"] as Uint8List,
|
utxoSigningData[utxo.txid]["output"] as Uint8List,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// todo: modify address once segwit support is in our bip47
|
// add rest of possible inputs
|
||||||
txb.addOutput(
|
for (var i = 1; i < utxosToUse.length; i++) {
|
||||||
targetPaymentCode.notificationAddressP2PKH(), _dustLimitP2PKH);
|
final utxo = utxosToUse[i];
|
||||||
txb.addOutput(opReturnScript, 0);
|
txb.addInput(
|
||||||
|
utxo.txid,
|
||||||
|
utxo.vout,
|
||||||
|
null,
|
||||||
|
utxoSigningData[utxo.txid]["output"] as Uint8List,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add possible change output and mark output as dangerous
|
// todo: modify address once segwit support is in our bip47
|
||||||
if (change > 0) {
|
txb.addOutput(
|
||||||
// generate new change address if current change address has been used
|
targetPaymentCode.notificationAddressP2PKH(),
|
||||||
await _checkChangeAddressForTransactions();
|
dustLimit ?? _dustLimitP2PKH,
|
||||||
final String changeAddress = await _getCurrentChangeAddress();
|
);
|
||||||
txb.addOutput(changeAddress, change);
|
txb.addOutput(opReturnScript, 0);
|
||||||
}
|
|
||||||
|
|
||||||
txb.sign(
|
// TODO: add possible change output and mark output as dangerous
|
||||||
vin: 0,
|
if (change > 0) {
|
||||||
keyPair: myKeyPair,
|
// generate new change address if current change address has been used
|
||||||
witnessValue: utxo.value,
|
await _checkChangeAddressForTransactions();
|
||||||
witnessScript: utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
|
final String changeAddress = await _getCurrentChangeAddress();
|
||||||
);
|
txb.addOutput(changeAddress, change);
|
||||||
|
}
|
||||||
|
|
||||||
// sign rest of possible inputs
|
|
||||||
for (var i = 1; i < utxosToUse.length; i++) {
|
|
||||||
final txid = utxosToUse[i].txid;
|
|
||||||
txb.sign(
|
txb.sign(
|
||||||
vin: i,
|
vin: 0,
|
||||||
keyPair: utxoSigningData[txid]["keyPair"] as btc_dart.ECPair,
|
keyPair: myKeyPair,
|
||||||
witnessValue: utxosToUse[i].value,
|
witnessValue: utxo.value,
|
||||||
witnessScript: utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
|
witnessScript: utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// sign rest of possible inputs
|
||||||
|
for (var i = 1; i < utxosToUse.length; i++) {
|
||||||
|
final txid = utxosToUse[i].txid;
|
||||||
|
txb.sign(
|
||||||
|
vin: i,
|
||||||
|
keyPair: utxoSigningData[txid]["keyPair"] as btc_dart.ECPair,
|
||||||
|
witnessValue: utxosToUse[i].value,
|
||||||
|
witnessScript:
|
||||||
|
utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final builtTx = txb.build();
|
||||||
|
|
||||||
|
return Tuple2(builtTx.toHex(), builtTx.virtualSize());
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"_createNotificationTx(): $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
final builtTx = txb.build();
|
|
||||||
|
|
||||||
return Tuple2(builtTx.toHex(), builtTx.virtualSize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> broadcastNotificationTx(
|
Future<String> broadcastNotificationTx(
|
||||||
|
|
Loading…
Reference in a new issue