Revise fee UI

* Change from a block target number to using speed labels which pick predefined fee values.
* Remove smart fee labels from send coins control dialog.
* Rename slider position configuration for Qt, as smart fee slider settings are not compatible with preset fee settings.
This commit is contained in:
Ross Nicoll 2021-09-19 20:14:39 +01:00
parent 661197f502
commit 36e39a395d
No known key found for this signature in database
GPG key ID: E679E30C312B94E0
8 changed files with 156 additions and 63 deletions

View file

@ -18,6 +18,50 @@
#endif
#ifdef ENABLE_WALLET
CFeeRate GetDogecoinFeeRate(int priority)
{
switch(priority)
{
case SUCH_EXPENSIVE:
return CFeeRate(COIN / 100 * 521); // 5.21 DOGE, but very carefully avoiding floating point maths
case MANY_GENEROUS:
return CFeeRate(CWallet::minTxFee.GetFeePerK() * 100);
case AMAZE:
return CFeeRate(CWallet::minTxFee.GetFeePerK() * 10);
case WOW:
return CFeeRate(CWallet::minTxFee.GetFeePerK() * 5);
case MORE:
return CFeeRate(CWallet::minTxFee.GetFeePerK() * 2);
case MINIMUM:
default:
break;
}
return CWallet::minTxFee;
}
const std::string GetDogecoinPriorityLabel(int priority)
{
switch(priority)
{
case SUCH_EXPENSIVE:
return _("Such expensive");
case MANY_GENEROUS:
return _("Many generous");
case AMAZE:
return _("Amaze");
case WOW:
return _("Wow");
case MORE:
return _("More");
case MINIMUM:
return _("Minimum");
default:
break;
}
return _("Default");
}
//mlumin 5/2021: walletfees, all attached to GetDogecoinWalletFeeRate which is just the newly exposed ::minWalletTxFee
CAmount GetDogecoinWalletFee(size_t nBytes_)
{
@ -31,7 +75,7 @@ CAmount GetDogecoinWalletFee(size_t nBytes_)
CFeeRate GetDogecoinWalletFeeRate()
{
//mlumin 5/2021: currently 1x COIN or 1 dogecoin or 100,000,000 koinu
return ::minWalletTxFeeRate;
return CWallet::minTxFee;
}
#endif

View file

@ -2,13 +2,32 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_DOGECOIN_FEES_H
#define BITCOIN_DOGECOIN_FEES_H
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#ifdef ENABLE_WALLET
enum FeeRatePreset
{
MINIMUM,
MORE,
WOW,
AMAZE,
MANY_GENEROUS,
SUCH_EXPENSIVE
};
/** Estimate fee rate needed to get into the next nBlocks */
CFeeRate GetDogecoinFeeRate(int priority);
const std::string GetDogecoinPriorityLabel(int priority);
CFeeRate GetDogecoinWalletFeeRate();
CAmount GetDogecoinMinWalletFee(unsigned int nBytes_);
#endif
#endif // ENABLE_WALLET
CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate);
#endif // BITCOIN_DOGECOIN_FEES_H

View file

@ -1005,7 +1005,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFee12">
<item>
<widget class="QLabel" name="labelSmartFee">
<widget class="QLabel" name="labelPriority">
<property name="text">
<string/>
</property>
@ -1020,17 +1020,8 @@
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelSmartFee2">
<property name="text">
<string>(Smart fee not initialized yet. This usually takes a few blocks...)</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
</item>
<!-- Dogecoin does not use smart fees, so we do not have the smart fee warning here -->
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
@ -1051,9 +1042,9 @@
<item>
<layout class="QVBoxLayout" name="verticalLayoutFee6">
<item>
<widget class="QLabel" name="labelSmartFee3">
<widget class="QLabel" name="labelPriority3">
<property name="text">
<string>Confirmation time target:</string>
<string>Priority:</string>
</property>
<property name="margin">
<number>2</number>
@ -1086,7 +1077,7 @@
<number>0</number>
</property>
<property name="maximum">
<number>23</number>
<number>5</number>
</property>
<property name="pageStep">
<number>1</number>
@ -1111,9 +1102,9 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFee10">
<item>
<widget class="QLabel" name="labelSmartFeeNormal">
<widget class="QLabel" name="labelPriorityLow">
<property name="text">
<string>normal</string>
<string>low</string>
</property>
</widget>
</item>
@ -1151,9 +1142,9 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="labelSmartFeeFast">
<widget class="QLabel" name="labelPriorityHigh">
<property name="text">
<string>fast</string>
<string>high</string>
</property>
</widget>
</item>

View file

