Bitcoin Core  29.1.0
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <bitcoin-build-config.h> // IWYU pragma: keep
6 
7 #include <qt/sendcoinsdialog.h>
8 #include <qt/forms/ui_sendcoinsdialog.h>
9 
10 #include <qt/addresstablemodel.h>
11 #include <qt/bitcoinunits.h>
12 #include <qt/clientmodel.h>
13 #include <qt/coincontroldialog.h>
14 #include <qt/guiutil.h>
15 #include <qt/optionsmodel.h>
16 #include <qt/platformstyle.h>
17 #include <qt/sendcoinsentry.h>
18 
19 #include <chainparams.h>
20 #include <interfaces/node.h>
21 #include <key_io.h>
22 #include <node/interface_ui.h>
23 #include <node/types.h>
24 #include <policy/fees.h>
25 #include <txmempool.h>
26 #include <validation.h>
27 #include <wallet/coincontrol.h>
28 #include <wallet/fees.h>
29 #include <wallet/wallet.h>
30 
31 #include <array>
32 #include <chrono>
33 #include <fstream>
34 #include <memory>
35 
36 #include <QDebug>
37 #include <QFontMetrics>
38 #include <QScrollBar>
39 #include <QSettings>
40 #include <QTextDocument>
41 
42 using common::PSBTError;
45 
46 static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
47 int getConfTargetForIndex(int index) {
48  if (index+1 > static_cast<int>(confTargets.size())) {
49  return confTargets.back();
50  }
51  if (index < 0) {
52  return confTargets[0];
53  }
54  return confTargets[index];
55 }
56 int getIndexForConfTarget(int target) {
57  for (unsigned int i = 0; i < confTargets.size(); i++) {
58  if (confTargets[i] >= target) {
59  return i;
60  }
61  }
62  return confTargets.size() - 1;
63 }
64 
65 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
66  QDialog(parent, GUIUtil::dialog_flags),
67  ui(new Ui::SendCoinsDialog),
68  m_coin_control(new CCoinControl),
69  platformStyle(_platformStyle)
70 {
71  ui->setupUi(this);
72 
73  if (!_platformStyle->getImagesOnButtons()) {
74  ui->addButton->setIcon(QIcon());
75  ui->clearButton->setIcon(QIcon());
76  ui->sendButton->setIcon(QIcon());
77  } else {
78  ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
79  ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
80  ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
81  }
82 
83  GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
84 
85  addEntry();
86 
87  connect(ui->addButton, &QPushButton::clicked, this, &SendCoinsDialog::addEntry);
88  connect(ui->clearButton, &QPushButton::clicked, this, &SendCoinsDialog::clear);
89 
90  // Coin Control
91  connect(ui->pushButtonCoinControl, &QPushButton::clicked, this, &SendCoinsDialog::coinControlButtonClicked);
92  connect(ui->checkBoxCoinControlChange, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
93  connect(ui->lineEditCoinControlChange, &QValidatedLineEdit::textEdited, this, &SendCoinsDialog::coinControlChangeEdited);
94 
95  // Coin Control: clipboard actions
96  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
97  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
98  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
99  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
100  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
101  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
102  connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity);
103  connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount);
104  connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee);
105  connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee);
106  connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes);
107  connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange);
108  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
109  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
110  ui->labelCoinControlFee->addAction(clipboardFeeAction);
111  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
112  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
113  ui->labelCoinControlChange->addAction(clipboardChangeAction);
114 
115  // init transaction fee section
116  QSettings settings;
117  if (!settings.contains("fFeeSectionMinimized"))
118  settings.setValue("fFeeSectionMinimized", true);
119  if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
120  settings.setValue("nFeeRadio", 1); // custom
121  if (!settings.contains("nFeeRadio"))
122  settings.setValue("nFeeRadio", 0); // recommended
123  if (!settings.contains("nSmartFeeSliderPosition"))
124  settings.setValue("nSmartFeeSliderPosition", 0);
125  if (!settings.contains("nTransactionFee"))
126  settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
127  ui->groupFee->setId(ui->radioSmartFee, 0);
128  ui->groupFee->setId(ui->radioCustomFee, 1);
129  ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
130  ui->customFee->SetAllowEmpty(false);
131  ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
132  minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
133 
134  GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked);
135 }
136 
138 {
139  this->clientModel = _clientModel;
140 
141  if (_clientModel) {
143  }
144 }
145 
147 {
148  this->model = _model;
149 
150  if(_model && _model->getOptionsModel())
151  {
152  for(int i = 0; i < ui->entries->count(); ++i)
153  {
154  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
155  if(entry)
156  {
157  entry->setModel(_model);
158  }
159  }
160 
163  refreshBalance();
164 
165  // Coin Control
168  ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
170 
171  // fee section
172  for (const int n : confTargets) {
173  ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
174  }
175  connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel);
176  connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels);
177 
178 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
179  connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::updateFeeSectionControls);
180  connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::coinControlUpdateLabels);
181 #else
182  connect(ui->groupFee, qOverload<int>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
183  connect(ui->groupFee, qOverload<int>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
184 #endif
185 
187  connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
188  connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
189  CAmount requiredFee = model->wallet().getRequiredFee(1000);
190  ui->customFee->SetMinValue(requiredFee);
191  if (ui->customFee->value() < requiredFee) {
192  ui->customFee->setValue(requiredFee);
193  }
194  ui->customFee->setSingleStep(requiredFee);
197 
198  // set default rbf checkbox state
199  ui->optInRBF->setCheckState(Qt::Checked);
200 
201  if (model->wallet().hasExternalSigner()) {
202  //: "device" usually means a hardware wallet.
203  ui->sendButton->setText(tr("Sign on device"));
204  if (model->getOptionsModel()->hasSigner()) {
205  ui->sendButton->setEnabled(true);
206  ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
207  } else {
208  ui->sendButton->setEnabled(false);
209  //: "External signer" means using devices such as hardware wallets.
210  ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
211  }
212  } else if (model->wallet().privateKeysDisabled()) {
213  ui->sendButton->setText(tr("Cr&eate Unsigned"));
214  ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(CLIENT_NAME));
215  }
216 
217  // set the smartfee-sliders default value (wallets default conf.target or last stored value)
218  QSettings settings;
219  if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
220  // migrate nSmartFeeSliderPosition to nConfTarget
221  // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
222  int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
223  settings.setValue("nConfTarget", nConfirmTarget);
224  settings.remove("nSmartFeeSliderPosition");
225  }
226  if (settings.value("nConfTarget").toInt() == 0)
227  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget()));
228  else
229  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
230  }
231 }
232 
234 {
235  QSettings settings;
236  settings.setValue("fFeeSectionMinimized", fFeeMinimized);
237  settings.setValue("nFeeRadio", ui->groupFee->checkedId());
238  settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
239  settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
240 
241  delete ui;
242 }
243 
244 bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text)
245 {
246  QList<SendCoinsRecipient> recipients;
247  bool valid = true;
248 
249  for(int i = 0; i < ui->entries->count(); ++i)
250  {
251  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
252  if(entry)
253  {
254  if(entry->validate(model->node()))
255  {
256  recipients.append(entry->getValue());
257  }
258  else if (valid)
259  {
260  ui->scrollArea->ensureWidgetVisible(entry);
261  valid = false;
262  }
263  }
264  }
265 
266  if(!valid || recipients.isEmpty())
267  {
268  return false;
269  }
270 
271  fNewRecipientAllowed = false;
273  if(!ctx.isValid())
274  {
275  // Unlock wallet was cancelled
276  fNewRecipientAllowed = true;
277  return false;
278  }
279 
280  // prepare transaction for getting txFee earlier
281  m_current_transaction = std::make_unique<WalletModelTransaction>(recipients);
282  WalletModel::SendCoinsReturn prepareStatus;
283 
285 
286  CCoinControl coin_control = *m_coin_control;
287  coin_control.m_allow_other_inputs = !coin_control.HasSelected(); // future, could introduce a checkbox to customize this value.
288  prepareStatus = model->prepareTransaction(*m_current_transaction, coin_control);
289 
290  // process prepareStatus and on error generate message shown to user
291  processSendCoinsReturn(prepareStatus,
293 
294  if(prepareStatus.status != WalletModel::OK) {
295  fNewRecipientAllowed = true;
296  return false;
297  }
298 
299  CAmount txFee = m_current_transaction->getTransactionFee();
300  QStringList formatted;
301  for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients())
302  {
303  // generate amount string with wallet name in case of multiwallet
304  QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
305  if (model->isMultiwallet()) {
306  amount = tr("%1 from wallet '%2'").arg(amount, GUIUtil::HtmlEscape(model->getWalletName()));
307  }
308 
309  // generate address string
310  QString address = rcp.address;
311 
312  QString recipientElement;
313 
314  {
315  if(rcp.label.length() > 0) // label with address
316  {
317  recipientElement.append(tr("%1 to '%2'").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
318  recipientElement.append(QString(" (%1)").arg(address));
319  }
320  else // just address
321  {
322  recipientElement.append(tr("%1 to %2").arg(amount, address));
323  }
324  }
325  formatted.append(recipientElement);
326  }
327 
328  /*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
329  that the displayed transaction details represent the transaction the user intends to create. */
330  question_string.append(tr("Do you want to create this transaction?"));
331  question_string.append("<br /><span style='font-size:10pt;'>");
333  /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
334  a user can only create a PSBT. This string is displayed when private keys are disabled and an external
335  signer is not available. */
336  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(CLIENT_NAME));
337  } else if (model->getOptionsModel()->getEnablePSBTControls()) {
338  /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
339  a user can send their transaction or create a PSBT. This string is displayed when both private keys
340  and PSBT controls are enabled. */
341  question_string.append(tr("Please, review your transaction. You can create and send this transaction or create 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(CLIENT_NAME));
342  } else {
343  /*: Text to prompt a user to review the details of the transaction they are attempting to send. */
344  question_string.append(tr("Please, review your transaction."));
345  }
346  question_string.append("</span>%1");
347 
348  if(txFee > 0)
349  {
350  // append fee string if a fee is required
351  question_string.append("<hr /><b>");
352  question_string.append(tr("Transaction fee"));
353  question_string.append("</b>");
354 
355  // append transaction size
356  //: When reviewing a newly created PSBT (via Send flow), the transaction fee is shown, with "virtual size" of the transaction displayed for context
357  question_string.append(" (" + tr("%1 kvB", "PSBT transaction creation").arg((double)m_current_transaction->getTransactionSize() / 1000, 0, 'g', 3) + "): ");
358 
359  // append transaction fee value
360  question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
361  question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
362  question_string.append("</span><br />");
363 
364  // append RBF message according to transaction's signalling
365  question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
366  if (ui->optInRBF->isChecked()) {
367  question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
368  } else {
369  question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
370  }
371  question_string.append("</span>");
372  }
373 
374  // add total amount in all subdivision units
375  question_string.append("<hr />");
376  CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee;
377  QStringList alternativeUnits;
378  for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
379  if(u != model->getOptionsModel()->getDisplayUnit())
380  alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
381  }
382  question_string.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
384  question_string.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
385  .arg(alternativeUnits.join(" " + tr("or") + " ")));
386 
387  if (formatted.size() > 1) {
388  question_string = question_string.arg("");
389  informative_text = tr("To review recipient list click \"Show Details…\"");
390  detailed_text = formatted.join("\n\n");
391  } else {
392  question_string = question_string.arg("<br /><br />" + formatted.at(0));
393  }
394 
395  return true;
396 }
397 
399 {
400  // Serialize the PSBT
401  DataStream ssTx{};
402  ssTx << psbtx;
403  GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
404  QMessageBox msgBox(this);
405  //: Caption of "PSBT has been copied" messagebox
406  msgBox.setText(tr("Unsigned Transaction", "PSBT copied"));
407  msgBox.setInformativeText(tr("The PSBT has been copied to the clipboard. You can also save it."));
408  msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
409  msgBox.setDefaultButton(QMessageBox::Discard);
410  msgBox.setObjectName("psbt_copied_message");
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  //: Expanded name of the binary PSBT file format. See: BIP 174.
429  tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selectedFilter);
430  if (filename.isEmpty()) {
431  return;
432  }
433  std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
434  out << ssTx.str();
435  out.close();
436  //: Popup message when a PSBT has been saved to a file
437  Q_EMIT message(tr("PSBT saved"), tr("PSBT saved to disk"), CClientUIInterface::MSG_INFORMATION);
438  break;
439  }
440  case QMessageBox::Discard:
441  break;
442  default:
443  assert(false);
444  } // msgBox.exec()
445 }
446 
448  std::optional<PSBTError> err;
449  try {
450  err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
451  } catch (const std::runtime_error& e) {
452  QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
453  return false;
454  }
455  if (err == PSBTError::EXTERNAL_SIGNER_NOT_FOUND) {
456  //: "External signer" means using devices such as hardware wallets.
457  const QString msg = tr("External signer not found");
458  QMessageBox::critical(nullptr, msg, msg);
459  return false;
460  }
461  if (err == PSBTError::EXTERNAL_SIGNER_FAILED) {
462  //: "External signer" means using devices such as hardware wallets.
463  const QString msg = tr("External signer failure");
464  QMessageBox::critical(nullptr, msg, msg);
465  return false;
466  }
467  if (err) {
468  qWarning() << "Failed to sign PSBT";
470  return false;
471  }
472  // fillPSBT does not always properly finalize
473  complete = FinalizeAndExtractPSBT(psbtx, mtx);
474  return true;
475 }
476 
477 void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
478 {
479  if(!model || !model->getOptionsModel())
480  return;
481 
482  QString question_string, informative_text, detailed_text;
483  if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
485 
486  const QString confirmation = tr("Confirm send coins");
487  const bool enable_send{!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner()};
488  const bool always_show_unsigned{model->getOptionsModel()->getEnablePSBTControls()};
489  auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, enable_send, always_show_unsigned, this);
490  confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
491  // TODO: Replace QDialog::exec() with safer QDialog::show().
492  const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
493 
494  if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
495  {
496  fNewRecipientAllowed = true;
497  return;
498  }
499 
500  bool send_failure = false;
501  if (retval == QMessageBox::Save) {
502  // "Create Unsigned" clicked
504  PartiallySignedTransaction psbtx(mtx);
505  bool complete = false;
506  // Fill without signing
507  const auto err{model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
508  assert(!complete);
509  assert(!err);
510 
511  // Copy PSBT to clipboard and offer to save
512  presentPSBT(psbtx);
513  } else {
514  // "Send" clicked
516  bool broadcast = true;
517  if (model->wallet().hasExternalSigner()) {
519  PartiallySignedTransaction psbtx(mtx);
520  bool complete = false;
521  // Always fill without signing first. This prevents an external signer
522  // from being called prematurely and is not expensive.
523  const auto err{model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
524  assert(!complete);
525  assert(!err);
526  send_failure = !signWithExternalSigner(psbtx, mtx, complete);
527  // Don't broadcast when user rejects it on the device or there's a failure:
528  broadcast = complete && !send_failure;
529  if (!send_failure) {
530  // A transaction signed with an external signer is not always complete,
531  // e.g. in a multisig wallet.
532  if (complete) {
533  // Prepare transaction for broadcast transaction if complete
534  const CTransactionRef tx = MakeTransactionRef(mtx);
535  m_current_transaction->setWtx(tx);
536  } else {
537  presentPSBT(psbtx);
538  }
539  }
540  }
541 
542  // Broadcast the transaction, unless an external signer was used and it
543  // failed, or more signatures are needed.
544  if (broadcast) {
545  // now send the prepared transaction
547  Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
548  }
549  }
550  if (!send_failure) {
551  accept();
552  m_coin_control->UnSelectAll();
554  }
555  fNewRecipientAllowed = true;
556  m_current_transaction.reset();
557 }
558 
560 {
561  m_current_transaction.reset();
562 
563  // Clear coin control settings
564  m_coin_control->UnSelectAll();
565  ui->checkBoxCoinControlChange->setChecked(false);
566  ui->lineEditCoinControlChange->clear();
568 
569  // Remove entries until only one left
570  while(ui->entries->count())
571  {
572  ui->entries->takeAt(0)->widget()->deleteLater();
573  }
574  addEntry();
575 
577 }
578 
580 {
581  clear();
582 }
583 
585 {
586  clear();
587 }
588 
590 {
591  SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
592  entry->setModel(model);
593  ui->entries->addWidget(entry);
598 
599  // Focus the field, so that entry can start immediately
600  entry->clear();
601  entry->setFocus();
602  ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
603 
604  // Scroll to the newly added entry on a QueuedConnection because Qt doesn't
605  // adjust the scroll area and scrollbar immediately when the widget is added.
606  // Invoking on a DirectConnection will only scroll to the second-to-last entry.
607  QMetaObject::invokeMethod(ui->scrollArea, [this] {
608  if (ui->scrollArea->verticalScrollBar()) {
609  ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());
610  }
611  }, Qt::QueuedConnection);
612 
613  updateTabsAndLabels();
614  return entry;
615 }
616 
618 {
619  setupTabChain(nullptr);
621 }
622 
624 {
625  entry->hide();
626 
627  // If the last entry is about to be removed add an empty one
628  if (ui->entries->count() == 1)
629  addEntry();
630 
631  entry->deleteLater();
632 
634 }
635 
636 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
637 {
638  for(int i = 0; i < ui->entries->count(); ++i)
639  {
640  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
641  if(entry)
642  {
643  prev = entry->setupTabChain(prev);
644  }
645  }
646  QWidget::setTabOrder(prev, ui->sendButton);
647  QWidget::setTabOrder(ui->sendButton, ui->clearButton);
648  QWidget::setTabOrder(ui->clearButton, ui->addButton);
649  return ui->addButton;
650 }
651 
652 void SendCoinsDialog::setAddress(const QString &address)
653 {
654  SendCoinsEntry *entry = nullptr;
655  // Replace the first entry if it is still unused
656  if(ui->entries->count() == 1)
657  {
658  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
659  if(first->isClear())
660  {
661  entry = first;
662  }
663  }
664  if(!entry)
665  {
666  entry = addEntry();
667  }
668 
669  entry->setAddress(address);
670 }
671 
673 {
675  return;
676 
677  SendCoinsEntry *entry = nullptr;
678  // Replace the first entry if it is still unused
679  if(ui->entries->count() == 1)
680  {
681  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
682  if(first->isClear())
683  {
684  entry = first;
685  }
686  }
687  if(!entry)
688  {
689  entry = addEntry();
690  }
691 
692  entry->setValue(rv);
694 }
695 
697 {
698  // Just paste the entry, all pre-checks
699  // are done in paymentserver.cpp.
700  pasteEntry(rv);
701  return true;
702 }
703 
705 {
706  if(model && model->getOptionsModel())
707  {
708  CAmount balance = balances.balance;
709  if (model->wallet().hasExternalSigner()) {
710  ui->labelBalanceName->setText(tr("External balance:"));
711  } else if (model->wallet().isLegacy() && model->wallet().privateKeysDisabled()) {
712  balance = balances.watch_only_balance;
713  ui->labelBalanceName->setText(tr("Watch-only balance:"));
714  }
715  ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
716  }
717 }
718 
720 {
722  ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
724 }
725 
726 void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
727 {
728  QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
729  // Default to a warning message, override if error message is needed
730  msgParams.second = CClientUIInterface::MSG_WARNING;
731 
732  // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
733  // All status values are used only in WalletModel::prepareTransaction()
734  switch(sendCoinsReturn.status)
735  {
737  msgParams.first = tr("The recipient address is not valid. Please recheck.");
738  break;
740  msgParams.first = tr("The amount to pay must be larger than 0.");
741  break;
743  msgParams.first = tr("The amount exceeds your balance.");
744  break;
746  msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
747  break;
749  msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
750  break;
752  msgParams.first = tr("Transaction creation failed!");
753  msgParams.second = CClientUIInterface::MSG_ERROR;
754  break;
756  msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
757  break;
758  // included to prevent a compiler warning.
759  case WalletModel::OK:
760  default:
761  return;
762  }
763 
764  Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
765 }
766 
768 {
769  ui->labelFeeMinimized->setVisible(fMinimize);
770  ui->buttonChooseFee ->setVisible(fMinimize);
771  ui->buttonMinimizeFee->setVisible(!fMinimize);
772  ui->frameFeeSelection->setVisible(!fMinimize);
773  ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
774  fFeeMinimized = fMinimize;
775 }
776 
778 {
779  minimizeFeeSection(false);
780 }
781 
783 {
785  minimizeFeeSection(true);
786 }
787 
789 {
790  // Include watch-only for wallets without private key
792 
793  // Same behavior as send: if we have selected coins, only obtain their available balance.
794  // Copy to avoid modifying the member's data.
795  CCoinControl coin_control = *m_coin_control;
796  coin_control.m_allow_other_inputs = !coin_control.HasSelected();
797 
798  // Calculate available amount to send.
799  CAmount amount = model->getAvailableBalance(&coin_control);
800  for (int i = 0; i < ui->entries->count(); ++i) {
801  SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
802  if (e && !e->isHidden() && e != entry) {
803  amount -= e->getValue().amount;
804  }
805  }
806 
807  if (amount > 0) {
809  entry->setAmount(amount);
810  } else {
811  entry->setAmount(0);
812  }
813 }
814 
816 {
817  ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
818  ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
819  ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
820  ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
821  ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
822  ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
823  ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
824  ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
825 }
826 
828 {
829  if(!model || !model->getOptionsModel())
830  return;
831 
832  if (ui->radioSmartFee->isChecked())
833  ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
834  else {
835  ui->labelFeeMinimized->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value())));
836  }
837 }
838 
840 {
841  if (ui->radioCustomFee->isChecked()) {
842  m_coin_control->m_feerate = CFeeRate(ui->customFee->value());
843  } else {
844  m_coin_control->m_feerate.reset();
845  }
846  // Avoid using global defaults when sending money from the GUI
847  // Either custom fee will be used or if not selected, the confirmation target from dropdown box
848  m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
849  m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
850  // Include watch-only for wallets without private key
852 }
853 
854 void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state) {
855  // During shutdown, clientModel will be nullptr. Attempting to update views at this point may cause a crash
856  // due to accessing backend models that might no longer exist.
857  if (!clientModel) return;
858  // Process event
859  if (sync_state == SynchronizationState::POST_INIT) {
861  }
862 }
863 
865 {
866  if(!model || !model->getOptionsModel())
867  return;
869  m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
870  int returned_target;
871  FeeReason reason;
872  CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
873 
874  ui->labelSmartFee->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK())));
875 
876  if (reason == FeeReason::FALLBACK) {
877  ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
878  ui->labelFeeEstimation->setText("");
879  ui->fallbackFeeWarningLabel->setVisible(true);
880  int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
881  QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
882  ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
883  ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x"));
884  }
885  else
886  {
887  ui->labelSmartFee2->hide();
888  ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target));
889  ui->fallbackFeeWarningLabel->setVisible(false);
890  }
891 
893 }
894 
895 // Coin Control: copy label "Quantity" to clipboard
897 {
898  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
899 }
900 
901 // Coin Control: copy label "Amount" to clipboard
903 {
904  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
905 }
906 
907 // Coin Control: copy label "Fee" to clipboard
909 {
910  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
911 }
912 
913 // Coin Control: copy label "After fee" to clipboard
915 {
916  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
917 }
918 
919 // Coin Control: copy label "Bytes" to clipboard
921 {
922  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
923 }
924 
925 // Coin Control: copy label "Change" to clipboard
927 {
928  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
929 }
930 
931 // Coin Control: settings menu - coin control enabled/disabled by user
933 {
934  ui->frameCoinControl->setVisible(checked);
935 
936  if (!checked && model) { // coin control features disabled
937  m_coin_control = std::make_unique<CCoinControl>();
938  }
939 
941 }
942 
943 // Coin Control: button inputs -> show actual coin control dialog
945 {
947  connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
949 }
950 
951 // Coin Control: checkbox custom change address
953 {
954  if (state == Qt::Unchecked)
955  {
956  m_coin_control->destChange = CNoDestination();
957  ui->labelCoinControlChangeLabel->clear();
958  }
959  else
960  // use this to re-validate an already entered address
961  coinControlChangeEdited(ui->lineEditCoinControlChange->text());
962 
963  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
964 }
965 
966 // Coin Control: custom change address changed
968 {
969  if (model && model->getAddressTableModel())
970  {
971  // Default to no change address until verified
972  m_coin_control->destChange = CNoDestination();
973  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
974 
975  const CTxDestination dest = DecodeDestination(text.toStdString());
976 
977  if (text.isEmpty()) // Nothing entered
978  {
979  ui->labelCoinControlChangeLabel->setText("");
980  }
981  else if (!IsValidDestination(dest)) // Invalid address
982  {
983  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
984  }
985  else // Valid address
986  {
987  if (!model->wallet().isSpendable(dest)) {
988  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
989 
990  // confirmation dialog
991  QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm custom change address"), tr("The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?"),
992  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
993 
994  if(btnRetVal == QMessageBox::Yes)
995  m_coin_control->destChange = dest;
996  else
997  {
998  ui->lineEditCoinControlChange->setText("");
999  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
1000  ui->labelCoinControlChangeLabel->setText("");
1001  }
1002  }
1003  else // Known change address
1004  {
1005  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
1006 
1007  // Query label
1008  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
1009  if (!associatedLabel.isEmpty())
1010  ui->labelCoinControlChangeLabel->setText(associatedLabel);
1011  else
1012  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
1013 
1014  m_coin_control->destChange = dest;
1015  }
1016  }
1017  }
1018 }
1019 
1020 // Coin Control: update labels
1022 {
1023  if (!model || !model->getOptionsModel())
1024  return;
1025 
1027 
1028  // set pay amounts
1031 
1032  for(int i = 0; i < ui->entries->count(); ++i)
1033  {
1034  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
1035  if(entry && !entry->isHidden())
1036  {
1037  SendCoinsRecipient rcp = entry->getValue();
1039  if (rcp.fSubtractFeeFromAmount)
1041  }
1042  }
1043 
1044  if (m_coin_control->HasSelected())
1045  {
1046  // actual coin control calculation
1048 
1049  // show coin control stats
1050  ui->labelCoinControlAutomaticallySelected->hide();
1051  ui->widgetCoinControl->show();
1052  }
1053  else
1054  {
1055  // hide coin control stats
1056  ui->labelCoinControlAutomaticallySelected->show();
1057  ui->widgetCoinControl->hide();
1058  ui->labelCoinControlInsuffFunds->hide();
1059  }
1060 }
1061 
1062 SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, bool enable_send, bool always_show_unsigned, QWidget* parent)
1063  : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
1064 {
1065  setIcon(QMessageBox::Question);
1066  setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
1067  setText(text);
1068  setInformativeText(informative_text);
1069  setDetailedText(detailed_text);
1070  setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1071  if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
1072  setDefaultButton(QMessageBox::Cancel);
1073  yesButton = button(QMessageBox::Yes);
1074  if (confirmButtonText.isEmpty()) {
1075  confirmButtonText = yesButton->text();
1076  }
1077  m_psbt_button = button(QMessageBox::Save);
1078  updateButtons();
1079  connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
1080 }
1081 
1083 {
1084  updateButtons();
1085  countDownTimer.start(1s);
1086  return QMessageBox::exec();
1087 }
1088 
1090 {
1091  secDelay--;
1092  updateButtons();
1093 
1094  if(secDelay <= 0)
1095  {
1096  countDownTimer.stop();
1097  }
1098 }
1099 
1101 {
1102  if(secDelay > 0)
1103  {
1104  yesButton->setEnabled(false);
1105  yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1106  if (m_psbt_button) {
1107  m_psbt_button->setEnabled(false);
1108  m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1109  }
1110  }
1111  else
1112  {
1113  yesButton->setEnabled(m_enable_send);
1114  yesButton->setText(confirmButtonText);
1115  if (m_psbt_button) {
1116  m_psbt_button->setEnabled(true);
1118  }
1119  }
1120 }
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
virtual bool privateKeysDisabled()=0
bool signWithExternalSigner(PartiallySignedTransaction &psbt, CMutableTransaction &mtx, bool &complete)
PSBTError
Definition: types.h:17
void removeEntry(SendCoinsEntry *entry)
Predefined combinations for certain default usage cases.
Definition: interface_ui.h:66
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
OptionsModel * getOptionsModel() const
Unit
Bitcoin units.
Definition: bitcoinunits.h:42
interfaces::Wallet & wallet() const
Definition: walletmodel.h:138
void setValue(const SendCoinsRecipient &value)
void payAmountChanged()
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:58
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:85
assert(!tx.IsCoinBase())
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl &coinControl)
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
void presentPSBT(PartiallySignedTransaction &psbt)
void sendButtonClicked(bool checked)
void updateFeeMinimizedLabel()
void setFocus()
void on_buttonChooseFee_clicked()
SendCoinsRecipient getValue()
bool hasSigner()
Whether -signer was set or not.
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text...
Definition: guiutil.cpp:915
void reject() override
UnlockContext requestUnlock()
void coinControlClipboardQuantity()
void coinControlClipboardAfterFee()
void setAddress(const QString &address)
interfaces::WalletBalances getCachedBalance() const
virtual CAmount getDefaultMaxTxFee()=0
Get max tx fee.
#define SEND_CONFIRM_DELAY
BitcoinUnit getDisplayUnit() const
Definition: optionsmodel.h:103
std::string EncodeBase64(Span< const unsigned char > input)
void coinControlFeaturesChanged(bool)
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:249
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
A version of CTransaction with the PSBT format.
Definition: psbt.h:950
virtual bool isLegacy()=0
Return whether is a legacy wallet.
void ShowModalDialogAsynchronously(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
Definition: guiutil.cpp:1004
static constexpr std::array confTargets
#define ASYMP_UTF8
bool FinalizeAndExtractPSBT(PartiallySignedTransaction &psbtx, CMutableTransaction &result)
Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized...
Definition: psbt.cpp:495
virtual bool isSpendable(const CTxDestination &dest)=0
Return whether wallet has private key.
A single entry in the dialog for sending bitcoins.
bool HasSelected() const
Returns true if there are pre-selected inputs.
Definition: coincontrol.cpp:15
static QString formatWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
constexpr auto dialog_flags
Definition: guiutil.h:60
void coinControlFeatureChanged(bool)
virtual bool hasExternalSigner()=0
std::unique_ptr< wallet::CCoinControl > m_coin_control
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
std::unique_ptr< WalletModelTransaction > m_current_transaction
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
constexpr CAmount DEFAULT_PAY_TX_FEE
-paytxfee default
Definition: wallet.h:104
static QList< CAmount > payAmounts
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
Ui::SendCoinsDialog * ui
SendCoinsEntry * addEntry()
void clear()
bool validate(interfaces::Node &node)
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:131
void displayUnitChanged(BitcoinUnit unit)
void setBalance(const interfaces::WalletBalances &balances)
void setAddress(const QString &address)
void coinControlClipboardChange()
Collection of wallet balances.
Definition: wallet.h:379
void useAvailableBalance(SendCoinsEntry *entry)
void setClientModel(ClientModel *clientModel)
void setClipboard(const QString &str)
Definition: guiutil.cpp:668
ClientModel * clientModel
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:146
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
auto ExceptionSafeConnect(Sender sender, Signal signal, Receiver receiver, Slot method, Qt::ConnectionType type=Qt::AutoConnection)
A drop-in replacement of QObject::connect function (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that guaranties that all exceptions are handled within the slot.
Definition: guiutil.h:391
WalletModel * model
Dialog for sending bitcoins.
void coinControlChangeEdited(const QString &)
FeeReason
Definition: fees.h:60
QString getWalletName() const
virtual std::optional< common::PSBTError > fillPSBT(int sighash_type, bool sign, bool bip32derivs, size_t *n_signed, PartiallySignedTransaction &psbtx, bool &complete)=0
Fill PSBT.
bool getEnablePSBTControls() const
Definition: optionsmodel.h:108
interfaces::Node & node() const
Definition: walletmodel.h:137
void removeEntry(SendCoinsEntry *entry)
Model for Bitcoin network client.
Definition: clientmodel.h:56
bool isMultiwallet() const
void setModel(WalletModel *model)
int getConfTargetForIndex(int index)
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=nullptr)
void accept() override
bool getCoinControlFeatures() const
Definition: optionsmodel.h:106
SendConfirmationDialog(const QString &title, const QString &text, const QString &informative_text="", const QString &detailed_text="", int secDelay=SEND_CONFIRM_DELAY, bool enable_send=true, bool always_show_unsigned=true, QWidget *parent=nullptr)
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
SyncType
Definition: clientmodel.h:42
void checkSubtractFeeFromAmount()
bool isClear()
Return whether the entry is still empty and unedited.
void minimizeFeeSection(bool fMinimize)
void updateFeeSectionControls()
void subtractFeeFromAmountChanged()
static bool fSubtractFeeFromAmount
bool PrepareSendText(QString &question_string, QString &informative_text, QString &detailed_text)
static void updateLabels(wallet::CCoinControl &m_coin_control, WalletModel *, QDialog *)
int getIndexForConfTarget(int target)
const CChainParams & Params()
Return the currently selected parameters.
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:47
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when ...
Definition: guiutil.cpp:313
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:140
void setAmount(const CAmount &amount)
void setModel(WalletModel *model)
bool m_allow_other_inputs
If true, the selection process can add extra unselected inputs from the wallet while requires all sel...
Definition: coincontrol.h:91
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
Definition: feerate.h:32
static int count
void coinControlClipboardBytes()
void useAvailableBalance(SendCoinsEntry *entry)
A mutable version of CTransaction.
Definition: transaction.h:377
virtual unsigned int getConfirmTarget()=0
Get tx confirm target.
AddressTableModel * getAddressTableModel() const
CAmount getAvailableBalance(const wallet::CCoinControl *control)
static QString formatHtmlWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
const PlatformStyle * platformStyle
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:787
void coinControlClipboardAmount()
void sendCoins(WalletModelTransaction &transaction)
void on_buttonMinimizeFee_clicked()
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Definition: key_io.cpp:299
is a home for public enum and struct type definitions that are used internally by node code...
void pasteEntry(const SendCoinsRecipient &rv)
QAbstractButton * yesButton
virtual CAmount getRequiredFee(unsigned int tx_bytes)=0
Get required fee.
void message(const QString &title, const QString &message, unsigned int style)
void coinsSent(const uint256 &txid)
QAbstractButton * m_psbt_button
virtual CAmount getMinimumFee(unsigned int tx_bytes, const wallet::CCoinControl &coin_control, int *returned_target, FeeReason *reason)=0
Get minimum fee.
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
Definition: feerate.h:60
void balanceChanged(const interfaces::WalletBalances &balances)
Coin Control Features.
Definition: coincontrol.h:80
bool getImagesOnButtons() const
Definition: platformstyle.h:21
void coinControlButtonClicked()
void coinControlClipboardFee()
void updateNumberOfBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
void coinControlChangeChecked(int)