5 #if defined(HAVE_CONFIG_H) 21 #include <chainparams.h> 26 #include <txmempool.h> 27 #include <validation.h> 37 #include <QFontMetrics> 40 #include <QTextDocument> 45 static constexpr std::array
confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
47 if (index+1 > static_cast<int>(
confTargets.size())) {
56 for (
unsigned int i = 0; i <
confTargets.size(); i++) {
68 platformStyle(_platformStyle)
95 QAction *clipboardQuantityAction =
new QAction(tr(
"Copy quantity"),
this);
96 QAction *clipboardAmountAction =
new QAction(tr(
"Copy amount"),
this);
97 QAction *clipboardFeeAction =
new QAction(tr(
"Copy fee"),
this);
98 QAction *clipboardAfterFeeAction =
new QAction(tr(
"Copy after fee"),
this);
99 QAction *clipboardBytesAction =
new QAction(tr(
"Copy bytes"),
this);
100 QAction *clipboardChangeAction =
new QAction(tr(
"Copy change"),
this);
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"))
128 ui->
groupFee->button((
int)std::max(0, std::min(1, settings.value(
"nFeeRadio").toInt())))->setChecked(
true);
147 this->
model = _model;
151 for(
int i = 0; i <
ui->
entries->count(); ++i)
177 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 205 ui->
sendButton->setToolTip(tr(
"Connect your hardware wallet first."));
209 ui->
sendButton->setToolTip(tr(
"Set external signer script path in Options -> Wallet"));
213 ui->
sendButton->setToolTip(tr(
"Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(
PACKAGE_NAME));
218 if (settings.value(
"nSmartFeeSliderPosition").toInt() != 0) {
221 int nConfirmTarget = 25 - settings.value(
"nSmartFeeSliderPosition").toInt();
222 settings.setValue(
"nConfTarget", nConfirmTarget);
223 settings.remove(
"nSmartFeeSliderPosition");
225 if (settings.value(
"nConfTarget").toInt() == 0)
236 settings.setValue(
"nFeeRadio",
ui->
groupFee->checkedId());
245 QList<SendCoinsRecipient> recipients;
248 for(
int i = 0; i <
ui->
entries->count(); ++i)
255 recipients.append(entry->
getValue());
265 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);
329 question_string.append(tr(
"Do you want to create this transaction?"));
330 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));
340 question_string.append(tr(
"Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(
PACKAGE_NAME));
343 question_string.append(tr(
"Please, review your transaction."));
345 question_string.append(
"</span>%1");
350 question_string.append(
"<hr /><b>");
351 question_string.append(tr(
"Transaction fee"));
352 question_string.append(
"</b>");
356 question_string.append(
" (" + tr(
"%1 kvB",
"PSBT transaction creation").arg((
double)
m_current_transaction->getTransactionSize() / 1000, 0,
'g', 3) +
"): ");
359 question_string.append(
"<span style='color:#aa0000; font-weight:bold;'>");
361 question_string.append(
"</span><br />");
364 question_string.append(
"<span style='font-size:10pt; font-weight:normal;'>");
366 question_string.append(tr(
"You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
368 question_string.append(tr(
"Not signalling Replace-By-Fee, BIP-125."));
370 question_string.append(
"</span>");
374 question_string.append(
"<hr />");
376 QStringList alternativeUnits;
381 question_string.append(QString(
"<b>%1</b>: <b>%2</b>").arg(tr(
"Total Amount"))
383 question_string.append(QString(
"<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
384 .arg(alternativeUnits.join(
" " + tr(
"or") +
" ")));
386 if (formatted.size() > 1) {
387 question_string = question_string.arg(
"");
388 informative_text = tr(
"To review recipient list click \"Show Details…\"");
389 detailed_text = formatted.join(
"\n\n");
391 question_string = question_string.arg(
"<br /><br />" + formatted.at(0));
403 QMessageBox msgBox(
this);
405 msgBox.setText(tr(
"Unsigned Transaction",
"PSBT copied"));
406 msgBox.setInformativeText(tr(
"The PSBT has been copied to the clipboard. You can also save it."));
407 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
408 msgBox.setDefaultButton(QMessageBox::Discard);
409 switch (msgBox.exec()) {
410 case QMessageBox::Save: {
411 QString selectedFilter;
412 QString fileNameSuggestion =
"";
416 fileNameSuggestion.append(
" - ");
418 QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
420 fileNameSuggestion.append(labelOrAddress +
"-" + amount);
423 fileNameSuggestion.append(
".psbt");
425 tr(
"Save Transaction Data"), fileNameSuggestion,
427 tr(
"Partially Signed Transaction (Binary)") + QLatin1String(
" (*.psbt)"), &selectedFilter);
428 if (filename.isEmpty()) {
438 case QMessageBox::Discard:
449 }
catch (
const std::runtime_error& e) {
450 QMessageBox::critical(
nullptr, tr(
"Sign failed"), e.what());
455 const QString
msg = tr(
"External signer not found");
456 QMessageBox::critical(
nullptr,
msg,
msg);
461 const QString
msg = tr(
"External signer failure");
462 QMessageBox::critical(
nullptr,
msg,
msg);
480 QString question_string, informative_text, detailed_text;
481 if (!
PrepareSendText(question_string, informative_text, detailed_text))
return;
484 const QString confirmation = tr(
"Confirm send coins");
488 confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
490 const auto retval =
static_cast<QMessageBox::StandardButton
>(confirmationDialog->exec());
492 if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
498 bool send_failure =
false;
499 if (retval == QMessageBox::Save) {
503 bool complete =
false;
514 bool broadcast =
true;
518 bool complete =
false;
526 broadcast = complete && !send_failure;
570 ui->
entries->takeAt(0)->widget()->deleteLater();
606 if (ui->scrollArea->verticalScrollBar()) {
607 ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());
609 }, Qt::QueuedConnection);
611 updateTabsAndLabels();
629 entry->deleteLater();
636 for(
int i = 0; i <
ui->
entries->count(); ++i)
726 QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
732 switch(sendCoinsReturn.
status)
735 msgParams.first = tr(
"The recipient address is not valid. Please recheck.");
738 msgParams.first = tr(
"The amount to pay must be larger than 0.");
741 msgParams.first = tr(
"The amount exceeds your balance.");
744 msgParams.first = tr(
"The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
747 msgParams.first = tr(
"Duplicate address found: addresses should only be used once each.");
750 msgParams.first = tr(
"Transaction creation failed!");
762 Q_EMIT
message(tr(
"Send Coins"), msgParams.first, msgParams.second);
798 for (
int i = 0; i <
ui->
entries->count(); ++i) {
800 if (e && !e->isHidden() && e != entry) {
875 QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
882 ui->
labelFeeEstimation->setText(tr(
"Estimated to begin confirmation within %n block(s).",
"", returned_target));
930 if (!checked &&
model) {
948 if (state == Qt::Unchecked)
985 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?"),
986 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
988 if(btnRetVal == QMessageBox::Yes)
1003 if (!associatedLabel.isEmpty())
1026 for(
int i = 0; i <
ui->
entries->count(); ++i)
1029 if(entry && !entry->isHidden())
1057 : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
1059 setIcon(QMessageBox::Question);
1060 setWindowTitle(title);
1062 setInformativeText(informative_text);
1063 setDetailedText(detailed_text);
1064 setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1065 if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
1066 setDefaultButton(QMessageBox::Cancel);
1080 return QMessageBox::exec();
std::shared_ptr< const CTransaction > CTransactionRef
virtual bool privateKeysDisabled()=0
bool signWithExternalSigner(PartiallySignedTransaction &psbt, CMutableTransaction &mtx, bool &complete)
QLabel * labelCoinControlChange
void removeEntry(SendCoinsEntry *entry)
Predefined combinations for certain default usage cases.
QLabel * labelCoinControlAmount
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
OptionsModel * getOptionsModel() const
interfaces::Wallet & wallet() const
void setValue(const SendCoinsRecipient &value)
Utility functions used by the Bitcoin Qt UI.
QLabel * labelFeeEstimation
SynchronizationState
Current sync state passed to tip changed callbacks.
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl &coinControl)
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination corresponds to one with an address.
void presentPSBT(PartiallySignedTransaction &psbt)
void sendButtonClicked(bool checked)
void updateFeeMinimizedLabel()
QLabel * labelCustomFeeWarning
void on_buttonChooseFee_clicked()
SendCoinsRecipient getValue()
bool hasSigner()
Whether -signer was set or not.
QHBoxLayout * horizontalLayoutSmartFee
QWidget * widgetCoinControl
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text...
UnlockContext requestUnlock()
QPushButton * clearButton
void coinControlClipboardQuantity()
void coinControlClipboardAfterFee()
void setAddress(const QString &address)
QFrame * frameFeeSelection
interfaces::WalletBalances getCachedBalance() const
virtual CAmount getDefaultMaxTxFee()=0
Get max tx fee.
QLabel * labelCoinControlFee
QValidatedLineEdit * lineEditCoinControlChange
#define SEND_CONFIRM_DELAY
BitcoinUnit getDisplayUnit() const
std::string EncodeBase64(Span< const unsigned char > input)
void coinControlFeaturesChanged(bool)
QRadioButton * radioCustomFee
QString HtmlEscape(const QString &str, bool fMultiLine)
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
A version of CTransaction with the PSBT format.
virtual bool isLegacy()=0
Return whether is a legacy wallet.
QLabel * labelCoinControlAfterFee
void ShowModalDialogAsynchronously(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
static constexpr std::array confTargets
bool FinalizeAndExtractPSBT(PartiallySignedTransaction &psbtx, CMutableTransaction &result)
Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized...
virtual bool isSpendable(const CTxDestination &dest)=0
Return whether wallet has private key.
QLabel * labelCoinControlQuantity
A single entry in the dialog for sending bitcoins.
bool HasSelected() const
Returns true if there are pre-selected inputs.
QPushButton * pushButtonCoinControl
QLabel * labelCoinControlAutomaticallySelected
static QString formatWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
constexpr auto dialog_flags
void coinControlFeatureChanged(bool)
virtual bool hasExternalSigner()=0
std::unique_ptr< wallet::CCoinControl > m_coin_control
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)
constexpr CAmount DEFAULT_PAY_TX_FEE
-paytxfee default
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://...
QLabel * labelBalanceName
SendCoinsEntry * addEntry()
bool validate(interfaces::Node &node)
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
CAmount watch_only_balance
void displayUnitChanged(BitcoinUnit unit)
void setBalance(const interfaces::WalletBalances &balances)
void setAddress(const QString &address)
void setEnabled(bool fEnabled)
Enable/Disable.
void coinControlClipboardChange()
Collection of wallet balances.
void useAvailableBalance(SendCoinsEntry *entry)
void setClientModel(ClientModel *clientModel)
void setClipboard(const QString &str)
ClientModel * clientModel
void updateCoinControlState()
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
QPushButton * buttonChooseFee
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.
Dialog for sending bitcoins.
QLabel * labelFeeMinimized
QCheckBox * checkBoxCoinControlChange
void coinControlChangeEdited(const QString &)
QString getWalletName() const
void SetMinValue(const CAmount &value)
Set the minimum value in satoshis.
bool getEnablePSBTControls() const
interfaces::Node & node() const
void removeEntry(SendCoinsEntry *entry)
Model for Bitcoin network client.
void setSingleStep(const CAmount &step)
Set single step in satoshis.
void coinControlUpdateLabels()
bool isMultiwallet() const
void setModel(WalletModel *model)
int getConfTargetForIndex(int index)
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=nullptr)
bool getCoinControlFeatures() const
void setupUi(QDialog *SendCoinsDialog)
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)
static CTransactionRef MakeTransactionRef(Tx &&txIn)
QRadioButton * radioSmartFee
void checkSubtractFeeFromAmount()
bool isClear()
Return whether the entry is still empty and unedited.
void minimizeFeeSection(bool fMinimize)
QLabel * labelCoinControlChangeLabel
void updateFeeSectionControls()
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
QWidget * scrollAreaWidgetContents
void subtractFeeFromAmountChanged()
static bool fSubtractFeeFromAmount
void updateSmartFeeLabel()
bool PrepareSendText(QString &question_string, QString &informative_text, QString &detailed_text)
static void updateLabels(wallet::CCoinControl &m_coin_control, WalletModel *, QDialog *)
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 ...
QLabel * labelCoinControlBytes
void setAmount(const CAmount &amount)
void setModel(WalletModel *model)
bool m_allow_other_inputs
If true, the selection process can add extra unselected inputs from the wallet while requires all sel...
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
QComboBox * confTargetSelector
void setEnabled(bool enabled)
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.
AddressTableModel * getAddressTableModel() const
CAmount getAvailableBalance(const wallet::CCoinControl *control)
static QString formatHtmlWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
bool fSubtractFeeFromAmount
QFrame * frameCoinControl
const PlatformStyle * platformStyle
QString formatNiceTimeOffset(qint64 secs)
void coinControlClipboardAmount()
void sendCoins(WalletModelTransaction &transaction)
void on_buttonMinimizeFee_clicked()
QLabel * fallbackFeeWarningLabel
QString m_psbt_button_text
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
void pasteEntry(const SendCoinsRecipient &rv)
void setDisplayUnit(BitcoinUnit new_unit)
Change unit used to display amount.
QAbstractButton * yesButton
BitcoinAmountField * customFee
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)
QLabel * labelCustomPerKilobyte
QAbstractButton * m_psbt_button
bool fNewRecipientAllowed
virtual CAmount getMinimumFee(unsigned int tx_bytes, const wallet::CCoinControl &coin_control, int *returned_target, FeeReason *reason)=0
Get minimum fee.
QPushButton * buttonMinimizeFee
void setValue(const CAmount &value)
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
void setText(const QString &)
void balanceChanged(const interfaces::WalletBalances &balances)
void coinControlButtonClicked()
void coinControlClipboardFee()
QLabel * labelCoinControlInsuffFunds
void SetAllowEmpty(bool allow)
If allow empty is set to false the field will be set to the minimum allowed value if left empty...
void updateNumberOfBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
void coinControlChangeChecked(int)