Bitcoin Core 28.99.0
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2022 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <bitcoin-build-config.h> // IWYU pragma: keep
6
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>
24#include <policy/fees.h>
25#include <txmempool.h>
26#include <validation.h>
27#include <wallet/coincontrol.h>
28#include <wallet/fees.h>
29#include <wallet/wallet.h>
30
31#include <array>
32#include <chrono>
33#include <fstream>
34#include <memory>
35
36#include <QFontMetrics>
37#include <QScrollBar>
38#include <QSettings>
39#include <QTextDocument>
40
44
45static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
46int 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}
55int 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
64SendCoinsDialog::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
82 GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
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
133 GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked);
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
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(CLIENT_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)
226 ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget()));
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
243bool 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
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) {
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 = tr("%1 from wallet '%2'").arg(amount, 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(CLIENT_NAME));
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(CLIENT_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()) {
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
400 DataStream ssTx{};
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 msgBox.setObjectName("psbt_copied_message");
410 switch (msgBox.exec()) {
411 case QMessageBox::Save: {
412 QString selectedFilter;
413 QString fileNameSuggestion = "";
414 bool first = true;
415 for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients()) {
416 if (!first) {
417 fileNameSuggestion.append(" - ");
418 }
419 QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
420 QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
421 fileNameSuggestion.append(labelOrAddress + "-" + amount);
422 first = false;
423 }
424 fileNameSuggestion.append(".psbt");
425 QString filename = GUIUtil::getSaveFileName(this,
426 tr("Save Transaction Data"), fileNameSuggestion,
427 //: Expanded name of the binary PSBT file format. See: BIP 174.
428 tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selectedFilter);
429 if (filename.isEmpty()) {
430 return;
431 }
432 std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
433 out << ssTx.str();
434 out.close();
435 //: Popup message when a PSBT has been saved to a file
436 Q_EMIT message(tr("PSBT saved"), tr("PSBT saved to disk"), CClientUIInterface::MSG_INFORMATION);
437 break;
438 }
439 case QMessageBox::Discard:
440 break;
441 default:
442 assert(false);
443 } // msgBox.exec()
444}
445
447 std::optional<PSBTError> err;
448 try {
449 err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
450 } catch (const std::runtime_error& e) {
451 QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
452 return false;
453 }
454 if (err == PSBTError::EXTERNAL_SIGNER_NOT_FOUND) {
455 //: "External signer" means using devices such as hardware wallets.
456 const QString msg = tr("External signer not found");
457 QMessageBox::critical(nullptr, msg, msg);
458 return false;
459 }
460 if (err == PSBTError::EXTERNAL_SIGNER_FAILED) {
461 //: "External signer" means using devices such as hardware wallets.
462 const QString msg = tr("External signer failure");
463 QMessageBox::critical(nullptr, msg, msg);
464 return false;
465 }
466 if (err) {
467 tfm::format(std::cerr, "Failed to sign PSBT");
469 return false;
470 }
471 // fillPSBT does not always properly finalize
472 complete = FinalizeAndExtractPSBT(psbtx, mtx);
473 return true;
474}
475
476void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
477{
478 if(!model || !model->getOptionsModel())
479 return;
480
481 QString question_string, informative_text, detailed_text;
482 if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
484
485 const QString confirmation = tr("Confirm send coins");
486 const bool enable_send{!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner()};
487 const bool always_show_unsigned{model->getOptionsModel()->getEnablePSBTControls()};
488 auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, enable_send, always_show_unsigned, this);
489 confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
490 // TODO: Replace QDialog::exec() with safer QDialog::show().
491 const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
492
493 if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
494 {
496 return;
497 }
498
499 bool send_failure = false;
500 if (retval == QMessageBox::Save) {
501 // "Create Unsigned" clicked
504 bool complete = false;
505 // Fill without signing
506 const auto err{model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
507 assert(!complete);
508 assert(!err);
509
510 // Copy PSBT to clipboard and offer to save
511 presentPSBT(psbtx);
512 } else {
513 // "Send" clicked
515 bool broadcast = true;
516 if (model->wallet().hasExternalSigner()) {
519 bool complete = false;
520 // Always fill without signing first. This prevents an external signer
521 // from being called prematurely and is not expensive.
522 const auto err{model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
523 assert(!complete);
524 assert(!err);
525 send_failure = !signWithExternalSigner(psbtx, mtx, complete);
526 // Don't broadcast when user rejects it on the device or there's a failure:
527 broadcast = complete && !send_failure;
528 if (!send_failure) {
529 // A transaction signed with an external signer is not always complete,
530 // e.g. in a multisig wallet.
531 if (complete) {
532 // Prepare transaction for broadcast transaction if complete
533 const CTransactionRef tx = MakeTransactionRef(mtx);
534 m_current_transaction->setWtx(tx);
535 } else {
536 presentPSBT(psbtx);
537 }
538 }
539 }
540
541 // Broadcast the transaction, unless an external signer was used and it
542 // failed, or more signatures are needed.
543 if (broadcast) {
544 // now send the prepared transaction
546 Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
547 }
548 }
549 if (!send_failure) {
550 accept();
551 m_coin_control->UnSelectAll();
553 }
555 m_current_transaction.reset();
556}
557
559{
560 m_current_transaction.reset();
561
562 // Clear coin control settings
563 m_coin_control->UnSelectAll();
564 ui->checkBoxCoinControlChange->setChecked(false);
565 ui->lineEditCoinControlChange->clear();
567
568 // Remove entries until only one left
569 while(ui->entries->count())
570 {
571 ui->entries->takeAt(0)->widget()->deleteLater();
572 }
573 addEntry();
574
576}
577
579{
580 clear();
581}
582
584{
585 clear();
586}
587
589{
590 SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
591 entry->setModel(model);
592 ui->entries->addWidget(entry);
597
598 // Focus the field, so that entry can start immediately
599 entry->clear();
600 entry->setFocus();
601 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
602
603 // Scroll to the newly added entry on a QueuedConnection because Qt doesn't
604 // adjust the scroll area and scrollbar immediately when the widget is added.
605 // Invoking on a DirectConnection will only scroll to the second-to-last entry.
606 QMetaObject::invokeMethod(ui->scrollArea, [this] {
607 if (ui->scrollArea->verticalScrollBar()) {
608 ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());
609 }
610 }, Qt::QueuedConnection);
611
612 updateTabsAndLabels();
613 return entry;
614}
615
617{
618 setupTabChain(nullptr);
620}
621
623{
624 entry->hide();
625
626 // If the last entry is about to be removed add an empty one
627 if (ui->entries->count() == 1)
628 addEntry();
629
630 entry->deleteLater();
631
633}
634
635QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
636{
637 for(int i = 0; i < ui->entries->count(); ++i)
638 {
639 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
640 if(entry)
641 {
642 prev = entry->setupTabChain(prev);
643 }
644 }
645 QWidget::setTabOrder(prev, ui->sendButton);
646 QWidget::setTabOrder(ui->sendButton, ui->clearButton);
647 QWidget::setTabOrder(ui->clearButton, ui->addButton);
648 return ui->addButton;
649}
650
651void SendCoinsDialog::setAddress(const QString &address)
652{
653 SendCoinsEntry *entry = nullptr;
654 // Replace the first entry if it is still unused
655 if(ui->entries->count() == 1)
656 {
657 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
658 if(first->isClear())
659 {
660 entry = first;
661 }
662 }
663 if(!entry)
664 {
665 entry = addEntry();
666 }
667
668 entry->setAddress(address);
669}
670
672{
674 return;
675
676 SendCoinsEntry *entry = nullptr;
677 // Replace the first entry if it is still unused
678 if(ui->entries->count() == 1)
679 {
680 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
681 if(first->isClear())
682 {
683 entry = first;
684 }
685 }
686 if(!entry)
687 {
688 entry = addEntry();
689 }
690
691 entry->setValue(rv);
693}
694
696{
697 // Just paste the entry, all pre-checks
698 // are done in paymentserver.cpp.
699 pasteEntry(rv);
700 return true;
701}
702
704{
705 if(model && model->getOptionsModel())
706 {
707 CAmount balance = balances.balance;
708 if (model->wallet().hasExternalSigner()) {
709 ui->labelBalanceName->setText(tr("External balance:"));
710 } else if (model->wallet().isLegacy() && model->wallet().privateKeysDisabled()) {
711 balance = balances.watch_only_balance;
712 ui->labelBalanceName->setText(tr("Watch-only balance:"));
713 }
714 ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
715 }
716}
717
719{
721 ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
723}
724
725void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
726{
727 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
728 // Default to a warning message, override if error message is needed
729 msgParams.second = CClientUIInterface::MSG_WARNING;
730
731 // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
732 // All status values are used only in WalletModel::prepareTransaction()
733 switch(sendCoinsReturn.status)
734 {
736 msgParams.first = tr("The recipient address is not valid. Please recheck.");
737 break;
739 msgParams.first = tr("The amount to pay must be larger than 0.");
740 break;
742 msgParams.first = tr("The amount exceeds your balance.");
743 break;
745 msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
746 break;
748 msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
749 break;
751 msgParams.first = tr("Transaction creation failed!");
752 msgParams.second = CClientUIInterface::MSG_ERROR;
753 break;
755 msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
756 break;
757 // included to prevent a compiler warning.
758 case WalletModel::OK:
759 default:
760 return;
761 }
762
763 Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
764}
765
767{
768 ui->labelFeeMinimized->setVisible(fMinimize);
769 ui->buttonChooseFee ->setVisible(fMinimize);
770 ui->buttonMinimizeFee->setVisible(!fMinimize);
771 ui->frameFeeSelection->setVisible(!fMinimize);
772 ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
773 fFeeMinimized = fMinimize;
774}
775
777{
778 minimizeFeeSection(false);
779}
780
782{
784 minimizeFeeSection(true);
785}
786
788{
789 // Include watch-only for wallets without private key
791
792 // Same behavior as send: if we have selected coins, only obtain their available balance.
793 // Copy to avoid modifying the member's data.
794 CCoinControl coin_control = *m_coin_control;
795 coin_control.m_allow_other_inputs = !coin_control.HasSelected();
796
797 // Calculate available amount to send.
798 CAmount amount = model->getAvailableBalance(&coin_control);
799 for (int i = 0; i < ui->entries->count(); ++i) {
800 SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
801 if (e && !e->isHidden() && e != entry) {
802 amount -= e->getValue().amount;
803 }
804 }
805
806 if (amount > 0) {
808 entry->setAmount(amount);
809 } else {
810 entry->setAmount(0);
811 }
812}
813
815{
816 ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
817 ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
818 ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
819 ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
820 ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
821 ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
822 ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
823 ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
824}
825
827{
828 if(!model || !model->getOptionsModel())
829 return;
830
831 if (ui->radioSmartFee->isChecked())
832 ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
833 else {
834 ui->labelFeeMinimized->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value())));
835 }
836}
837
839{
840 if (ui->radioCustomFee->isChecked()) {
841 m_coin_control->m_feerate = CFeeRate(ui->customFee->value());
842 } else {
843 m_coin_control->m_feerate.reset();
844 }
845 // Avoid using global defaults when sending money from the GUI
846 // Either custom fee will be used or if not selected, the confirmation target from dropdown box
847 m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
848 m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
849 // Include watch-only for wallets without private key
851}
852
853void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state) {
854 if (sync_state == SynchronizationState::POST_INIT) {
856 }
857}
858
860{
861 if(!model || !model->getOptionsModel())
862 return;
864 m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
865 int returned_target;
866 FeeReason reason;
867 CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
868
869 ui->labelSmartFee->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK())));
870
871 if (reason == FeeReason::FALLBACK) {
872 ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
873 ui->labelFeeEstimation->setText("");
874 ui->fallbackFeeWarningLabel->setVisible(true);
875 int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
876 QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
877 ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
878 ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x"));
879 }
880 else
881 {
882 ui->labelSmartFee2->hide();
883 ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target));
884 ui->fallbackFeeWarningLabel->setVisible(false);
885 }
886
888}
889
890// Coin Control: copy label "Quantity" to clipboard
892{
893 GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
894}
895
896// Coin Control: copy label "Amount" to clipboard
898{
899 GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
900}
901
902// Coin Control: copy label "Fee" to clipboard
904{
905 GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
906}
907
908// Coin Control: copy label "After fee" to clipboard
910{
911 GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
912}
913
914// Coin Control: copy label "Bytes" to clipboard
916{
917 GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
918}
919
920// Coin Control: copy label "Change" to clipboard
922{
923 GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
924}
925
926// Coin Control: settings menu - coin control enabled/disabled by user
928{
929 ui->frameCoinControl->setVisible(checked);
930
931 if (!checked && model) { // coin control features disabled
932 m_coin_control = std::make_unique<CCoinControl>();
933 }
934
936}
937
938// Coin Control: button inputs -> show actual coin control dialog
940{
942 connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
944}
945
946// Coin Control: checkbox custom change address
948{
949 if (state == Qt::Unchecked)
950 {
951 m_coin_control->destChange = CNoDestination();
952 ui->labelCoinControlChangeLabel->clear();
953 }
954 else
955 // use this to re-validate an already entered address
956 coinControlChangeEdited(ui->lineEditCoinControlChange->text());
957
958 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
959}
960
961// Coin Control: custom change address changed
963{
965 {
966 // Default to no change address until verified
967 m_coin_control->destChange = CNoDestination();
968 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
969
970 const CTxDestination dest = DecodeDestination(text.toStdString());
971
972 if (text.isEmpty()) // Nothing entered
973 {
974 ui->labelCoinControlChangeLabel->setText("");
975 }
976 else if (!IsValidDestination(dest)) // Invalid address
977 {
978 ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
979 }
980 else // Valid address
981 {
982 if (!model->wallet().isSpendable(dest)) {
983 ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
984
985 // confirmation dialog
986 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?"),
987 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
988
989 if(btnRetVal == QMessageBox::Yes)
990 m_coin_control->destChange = dest;
991 else
992 {
993 ui->lineEditCoinControlChange->setText("");
994 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
995 ui->labelCoinControlChangeLabel->setText("");
996 }
997 }
998 else // Known change address
999 {
1000 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
1001
1002 // Query label
1003 QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
1004 if (!associatedLabel.isEmpty())
1005 ui->labelCoinControlChangeLabel->setText(associatedLabel);
1006 else
1007 ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
1008
1009 m_coin_control->destChange = dest;
1010 }
1011 }
1012 }
1013}
1014
1015// Coin Control: update labels
1017{
1018 if (!model || !model->getOptionsModel())
1019 return;
1020
1022
1023 // set pay amounts
1026
1027 for(int i = 0; i < ui->entries->count(); ++i)
1028 {
1029 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
1030 if(entry && !entry->isHidden())
1031 {
1032 SendCoinsRecipient rcp = entry->getValue();
1034 if (rcp.fSubtractFeeFromAmount)
1036 }
1037 }
1038
1039 if (m_coin_control->HasSelected())
1040 {
1041 // actual coin control calculation
1043
1044 // show coin control stats
1045 ui->labelCoinControlAutomaticallySelected->hide();
1046 ui->widgetCoinControl->show();
1047 }
1048 else
1049 {
1050 // hide coin control stats
1051 ui->labelCoinControlAutomaticallySelected->show();
1052 ui->widgetCoinControl->hide();
1053 ui->labelCoinControlInsuffFunds->hide();
1054 }
1055}
1056
1057SendConfirmationDialog::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)
1058 : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
1059{
1060 setIcon(QMessageBox::Question);
1061 setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
1062 setText(text);
1063 setInformativeText(informative_text);
1064 setDetailedText(detailed_text);
1065 setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1066 if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
1067 setDefaultButton(QMessageBox::Cancel);
1068 yesButton = button(QMessageBox::Yes);
1069 if (confirmButtonText.isEmpty()) {
1070 confirmButtonText = yesButton->text();
1071 }
1072 m_psbt_button = button(QMessageBox::Save);
1073 updateButtons();
1074 connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
1075}
1076
1078{
1079 updateButtons();
1080 countDownTimer.start(1s);
1081 return QMessageBox::exec();
1082}
1083
1085{
1086 secDelay--;
1087 updateButtons();
1088
1089 if(secDelay <= 0)
1090 {
1091 countDownTimer.stop();
1092 }
1093}
1094
1096{
1097 if(secDelay > 0)
1098 {
1099 yesButton->setEnabled(false);
1100 yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1101 if (m_psbt_button) {
1102 m_psbt_button->setEnabled(false);
1103 m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1104 }
1105 }
1106 else
1107 {
1108 yesButton->setEnabled(m_enable_send);
1109 yesButton->setText(confirmButtonText);
1110 if (m_psbt_button) {
1111 m_psbt_button->setEnabled(true);
1113 }
1114 }
1115}
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:140
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 kilovirtualbyte: CAmount / kvB.
Definition: feerate.h:33
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
Definition: feerate.h:63
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:147
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 coinControlChangeChecked(int)
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)
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
void setBalance(const interfaces::WalletBalances &balances)
void coinControlClipboardAmount()
void setAddress(const QString &address)
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 coinsSent(const uint256 &txid)
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:48
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
@ AmountWithFeeExceedsBalance
Definition: walletmodel.h:61
@ TransactionCreationFailed
Definition: walletmodel.h:63
@ AmountExceedsBalance
Definition: walletmodel.h:60
@ DuplicateAddress
Definition: walletmodel.h:62
virtual bool isLegacy()=0
Return whether is a legacy wallet.
virtual CAmount getRequiredFee(unsigned int tx_bytes)=0
Get required fee.
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 std::optional< common::PSBTError > fillPSBT(int sighash_type, bool sign, bool bip32derivs, size_t *n_signed, PartiallySignedTransaction &psbtx, bool &complete)=0
Fill PSBT.
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:81
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:91
SyncType
Definition: clientmodel.h:42
#define ASYMP_UTF8
@ SIGHASH_ALL
Definition: interpreter.h:30
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:1004
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:787
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:391
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:915
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:131
void setClipboard(const QString &str)
Definition: guiutil.cpp:668
PSBTError
Definition: types.h:17
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1072
constexpr CAmount DEFAULT_PAY_TX_FEE
-paytxfee default
Definition: wallet.h:104
is a home for public enum and struct type definitions that are used by internally by node code,...
FeeReason
Definition: fees.h:60
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
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
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:378
A version of CTransaction with the PSBT format.
Definition: psbt.h:951
Collection of wallet balances.
Definition: wallet.h:380
static int count
std::string EncodeBase64(Span< const unsigned char > input)
assert(!tx.IsCoinBase())
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:85