5 #if defined(HAVE_CONFIG_H) 10 #include <qt/forms/ui_sendcoinsdialog.h> 31 #include <validation.h> 33 #include <QFontMetrics> 36 #include <QTextDocument> 38 static constexpr std::array
confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
40 if (index+1 > static_cast<int>(
confTargets.size())) {
49 for (
unsigned int i = 0; i <
confTargets.size(); i++) {
63 fNewRecipientAllowed(true),
65 platformStyle(_platformStyle)
70 ui->addButton->setIcon(QIcon());
71 ui->clearButton->setIcon(QIcon());
72 ui->sendButton->setIcon(QIcon());
92 QAction *clipboardQuantityAction =
new QAction(tr(
"Copy quantity"),
this);
93 QAction *clipboardAmountAction =
new QAction(tr(
"Copy amount"),
this);
94 QAction *clipboardFeeAction =
new QAction(tr(
"Copy fee"),
this);
95 QAction *clipboardAfterFeeAction =
new QAction(tr(
"Copy after fee"),
this);
96 QAction *clipboardBytesAction =
new QAction(tr(
"Copy bytes"),
this);
97 QAction *clipboardLowOutputAction =
new QAction(tr(
"Copy dust"),
this);
98 QAction *clipboardChangeAction =
new QAction(tr(
"Copy change"),
this);
106 ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
107 ui->labelCoinControlAmount->addAction(clipboardAmountAction);
108 ui->labelCoinControlFee->addAction(clipboardFeeAction);
109 ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
110 ui->labelCoinControlBytes->addAction(clipboardBytesAction);
111 ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
112 ui->labelCoinControlChange->addAction(clipboardChangeAction);
116 if (!settings.contains(
"fFeeSectionMinimized"))
117 settings.setValue(
"fFeeSectionMinimized",
true);
118 if (!settings.contains(
"nFeeRadio") && settings.contains(
"nTransactionFee") && settings.value(
"nTransactionFee").toLongLong() > 0)
119 settings.setValue(
"nFeeRadio", 1);
120 if (!settings.contains(
"nFeeRadio"))
121 settings.setValue(
"nFeeRadio", 0);
122 if (!settings.contains(
"nSmartFeeSliderPosition"))
123 settings.setValue(
"nSmartFeeSliderPosition", 0);
124 if (!settings.contains(
"nTransactionFee"))
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());
147 this->
model = _model;
151 for(
int i = 0; i <
ui->entries->count(); ++i)
179 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 191 ui->customFee->SetMinValue(requiredFee);
192 if (
ui->customFee->value() < requiredFee) {
193 ui->customFee->setValue(requiredFee);
195 ui->customFee->setSingleStep(requiredFee);
200 ui->optInRBF->setCheckState(Qt::Checked);
204 ui->sendButton->setText(tr(
"Sign on device"));
206 ui->sendButton->setEnabled(
true);
207 ui->sendButton->setToolTip(tr(
"Connect your hardware wallet first."));
209 ui->sendButton->setEnabled(
false);
211 ui->sendButton->setToolTip(tr(
"Set external signer script path in Options -> Wallet"));
214 ui->sendButton->setText(tr(
"Cr&eate Unsigned"));
215 ui->sendButton->setToolTip(tr(
"Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(
PACKAGE_NAME));
220 if (settings.value(
"nSmartFeeSliderPosition").toInt() != 0) {
223 int nConfirmTarget = 25 - settings.value(
"nSmartFeeSliderPosition").toInt();
224 settings.setValue(
"nConfTarget", nConfirmTarget);
225 settings.remove(
"nSmartFeeSliderPosition");
227 if (settings.value(
"nConfTarget").toInt() == 0)
238 settings.setValue(
"nFeeRadio",
ui->groupFee->checkedId());
240 settings.setValue(
"nTransactionFee", (qint64)
ui->customFee->value());
247 QList<SendCoinsRecipient> recipients;
250 for(
int i = 0; i <
ui->entries->count(); ++i)
257 recipients.append(entry->
getValue());
261 ui->scrollArea->ensureWidgetVisible(entry);
267 if(!valid || recipients.isEmpty())
299 QStringList formatted;
309 QString address = rcp.address;
311 QString recipientElement;
314 if(rcp.label.length() > 0)
317 recipientElement.append(QString(
" (%1)").arg(address));
321 recipientElement.append(tr(
"%1 to %2").arg(amount, address));
324 formatted.append(recipientElement);
328 question_string.append(tr(
"Do you want to draft this transaction?"));
330 question_string.append(tr(
"Are you sure you want to send?"));
333 question_string.append(
"<br /><span style='font-size:10pt;'>");
335 question_string.append(tr(
"Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(
PACKAGE_NAME));
337 question_string.append(tr(
"Please, review your transaction."));
339 question_string.append(
"</span>%1");
344 question_string.append(
"<hr /><b>");
345 question_string.append(tr(
"Transaction fee"));
346 question_string.append(
"</b>");
349 question_string.append(
" (" + QString::number((
double)
m_current_transaction->getTransactionSize() / 1000) +
" kB): ");
352 question_string.append(
"<span style='color:#aa0000; font-weight:bold;'>");
354 question_string.append(
"</span><br />");
357 question_string.append(
"<span style='font-size:10pt; font-weight:normal;'>");
358 if (
ui->optInRBF->isChecked()) {
359 question_string.append(tr(
"You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
361 question_string.append(tr(
"Not signalling Replace-By-Fee, BIP-125."));
363 question_string.append(
"</span>");
367 question_string.append(
"<hr />");
369 QStringList alternativeUnits;
375 question_string.append(QString(
"<b>%1</b>: <b>%2</b>").arg(tr(
"Total Amount"))
377 question_string.append(QString(
"<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
378 .arg(alternativeUnits.join(
" " + tr(
"or") +
" ")));
380 if (formatted.size() > 1) {
381 question_string = question_string.arg(
"");
382 informative_text = tr(
"To review recipient list click \"Show Details…\"");
383 detailed_text = formatted.join(
"\n\n");
385 question_string = question_string.arg(
"<br /><br />" + formatted.at(0));
396 QString question_string, informative_text, detailed_text;
397 if (!
PrepareSendText(question_string, informative_text, detailed_text))
return;
403 confirmationDialog.
exec();
404 QMessageBox::StandardButton retval =
static_cast<QMessageBox::StandardButton
>(confirmationDialog.result());
406 if(retval != QMessageBox::Yes)
412 bool send_failure =
false;
416 bool complete =
false;
425 }
catch (
const std::runtime_error& e) {
426 QMessageBox::critical(
nullptr, tr(
"Sign failed"), e.what());
432 QMessageBox::critical(
nullptr, tr(
"External signer not found"),
"External signer not found");
438 QMessageBox::critical(
nullptr, tr(
"External signer failure"),
"External signer failure");
476 msgBox.setText(
"Unsigned Transaction");
477 msgBox.setInformativeText(
"The PSBT has been copied to the clipboard. You can also save it.");
478 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
479 msgBox.setDefaultButton(QMessageBox::Discard);
480 switch (msgBox.exec()) {
481 case QMessageBox::Save: {
482 QString selectedFilter;
483 QString fileNameSuggestion =
"";
487 fileNameSuggestion.append(
" - ");
489 QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
491 fileNameSuggestion.append(labelOrAddress +
"-" + amount);
494 fileNameSuggestion.append(
".psbt");
496 tr(
"Save Transaction Data"), fileNameSuggestion,
498 tr(
"Partially Signed Transaction (Binary)") + QLatin1String(
" (*.psbt)"), &selectedFilter);
499 if (filename.isEmpty()) {
502 std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
508 case QMessageBox::Discard:
540 ui->checkBoxCoinControlChange->setChecked(
false);
541 ui->lineEditCoinControlChange->clear();
545 while(
ui->entries->count())
547 ui->entries->takeAt(0)->widget()->deleteLater();
568 ui->entries->addWidget(entry);
577 ui->scrollAreaWidgetContents->resize(
ui->scrollAreaWidgetContents->sizeHint());
578 qApp->processEvents();
579 QScrollBar* bar =
ui->scrollArea->verticalScrollBar();
581 bar->setSliderPosition(bar->maximum());
598 if (
ui->entries->count() == 1)
601 entry->deleteLater();
608 for(
int i = 0; i <
ui->entries->count(); ++i)
616 QWidget::setTabOrder(prev,
ui->sendButton);
617 QWidget::setTabOrder(
ui->sendButton,
ui->clearButton);
618 QWidget::setTabOrder(
ui->clearButton,
ui->addButton);
619 return ui->addButton;
626 if(
ui->entries->count() == 1)
649 if(
ui->entries->count() == 1)
680 ui->labelBalanceName->setText(tr(
"External balance:"));
683 ui->labelBalanceName->setText(tr(
"Watch-only balance:"));
698 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
704 switch(sendCoinsReturn.
status)
707 msgParams.first = tr(
"The recipient address is not valid. Please recheck.");
710 msgParams.first = tr(
"The amount to pay must be larger than 0.");
713 msgParams.first = tr(
"The amount exceeds your balance.");
716 msgParams.first = tr(
"The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
719 msgParams.first = tr(
"Duplicate address found: addresses should only be used once each.");
722 msgParams.first = tr(
"Transaction creation failed!");
729 msgParams.first = tr(
"Payment request expired.");
738 Q_EMIT
message(tr(
"Send Coins"), msgParams.first, msgParams.second);
743 ui->labelFeeMinimized->setVisible(fMinimize);
744 ui->buttonChooseFee ->setVisible(fMinimize);
745 ui->buttonMinimizeFee->setVisible(!fMinimize);
746 ui->frameFeeSelection->setVisible(!fMinimize);
747 ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
769 for (
int i = 0; i <
ui->entries->count(); ++i) {
771 if (e && !e->isHidden() && e != entry) {
786 ui->confTargetSelector ->setEnabled(
ui->radioSmartFee->isChecked());
787 ui->labelSmartFee ->setEnabled(
ui->radioSmartFee->isChecked());
788 ui->labelSmartFee2 ->setEnabled(
ui->radioSmartFee->isChecked());
789 ui->labelSmartFee3 ->setEnabled(
ui->radioSmartFee->isChecked());
790 ui->labelFeeEstimation ->setEnabled(
ui->radioSmartFee->isChecked());
791 ui->labelCustomFeeWarning ->setEnabled(
ui->radioCustomFee->isChecked());
792 ui->labelCustomPerKilobyte ->setEnabled(
ui->radioCustomFee->isChecked());
793 ui->customFee ->setEnabled(
ui->radioCustomFee->isChecked());
801 if (
ui->radioSmartFee->isChecked())
802 ui->labelFeeMinimized->setText(
ui->labelSmartFee->text());
810 if (
ui->radioCustomFee->isChecked()) {
842 ui->labelSmartFee2->show();
843 ui->labelFeeEstimation->setText(
"");
844 ui->fallbackFeeWarningLabel->setVisible(
true);
845 int lightness =
ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
846 QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
847 ui->fallbackFeeWarningLabel->setStyleSheet(
"QLabel { color: " + warning_colour.name() +
"; }");
848 ui->fallbackFeeWarningLabel->setIndent(
GUIUtil::TextWidth(QFontMetrics(
ui->fallbackFeeWarningLabel->font()),
"x"));
852 ui->labelSmartFee2->hide();
853 ui->labelFeeEstimation->setText(tr(
"Estimated to begin confirmation within %n block(s).",
"", returned_target));
854 ui->fallbackFeeWarningLabel->setVisible(
false);
905 ui->frameCoinControl->setVisible(checked);
907 if (!checked &&
model) {
925 if (state == Qt::Unchecked)
928 ui->labelCoinControlChangeLabel->clear();
934 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
944 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:red;}");
950 ui->labelCoinControlChangeLabel->setText(
"");
954 ui->labelCoinControlChangeLabel->setText(tr(
"Warning: Invalid Bitcoin address"));
959 ui->labelCoinControlChangeLabel->setText(tr(
"Warning: Unknown change address"));
962 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?"),
963 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
965 if(btnRetVal == QMessageBox::Yes)
969 ui->lineEditCoinControlChange->setText(
"");
970 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
971 ui->labelCoinControlChangeLabel->setText(
"");
976 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
980 if (!associatedLabel.isEmpty())
981 ui->labelCoinControlChangeLabel->setText(associatedLabel);
983 ui->labelCoinControlChangeLabel->setText(tr(
"(no label)"));
1003 for(
int i = 0; i <
ui->entries->count(); ++i)
1006 if(entry && !entry->isHidden())
1021 ui->labelCoinControlAutomaticallySelected->hide();
1022 ui->widgetCoinControl->show();
1027 ui->labelCoinControlAutomaticallySelected->show();
1028 ui->widgetCoinControl->hide();
1029 ui->labelCoinControlInsuffFunds->hide();
1034 : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
1036 setIcon(QMessageBox::Question);
1037 setWindowTitle(title);
1039 setInformativeText(informative_text);
1040 setDetailedText(detailed_text);
1041 setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1042 setDefaultButton(QMessageBox::Cancel);
1055 return QMessageBox::exec();
std::shared_ptr< const CTransaction > CTransactionRef
virtual bool privateKeysDisabled()=0
void removeEntry(SendCoinsEntry *entry)
void updateNumberOfBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state)
interfaces::Wallet & wallet() const
void setValue(const SendCoinsRecipient &value)
Utility functions used by the Bitcoin Qt UI.
SynchronizationState
Current sync state passed to tip changed callbacks.
void sendButtonClicked(bool checked)
void updateFeeMinimizedLabel()
constexpr CAmount DEFAULT_PAY_TX_FEE
-paytxfee default
void on_buttonChooseFee_clicked()
SendCoinsRecipient getValue()
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text...
UnlockContext requestUnlock()
void coinControlClipboardQuantity()
void coinControlClipboardAfterFee()
void setAddress(const QString &address)
virtual CAmount getDefaultMaxTxFee()=0
Get max tx fee.
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
#define SEND_CONFIRM_DELAY
SendCoinsReturn sendCoins(WalletModelTransaction &transaction)
void coinControlFeaturesChanged(bool)
QString HtmlEscape(const QString &str, bool fMultiLine)
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg)
A version of CTransaction with the PSBT format.
Double ended buffer combining vector and stream-like interfaces.
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
static constexpr std::array confTargets
AddressTableModel * getAddressTableModel()
bool FinalizeAndExtractPSBT(PartiallySignedTransaction &psbtx, CMutableTransaction &result)
Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized...
static void updateLabels(CCoinControl &m_coin_control, WalletModel *, QDialog *)
virtual bool isSpendable(const CTxDestination &dest)=0
Return whether wallet has private key.
A single entry in the dialog for sending bitcoins.
int getDisplayUnit() const
constexpr auto dialog_flags
void coinControlFeatureChanged(bool)
virtual bool hasExternalSigner()=0
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
std::unique_ptr< WalletModelTransaction > m_current_transaction
int64_t CAmount
Amount in satoshis (Can be negative)
static QList< CAmount > payAmounts
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
std::string EncodeBase64(Span< const unsigned char > input)
SendCoinsEntry * addEntry()
bool validate(interfaces::Node &node)
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
CAmount watch_only_balance
void setBalance(const interfaces::WalletBalances &balances)
void setAddress(const QString &address)
void coinControlClipboardChange()
Collection of wallet balances.
void useAvailableBalance(SendCoinsEntry *entry)
void setClientModel(ClientModel *clientModel)
void setClipboard(const QString &str)
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
ClientModel * clientModel
void updateCoinControlState()
static secp256k1_context * ctx
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
auto ExceptionSafeConnect(Sender sender, Signal signal, Receiver receiver, Slot method, Qt::ConnectionType type=Qt::AutoConnection)
A drop-in replacement of QObject::connect function (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that guaranties that all exceptions are handled within the slot.
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl &coinControl)
virtual CAmount getMinimumFee(unsigned int tx_bytes, const CCoinControl &coin_control, int *returned_target, FeeReason *reason)=0
Get minimum fee.
Dialog for sending bitcoins.
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
void coinControlChangeEdited(const QString &)
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
QString getWalletName() const
interfaces::Node & node() const
void removeEntry(SendCoinsEntry *entry)
void displayUnitChanged(int unit)
Model for Bitcoin network client.
void coinControlUpdateLabels()
void setModel(WalletModel *model)
int getConfTargetForIndex(int index)
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=nullptr)
SendConfirmationDialog(const QString &title, const QString &text, const QString &informative_text="", const QString &detailed_text="", int secDelay=SEND_CONFIRM_DELAY, const QString &confirmText="", QWidget *parent=nullptr)
bool getCoinControlFeatures() const
static CTransactionRef MakeTransactionRef(Tx &&txIn)
void checkSubtractFeeFromAmount()
bool isClear()
Return whether the entry is still empty and unedited.
void minimizeFeeSection(bool fMinimize)
void coinControlClipboardLowOutput()
void updateFeeSectionControls()
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
void subtractFeeFromAmountChanged()
static bool fSubtractFeeFromAmount
void updateSmartFeeLabel()
bool PrepareSendText(QString &question_string, QString &informative_text, QString &detailed_text)
QString confirmButtonText
int getIndexForConfTarget(int target)
void updateTabsAndLabels()
const CChainParams & Params()
Return the currently selected parameters.
Interface to Bitcoin wallet from Qt view code.
static const int PROTOCOL_VERSION
network protocol versioning
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 ...
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
void setAmount(const CAmount &amount)
void setModel(WalletModel *model)
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
Fee rate in satoshis per kilobyte: CAmount / kB.
void coinControlClipboardBytes()
void useAvailableBalance(SendCoinsEntry *entry)
A mutable version of CTransaction.
virtual TransactionError fillPSBT(int sighash_type, bool sign, bool bip32derivs, size_t *n_signed, PartiallySignedTransaction &psbtx, bool &complete)=0
Fill PSBT.
virtual unsigned int getConfirmTarget()=0
Get tx confirm target.
bool fSubtractFeeFromAmount
const PlatformStyle * platformStyle
QString formatNiceTimeOffset(qint64 secs)
std::unique_ptr< CCoinControl > m_coin_control
void coinControlClipboardAmount()
void on_buttonMinimizeFee_clicked()
virtual CAmount getAvailableBalance(const CCoinControl &coin_control)=0
Get available balance.
virtual WalletBalances getBalances()=0
Get balances.
void pasteEntry(const SendCoinsRecipient &rv)
QAbstractButton * yesButton
virtual CAmount getRequiredFee(unsigned int tx_bytes)=0
Get required fee.
void message(const QString &title, const QString &message, unsigned int style)
void coinsSent(const uint256 &txid)
bool fNewRecipientAllowed
CAmount GetFeePerK() const
Return the fee in satoshis for a size of 1000 bytes.
void balanceChanged(const interfaces::WalletBalances &balances)
void coinControlButtonClicked()
void coinControlClipboardFee()
OptionsModel * getOptionsModel()
Predefined combinations for certain default usage cases.
void coinControlChangeChecked(int)