diff options
author | Cozz Lovan <cozzlovan@yahoo.com> | 2014-07-23 14:34:36 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2015-03-13 11:04:18 +0100 |
commit | 292623adf59edea52b2927b3d67fd2ff7f997882 (patch) | |
tree | afec21b4e7ff1c2b551b060f5f435895d43d15d6 /src/qt | |
parent | 90a43c1e93318584d5443b513c2c0e937acb966a (diff) |
Subtract fee from amount
Fixes #2724 and #1570.
Adds the
automatically-subtract-the-fee-from-the-amount-and-send-whats-left
feature to the GUI and RPC (sendtoaddress,sendmany).
Diffstat (limited to 'src/qt')
-rw-r--r-- | src/qt/coincontroldialog.cpp | 23 | ||||
-rw-r--r-- | src/qt/coincontroldialog.h | 1 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsentry.ui | 16 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 13 | ||||
-rw-r--r-- | src/qt/sendcoinsentry.cpp | 6 | ||||
-rw-r--r-- | src/qt/sendcoinsentry.h | 1 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 22 | ||||
-rw-r--r-- | src/qt/walletmodel.h | 6 | ||||
-rw-r--r-- | src/qt/walletmodeltransaction.cpp | 32 | ||||
-rw-r--r-- | src/qt/walletmodeltransaction.h | 4 |
10 files changed, 105 insertions, 19 deletions
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 3f4f082b8c..5042ff06a2 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -33,6 +33,7 @@ using namespace std; QList<CAmount> CoinControlDialog::payAmounts; CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); +bool CoinControlDialog::fSubtractFeeFromAmount = false; CoinControlDialog::CoinControlDialog(QWidget *parent) : QDialog(parent), @@ -541,6 +542,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority) sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority); + // in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate + if (CoinControlDialog::fSubtractFeeFromAmount) + if (nAmount - nPayAmount == 0) + nBytes -= 34; + // Fee nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool); @@ -556,7 +562,9 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nPayAmount > 0) { - nChange = nAmount - nPayFee - nPayAmount; + nChange = nAmount - nPayAmount; + if (!CoinControlDialog::fSubtractFeeFromAmount) + nChange -= nPayFee; // Never create dust outputs; if we would, just add the dust to the fee. if (nChange > 0 && nChange < CENT) @@ -564,12 +572,17 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0)); if (txout.IsDust(::minRelayTxFee)) { - nPayFee += nChange; - nChange = 0; + if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust + nChange = txout.GetDustThreshold(::minRelayTxFee); + else + { + nPayFee += nChange; + nChange = 0; + } } } - if (nChange == 0) + if (nChange == 0 && !CoinControlDialog::fSubtractFeeFromAmount) nBytes -= 34; } @@ -612,7 +625,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { l3->setText(ASYMP_UTF8 + l3->text()); l4->setText(ASYMP_UTF8 + l4->text()); - if (nChange > 0) + if (nChange > 0 && !CoinControlDialog::fSubtractFeeFromAmount) l8->setText(ASYMP_UTF8 + l8->text()); } diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 5a91876f1f..5ec382838f 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -43,6 +43,7 @@ public: static QList<CAmount> payAmounts; static CCoinControl *coinControl; + static bool fSubtractFeeFromAmount; private: Ui::CoinControlDialog *ui; diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 9f8c0a4844..b362928438 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -157,7 +157,21 @@ </widget> </item> <item row="2" column="1"> - <widget class="BitcoinAmountField" name="payAmount"/> + <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1"> + <item> + <widget class="BitcoinAmountField" name="payAmount"/> + </item> + <item> + <widget class="QCheckBox" name="checkboxSubtractFeeFromAmount"> + <property name="toolTip"> + <string>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</string> + </property> + <property name="text"> + <string>S&ubtract fee from amount</string> + </property> + </widget> + </item> + </layout> </item> <item row="3" column="0"> <widget class="QLabel" name="messageLabel"> diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 4338e58e26..55ca65c8e6 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -221,8 +221,6 @@ void SendCoinsDialog::on_sendButton_clicked() } fNewRecipientAllowed = false; - - WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) { @@ -252,7 +250,7 @@ void SendCoinsDialog::on_sendButton_clicked() // Format confirmation message QStringList formatted; - foreach(const SendCoinsRecipient &rcp, recipients) + foreach(const SendCoinsRecipient &rcp, currentTransaction.getRecipients()) { // generate bold amount string QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); @@ -369,6 +367,7 @@ SendCoinsEntry *SendCoinsDialog::addEntry() ui->entries->addWidget(entry); connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); + connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels())); updateTabsAndLabels(); @@ -784,11 +783,17 @@ void SendCoinsDialog::coinControlUpdateLabels() // set pay amounts CoinControlDialog::payAmounts.clear(); + CoinControlDialog::fSubtractFeeFromAmount = false; for(int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); if(entry) - CoinControlDialog::payAmounts.append(entry->getValue().amount); + { + SendCoinsRecipient rcp = entry->getValue(); + CoinControlDialog::payAmounts.append(rcp.amount); + if (rcp.fSubtractFeeFromAmount) + CoinControlDialog::fSubtractFeeFromAmount = true; + } } if (CoinControlDialog::coinControl->HasSelected()) diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 6db6eee75b..6ac650e74f 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -44,6 +44,7 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) : // Connect signals connect(ui->payAmount, SIGNAL(valueChanged()), this, SIGNAL(payAmountChanged())); + connect(ui->checkboxSubtractFeeFromAmount, SIGNAL(toggled(bool)), this, SIGNAL(subtractFeeFromAmountChanged())); connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); @@ -94,6 +95,7 @@ void SendCoinsEntry::clear() ui->payTo->clear(); ui->addAsLabel->clear(); ui->payAmount->clear(); + ui->checkboxSubtractFeeFromAmount->setCheckState(Qt::Unchecked); ui->messageTextLabel->clear(); ui->messageTextLabel->hide(); ui->messageLabel->hide(); @@ -165,6 +167,7 @@ SendCoinsRecipient SendCoinsEntry::getValue() recipient.label = ui->addAsLabel->text(); recipient.amount = ui->payAmount->value(); recipient.message = ui->messageTextLabel->text(); + recipient.fSubtractFeeFromAmount = (ui->checkboxSubtractFeeFromAmount->checkState() == Qt::Checked); return recipient; } @@ -174,7 +177,8 @@ QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) QWidget::setTabOrder(prev, ui->payTo); QWidget::setTabOrder(ui->payTo, ui->addAsLabel); QWidget *w = ui->payAmount->setupTabChain(ui->addAsLabel); - QWidget::setTabOrder(w, ui->addressBookButton); + QWidget::setTabOrder(w, ui->checkboxSubtractFeeFromAmount); + QWidget::setTabOrder(ui->checkboxSubtractFeeFromAmount, ui->addressBookButton); QWidget::setTabOrder(ui->addressBookButton, ui->pasteButton); QWidget::setTabOrder(ui->pasteButton, ui->deleteButton); return ui->deleteButton; diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 4cb00cd36a..c2d1185bdd 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -51,6 +51,7 @@ public slots: signals: void removeEntry(SendCoinsEntry *entry); void payAmountChanged(); + void subtractFeeFromAmountChanged(); private slots: void deleteClicked(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 79f5191fc0..1baa5eb932 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -6,6 +6,7 @@ #include "addresstablemodel.h" #include "guiconstants.h" +#include "guiutil.h" #include "paymentserver.h" #include "recentrequeststablemodel.h" #include "transactiontablemodel.h" @@ -192,8 +193,9 @@ bool WalletModel::validateAddress(const QString &address) WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { CAmount total = 0; + bool fSubtractFeeFromAmount = false; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); - std::vector<std::pair<CScript, CAmount> > vecSend; + std::vector<CRecipient> vecSend; if(recipients.empty()) { @@ -206,6 +208,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { + if (rcp.fSubtractFeeFromAmount) + fSubtractFeeFromAmount = true; + if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; @@ -217,7 +222,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); - vecSend.push_back(std::pair<CScript, CAmount>(scriptPubKey, out.amount())); + CAmount nAmount = out.amount(); + CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; + vecSend.push_back(recipient); } if (subtotal <= 0) { @@ -239,7 +246,8 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact ++nAddresses; CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); - vecSend.push_back(std::pair<CScript, CAmount>(scriptPubKey, rcp.amount)); + CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; + vecSend.push_back(recipient); total += rcp.amount; } @@ -260,17 +268,21 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); + CAmount nFeeRequired = 0; + int nChangePosRet = -1; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); - bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl); + bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); + if (fSubtractFeeFromAmount && fCreated) + transaction.reassignAmounts(nChangePosRet); if(!fCreated) { - if((total + nFeeRequired) > nBalance) + if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 4a9a12beaa..de915165f9 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -36,9 +36,9 @@ QT_END_NAMESPACE class SendCoinsRecipient { public: - explicit SendCoinsRecipient() : amount(0), nVersion(SendCoinsRecipient::CURRENT_VERSION) { } + explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { } explicit SendCoinsRecipient(const QString &addr, const QString &label, const CAmount& amount, const QString &message): - address(addr), label(label), amount(amount), message(message), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} + address(addr), label(label), amount(amount), message(message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} // If from an unauthenticated payment request, this is used for storing // the addresses, e.g. address-A<br />address-B<br />address-C. @@ -56,6 +56,8 @@ public: // Empty if no authentication or invalid signature/cert/etc. QString authenticatedMerchant; + bool fSubtractFeeFromAmount; // memory only + static const int CURRENT_VERSION = 1; int nVersion; diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 8f32e46148..c97add6bef 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -46,6 +46,38 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee) fee = newFee; } +void WalletModelTransaction::reassignAmounts(int nChangePosRet) +{ + int i = 0; + for (QList<SendCoinsRecipient>::iterator it = recipients.begin(); it != recipients.end(); ++it) + { + SendCoinsRecipient& rcp = (*it); + + if (rcp.paymentRequest.IsInitialized()) + { + CAmount subtotal = 0; + const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); + for (int j = 0; j < details.outputs_size(); j++) + { + const payments::Output& out = details.outputs(j); + if (out.amount() <= 0) continue; + if (i == nChangePosRet) + i++; + subtotal += walletTransaction->vout[i].nValue; + i++; + } + rcp.amount = subtotal; + } + else // normal recipient (no payment request) + { + if (i == nChangePosRet) + i++; + rcp.amount = walletTransaction->vout[i].nValue; + i++; + } + } +} + CAmount WalletModelTransaction::getTotalTransactionAmount() { CAmount totalTransactionAmount = 0; diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index b6bb6d67f6..7765fea4af 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -35,8 +35,10 @@ public: void newPossibleKeyChange(CWallet *wallet); CReserveKey *getPossibleKeyChange(); + void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature + private: - const QList<SendCoinsRecipient> recipients; + QList<SendCoinsRecipient> recipients; CWalletTx *walletTransaction; CReserveKey *keyChange; CAmount fee; |