@ -17,6 +17,7 @@
#include "base58.h"
#include "chainparams.h"
#include "dogecoin-fees.h"
#include "wallet/coincontrol.h"
#include "validation.h" // mempool and minRelayTxFeeRate
#include "ui_interface.h"
@ -100,8 +101,8 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
settings.setValue("nCustomFeeRadio", 1); // total at least
if (!settings.contains("nCustomFeeRadio"))
settings.setValue("nCustomFeeRadio", 0); // per kilobyte
if (!settings.contains("nSmartFeeSliderPosition"))
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nPresetFeeSliderPosition"))
settings.setValue("nPresetFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
if (!settings.contains("fPayOnlyMinFee"))
@ -122,7 +123,7 @@ void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
this->clientModel = _clientModel;
if (_clientModel) {
connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateFeeLabel()));
}
}
@ -154,7 +155,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
coinControlUpdateLabels();
// fee section
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateFeeLabel()));
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
@ -171,15 +172,12 @@ void SendCoinsDialog::setModel(WalletModel *_model)
ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
updateFeeSectionControls();
updateMinFeeLabel();
updateSmartFeeLabel();
updateFeeLabel();
updateGlobalFeeVariables();
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2);
else
ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
ui->sliderSmartFee->setValue(settings.value("nPresetFeeSliderPosition").toInt());
}
}
@ -189,7 +187,7 @@ SendCoinsDialog::~SendCoinsDialog()
settings.setValue("fFeeSectionMinimized", fFeeMinimized);
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
settings.setValue("nPresetFeeSliderPosition", ui->sliderSmartFee->value());
settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
@ -243,9 +241,9 @@ void SendCoinsDialog::on_sendButton_clicked()
if (model->getOptionsModel()->getCoinControlFeatures())
ctrl = *CoinControlDialog::coinControl;
if (ui->radioSmartFee->isChecked())
ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
ctrl.nPriority = static_cast<FeeRatePreset>(ui->sliderSmartFee->value());
else
ctrl.nConfirmTarget = 0;
ctrl.nPriority = MINIMUM;
prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
@ -502,7 +500,7 @@ void SendCoinsDialog::updateDisplayUnit()
setBalance(model->getBalance(), 0, 0, 0, 0, 0);
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
updateMinFeeLabel();
updateSmartFeeLabel();
updateFeeLabel();
}
void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
@ -585,13 +583,13 @@ void SendCoinsDialog::setMinimumFee()
void SendCoinsDialog::updateFeeSectionControls()
{
ui->sliderSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelPriority ->setEnabled(ui->radioSmartFee->isChecked());
// Dogecoin: We don't use smart fees in the UI, so don't need to warn they're not available
// ui->labelPriority2 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelPriority3 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
//mlumin: 5/2021 - this label actually gates the 'slider and smart fee' functionality, so turn it off for dogecoin.
ui->labelPriorityLow ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelPriorityHigh ->setEnabled(ui->radioSmartFee->isChecked());
ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked());
ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
@ -604,7 +602,7 @@ void SendCoinsDialog::updateGlobalFeeVariables()
{
if (ui->radioSmartFee->isChecked())
{
int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int nPriority = ui->sliderSmartFee->value();
payTxFee = CFeeRate(0);
// set nMinimumTotalFee to 0 to not accidentally pay a custom fee
@ -612,7 +610,7 @@ void SendCoinsDialog::updateGlobalFeeVariables()
// show the estimated required time for confirmation
// Dogecoin: We manually set height well past the last hard fork here
ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget * Params().GetConsensus(400000).nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget));
ui->confirmationTargetLabel->setText(GetDogecoinPriorityLabel(nPriority).c_str());
}
else
{
@ -630,7 +628,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel()
return;
if (ui->radioSmartFee->isChecked())
ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
ui->labelFeeMinimized->setText(ui->labelPriority->text());
else {
ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
@ -645,19 +643,18 @@ void SendCoinsDialog::updateMinFeeLabel()
);
}
void SendCoinsDialog::updateSmartFeeLabel()
void SendCoinsDialog::updateFeeLabel()
{
if(!model || !model->getOptionsModel())
return;
int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
int estimateFoundAtBlocks = nBlocksToConfirm;
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
int nPriority = ui->sliderSmartFee->value();
CFeeRate feeRate = GetDogecoinFeeRate(nPriority);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
ui->labelPriority->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
// ui->labelPriority2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
ui->labelFeeEstimation->setText("");
ui->fallbackFeeWarningLabel->setVisible(true);
int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
@ -667,10 +664,12 @@ void SendCoinsDialog::updateSmartFeeLabel()
}
else
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
ui->labelPriority->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
ui->labelSmartFee2->hide();
ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
// ui->labelPriority2->hide();
// Dogecoin: We don't use smart fees, so we don't have the data to estimate when it will get in
ui->labelFeeEstimation->setText("");
// ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
ui->fallbackFeeWarningLabel->setVisible(false);
}

View file

@ -91,7 +91,7 @@ private Q_SLOTS:
void setMinimumFee();
void updateFeeSectionControls();
void updateMinFeeLabel();
void updateSmartFeeLabel();
void updateFeeLabel();
void updateGlobalFeeVariables();
Q_SIGNALS:

