5#include <bitcoin-build-config.h>
8#include <qt/forms/ui_sendcoinsdialog.h>
19#include <chainparams.h>
26#include <validation.h>
36#include <QFontMetrics>
39#include <QTextDocument>
44static constexpr std::array
confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
46 if (index+1 >
static_cast<int>(
confTargets.size())) {
55 for (
unsigned int i = 0; i <
confTargets.size(); i++) {
64 QDialog(parent,
GUIUtil::dialog_flags),
72 ui->addButton->setIcon(
QIcon());
73 ui->clearButton->setIcon(
QIcon());
74 ui->sendButton->setIcon(
QIcon());
90#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
119 if (!
settings.contains(
"fFeeSectionMinimized"))
120 settings.setValue(
"fFeeSectionMinimized",
true);
121 if (!
settings.contains(
"nFeeRadio") &&
settings.contains(
"nTransactionFee") &&
settings.value(
"nTransactionFee").toLongLong() > 0)
123 if (!
settings.contains(
"nFeeRadio"))
125 if (!
settings.contains(
"nSmartFeeSliderPosition"))
126 settings.setValue(
"nSmartFeeSliderPosition", 0);
127 ui->groupFee->setId(
ui->radioSmartFee, 0);
128 ui->groupFee->setId(
ui->radioCustomFee, 1);
129 ui->groupFee->button((
int)std::max(0, std::min(1,
settings.value(
"nFeeRadio").toInt())))->setChecked(
true);
130 ui->customFee->SetAllowEmpty(
false);
131 ui->customFee->setValue(
settings.value(
"nTransactionFee").toLongLong());
152 for(
int i = 0; i <
ui->entries->count(); ++i)
168 ui->frameCoinControl->setVisible(
_model->getOptionsModel()->getCoinControlFeatures());
182#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
199 ui->optInRBF->setCheckState(Qt::Checked);
203 ui->sendButton->setText(
tr(
"Sign on device"));
205 ui->sendButton->setEnabled(
true);
206 ui->sendButton->setToolTip(
tr(
"Connect your hardware wallet first."));
208 ui->sendButton->setEnabled(
false);
210 ui->sendButton->setToolTip(
tr(
"Set external signer script path in Options -> Wallet"));
213 ui->sendButton->setText(
tr(
"Cr&eate Unsigned"));
214 ui->sendButton->setToolTip(
tr(
"Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").
arg(
CLIENT_NAME));
219 if (
settings.value(
"nSmartFeeSliderPosition").toInt() != 0) {
224 settings.remove(
"nSmartFeeSliderPosition");
226 if (
settings.value(
"nConfTarget").toInt() == 0)
237 settings.setValue(
"nFeeRadio",
ui->groupFee->checkedId());
249 for(
int i = 0; i <
ui->entries->count(); ++i)
256 recipients.append(entry->
getValue());
260 ui->scrollArea->ensureWidgetVisible(entry);
266 if(!valid || recipients.isEmpty())
315 if(
rcp.label.length() > 0)
336 question_string.append(
tr(
"Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").
arg(
CLIENT_NAME));
341 question_string.append(
tr(
"Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.").
arg(
CLIENT_NAME));
360 question_string.append(
"<span style='color:#aa0000; font-weight:bold;'>");
365 question_string.append(
"<span style='font-size:10pt; font-weight:normal;'>");
366 if (
ui->optInRBF->isChecked()) {
367 question_string.append(
tr(
"You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
406 msgBox.setText(
tr(
"Unsigned Transaction",
"PSBT copied"));
407 msgBox.setInformativeText(
tr(
"The PSBT has been copied to the clipboard. You can also save it."));
408 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
409 msgBox.setDefaultButton(QMessageBox::Discard);
410 msgBox.setObjectName(
"psbt_copied_message");
412 case QMessageBox::Save: {
430 if (filename.isEmpty()) {
433 std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
440 case QMessageBox::Discard:
448 std::optional<PSBTError> err;
451 }
catch (
const std::runtime_error&
e) {
452 QMessageBox::critical(
nullptr,
tr(
"Sign failed"),
e.what());
455 if (err == PSBTError::EXTERNAL_SIGNER_NOT_FOUND) {
457 const QString msg =
tr(
"External signer not found");
458 QMessageBox::critical(
nullptr, msg, msg);
461 if (err == PSBTError::EXTERNAL_SIGNER_FAILED) {
463 const QString msg =
tr(
"External signer failure");
464 QMessageBox::critical(
nullptr, msg, msg);
468 qWarning() <<
"Failed to sign PSBT";
494 if(
retval != QMessageBox::Yes &&
retval != QMessageBox::Save)
501 if (
retval == QMessageBox::Save) {
505 bool complete =
false;
520 bool complete =
false;
565 ui->checkBoxCoinControlChange->setChecked(
false);
566 ui->lineEditCoinControlChange->clear();
570 while(
ui->entries->count())
572 ui->entries->takeAt(0)->widget()->deleteLater();
593 ui->entries->addWidget(entry);
602 ui->scrollAreaWidgetContents->resize(
ui->scrollAreaWidgetContents->sizeHint());
607 QMetaObject::invokeMethod(
ui->scrollArea, [
this] {
608 if (ui->scrollArea->verticalScrollBar()) {
609 ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());
611 }, Qt::QueuedConnection);
613 updateTabsAndLabels();
628 if (
ui->entries->count() == 1)
631 entry->deleteLater();
638 for(
int i = 0; i <
ui->entries->count(); ++i)
646 QWidget::setTabOrder(
prev,
ui->sendButton);
647 QWidget::setTabOrder(
ui->sendButton,
ui->clearButton);
648 QWidget::setTabOrder(
ui->clearButton,
ui->addButton);
649 return ui->addButton;
656 if(
ui->entries->count() == 1)
679 if(
ui->entries->count() == 1)
710 ui->labelBalanceName->setText(
tr(
"External balance:"));
734 msgParams.first =
tr(
"The recipient address is not valid. Please recheck.");
737 msgParams.first =
tr(
"The amount to pay must be larger than 0.");
740 msgParams.first =
tr(
"The amount exceeds your balance.");
743 msgParams.first =
tr(
"Duplicate address found: addresses should only be used once each.");
746 msgParams.first =
tr(
"Transaction creation failed!");
767 ui->horizontalLayoutSmartFee->setContentsMargins(0, (
fMinimize ? 0 : 6), 0, 0);
791 for (
int i = 0; i <
ui->entries->count(); ++i) {
793 if (
e && !
e->isHidden() &&
e != entry) {
794 amount -=
e->getValue().amount;
808 ui->confTargetSelector ->setEnabled(
ui->radioSmartFee->isChecked());
809 ui->labelSmartFee ->setEnabled(
ui->radioSmartFee->isChecked());
810 ui->labelSmartFee2 ->setEnabled(
ui->radioSmartFee->isChecked());
811 ui->labelSmartFee3 ->setEnabled(
ui->radioSmartFee->isChecked());
812 ui->labelFeeEstimation ->setEnabled(
ui->radioSmartFee->isChecked());
813 ui->labelCustomFeeWarning ->setEnabled(
ui->radioCustomFee->isChecked());
814 ui->labelCustomPerKilobyte ->setEnabled(
ui->radioCustomFee->isChecked());
815 ui->customFee ->setEnabled(
ui->radioCustomFee->isChecked());
823 if (
ui->radioSmartFee->isChecked())
824 ui->labelFeeMinimized->setText(
ui->labelSmartFee->text());
832 if (
ui->radioCustomFee->isChecked()) {
866 ui->labelSmartFee2->show();
867 ui->labelFeeEstimation->setText(
"");
868 ui->fallbackFeeWarningLabel->setVisible(
true);
869 int lightness =
ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
871 ui->fallbackFeeWarningLabel->setStyleSheet(
"QLabel { color: " +
warning_colour.name() +
"; }");
876 ui->labelSmartFee2->hide();
877 ui->labelFeeEstimation->setText(
tr(
"Estimated to begin confirmation within %n block(s).",
"",
returned_target));
878 ui->fallbackFeeWarningLabel->setVisible(
false);
923 ui->frameCoinControl->setVisible(
checked);
941#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
947 if (state == Qt::Unchecked)
950 ui->labelCoinControlChangeLabel->clear();
954 coinControlChangeEdited(ui->lineEditCoinControlChange->text());
956 ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
966 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:red;}");
972 ui->labelCoinControlChangeLabel->setText(
"");
976 ui->labelCoinControlChangeLabel->setText(
tr(
"Warning: Invalid Bitcoin address"));
981 ui->labelCoinControlChangeLabel->setText(
tr(
"Warning: Unknown change address"));
984 QMessageBox::StandardButton
btnRetVal = QMessageBox::question(
this,
tr(
"Confirm custom change address"),
tr(
"The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?"),
985 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
991 ui->lineEditCoinControlChange->setText(
"");
992 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
993 ui->labelCoinControlChangeLabel->setText(
"");
998 ui->labelCoinControlChangeLabel->setStyleSheet(
"QLabel{color:black;}");
1005 ui->labelCoinControlChangeLabel->setText(
tr(
"(no label)"));
1025 for(
int i = 0; i <
ui->entries->count(); ++i)
1028 if(entry && !entry->isHidden())
1032 if (
rcp.fSubtractFeeFromAmount)
1043 ui->labelCoinControlAutomaticallySelected->hide();
1044 ui->widgetCoinControl->show();
1049 ui->labelCoinControlAutomaticallySelected->show();
1050 ui->widgetCoinControl->hide();
1051 ui->labelCoinControlInsuffFunds->hide();
1058 setIcon(QMessageBox::Question);
1079 return QMessageBox::exec();
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.
int64_t CAmount
Amount in satoshis (Can be negative)
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)
@ MSG_INFORMATION
Predefined combinations for certain default usage cases.
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
Model for Bitcoin network client.
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.
bool getEnablePSBTControls() const
void coinControlFeaturesChanged(bool)
void displayUnitChanged(BitcoinUnit unit)
BitcoinUnit getDisplayUnit() const
bool hasSigner()
Whether -signer was set or not.
Dialog for sending bitcoins.
void useAvailableBalance(SendCoinsEntry *entry)
void presentPSBT(PartiallySignedTransaction &psbt)
ClientModel * clientModel
void coinControlChangeEdited(const QString &)
void coinControlClipboardFee()
void on_buttonChooseFee_clicked()
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
void setClientModel(ClientModel *clientModel)
void updateTabsAndLabels()
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()
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
bool fNewRecipientAllowed
void removeEntry(SendCoinsEntry *entry)
void updateSmartFeeLabel()
void updateCoinControlState()
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 coinControlUpdateLabels()
void coinControlFeatureChanged(bool)
void minimizeFeeSection(bool fMinimize)
A single entry in the dialog for sending bitcoins.
void setAddress(const QString &address)
void subtractFeeFromAmountChanged()
void useAvailableBalance(SendCoinsEntry *entry)
void setValue(const SendCoinsRecipient &value)
void setModel(WalletModel *model)
void removeEntry(SendCoinsEntry *entry)
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://...
bool validate(interfaces::Node &node)
void checkSubtractFeeFromAmount()
SendCoinsRecipient getValue()
QString m_psbt_button_text
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
QString confirmButtonText
Interface to Bitcoin wallet from Qt view code.
interfaces::Node & node() const
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
OptionsModel * getOptionsModel() const
UnlockContext requestUnlock()
void balanceChanged(const interfaces::WalletBalances &balances)
interfaces::WalletBalances getCachedBalance() const
QString getWalletName() const
@ TransactionCreationFailed
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.
bool m_allow_other_inputs
If true, the selection process can add extra unselected inputs from the wallet while requires all sel...
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Utility functions used by the Bitcoin Qt UI.
QString HtmlEscape(const QString &str, bool fMultiLine)
void ShowModalDialogAsynchronously(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
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 ...
QString formatNiceTimeOffset(qint64 secs)
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....
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
void setClipboard(const QString &str)
is a home for public enum and struct type definitions that are used internally by node code,...
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
bool FinalizeAndExtractPSBT(PartiallySignedTransaction &psbtx, CMutableTransaction &result)
Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized.
int getConfTargetForIndex(int index)
int getIndexForConfTarget(int target)
static constexpr std::array confTargets
#define SEND_CONFIRM_DELAY
A mutable version of CTransaction.
A version of CTransaction with the PSBT format.
Collection of wallet balances.
std::string EncodeBase64(std::span< const unsigned char > input)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
SynchronizationState
Current sync state passed to tip changed callbacks.