5#if defined(HAVE_CONFIG_H)
6#include <config/bitcoin-config.h>
9#include <qt/forms/ui_sendcoinsdialog.h>
12#include <chainparams.h>
29#include <validation.h>
37#include <QTextDocument>
43 fNewRecipientAllowed(
true), fFeeMinimized(
true),
48 ui->addButton->setIcon(
QIcon());
49 ui->clearButton->setIcon(
QIcon());
50 ui->sendButton->setIcon(
QIcon());
53 ui->clearButton->setIcon(
55 ui->sendButton->setIcon(
63 connect(
ui->addButton, &QPushButton::clicked,
this,
65 connect(
ui->clearButton, &QPushButton::clicked,
this,
69 connect(
ui->pushButtonCoinControl, &QPushButton::clicked,
this,
71 connect(
ui->checkBoxCoinControlChange, &QCheckBox::stateChanged,
this,
73 connect(
ui->lineEditCoinControlChange, &QValidatedLineEdit::textEdited,
108 if (!
settings.contains(
"fFeeSectionMinimized")) {
109 settings.setValue(
"fFeeSectionMinimized",
true);
112 if (!
settings.contains(
"nFeeRadio") &&
113 settings.contains(
"nTransactionFee") &&
114 settings.value(
"nTransactionFee").toLongLong() > 0) {
118 if (!
settings.contains(
"nFeeRadio")) {
122 if (!
settings.contains(
"nTransactionFee")) {
123 settings.setValue(
"nTransactionFee",
126 ui->groupFee->setId(
ui->radioSmartFee, 0);
127 ui->groupFee->setId(
ui->radioCustomFee, 1);
130 std::max<int>(0, std::min(1,
settings.value(
"nFeeRadio").toInt())))
132 ui->customFee->SetAllowEmpty(
false);
133 ui->customFee->setValue(
154 for (
int i = 0; i <
ui->entries->count(); ++i) {
156 ui->entries->itemAt(i)->widget());
176 ui->frameCoinControl->setVisible(
177 _model->getOptionsModel()->getCoinControlFeatures());
181#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
183 QOverload<int>::of(&QButtonGroup::idClicked);
188 &QButtonGroup::buttonClicked);
206 ui->sendButton->setText(
tr(
"Cr&eate Unsigned"));
207 ui->sendButton->setToolTip(
208 tr(
"Creates a Partially Signed Bitcoin Transaction (PSBT) for "
209 "use with e.g. an offline %1 wallet, or a PSBT-compatible "
219 settings.setValue(
"nFeeRadio",
ui->groupFee->checkedId());
220 settings.setValue(
"nTransactionFee",
232 for (
int i = 0; i <
ui->entries->count(); ++i) {
237 recipients.append(entry->
getValue());
239 ui->scrollArea->ensureWidgetVisible(entry);
245 if (!valid || recipients.isEmpty()) {
251 if (!
ctx.isValid()) {
259 std::make_unique<WalletModelTransaction>(recipients);
287 tr(
" from wallet '%1'")
297 if (!
rcp.paymentRequest.IsInitialized())
300 if (
rcp.label.length() > 0) {
313 else if (!
rcp.authenticatedMerchant.isEmpty()) {
315 tr(
"%1 to '%2'").
arg(amount,
rcp.authenticatedMerchant));
334 tr(
"Please, review your transaction proposal. This will produce a "
335 "Partially Signed Bitcoin Transaction (PSBT) which you can save "
336 "or copy and then sign with e.g. an offline %1 wallet, or a "
337 "PSBT-compatible hardware wallet.")
359 "<span style='color:#aa0000; font-weight:bold;'>");
377 QString(
"<b>%1</b>: <b>%2</b>")
378 .arg(
tr(
"Total Amount"))
382 QString(
"<br /><span style='font-size:10pt; "
383 "font-weight:normal;'>(=%1)</span>")
389 tr(
"To review recipient list click \"Show Details...\"");
410 ?
tr(
"Confirm transaction proposal")
411 :
tr(
"Confirm send coins");
413 ?
tr(
"Create Unsigned")
419 QMessageBox::StandardButton
retval =
422 if (
retval != QMessageBox::Yes) {
432 bool complete =
false;
435 true ,
psbtx, complete);
443 msgBox.setText(
"Unsigned Transaction");
444 msgBox.setInformativeText(
445 "The PSBT has been copied to the clipboard. You can also save it.");
446 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
447 msgBox.setDefaultButton(QMessageBox::Discard);
449 case QMessageBox::Save: {
459 rcp.label.isEmpty() ?
rcp.address :
rcp.label;
468 tr(
"Partially Signed Transaction (Binary) (*.psbt)"),
470 if (filename.isEmpty()) {
473 std::ofstream out{filename.toLocal8Bit().data(),
474 std::ofstream::out | std::ofstream::binary};
481 case QMessageBox::Discard:
513 ui->checkBoxCoinControlChange->setChecked(
false);
514 ui->lineEditCoinControlChange->clear();
518 while (
ui->entries->count()) {
519 ui->entries->takeAt(0)->widget()->deleteLater();
536 ui->entries->addWidget(entry);
549 ui->scrollAreaWidgetContents->resize(
550 ui->scrollAreaWidgetContents->sizeHint());
551 qApp->processEvents();
554 bar->setSliderPosition(
bar->maximum());
570 if (
ui->entries->count() == 1) {
574 entry->deleteLater();
580 for (
int i = 0; i <
ui->entries->count(); ++i) {
587 QWidget::setTabOrder(
prev,
ui->sendButton);
588 QWidget::setTabOrder(
ui->sendButton,
ui->clearButton);
589 QWidget::setTabOrder(
ui->clearButton,
ui->addButton);
590 return ui->addButton;
596 if (
ui->entries->count() == 1) {
617 if (
ui->entries->count() == 1) {
643 ui->labelBalanceName->setText(
tr(
"Watch-only balance:"));
669 tr(
"The recipient address is not valid. Please recheck.");
672 msgParams.first =
tr(
"The amount to pay must be larger than 0.");
675 msgParams.first =
tr(
"The amount exceeds your balance.");
678 msgParams.first =
tr(
"The total exceeds your balance when the %1 "
679 "transaction fee is included.")
683 msgParams.first =
tr(
"Duplicate address found: addresses should "
684 "only be used once each.");
687 msgParams.first =
tr(
"Transaction creation failed!");
692 tr(
"A fee higher than %1 is considered an absurdly high fee.")
715 ui->horizontalLayoutSmartFee->setContentsMargins(0, (
fMinimize ? 0 : 6), 0,
735 for (
int i = 0; i <
ui->entries->count(); ++i) {
738 if (
e && !
e->isHidden() &&
e != entry) {
739 amount -=
e->getValue().
amount;
752 ui->labelSmartFee->setEnabled(
ui->radioSmartFee->isChecked());
753 ui->labelSmartFee2->setEnabled(
ui->radioSmartFee->isChecked());
754 ui->labelFeeEstimation->setEnabled(
ui->radioSmartFee->isChecked());
755 ui->labelCustomFeeWarning->setEnabled(
ui->radioCustomFee->isChecked());
756 ui->labelCustomPerKilobyte->setEnabled(
ui->radioCustomFee->isChecked());
757 ui->customFee->setEnabled(
ui->radioCustomFee->isChecked());
765 if (
ui->radioSmartFee->isChecked()) {
766 ui->labelFeeMinimized->setText(
ui->labelSmartFee->text());
768 ui->labelFeeMinimized->setText(
771 ui->customFee->value()) +
777 if (
ui->radioCustomFee->isChecked()) {
780 ctrl.m_feerate.reset();
806 ui->labelSmartFee->setText(
813 ui->labelSmartFee2->show();
814 ui->labelFeeEstimation->setText(
"");
816 ui->labelSmartFee2->hide();
817 ui->labelFeeEstimation->setText(
818 tr(
"Estimated to begin confirmation by next block."));
832 ui->labelCoinControlAmount->text().indexOf(
" ")));
838 ui->labelCoinControlFee->text()
839 .left(
ui->labelCoinControlFee->text().indexOf(
" "))
846 ui->labelCoinControlAfterFee->text()
847 .left(
ui->labelCoinControlAfterFee->text().indexOf(
" "))
854 ui->labelCoinControlBytes->text().replace(
ASYMP_UTF8,
""));
865 ui->labelCoinControlChange->text()
866 .left(
ui->labelCoinControlChange->text().indexOf(
" "))
872 ui->frameCoinControl->setVisible(
checked);
891 if (state == Qt::Unchecked) {
893 ui->labelCoinControlChangeLabel->clear();
899 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
907 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:red;}");
912 if (text.isEmpty()) {
914 ui->labelCoinControlChangeLabel->setText(
"");
917 ui->labelCoinControlChangeLabel->setText(
918 tr(
"Warning: Invalid Bitcoin address"));
922 ui->labelCoinControlChangeLabel->setText(
923 tr(
"Warning: Unknown change address"));
926 QMessageBox::StandardButton
btnRetVal = QMessageBox::question(
927 this,
tr(
"Confirm custom change address"),
928 tr(
"The address you selected for change is not part of "
929 "this wallet. Any or all funds in your wallet may be "
930 "sent to this address. Are you sure?"),
931 QMessageBox::Yes | QMessageBox::Cancel,
932 QMessageBox::Cancel);
937 ui->lineEditCoinControlChange->setText(
"");
938 ui->labelCoinControlChangeLabel->setStyleSheet(
939 "QLabel{color:black;}");
940 ui->labelCoinControlChangeLabel->setText(
"");
944 ui->labelCoinControlChangeLabel->setStyleSheet(
945 "QLabel{color:black;}");
953 ui->labelCoinControlChangeLabel->setText(
tr(
"(no label)"));
973 for (
int i = 0; i <
ui->entries->count(); ++i) {
976 if (entry && !entry->isHidden()) {
979 if (
rcp.fSubtractFeeFromAmount) {
990 ui->labelCoinControlAutomaticallySelected->hide();
991 ui->widgetCoinControl->show();
994 ui->labelCoinControlAutomaticallySelected->show();
995 ui->widgetCoinControl->hide();
996 ui->labelCoinControlInsuffFunds->hide();
1004 : QMessageBox(parent), secDelay(
_secDelay),
1006 setIcon(QMessageBox::Question);
1024 return QMessageBox::exec();
static constexpr Amount SATOSHI
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
static QString formatWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
Unit
Currency units Please add only sensible ones.
static QString formatHtmlWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
@ MSG_INFORMATION
Predefined combinations for certain default usage cases.
Double ended buffer combining vector and stream-like interfaces.
Fee rate in satoshis per kilobyte: Amount / kB.
A mutable version of CTransaction.
Model for Bitcoin network client.
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
static QList< Amount > payAmounts
static void updateLabels(CCoinControl &m_coin_control, WalletModel *, QDialog *)
static bool fSubtractFeeFromAmount
int getDisplayUnit() const
void coinControlFeaturesChanged(bool)
void displayUnitChanged(int unit)
Dialog for sending bitcoins.
void useAvailableBalance(SendCoinsEntry *entry)
ClientModel * clientModel
void coinControlChangeEdited(const QString &)
void coinControlChangeChecked(int)
void coinControlClipboardFee()
void on_sendButton_clicked()
void on_buttonChooseFee_clicked()
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
void setClientModel(ClientModel *clientModel)
void updateTabsAndLabels()
void updateFeeSectionControls()
std::unique_ptr< CCoinControl > m_coin_control
SendCoinsEntry * addEntry()
void updateNumberOfBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
void pasteEntry(const SendCoinsRecipient &rv)
void updateFeeMinimizedLabel()
const PlatformStyle * platformStyle
void coinControlClipboardQuantity()
void coinControlButtonClicked()
void coinControlClipboardAfterFee()
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 setModel(WalletModel *model)
void coinControlClipboardLowOutput()
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
SendCoinsDialog(const PlatformStyle *platformStyle, WalletModel *model, QWidget *parent=nullptr)
void setBalance(const interfaces::WalletBalances &balances)
void coinControlClipboardAmount()
void updateCoinControlState(CCoinControl &ctrl)
void setAddress(const QString &address)
void coinControlClipboardChange()
std::unique_ptr< WalletModelTransaction > m_current_transaction
bool fNewRecipientAllowed
void removeEntry(SendCoinsEntry *entry)
void updateSmartFeeLabel()
void coinControlClipboardBytes()
void message(const QString &title, const QString &message, unsigned int style)
void coinsSent(const uint256 &txid)
void on_buttonMinimizeFee_clicked()
void coinControlUpdateLabels()
void coinControlFeatureChanged(bool)
void minimizeFeeSection(bool fMinimize)
A single entry in the dialog for sending bitcoins.
void setAmount(const Amount amount)
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)
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 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, const QString &confirmText="Send", QWidget *parent=nullptr)
QAbstractButton * yesButton
QString confirmButtonText
Signature hash type wrapper class.
Interface to Bitcoin wallet from Qt view code.
interfaces::Node & node() const
SendCoinsReturn sendCoins(WalletModelTransaction &transaction)
const CChainParams & getChainParams() const
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl &coinControl)
interfaces::Wallet & wallet() const
UnlockContext requestUnlock()
void balanceChanged(const interfaces::WalletBalances &balances)
QString getWalletName() const
@ AmountWithFeeExceedsBalance
@ TransactionCreationFailed
virtual Amount getAvailableBalance(const CCoinControl &coin_control)=0
Get available balance.
virtual bool isSpendable(const CTxDestination &dest)=0
Return whether wallet has private key.
virtual WalletBalances getBalances()=0
Get balances.
virtual TransactionError fillPSBT(SigHashType sighash_type, bool sign, bool bip32derivs, PartiallySignedTransaction &psbtx, bool &complete) const =0
Fill PSBT.
virtual bool privateKeysDisabled()=0
virtual Amount getMinimumFee(unsigned int tx_bytes, const CCoinControl &coin_control)=0
Get minimum fee.
virtual Amount getDefaultMaxTxFee()=0
Get max tx fee.
virtual Amount getRequiredFee(unsigned int tx_bytes)=0
Get required fee.
CTxDestination DecodeDestination(const std::string &addr, const CChainParams ¶ms)
QString HtmlEscape(const QString &str, bool fMultiLine)
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 ...
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
void setClipboard(const QString &str)
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
#define SEND_CONFIRM_DELAY
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
static constexpr Amount zero() noexcept
A version of CTransaction with the PSBT format.
Collection of wallet balances.
std::string EncodeBase64(Span< const uint8_t > input)
SynchronizationState
Current sync state passed to tip changed callbacks.
static const int PROTOCOL_VERSION
network protocol versioning
constexpr Amount DEFAULT_PAY_TX_FEE
-paytxfee default