Bitcoin Core 30.99.0
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-present 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
8#include <qt/forms/ui_sendcoinsdialog.h>
9
11#include <qt/bitcoinunits.h>
12#include <qt/clientmodel.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>
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 <QFontMetrics>
37#include <QScrollBar>
38#include <QSettings>
39#include <QTextDocument>
40
43
44static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
45int getConfTargetForIndex(int index) {
46 if (index+1 > static_cast<int>(confTargets.size())) {
47 return confTargets.back();
48 }
49 if (index < 0) {
50 return confTargets[0];
51 }
52 return confTargets[index];
53}
54int getIndexForConfTarget(int target) {
55 for (unsigned int i = 0; i < confTargets.size(); i++) {
56 if (confTargets[i] >= target) {
57 return i;
58 }
59 }
60 return confTargets.size() - 1;
61}
62
63SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
64 QDialog(parent, GUIUtil::dialog_flags),
65 ui(new Ui::SendCoinsDialog),
66 m_coin_control(new CCoinControl),
67 platformStyle(_platformStyle)
68{
69 ui->setupUi(this);
70
71 if (!_platformStyle->getImagesOnButtons()) {
72 ui->addButton->setIcon(QIcon());
73 ui->clearButton->setIcon(QIcon());
74 ui->sendButton->setIcon(QIcon());
75 } else {
76 ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
77 ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
78 ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
79 }
80
81 GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
82
83 addEntry();
84
85 connect(ui->addButton, &QPushButton::clicked, this, &SendCoinsDialog::addEntry);
86 connect(ui->clearButton, &QPushButton::clicked, this, &SendCoinsDialog::clear);
87
88 // Coin Control
89 connect(ui->pushButtonCoinControl, &QPushButton::clicked, this, &SendCoinsDialog::coinControlButtonClicked);
90#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
91 connect(ui->checkBoxCoinControlChange, &QCheckBox::checkStateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
92#else
93 connect(ui->checkBoxCoinControlChange, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
94#endif
95 connect(ui->lineEditCoinControlChange, &QValidatedLineEdit::textEdited, this, &SendCoinsDialog::coinControlChangeEdited);
96
97 // Coin Control: clipboard actions
98 QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
99 QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
100 QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
101 QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
102 QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
103 QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
104 connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity);
105 connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount);
106 connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee);
107 connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee);
108 connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes);
109 connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange);
110 ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
111 ui->labelCoinControlAmount->addAction(clipboardAmountAction);
112 ui->labelCoinControlFee->addAction(clipboardFeeAction);
113 ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
114 ui->labelCoinControlBytes->addAction(clipboardBytesAction);
115 ui->labelCoinControlChange->addAction(clipboardChangeAction);
116
117 // init transaction fee section
118 QSettings settings;
119 if (!settings.contains("fFeeSectionMinimized"))
120 settings.setValue("fFeeSectionMinimized", true);
121 if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
122 settings.setValue("nFeeRadio", 1); // custom
123 if (!settings.contains("nFeeRadio"))
124 settings.setValue("nFeeRadio", 0); // recommended
125 if (!settings.contains("nSmartFeeSliderPosition"))
126 settings.setValue("nSmartFeeSliderPosition", 0);
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
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 connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::updateFeeSectionControls);
179 connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::coinControlUpdateLabels);
180
182#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
183 connect(ui->optInRBF, &QCheckBox::checkStateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
184 connect(ui->optInRBF, &QCheckBox::checkStateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
185#else
186 connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
187 connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
188#endif
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
244bool 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
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) {
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));
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()) {
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(std::nullopt, /*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
477void 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 {
497 return;
498 }
499
500 bool send_failure = false;
501 if (retval == QMessageBox::Save) {
502 // "Create Unsigned" clicked
505 bool complete = false;
506 // Fill without signing
507 const auto err{model->wallet().fillPSBT(std::nullopt, /*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()) {
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(std::nullopt, /*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 }
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
636QWidget *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
652void 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 }
712 ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
713 }
714}
715
717{
719 ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
721}
722
723void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
724{
725 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
726 // Default to a warning message, override if error message is needed
727 msgParams.second = CClientUIInterface::MSG_WARNING;
728
729 // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
730 // All status values are used only in WalletModel::prepareTransaction()
731 switch(sendCoinsReturn.status)
732 {
734 msgParams.first = tr("The recipient address is not valid. Please recheck.");
735 break;
737 msgParams.first = tr("The amount to pay must be larger than 0.");
738 break;
740 msgParams.first = tr("The amount exceeds your balance.");
741 break;
743 msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
744 break;
746 msgParams.first = tr("Transaction creation failed!");
747 msgParams.second = CClientUIInterface::MSG_ERROR;
748 break;
750 msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
751 break;
752 // included to prevent a compiler warning.
753 case WalletModel::OK:
754 default:
755 return;
756 }
757
758 Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
759}
760
762{
763 ui->labelFeeMinimized->setVisible(fMinimize);
764 ui->buttonChooseFee ->setVisible(fMinimize);
765 ui->buttonMinimizeFee->setVisible(!fMinimize);
766 ui->frameFeeSelection->setVisible(!fMinimize);
767 ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
768 fFeeMinimized = fMinimize;
769}
770
772{
773 minimizeFeeSection(false);
774}
775
777{
779 minimizeFeeSection(true);
780}
781
783{
784 // Same behavior as send: if we have selected coins, only obtain their available balance.
785 // Copy to avoid modifying the member's data.
786 CCoinControl coin_control = *m_coin_control;
787 coin_control.m_allow_other_inputs = !coin_control.HasSelected();
788
789 // Calculate available amount to send.
790 CAmount amount = model->getAvailableBalance(&coin_control);
791 for (int i = 0; i < ui->entries->count(); ++i) {
792 SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
793 if (e && !e->isHidden() && e != entry) {
794 amount -= e->getValue().amount;
795 }
796 }
797
798 if (amount > 0) {
800 entry->setAmount(amount);
801 } else {
802 entry->setAmount(0);
803 }
804}
805
807{
808 ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
809 ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
810 ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
811 ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
812 ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
813 ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
814 ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
815 ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
816}
817
819{
820 if(!model || !model->getOptionsModel())
821 return;
822
823 if (ui->radioSmartFee->isChecked())
824 ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
825 else {
826 ui->labelFeeMinimized->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value())));
827 }
828}
829
831{
832 if (ui->radioCustomFee->isChecked()) {
833 m_coin_control->m_feerate = CFeeRate(ui->customFee->value());
834 } else {
835 m_coin_control->m_feerate.reset();
836 }
837 // Avoid using global defaults when sending money from the GUI
838 // Either custom fee will be used or if not selected, the confirmation target from dropdown box
839 m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
840 m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
841}
842
843void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state) {
844 // During shutdown, clientModel will be nullptr. Attempting to update views at this point may cause a crash
845 // due to accessing backend models that might no longer exist.
846 if (!clientModel) return;
847 // Process event
848 if (sync_state == SynchronizationState::POST_INIT) {
850 }
851}
852
854{
855 if(!model || !model->getOptionsModel())
856 return;
858 m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
859 int returned_target;
860 FeeReason reason;
861 CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
862
863 ui->labelSmartFee->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK())));
864
865 if (reason == FeeReason::FALLBACK) {
866 ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
867 ui->labelFeeEstimation->setText("");
868 ui->fallbackFeeWarningLabel->setVisible(true);
869 int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
870 QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
871 ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
872 ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x"));
873 }
874 else
875 {
876 ui->labelSmartFee2->hide();
877 ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target));
878 ui->fallbackFeeWarningLabel->setVisible(false);
879 }
880
882}
883
884// Coin Control: copy label "Quantity" to clipboard
886{
887 GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
888}
889
890// Coin Control: copy label "Amount" to clipboard
892{
893 GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
894}
895
896// Coin Control: copy label "Fee" to clipboard
898{
899 GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
900}
901
902// Coin Control: copy label "After fee" to clipboard
904{
905 GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
906}
907
908// Coin Control: copy label "Bytes" to clipboard
910{
911 GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
912}
913
914// Coin Control: copy label "Change" to clipboard
916{
917 GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
918}
919
920// Coin Control: settings menu - coin control enabled/disabled by user
922{
923 ui->frameCoinControl->setVisible(checked);
924
925 if (!checked && model) { // coin control features disabled
926 m_coin_control = std::make_unique<CCoinControl>();
927 }
928
930}
931
932// Coin Control: button inputs -> show actual coin control dialog
934{
936 connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
938}
939
940// Coin Control: checkbox custom change address
941#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
943#else
945#endif
946{
947 if (state == Qt::Unchecked)
948 {
949 m_coin_control->destChange = CNoDestination();
950 ui->labelCoinControlChangeLabel->clear();
951 }
952 else
953 // use this to re-validate an already entered address
954 coinControlChangeEdited(ui->lineEditCoinControlChange->text());
955
956 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
957}
958
959// Coin Control: custom change address changed
961{
963 {
964 // Default to no change address until verified
965 m_coin_control->destChange = CNoDestination();
966 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
967
968 const CTxDestination dest = DecodeDestination(text.toStdString());
969
970 if (text.isEmpty()) // Nothing entered
971 {
972 ui->labelCoinControlChangeLabel->setText("");
973 }
974 else if (!IsValidDestination(dest)) // Invalid address
975 {
976 ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
977 }
978 else // Valid address
979 {
980 if (!model->wallet().isSpendable(dest)) {
981 ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
982
983 // confirmation dialog
984 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?"),
985 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
986
987 if(btnRetVal == QMessageBox::Yes)
988 m_coin_control->destChange = dest;
989 else
990 {
991 ui->lineEditCoinControlChange->setText("");
992 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
993 ui->labelCoinControlChangeLabel->setText("");
994 }
995 }
996 else // Known change address
997 {
998 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
999
1000 // Query label
1001 QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
1002 if (!associatedLabel.isEmpty())
1003 ui->labelCoinControlChangeLabel->setText(associatedLabel);
1004 else
1005 ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
1006
1007 m_coin_control->destChange = dest;
1008 }
1009 }
1010 }
1011}
1012
1013// Coin Control: update labels
1015{
1016 if (!model || !model->getOptionsModel())
1017 return;
1018
1020
1021 // set pay amounts
1024
1025 for(int i = 0; i < ui->entries->count(); ++i)
1026 {
1027 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
1028 if(entry && !entry->isHidden())
1029 {
1030 SendCoinsRecipient rcp = entry->getValue();
1032 if (rcp.fSubtractFeeFromAmount)
1034 }
1035 }
1036
1037 if (m_coin_control->HasSelected())
1038 {
1039 // actual coin control calculation
1041
1042 // show coin control stats
1043 ui->labelCoinControlAutomaticallySelected->hide();
1044 ui->widgetCoinControl->show();
1045 }
1046 else
1047 {
1048 // hide coin control stats
1049 ui->labelCoinControlAutomaticallySelected->show();
1050 ui->widgetCoinControl->hide();
1051 ui->labelCoinControlInsuffFunds->hide();
1052 }
1053}
1054
1055SendConfirmationDialog::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)
1056 : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
1057{
1058 setIcon(QMessageBox::Question);
1059 setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
1060 setText(text);
1061 setInformativeText(informative_text);
1062 setDetailedText(detailed_text);
1063 setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1064 if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
1065 setDefaultButton(QMessageBox::Cancel);
1066 yesButton = button(QMessageBox::Yes);
1067 if (confirmButtonText.isEmpty()) {
1068 confirmButtonText = yesButton->text();
1069 }
1070 m_psbt_button = button(QMessageBox::Save);
1071 updateButtons();
1072 connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
1073}
1074
1076{
1077 updateButtons();
1078 countDownTimer.start(1s);
1079 return QMessageBox::exec();
1080}
1081
1083{
1084 secDelay--;
1085 updateButtons();
1086
1087 if(secDelay <= 0)
1088 {
1089 countDownTimer.stop();
1090 }
1091}
1092
1094{
1095 if(secDelay > 0)
1096 {
1097 yesButton->setEnabled(false);
1098 yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1099 if (m_psbt_button) {
1100 m_psbt_button->setEnabled(false);
1101 m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1102 }
1103 }
1104 else
1105 {
1106 yesButton->setEnabled(m_enable_send);
1107 yesButton->setText(confirmButtonText);
1108 if (m_psbt_button) {
1109 m_psbt_button->setEnabled(true);
1111 }
1112 }
1113}
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:143
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
const CChainParams & Params()
Return the currently selected parameters.
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
static QString formatHtmlWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
static QString formatWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
Unit
Bitcoin units.
Definition: bitcoinunits.h:42
@ MSG_INFORMATION
Predefined combinations for certain default usage cases.
Definition: interface_ui.h:66
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
Definition: feerate.h:32
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
Definition: feerate.h:62
Model for Bitcoin network client.
Definition: clientmodel.h:57
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
static QList< CAmount > payAmounts
static void updateLabels(wallet::CCoinControl &m_coin_control, WalletModel *, QDialog *)
static bool fSubtractFeeFromAmount
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:133
bool getCoinControlFeatures() const
Definition: optionsmodel.h:106
bool getEnablePSBTControls() const
Definition: optionsmodel.h:108
void coinControlFeaturesChanged(bool)
void displayUnitChanged(BitcoinUnit unit)
BitcoinUnit getDisplayUnit() const
Definition: optionsmodel.h:103
bool hasSigner()
Whether -signer was set or not.
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
bool getImagesOnButtons() const
Definition: platformstyle.h:21
Dialog for sending bitcoins.
void useAvailableBalance(SendCoinsEntry *entry)
WalletModel * model
void presentPSBT(PartiallySignedTransaction &psbt)
ClientModel * clientModel
void coinControlChangeEdited(const QString &)
void coinControlClipboardFee()
Ui::SendCoinsDialog * ui
void on_buttonChooseFee_clicked()
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
void setClientModel(ClientModel *clientModel)
void updateFeeSectionControls()
SendCoinsEntry * addEntry()
void updateNumberOfBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
void pasteEntry(const SendCoinsRecipient &rv)
void updateFeeMinimizedLabel()
void accept() override
const PlatformStyle * platformStyle
std::unique_ptr< wallet::CCoinControl > m_coin_control
void coinControlClipboardQuantity()
void coinControlButtonClicked()
void coinControlClipboardAfterFee()
bool signWithExternalSigner(PartiallySignedTransaction &psbt, CMutableTransaction &mtx, bool &complete)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
bool PrepareSendText(QString &question_string, QString &informative_text, QString &detailed_text)
void sendButtonClicked(bool checked)
void setModel(WalletModel *model)
void coinControlChangeChecked(Qt::CheckState)
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
void setBalance(const interfaces::WalletBalances &balances)
void coinControlClipboardAmount()
void setAddress(const QString &address)
void coinsSent(const Txid &txid)
void coinControlClipboardChange()
std::unique_ptr< WalletModelTransaction > m_current_transaction
void removeEntry(SendCoinsEntry *entry)
void reject() override
void coinControlClipboardBytes()
void message(const QString &title, const QString &message, unsigned int style)
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=nullptr)
void on_buttonMinimizeFee_clicked()
void coinControlFeatureChanged(bool)
void minimizeFeeSection(bool fMinimize)
A single entry in the dialog for sending bitcoins.
void setFocus()
void setAddress(const QString &address)
bool isClear()
Return whether the entry is still empty and unedited.
void subtractFeeFromAmountChanged()
void useAvailableBalance(SendCoinsEntry *entry)
void setValue(const SendCoinsRecipient &value)
void setModel(WalletModel *model)
void removeEntry(SendCoinsEntry *entry)
void payAmountChanged()
void setAmount(const CAmount &amount)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
void clear()
bool validate(interfaces::Node &node)
void checkSubtractFeeFromAmount()
SendCoinsRecipient getValue()
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)
QAbstractButton * m_psbt_button
QAbstractButton * yesButton
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:49
interfaces::Node & node() const
Definition: walletmodel.h:137
AddressTableModel * getAddressTableModel() const
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl &coinControl)
void sendCoins(WalletModelTransaction &transaction)
CAmount getAvailableBalance(const wallet::CCoinControl *control)
bool isMultiwallet() const
interfaces::Wallet & wallet() const
Definition: walletmodel.h:138
OptionsModel * getOptionsModel() const
UnlockContext requestUnlock()
void balanceChanged(const interfaces::WalletBalances &balances)
interfaces::WalletBalances getCachedBalance() const
QString getWalletName() const
@ TransactionCreationFailed
Definition: walletmodel.h:63
@ AmountExceedsBalance
Definition: walletmodel.h:61
@ DuplicateAddress
Definition: walletmodel.h:62
virtual CAmount getRequiredFee(unsigned int tx_bytes)=0
Get required fee.
virtual std::optional< common::PSBTError > fillPSBT(std::optional< 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.
virtual bool hasExternalSigner()=0
virtual CAmount getDefaultMaxTxFee()=0
Get max tx fee.
virtual bool isSpendable(const CTxDestination &dest)=0
Return whether wallet has private key.
virtual bool privateKeysDisabled()=0
virtual CAmount getMinimumFee(unsigned int tx_bytes, const wallet::CCoinControl &coin_control, int *returned_target, FeeReason *reason)=0
Get minimum fee.
Coin Control Features.
Definition: coincontrol.h:84
bool HasSelected() const
Returns true if there are pre-selected inputs.
Definition: coincontrol.cpp:15
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:94
SyncType
Definition: clientmodel.h:42
#define ASYMP_UTF8
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Definition: key_io.cpp:299
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:58
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:249
void ShowModalDialogAsynchronously(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
Definition: guiutil.cpp:978
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
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:782
constexpr auto dialog_flags
Definition: guiutil.h:60
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....
Definition: guiutil.h:369
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:910
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:131
void setClipboard(const QString &str)
Definition: guiutil.cpp:661
PSBTError
Definition: types.h:17
is a home for public enum and struct type definitions that are used internally by node code,...
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
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:567
int getConfTargetForIndex(int index)
int getIndexForConfTarget(int target)
static constexpr std::array confTargets
#define SEND_CONFIRM_DELAY
A mutable version of CTransaction.
Definition: transaction.h:358
A version of CTransaction with the PSBT format.
Definition: psbt.h:1139
Collection of wallet balances.
Definition: wallet.h:368
static int count
std::string EncodeBase64(std::span< const unsigned char > input)
assert(!tx.IsCoinBase())
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:93