View file

@ -6,6 +6,7 @@
#define BITCOIN_WALLET_COINCONTROL_H
#include "primitives/transaction.h"
#include "dogecoin-fees.h"
/** Coin Control Features. */
class CCoinControl
@ -22,8 +23,8 @@ public:
bool fOverrideFeeRate;
//! Feerate to use if overrideFeeRate is true
CFeeRate nFeeRate;
//! Override the default confirmation target, 0 = use default
int nConfirmTarget;
//! Override the default transaction speed, 0 = use default
FeeRatePreset nPriority;
CCoinControl()
{
@ -39,7 +40,7 @@ public:
nMinimumTotalFee = 0;
nFeeRate = CFeeRate(0);
fOverrideFeeRate = false;
nConfirmTarget = 0;
nPriority = MINIMUM;
}
bool HasSelected() const

View file

@ -39,8 +39,6 @@ using namespace std;
CWallet* pwalletMain = NULL;
/** Transaction fee set by the user */
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
//mlumin 5/2021: Add minimum wallet tx fee. This really should be expressed as a rate not a final fee.
CFeeRate minWalletTxFeeRate = CFeeRate(DEFAULT_MIN_WALLET_TX_FEE);
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
@ -2661,8 +2659,11 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Allow to override the default confirmation target over the CoinControl instance
int currentConfirmationTarget = nTxConfirmTarget;
if (coinControl && coinControl->nConfirmTarget > 0)
currentConfirmationTarget = coinControl->nConfirmTarget;
FeeRatePreset nPriority;
if (coinControl && coinControl->nPriority > 0)
nPriority = coinControl->nPriority;
else
nPriority = MINIMUM;
// Can we complete this as a free transaction?
if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
@ -2674,7 +2675,13 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
break;
}
CAmount nFeeNeeded = GetMinimumFee(txNew, nBytes, currentConfirmationTarget, mempool);
CAmount nFeeNeeded;
if (nPriority == MINIMUM) {
nFeeNeeded = GetMinimumFee(txNew, nBytes, currentConfirmationTarget, mempool);
} else {
// Force the fee rate higher
nFeeNeeded = GetDogecoinPriorityFee(txNew, nBytes, nPriority);
}
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
@ -2887,6 +2894,31 @@ CAmount CWallet::GetMinimumFee(const CMutableTransaction& tx, unsigned int nTxBy
}
CAmount CWallet::GetDogecoinPriorityFee(const CMutableTransaction& tx, unsigned int nTxBytes, FeeRatePreset nPriority)
{
// payTxFee is the user-set global for desired feerate
return GetDogecoinPriorityFee(tx, nTxBytes, nPriority, payTxFee.GetFee(nTxBytes));
}
CAmount CWallet::GetDogecoinPriorityFee(const CMutableTransaction& tx, unsigned int nTxBytes, FeeRatePreset nPriority, CAmount targetFee)
{
CAmount nFeeNeeded = targetFee;
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0) {
nFeeNeeded = GetDogecoinFeeRate(nPriority).GetFee(nTxBytes);
}
// prevent user from paying a fee below minRelayTxFee or minTxFee
// Dogecoin: as we're adapting minTxFee to never be higher than
// payTxFee unless explicitly set, this should be fine
nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(tx, nTxBytes));
// But always obey the maximum
if (nFeeNeeded > maxTxFee)
nFeeNeeded = maxTxFee;
return nFeeNeeded;
}
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
if (!fFileBacked)

View file

@ -8,6 +8,7 @@
#include "amount.h"
#include "auxpow.h"
#include "dogecoin-fees.h"
#include "streams.h"
#include "tinyformat.h"
#include "ui_interface.h"
@ -54,8 +55,6 @@ static const CAmount DEFAULT_TRANSACTION_FEE = COIN / 100;
static const CAmount DEFAULT_FALLBACK_FEE = COIN / 100;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = COIN / 100;
//mlumin 5/2021: adding a minimum Wallet fee vs relay, currently still 1 COIN, to be reduced.
static const CAmount DEFAULT_MIN_WALLET_TX_FEE = COIN / 100;
//! minimum recommended increment for BIP 125 replacement txs
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = COIN/10 * 5;
//! target minimum change amount
@ -761,6 +760,14 @@ public:
* then fee estimation for nConfirmTarget
*/
static CAmount GetMinimumFee(const CMutableTransaction& tx, unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee);
/**
* Dogecoin: Get a fee targetting a specific transaction speed.
*/
CAmount GetDogecoinPriorityFee(const CMutableTransaction& tx, unsigned int nTxBytes, FeeRatePreset nSpeed);
/**
* Dogecoin: Get a fee targetting a specific transaction speed.
*/
static CAmount GetDogecoinPriorityFee(const CMutableTransaction& tx, unsigned int nTxBytes, FeeRatePreset nSpeed, CAmount targetFee);
/**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee