@@ -219,11 +219,8 @@ SendCoinsDialog::~SendCoinsDialog()
219
219
delete ui;
220
220
}
221
221
222
- void SendCoinsDialog::on_sendButton_clicked ( )
222
+ bool SendCoinsDialog::PrepareSendText (QString& question_string, QString& informative_text, QString& detailed_text )
223
223
{
224
- if (!model || !model->getOptionsModel ())
225
- return ;
226
-
227
224
QList<SendCoinsRecipient> recipients;
228
225
bool valid = true ;
229
226
@@ -246,7 +243,7 @@ void SendCoinsDialog::on_sendButton_clicked()
246
243
247
244
if (!valid || recipients.isEmpty ())
248
245
{
249
- return ;
246
+ return false ;
250
247
}
251
248
252
249
fNewRecipientAllowed = false ;
@@ -255,11 +252,11 @@ void SendCoinsDialog::on_sendButton_clicked()
255
252
{
256
253
// Unlock wallet was cancelled
257
254
fNewRecipientAllowed = true ;
258
- return ;
255
+ return false ;
259
256
}
260
257
261
258
// prepare transaction for getting txFee earlier
262
- WalletModelTransaction currentTransaction (recipients);
259
+ m_current_transaction = MakeUnique<WalletModelTransaction> (recipients);
263
260
WalletModel::SendCoinsReturn prepareStatus;
264
261
265
262
// Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
@@ -269,22 +266,20 @@ void SendCoinsDialog::on_sendButton_clicked()
269
266
270
267
updateCoinControlState (ctrl);
271
268
272
- prepareStatus = model->prepareTransaction (currentTransaction , ctrl);
269
+ prepareStatus = model->prepareTransaction (*m_current_transaction , ctrl);
273
270
274
271
// process prepareStatus and on error generate message shown to user
275
272
processSendCoinsReturn (prepareStatus,
276
- BitcoinUnits::formatWithUnit (model->getOptionsModel ()->getDisplayUnit (), currentTransaction. getTransactionFee ()));
273
+ BitcoinUnits::formatWithUnit (model->getOptionsModel ()->getDisplayUnit (), m_current_transaction-> getTransactionFee ()));
277
274
278
275
if (prepareStatus.status != WalletModel::OK) {
279
276
fNewRecipientAllowed = true ;
280
- return ;
277
+ return false ;
281
278
}
282
279
283
- CAmount txFee = currentTransaction.getTransactionFee ();
284
-
285
- // Format confirmation message
280
+ CAmount txFee = m_current_transaction->getTransactionFee ();
286
281
QStringList formatted;
287
- for (const SendCoinsRecipient &rcp : currentTransaction. getRecipients ())
282
+ for (const SendCoinsRecipient &rcp : m_current_transaction-> getRecipients ())
288
283
{
289
284
// generate amount string with wallet name in case of multiwallet
290
285
QString amount = BitcoinUnits::formatWithUnit (model->getOptionsModel ()->getDisplayUnit (), rcp.amount );
@@ -311,72 +306,82 @@ void SendCoinsDialog::on_sendButton_clicked()
311
306
formatted.append (recipientElement);
312
307
}
313
308
314
- QString questionString;
315
309
if (model->wallet ().privateKeysDisabled ()) {
316
- questionString .append (tr (" Do you want to draft this transaction?" ));
310
+ question_string .append (tr (" Do you want to draft this transaction?" ));
317
311
} else {
318
- questionString .append (tr (" Are you sure you want to send?" ));
312
+ question_string .append (tr (" Are you sure you want to send?" ));
319
313
}
320
314
321
- questionString .append (" <br /><span style='font-size:10pt;'>" );
315
+ question_string .append (" <br /><span style='font-size:10pt;'>" );
322
316
if (model->wallet ().privateKeysDisabled ()) {
323
- questionString .append (tr (" Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet." ).arg (PACKAGE_NAME));
317
+ question_string .append (tr (" Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet." ).arg (PACKAGE_NAME));
324
318
} else {
325
- questionString .append (tr (" Please, review your transaction." ));
319
+ question_string .append (tr (" Please, review your transaction." ));
326
320
}
327
- questionString .append (" </span>%1" );
321
+ question_string .append (" </span>%1" );
328
322
329
323
if (txFee > 0 )
330
324
{
331
325
// append fee string if a fee is required
332
- questionString .append (" <hr /><b>" );
333
- questionString .append (tr (" Transaction fee" ));
334
- questionString .append (" </b>" );
326
+ question_string .append (" <hr /><b>" );
327
+ question_string .append (tr (" Transaction fee" ));
328
+ question_string .append (" </b>" );
335
329
336
330
// append transaction size
337
- questionString .append (" (" + QString::number ((double )currentTransaction. getTransactionSize () / 1000 ) + " kB): " );
331
+ question_string .append (" (" + QString::number ((double )m_current_transaction-> getTransactionSize () / 1000 ) + " kB): " );
338
332
339
333
// append transaction fee value
340
- questionString .append (" <span style='color:#aa0000; font-weight:bold;'>" );
341
- questionString .append (BitcoinUnits::formatHtmlWithUnit (model->getOptionsModel ()->getDisplayUnit (), txFee));
342
- questionString .append (" </span><br />" );
334
+ question_string .append (" <span style='color:#aa0000; font-weight:bold;'>" );
335
+ question_string .append (BitcoinUnits::formatHtmlWithUnit (model->getOptionsModel ()->getDisplayUnit (), txFee));
336
+ question_string .append (" </span><br />" );
343
337
344
338
// append RBF message according to transaction's signalling
345
- questionString .append (" <span style='font-size:10pt; font-weight:normal;'>" );
339
+ question_string .append (" <span style='font-size:10pt; font-weight:normal;'>" );
346
340
if (ui->optInRBF ->isChecked ()) {
347
- questionString .append (tr (" You can increase the fee later (signals Replace-By-Fee, BIP-125)." ));
341
+ question_string .append (tr (" You can increase the fee later (signals Replace-By-Fee, BIP-125)." ));
348
342
} else {
349
- questionString .append (tr (" Not signalling Replace-By-Fee, BIP-125." ));
343
+ question_string .append (tr (" Not signalling Replace-By-Fee, BIP-125." ));
350
344
}
351
- questionString .append (" </span>" );
345
+ question_string .append (" </span>" );
352
346
}
353
347
354
348
// add total amount in all subdivision units
355
- questionString .append (" <hr />" );
356
- CAmount totalAmount = currentTransaction. getTotalTransactionAmount () + txFee;
349
+ question_string .append (" <hr />" );
350
+ CAmount totalAmount = m_current_transaction-> getTotalTransactionAmount () + txFee;
357
351
QStringList alternativeUnits;
358
352
for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits ())
359
353
{
360
354
if (u != model->getOptionsModel ()->getDisplayUnit ())
361
355
alternativeUnits.append (BitcoinUnits::formatHtmlWithUnit (u, totalAmount));
362
356
}
363
- questionString .append (QString (" <b>%1</b>: <b>%2</b>" ).arg (tr (" Total Amount" ))
357
+ question_string .append (QString (" <b>%1</b>: <b>%2</b>" ).arg (tr (" Total Amount" ))
364
358
.arg (BitcoinUnits::formatHtmlWithUnit (model->getOptionsModel ()->getDisplayUnit (), totalAmount)));
365
- questionString .append (QString (" <br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>" )
359
+ question_string .append (QString (" <br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>" )
366
360
.arg (alternativeUnits.join (" " + tr (" or" ) + " " )));
367
361
368
- QString informative_text;
369
- QString detailed_text;
370
362
if (formatted.size () > 1 ) {
371
- questionString = questionString .arg (" " );
363
+ question_string = question_string .arg (" " );
372
364
informative_text = tr (" To review recipient list click \" Show Details...\" " );
373
365
detailed_text = formatted.join (" \n\n " );
374
366
} else {
375
- questionString = questionString .arg (" <br /><br />" + formatted.at (0 ));
367
+ question_string = question_string .arg (" <br /><br />" + formatted.at (0 ));
376
368
}
369
+
370
+ return true ;
371
+ }
372
+
373
+ void SendCoinsDialog::on_sendButton_clicked ()
374
+ {
375
+ if (!model || !model->getOptionsModel ())
376
+ return ;
377
+
378
+ QString question_string, informative_text, detailed_text;
379
+ if (!PrepareSendText (question_string, informative_text, detailed_text)) return ;
380
+ assert (m_current_transaction);
381
+
377
382
const QString confirmation = model->wallet ().privateKeysDisabled () ? tr (" Confirm transaction proposal" ) : tr (" Confirm send coins" );
378
- const QString confirmButtonText = model->wallet ().privateKeysDisabled () ? tr (" Copy PSBT to clipboard " ) : tr (" Send" );
379
- SendConfirmationDialog confirmationDialog (confirmation, questionString , informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this );
383
+ const QString confirmButtonText = model->wallet ().privateKeysDisabled () ? tr (" Create Unsigned " ) : tr (" Send" );
384
+ SendConfirmationDialog confirmationDialog (confirmation, question_string , informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this );
380
385
confirmationDialog.exec ();
381
386
QMessageBox::StandardButton retval = static_cast <QMessageBox::StandardButton>(confirmationDialog.result ());
382
387
@@ -388,7 +393,7 @@ void SendCoinsDialog::on_sendButton_clicked()
388
393
389
394
bool send_failure = false ;
390
395
if (model->wallet ().privateKeysDisabled ()) {
391
- CMutableTransaction mtx = CMutableTransaction{*(currentTransaction. getWtx ())};
396
+ CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction-> getWtx ())};
392
397
PartiallySignedTransaction psbtx (mtx);
393
398
bool complete = false ;
394
399
const TransactionError err = model->wallet ().fillPSBT (SIGHASH_ALL, false /* sign */ , true /* bip32derivs */ , psbtx, complete);
@@ -398,15 +403,51 @@ void SendCoinsDialog::on_sendButton_clicked()
398
403
CDataStream ssTx (SER_NETWORK, PROTOCOL_VERSION);
399
404
ssTx << psbtx;
400
405
GUIUtil::setClipboard (EncodeBase64 (ssTx.str ()).c_str ());
401
- Q_EMIT message (tr (" PSBT copied" ), " Copied to clipboard" , CClientUIInterface::MSG_INFORMATION);
406
+ QMessageBox msgBox;
407
+ msgBox.setText (" Unsigned Transaction" );
408
+ msgBox.setInformativeText (" The PSBT has been copied to the clipboard. You can also save it." );
409
+ msgBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard);
410
+ msgBox.setDefaultButton (QMessageBox::Discard);
411
+ switch (msgBox.exec ()) {
412
+ case QMessageBox::Save: {
413
+ QString selectedFilter;
414
+ QString fileNameSuggestion = " " ;
415
+ bool first = true ;
416
+ for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients ()) {
417
+ if (!first) {
418
+ fileNameSuggestion.append (" - " );
419
+ }
420
+ QString labelOrAddress = rcp.label .isEmpty () ? rcp.address : rcp.label ;
421
+ QString amount = BitcoinUnits::formatWithUnit (model->getOptionsModel ()->getDisplayUnit (), rcp.amount );
422
+ fileNameSuggestion.append (labelOrAddress + " -" + amount);
423
+ first = false ;
424
+ }
425
+ fileNameSuggestion.append (" .psbt" );
426
+ QString filename = GUIUtil::getSaveFileName (this ,
427
+ tr (" Save Transaction Data" ), fileNameSuggestion,
428
+ tr (" Partially Signed Transaction (Binary) (*.psbt)" ), &selectedFilter);
429
+ if (filename.isEmpty ()) {
430
+ return ;
431
+ }
432
+ std::ofstream out (filename.toLocal8Bit ().data ());
433
+ out << ssTx.str ();
434
+ out.close ();
435
+ Q_EMIT message (tr (" PSBT saved" ), " PSBT saved to disk" , CClientUIInterface::MSG_INFORMATION);
436
+ break ;
437
+ }
438
+ case QMessageBox::Discard:
439
+ break ;
440
+ default :
441
+ assert (false );
442
+ }
402
443
} else {
403
444
// now send the prepared transaction
404
- WalletModel::SendCoinsReturn sendStatus = model->sendCoins (currentTransaction );
445
+ WalletModel::SendCoinsReturn sendStatus = model->sendCoins (*m_current_transaction );
405
446
// process sendStatus and on error generate message shown to user
406
447
processSendCoinsReturn (sendStatus);
407
448
408
449
if (sendStatus.status == WalletModel::OK) {
409
- Q_EMIT coinsSent (currentTransaction. getWtx ()->GetHash ());
450
+ Q_EMIT coinsSent (m_current_transaction-> getWtx ()->GetHash ());
410
451
} else {
411
452
send_failure = true ;
412
453
}
@@ -417,10 +458,13 @@ void SendCoinsDialog::on_sendButton_clicked()
417
458
coinControlUpdateLabels ();
418
459
}
419
460
fNewRecipientAllowed = true ;
461
+ m_current_transaction.reset ();
420
462
}
421
463
422
464
void SendCoinsDialog::clear ()
423
465
{
466
+ m_current_transaction.reset ();
467
+
424
468
// Clear coin control settings
425
469
CoinControlDialog::coinControl ()->UnSelectAll ();
426
470
ui->checkBoxCoinControlChange ->setChecked (false );
0 commit comments