feat: add unsignedPsbt to PendingBitcoinTransaction

This commit is contained in:
Konstantin Ullrich 2025-05-28 13:19:33 +02:00 committed by cyan
parent 355218753d
commit a9df2ee285
3 changed files with 43 additions and 22 deletions

View file

@ -372,8 +372,13 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
publicKeys: tx.publicKeys!, publicKeys: tx.publicKeys!,
masterFingerprint: Uint8List(0)); masterFingerprint: Uint8List(0));
final originalPsbt = await signPsbt( if (tx.isViewOnly) {
base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys()); tx.unsignedPsbt = transaction.serialize();
return tx;
}
final originalPsbt =
await signPsbt(base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
tx.commitOverride = () async { tx.commitOverride = () async {
final sender = await payjoinManager.initSender( final sender = await payjoinManager.initSender(
@ -405,6 +410,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
feeRate: "", feeRate: "",
network: network, network: network,
hasChange: true, hasChange: true,
isViewOnly: false,
).commit(); ).commit();
} }

View file

@ -1119,7 +1119,8 @@ abstract class ElectrumWalletBase
network: network, network: network,
hasChange: estimatedTx.hasChange, hasChange: estimatedTx.hasChange,
isSendAll: estimatedTx.isSendAll, isSendAll: estimatedTx.isSendAll,
hasTaprootInputs: false, // ToDo: (Konsti) Support Taproot hasTaprootInputs: false, // ToDo: (Konsti) Support Taproot,
isViewOnly: false,
)..addListener((transaction) async { )..addListener((transaction) async {
transactionHistory.addOne(transaction); transactionHistory.addOne(transaction);
await updateBalance(); await updateBalance();
@ -1190,18 +1191,21 @@ abstract class ElectrumWalletBase
} }
}); });
return PendingBitcoinTransaction(transaction, type, return PendingBitcoinTransaction(
electrumClient: electrumClient, transaction,
amount: estimatedTx.amount, type,
fee: estimatedTx.fee, electrumClient: electrumClient,
feeRate: feeRateInt.toString(), amount: estimatedTx.amount,
network: network, fee: estimatedTx.fee,
hasChange: estimatedTx.hasChange, feeRate: feeRateInt.toString(),
isSendAll: estimatedTx.isSendAll, network: network,
hasTaprootInputs: hasTaprootInputs, hasChange: estimatedTx.hasChange,
utxos: estimatedTx.utxos, isSendAll: estimatedTx.isSendAll,
publicKeys: estimatedTx.publicKeys) hasTaprootInputs: hasTaprootInputs,
..addListener((transaction) async { utxos: estimatedTx.utxos,
publicKeys: estimatedTx.publicKeys,
isViewOnly: keys.privateKey.isEmpty,
)..addListener((transaction) async {
transactionHistory.addOne(transaction); transactionHistory.addOne(transaction);
if (estimatedTx.spendsSilentPayment) { if (estimatedTx.spendsSilentPayment) {
transactionHistory.transactions.values.forEach((tx) { transactionHistory.transactions.values.forEach((tx) {
@ -1862,6 +1866,7 @@ abstract class ElectrumWalletBase
network: network, network: network,
hasChange: changeOutputs.isNotEmpty, hasChange: changeOutputs.isNotEmpty,
feeRate: newFee.toString(), feeRate: newFee.toString(),
isViewOnly: keys.privateKey.isEmpty,
)..addListener((transaction) async { )..addListener((transaction) async {
transactionHistory.transactions.values.forEach((tx) { transactionHistory.transactions.values.forEach((tx) {
if (tx.id == hash) { if (tx.id == hash) {

View file

@ -34,6 +34,8 @@ class PendingBitcoinTransaction with PendingTransaction {
this.utxos = const [], this.utxos = const [],
this.publicKeys, this.publicKeys,
this.commitOverride, this.commitOverride,
this.unsignedPsbt,
required this.isViewOnly,
}) : _listeners = <void Function(ElectrumTransactionInfo transaction)>[]; }) : _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
final WalletType type; final WalletType type;
@ -46,6 +48,7 @@ class PendingBitcoinTransaction with PendingTransaction {
final bool isSendAll; final bool isSendAll;
final bool hasChange; final bool hasChange;
final bool hasTaprootInputs; final bool hasTaprootInputs;
final bool isViewOnly;
List<UtxoWithAddress> utxos; List<UtxoWithAddress> utxos;
bool isMweb; bool isMweb;
String? changeAddressOverride; String? changeAddressOverride;
@ -55,6 +58,8 @@ class PendingBitcoinTransaction with PendingTransaction {
final Map<String, PublicKeyWithDerivationPath>? publicKeys; final Map<String, PublicKeyWithDerivationPath>? publicKeys;
Future<void> Function()? commitOverride; Future<void> Function()? commitOverride;
Uint8List? unsignedPsbt;
@override @override
String get id => idOverride ?? _tx.txId(); String get id => idOverride ?? _tx.txId();
@ -78,9 +83,11 @@ class PendingBitcoinTransaction with PendingTransaction {
try { try {
final change = _tx.outputs.firstWhere((out) => out.isChange); final change = _tx.outputs.firstWhere((out) => out.isChange);
if (changeAddressOverride != null) { if (changeAddressOverride != null) {
return PendingChange(changeAddressOverride!, BtcUtils.fromSatoshi(change.amount)); return PendingChange(
changeAddressOverride!, BtcUtils.fromSatoshi(change.amount));
} }
return PendingChange(change.scriptPubKey.toAddress(), BtcUtils.fromSatoshi(change.amount)); return PendingChange(
change.scriptPubKey.toAddress(), BtcUtils.fromSatoshi(change.amount));
} catch (_) { } catch (_) {
return null; return null;
} }
@ -129,12 +136,14 @@ class PendingBitcoinTransaction with PendingTransaction {
Future<void> _ltcCommit() async { Future<void> _ltcCommit() async {
try { try {
final resp = await CwMweb.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex))); final resp = await CwMweb.broadcast(
BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
idOverride = resp.txid; idOverride = resp.txid;
} on GrpcError catch (e) { } on GrpcError catch (e) {
throw BitcoinTransactionCommitFailed(errorMessage: e.message); throw BitcoinTransactionCommitFailed(errorMessage: e.message);
} catch (e) { } catch (e) {
throw BitcoinTransactionCommitFailed(errorMessage: "Unknown error: ${e.toString()}"); throw BitcoinTransactionCommitFailed(
errorMessage: "Unknown error: ${e.toString()}");
} }
} }
@ -153,7 +162,8 @@ class PendingBitcoinTransaction with PendingTransaction {
_listeners.forEach((listener) => listener(transactionInfo())); _listeners.forEach((listener) => listener(transactionInfo()));
} }
void addListener(void Function(ElectrumTransactionInfo transaction) listener) => void addListener(
void Function(ElectrumTransactionInfo transaction) listener) =>
_listeners.add(listener); _listeners.add(listener);
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type, ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
@ -168,10 +178,10 @@ class PendingBitcoinTransaction with PendingTransaction {
inputAddresses: _tx.inputs.map((input) => input.txId).toList(), inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
outputAddresses: outputAddresses, outputAddresses: outputAddresses,
fee: fee); fee: fee);
@override @override
Future<String?> commitUR() { Future<String?> commitUR() {
var sourceBytes = Uint8List.fromList(utf8.encode(hex)); var sourceBytes = unsignedPsbt!;
var cborEncoder = CBOREncoder(); var cborEncoder = CBOREncoder();
cborEncoder.encodeBytes(sourceBytes); cborEncoder.encodeBytes(sourceBytes);
var ur = UR("psbt", cborEncoder.getBytes()); var ur = UR("psbt", cborEncoder.getBytes());