Bitcoin Core 29.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#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
92 connect(ui->checkBoxCoinControlChange, &QCheckBox::checkStateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
93#else
94 connect(ui->checkBoxCoinControlChange, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
95#endif
96 connect(ui->lineEditCoinControlChange, &QValidatedLineEdit::textEdited, this, &SendCoinsDialog::coinControlChangeEdited);
97
98 // Coin Control: clipboard actions
99 QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
100 QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
101 QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
102 QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
103 QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
104 QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
105 connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity);
106 connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount);
107 connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee);
108 connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee);
109 connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes);
110 connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange);
111 ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
112 ui->labelCoinControlAmount->addAction(clipboardAmountAction);
113 ui->labelCoinControlFee->addAction(clipboardFeeAction);
114 ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
115 ui->labelCoinControlBytes->addAction(clipboardBytesAction);
116 ui->labelCoinControlChange->addAction(clipboardChangeAction);
117
118 // init transaction fee section
119 QSettings settings;
120 if (!settings.contains("fFeeSectionMinimized"))
121 settings.setValue("fFeeSectionMinimized", true);
122 if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
123 settings.setValue("nFeeRadio", 1); // custom
124 if (!settings.contains("nFeeRadio"))
125 settings.setValue("nFeeRadio", 0); // recommended
126 if (!settings.contains("nSmartFeeSliderPosition"))
127 settings.setValue("nSmartFeeSliderPosition", 0);
128 if (!settings.contains("nTransactionFee"))
129 settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
130 ui->groupFee->setId(ui->radioSmartFee, 0);
131 ui->groupFee->setId(ui->radioCustomFee, 1);
132 ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
133 ui->customFee->SetAllowEmpty(false);
134 ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
135 minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
136
137 GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked);
138}
139
141{
142 this->clientModel = _clientModel;
143
144 if (_clientModel) {
146 }
147}
148
150{
151 this->model = _model;
152
153 if(_model && _model->getOptionsModel())
154 {
155 for(int i = 0; i < ui->entries->count(); ++i)
156 {
157 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
158 if(entry)
159 {
160 entry->setModel(_model);
161 }
162 }
163
167
168 // Coin Control
171 ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
173
174 // fee section
175 for (const int n : confTargets) {
176 ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
177 }
178 connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel);
179 connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels);
180
181 connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::updateFeeSectionControls);
182 connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::coinControlUpdateLabels);
183
185#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
186 connect(ui->optInRBF, &QCheckBox::checkStateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
187 connect(ui->optInRBF, &QCheckBox::checkStateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
188#else
189 connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
190 connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
191#endif
192 CAmount requiredFee = model->wallet().getRequiredFee(1000);
193 ui->customFee->SetMinValue(requiredFee);
194 if (ui->customFee->value() < requiredFee) {
195 ui->customFee->setValue(requiredFee);
196 }
197 ui->customFee->setSingleStep(requiredFee);
200
201 // set default rbf checkbox state
202 ui->optInRBF->setCheckState(Qt::Checked);
203
204 if (model->wallet().hasExternalSigner()) {
205 //: "device" usually means a hardware wallet.
206 ui->sendButton->setText(tr("Sign on device"));
207 if (model->getOptionsModel()->hasSigner()) {
208 ui->sendButton->setEnabled(true);
209 ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
210 } else {
211 ui->sendButton->setEnabled(false);
212 //: "External signer" means using devices such as hardware wallets.
213 ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
214 }
215 } else if (model->wallet().privateKeysDisabled()) {
216 ui->sendButton->setText(tr("Cr&eate Unsigned"));
217 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));
218 }
219
220 // set the smartfee-sliders default value (wallets default conf.target or last stored value)
221 QSettings settings;
222 if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
223 // migrate nSmartFeeSliderPosition to nConfTarget
224 // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
225 int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
226 settings.setValue("nConfTarget", nConfirmTarget);
227 settings.remove("nSmartFeeSliderPosition");
228 }
229 if (settings.value("nConfTarget").toInt() == 0)
230 ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget()));
231 else
232 ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
233 }
234}
235
237{
238 QSettings settings;
239 settings.setValue("fFeeSectionMinimized", fFeeMinimized);
240 settings.setValue("nFeeRadio", ui->groupFee->checkedId());
241 settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
242 settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
243
244 delete ui;
245}
246
247bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text)
248{
249 QList<SendCoinsRecipient> recipients;
250 bool valid = true;
251
252 for(int i = 0; i < ui->entries->count(); ++i)
253 {
254 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
255 if(entry)
256 {
257 if(entry->validate(model->node()))
258 {
259 recipients.append(entry->getValue());
260 }
261 else if (valid)
262 {
263 ui->scrollArea->ensureWidgetVisible(entry);
264 valid = false;
265 }
266 }
267 }
268
269 if(!valid || recipients.isEmpty())
270 {
271 return false;
272 }
273
274 fNewRecipientAllowed = false;
276 if(!ctx.isValid())
277 {
278 // Unlock wallet was cancelled
280 return false;
281 }
282
283 // prepare transaction for getting txFee earlier
284 m_current_transaction = std::make_unique<WalletModelTransaction>(recipients);
285 WalletModel::SendCoinsReturn prepareStatus;
286
288
289 CCoinControl coin_control = *m_coin_control;
290 coin_control.m_allow_other_inputs = !coin_control.HasSelected(); // future, could introduce a checkbox to customize this value.
291 prepareStatus = model->prepareTransaction(*m_current_transaction, coin_control);
292
293 // process prepareStatus and on error generate message shown to user
294 processSendCoinsReturn(prepareStatus,
296
297 if(prepareStatus.status != WalletModel::OK) {
299 return false;
300 }
301
302 CAmount txFee = m_current_transaction->getTransactionFee();
303 QStringList formatted;
304 for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients())
305 {
306 // generate amount string with wallet name in case of multiwallet
307 QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
308 if (model->isMultiwallet()) {
309 amount = tr("%1 from wallet '%2'").arg(amount, GUIUtil::HtmlEscape(model->getWalletName()));
310 }
311
312 // generate address string
313 QString address = rcp.address;
314
315 QString recipientElement;
316
317 {
318 if(rcp.label.length() > 0) // label with address
319 {
320 recipientElement.append(tr("%1 to '%2'").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
321 recipientElement.append(QString(" (%1)").arg(address));
322 }
323 else // just address
324 {
325 recipientElement.append(tr("%1 to %2").arg(amount, address));
326 }
327 }
328 formatted.append(recipientElement);
329 }
330
331 /*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
332 that the displayed transaction details represent the transaction the user intends to create. */
333 question_string.append(tr("Do you want to create this transaction?"));
334 question_string.append("<br /><span style='font-size:10pt;'>");
336 /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
337 a user can only create a PSBT. This string is displayed when private keys are disabled and an external
338 signer is not available. */
339 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));
341 /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
342 a user can send their transaction or create a PSBT. This string is displayed when both private keys
343 and PSBT controls are enabled. */
344 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));
345 } else {
346 /*: Text to prompt a user to review the details of the transaction they are attempting to send. */
347 question_string.append(tr("Please, review your transaction."));
348 }
349 question_string.append("</span>%1");
350
351 if(txFee > 0)
352 {
353 // append fee string if a fee is required
354 question_string.append("<hr /><b>");
355 question_string.append(tr("Transaction fee"));
356 question_string.append("</b>");
357
358 // append transaction size
359 //: When reviewing a newly created PSBT (via Send flow), the transaction fee is shown, with "virtual size" of the transaction displayed for context
360 question_string.append(" (" + tr("%1 kvB", "PSBT transaction creation").arg((double)m_current_transaction->getTransactionSize() / 1000, 0, 'g', 3) + "): ");
361
362 // append transaction fee value
363 question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
364 question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
365 question_string.append("</span><br />");
366
367 // append RBF message according to transaction's signalling
368 question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
369 if (ui->optInRBF->isChecked()) {
370 question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
371 } else {
372 question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
373 }
374 question_string.append("</span>");
375 }
376
377 // add total amount in all subdivision units
378 question_string.append("<hr />");
379 CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee;
380 QStringList alternativeUnits;
381 for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
383 alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
384 }
385 question_string.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
387 question_string.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
388 .arg(alternativeUnits.join(" " + tr("or") + " ")));
389
390 if (formatted.size() > 1) {
391 question_string = question_string.arg("");
392 informative_text = tr("To review recipient list click \"Show Details…\"");
393 detailed_text = formatted.join("\n\n");
394 } else {
395 question_string = question_string.arg("<br /><br />" + formatted.at(0));
396 }
397
398 return true;
399}
400
402{
403 // Serialize the PSBT
404 DataStream ssTx{};
405 ssTx << psbtx;
406 GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
407 QMessageBox msgBox(this);
408 //: Caption of "PSBT has been copied" messagebox
409 msgBox.setText(tr("Unsigned Transaction", "PSBT copied"));
410 msgBox.setInformativeText(tr("The PSBT has been copied to the clipboard. You can also save it."));
411 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
412 msgBox.setDefaultButton(QMessageBox::Discard);
413 msgBox.setObjectName("psbt_copied_message");
414 switch (msgBox.exec()) {
415 case QMessageBox::Save: {
416 QString selectedFilter;
417 QString fileNameSuggestion = "";
418 bool first = true;
419 for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients()) {
420 if (!first) {
421 fileNameSuggestion.append(" - ");
422 }
423 QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
424 QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
425 fileNameSuggestion.append(labelOrAddress + "-" + amount);
426 first = false;
427 }
428 fileNameSuggestion.append(".psbt");
429 QString filename = GUIUtil::getSaveFileName(this,
430 tr("Save Transaction Data"), fileNameSuggestion,
431 //: Expanded name of the binary PSBT file format. See: BIP 174.
432 tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selectedFilter);
433 if (filename.isEmpty()) {
434 return;
435 }
436 std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
437 out << ssTx.str();
438 out.close();
439 //: Popup message when a PSBT has been saved to a file
440 Q_EMIT message(tr("PSBT saved"), tr("PSBT saved to disk"), CClientUIInterface::MSG_INFORMATION);
441 break;
442 }
443 case QMessageBox::Discard:
444 break;
445 default:
446 assert(false);
447 } // msgBox.exec()
448}
449
451 std::optional<PSBTError> err;
452 try {
453 err = model->wallet().fillPSBT(std::nullopt, /*sign=*/true, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
454 } catch (const std::runtime_error& e) {
455 QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
456 return false;
457 }
458 if (err == PSBTError::EXTERNAL_SIGNER_NOT_FOUND) {
459 //: "External signer" means using devices such as hardware wallets.
460 const QString msg = tr("External signer not found");
461 QMessageBox::critical(nullptr, msg, msg);
462 return false;
463 }
464 if (err == PSBTError::EXTERNAL_SIGNER_FAILED) {
465 //: "External signer" means using devices such as hardware wallets.
466 const QString msg = tr("External signer failure");
467 QMessageBox::critical(nullptr, msg, msg);
468 return false;
469 }
470 if (err) {
471 qWarning() << "Failed to sign PSBT";
473 return false;
474 }
475 // fillPSBT does not always properly finalize
476 complete = FinalizeAndExtractPSBT(psbtx, mtx);
477 return true;
478}
479
480void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
481{
482 if(!model || !model->getOptionsModel())
483 return;
484
485 QString question_string, informative_text, detailed_text;
486 if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
488
489 const QString confirmation = tr("Confirm send coins");
490 const bool enable_send{!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner()};
491 const bool always_show_unsigned{model->getOptionsModel()->getEnablePSBTControls()};
492 auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, enable_send, always_show_unsigned, this);
493 confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
494 // TODO: Replace QDialog::exec() with safer QDialog::show().
495 const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
496
497 if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
498 {
500 return;
501 }
502
503 bool send_failure = false;
504 if (retval == QMessageBox::Save) {
505 // "Create Unsigned" clicked
508 bool complete = false;
509 // Fill without signing
510 const auto err{model->wallet().fillPSBT(std::nullopt, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
511 assert(!complete);
512 assert(!err);
513
514 // Copy PSBT to clipboard and offer to save
515 presentPSBT(psbtx);
516 } else {
517 // "Send" clicked
519 bool broadcast = true;
520 if (model->wallet().hasExternalSigner()) {
523 bool complete = false;
524 // Always fill without signing first. This prevents an external signer
525 // from being called prematurely and is not expensive.
526 const auto err{model->wallet().fillPSBT(std::nullopt, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
527 assert(!complete);
528 assert(!err);
529 send_failure = !signWithExternalSigner(psbtx, mtx, complete);
530 // Don't broadcast when user rejects it on the device or there's a failure:
531 broadcast = complete && !send_failure;
532 if (!send_failure) {
533 // A transaction signed with an external signer is not always complete,
534 // e.g. in a multisig wallet.
535 if (complete) {
536 // Prepare transaction for broadcast transaction if complete
537 const CTransactionRef tx = MakeTransactionRef(mtx);
538 m_current_transaction->setWtx(tx);
539 } else {
540 presentPSBT(psbtx);
541 }
542 }
543 }
544
545 // Broadcast the transaction, unless an external signer was used and it
546 // failed, or more signatures are needed.
547 if (broadcast) {
548 // now send the prepared transaction
550 Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
551 }
552 }
553 if (!send_failure) {
554 accept();
555 m_coin_control->UnSelectAll();
557 }
559 m_current_transaction.reset();
560}
561
563{
564 m_current_transaction.reset();
565
566 // Clear coin control settings
567 m_coin_control->UnSelectAll();
568 ui->checkBoxCoinControlChange->setChecked(false);
569 ui->lineEditCoinControlChange->clear();
571
572 // Remove entries until only one left
573 while(ui->entries->count())
574 {
575 ui->entries->takeAt(0)->widget()->deleteLater();
576 }
577 addEntry();
578
580}
581
583{
584 clear();
585}
586
588{
589 clear();
590}
591
593{
594 SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
595 entry->setModel(model);
596 ui->entries->addWidget(entry);
601
602 // Focus the field, so that entry can start immediately
603 entry->clear();
604 entry->setFocus();
605 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
606
607 // Scroll to the newly added entry on a QueuedConnection because Qt doesn't
608 // adjust the scroll area and scrollbar immediately when the widget is added.
609 // Invoking on a DirectConnection will only scroll to the second-to-last entry.
610 QMetaObject::invokeMethod(ui->scrollArea, [this] {
611 if (ui->scrollArea->verticalScrollBar()) {
612 ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());
613 }
614 }, Qt::QueuedConnection);
615
616 updateTabsAndLabels();
617 return entry;
618}
619
621{
622 setupTabChain(nullptr);
624}
625
627{
628 entry->hide();
629
630 // If the last entry is about to be removed add an empty one
631 if (ui->entries->count() == 1)
632 addEntry();
633
634 entry->deleteLater();
635
637}
638
639QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
640{
641 for(int i = 0; i < ui->entries->count(); ++i)
642 {
643 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
644 if(entry)
645 {
646 prev = entry->setupTabChain(prev);
647 }
648 }
649 QWidget::setTabOrder(prev, ui->sendButton);
650 QWidget::setTabOrder(ui->sendButton, ui->clearButton);
651 QWidget::setTabOrder(ui->clearButton, ui->addButton);
652 return ui->addButton;
653}
654
655void SendCoinsDialog::setAddress(const QString &address)
656{
657 SendCoinsEntry *entry = nullptr;
658 // Replace the first entry if it is still unused
659 if(ui->entries->count() == 1)
660 {
661 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
662 if(first->isClear())
663 {
664 entry = first;
665 }
666 }
667 if(!entry)
668 {
669 entry = addEntry();
670 }
671
672 entry->setAddress(address);
673}
674
676{
678 return;
679
680 SendCoinsEntry *entry = nullptr;
681 // Replace the first entry if it is still unused
682 if(ui->entries->count() == 1)
683 {
684 SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
685 if(first->isClear())
686 {
687 entry = first;
688 }
689 }
690 if(!entry)
691 {
692 entry = addEntry();
693 }
694
695 entry->setValue(rv);
697}
698
700{
701 // Just paste the entry, all pre-checks
702 // are done in paymentserver.cpp.
703 pasteEntry(rv);
704 return true;
705}
706
708{
709 if(model && model->getOptionsModel())
710 {
711 CAmount balance = balances.balance;
712 if (model->wallet().hasExternalSigner()) {
713 ui->labelBalanceName->setText(tr("External balance:"));
714 }
715 ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
716 }
717}
718
720{
722 ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
724}
725
726void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
727{
728 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
729 // Default to a warning message, override if error message is needed
730 msgParams.second = CClientUIInterface::MSG_WARNING;
731
732 // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
733 // All status values are used only in WalletModel::prepareTransaction()
734 switch(sendCoinsReturn.status)
735 {
737 msgParams.first = tr("The recipient address is not valid. Please recheck.");
738 break;
740 msgParams.first = tr("The amount to pay must be larger than 0.");
741 break;
743 msgParams.first = tr("The amount exceeds your balance.");
744 break;
746 msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
747 break;
749 msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
750 break;
752 msgParams.first = tr("Transaction creation failed!");
753 msgParams.second = CClientUIInterface::MSG_ERROR;
754 break;
756 msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
757 break;
758 // included to prevent a compiler warning.
759 case WalletModel::OK:
760 default:
761 return;
762 }
763
764 Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
765}
766
768{
769 ui->labelFeeMinimized->setVisible(fMinimize);
770 ui->buttonChooseFee ->setVisible(fMinimize);
771 ui->buttonMinimizeFee->setVisible(!fMinimize);
772 ui->frameFeeSelection->setVisible(!fMinimize);
773 ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
774 fFeeMinimized = fMinimize;
775}
776
778{
779 minimizeFeeSection(false);
780}
781
783{
785 minimizeFeeSection(true);
786}
787
789{
790 // Same behavior as send: if we have selected coins, only obtain their available balance.
791 // Copy to avoid modifying the member's data.
792 CCoinControl coin_control = *m_coin_control;
793 coin_control.m_allow_other_inputs = !coin_control.HasSelected();
794
795 // Calculate available amount to send.
796 CAmount amount = model->getAvailableBalance(&coin_control);
797 for (int i = 0; i < ui->entries->count(); ++i) {
798 SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
799 if (e && !e->isHidden() && e != entry) {
800 amount -= e->getValue().amount;
801 }
802 }
803
804 if (amount > 0) {
806 entry->setAmount(amount);
807 } else {
808 entry->setAmount(0);
809 }
810}
811
813{
814 ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
815 ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
816 ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
817 ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
818 ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
819 ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
820 ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
821 ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
822}
823
825{
826 if(!model || !model->getOptionsModel())
827 return;
828
829 if (ui->radioSmartFee->isChecked())
830 ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
831 else {
832 ui->labelFeeMinimized->setText(tr("%1/kvB").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value())));
833 }
834}
835
837{
838 if (ui->radioCustomFee->isChecked()) {
839 m_coin_control->m_feerate = CFeeRate(ui->customFee->value());
840 } else {
841 m_coin_control->m_feerate.reset();
842 }
843 // Avoid using global defaults when sending money from the GUI
844 // Either custom fee will be used or if not selected, the confirmation target from dropdown box
845 m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
846 m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
847}
848
849void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state) {
850 // During shutdown, clientModel will be nullptr. Attempting to update views at this point may cause a crash
851 // due to accessing backend models that might no longer exist.
852 if (!clientModel) return;
853 // Process event
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
947#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
949#else
951#endif
952{
953 if (state == Qt::Unchecked)
954 {
955 m_coin_control->destChange = CNoDestination();
956 ui->labelCoinControlChangeLabel->clear();
957 }
958 else
959 // use this to re-validate an already entered address
960 coinControlChangeEdited(ui->lineEditCoinControlChange->text());
961
962 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
963}
964
965// Coin Control: custom change address changed
967{
969 {
970 // Default to no change address until verified
971 m_coin_control->destChange = CNoDestination();
972 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
973
974 const CTxDestination dest = DecodeDestination(text.toStdString());
975
976 if (text.isEmpty()) // Nothing entered
977 {
978 ui->labelCoinControlChangeLabel->setText("");
979 }
980 else if (!IsValidDestination(dest)) // Invalid address
981 {
982 ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
983 }
984 else // Valid address
985 {
986 if (!model->wallet().isSpendable(dest)) {
987 ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
988
989 // confirmation dialog
990 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?"),
991 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
992
993 if(btnRetVal == QMessageBox::Yes)
994 m_coin_control->destChange = dest;
995 else
996 {
997 ui->lineEditCoinControlChange->setText("");
998 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
999 ui->labelCoinControlChangeLabel->setText("");
1000 }
1001 }
1002 else // Known change address
1003 {
1004 ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
1005
1006 // Query label
1007 QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
1008 if (!associatedLabel.isEmpty())
1009 ui->labelCoinControlChangeLabel->setText(associatedLabel);
1010 else
1011 ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
1012
1013 m_coin_control->destChange = dest;
1014 }
1015 }
1016 }
1017}
1018
1019// Coin Control: update labels
1021{
1022 if (!model || !model->getOptionsModel())
1023 return;
1024
1026
1027 // set pay amounts
1030
1031 for(int i = 0; i < ui->entries->count(); ++i)
1032 {
1033 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
1034 if(entry && !entry->isHidden())
1035 {
1036 SendCoinsRecipient rcp = entry->getValue();
1038 if (rcp.fSubtractFeeFromAmount)
1040 }
1041 }
1042
1043 if (m_coin_control->HasSelected())
1044 {
1045 // actual coin control calculation
1047
1048 // show coin control stats
1049 ui->labelCoinControlAutomaticallySelected->hide();
1050 ui->widgetCoinControl->show();
1051 }
1052 else
1053 {
1054 // hide coin control stats
1055 ui->labelCoinControlAutomaticallySelected->show();
1056 ui->widgetCoinControl->hide();
1057 ui->labelCoinControlInsuffFunds->hide();
1058 }
1059}
1060
1061SendConfirmationDialog::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)
1062 : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
1063{
1064 setIcon(QMessageBox::Question);
1065 setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
1066 setText(text);
1067 setInformativeText(informative_text);
1068 setDetailedText(detailed_text);
1069 setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1070 if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
1071 setDefaultButton(QMessageBox::Cancel);
1072 yesButton = button(QMessageBox::Yes);
1073 if (confirmButtonText.isEmpty()) {
1074 confirmButtonText = yesButton->text();
1075 }
1076 m_psbt_button = button(QMessageBox::Save);
1077 updateButtons();
1078 connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
1079}
1080
1082{
1083 updateButtons();
1084 countDownTimer.start(1s);
1085 return QMessageBox::exec();
1086}
1087
1089{
1090 secDelay--;
1091 updateButtons();
1092
1093 if(secDelay <= 0)
1094 {
1095 countDownTimer.stop();
1096 }
1097}
1098
1100{
1101 if(secDelay > 0)
1102 {
1103 yesButton->setEnabled(false);
1104 yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1105 if (m_psbt_button) {
1106 m_psbt_button->setEnabled(false);
1107 m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1108 }
1109 }
1110 else
1111 {
1112 yesButton->setEnabled(m_enable_send);
1113 yesButton->setText(confirmButtonText);
1114 if (m_psbt_button) {
1115 m_psbt_button->setEnabled(true);
1117 }
1118 }
1119}
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 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 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:138
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:139
OptionsModel * getOptionsModel() const
UnlockContext requestUnlock()
void balanceChanged(const interfaces::WalletBalances &balances)
interfaces::WalletBalances getCachedBalance() const
QString getWalletName() const
@ AmountWithFeeExceedsBalance
Definition: walletmodel.h:62
@ TransactionCreationFailed
Definition: walletmodel.h:64
@ AmountExceedsBalance
Definition: walletmodel.h:61
@ DuplicateAddress
Definition: walletmodel.h:63
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: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
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:983
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: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: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
constexpr CAmount DEFAULT_PAY_TX_FEE
-paytxfee default
Definition: wallet.h:105
is a home for public enum and struct type definitions that are used 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:543
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:1119
Collection of wallet balances.
Definition: wallet.h:371
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:86