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