diff --git a/src/gridcoin/accrual/computer.h b/src/gridcoin/accrual/computer.h index 4ea2d810a7..96818a2a6c 100644 --- a/src/gridcoin/accrual/computer.h +++ b/src/gridcoin/accrual/computer.h @@ -71,6 +71,12 @@ class IAccrualComputer //! virtual CAmount PaymentPerDayLimit() const = 0; + //! + //! \brief Return an accrual value that is nearing the limit based on accrual rate. + //! \return Value of near limit in CAmount units. + //! + virtual CAmount NearRewardLimit() const = 0; + //! //! \brief Determine whether the account exceeded the daily payment limit. //! diff --git a/src/gridcoin/accrual/newbie.h b/src/gridcoin/accrual/newbie.h index bfe1646d53..dd46733228 100644 --- a/src/gridcoin/accrual/newbie.h +++ b/src/gridcoin/accrual/newbie.h @@ -84,6 +84,16 @@ class NewbieAccrualComputer : public IAccrualComputer return MaxReward(); } + CAmount NearRewardLimit() const override + { + // This returns MaxReward() - 2 * ExpectedDaily() or 1/2 of MaxReward(), whichever + // is greater + + CAmount threshold = std::max(MaxReward() / 2, MaxReward() - 2 * ExpectedDaily()); + + return threshold; + } + bool ExceededRecentPayments() const override { return RawAccrual() > PaymentPerDayLimit(); diff --git a/src/gridcoin/accrual/null.h b/src/gridcoin/accrual/null.h index 58eaa91540..960cec45a9 100644 --- a/src/gridcoin/accrual/null.h +++ b/src/gridcoin/accrual/null.h @@ -57,6 +57,11 @@ class NullAccrualComputer : public IAccrualComputer return 0; } + CAmount NearRewardLimit() const override + { + return 0; + } + bool ExceededRecentPayments() const override { return false; diff --git a/src/gridcoin/accrual/research_age.h b/src/gridcoin/accrual/research_age.h index 9fc7c84916..8018db0fb1 100644 --- a/src/gridcoin/accrual/research_age.h +++ b/src/gridcoin/accrual/research_age.h @@ -165,6 +165,16 @@ class ResearchAgeComputer : public IAccrualComputer return m_account.AverageLifetimeMagnitude() * m_magnitude_unit * COIN * 5; } + CAmount NearRewardLimit() const override + { + // This returns MaxReward() - 2 * ExpectedDaily() or 1/2 of MaxReward(), whichever + // is greater + + CAmount threshold = std::max(MaxReward() / 2, MaxReward() - 2 * ExpectedDaily()); + + return threshold; + } + //! //! \brief Determine whether the account exceeded the daily payment limit. //! diff --git a/src/gridcoin/accrual/snapshot.h b/src/gridcoin/accrual/snapshot.h index 5b7ad3fe4a..1bd009e811 100644 --- a/src/gridcoin/accrual/snapshot.h +++ b/src/gridcoin/accrual/snapshot.h @@ -361,6 +361,16 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator return MaxReward(); } + CAmount NearRewardLimit() const override + { + // This returns MaxReward() - 2 * ExpectedDaily() or 1/2 of MaxReward(), whichever + // is greater + + CAmount threshold = std::max(MaxReward() / 2, MaxReward() - 2 * ExpectedDaily()); + + return threshold; + } + bool ExceededRecentPayments() const override { return RawAccrual() > PaymentPerDayLimit(); diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index 7537b183dc..c4b57ad034 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -1329,6 +1329,21 @@ CAmount Researcher::Accrual() const return Tally::GetAccrual(*cpid, now, pindexBest); } +CAmount Researcher::AccrualNearLimit() const +{ + const CpidOption cpid = m_mining_id.TryCpid(); + + if (!cpid || !pindexBest) { + return false; + } + + const int64_t now = OutOfSyncByAge() ? pindexBest->nTime : GetAdjustedTime(); + + LOCK(cs_main); + + return Tally::AccrualNearLimit(*cpid, now, pindexBest); +} + ResearcherStatus Researcher::Status() const { if (Eligible()) { diff --git a/src/gridcoin/researcher.h b/src/gridcoin/researcher.h index 3ad03d197d..325f4f94fc 100644 --- a/src/gridcoin/researcher.h +++ b/src/gridcoin/researcher.h @@ -550,6 +550,12 @@ class Researcher //! CAmount Accrual() const; + //! + //! \brief Value of account accrual that is near MaxReward() based on accrual rate.. + //! \return CAmount value. This is implemented in IAccrualComputer::NearRewardLimit(). + //! + CAmount AccrualNearLimit() const; + //! //! \brief Get a value that indicates how the wallet participates in the //! research reward protocol. diff --git a/src/gridcoin/tally.cpp b/src/gridcoin/tally.cpp index 46e965dc09..73929b9bc5 100644 --- a/src/gridcoin/tally.cpp +++ b/src/gridcoin/tally.cpp @@ -1090,6 +1090,14 @@ CAmount Tally::GetAccrual( return GetComputer(cpid, payment_time, last_block_ptr)->Accrual(); } +CAmount Tally::AccrualNearLimit( + const Cpid cpid, + const int64_t payment_time, + const CBlockIndex* const last_block_ptr) +{ + return (GetComputer(cpid, payment_time, last_block_ptr)->NearRewardLimit()) ; +} + //! //! \brief Compute "catch-up" accrual to correct for newbie accrual bug. //! diff --git a/src/gridcoin/tally.h b/src/gridcoin/tally.h index 6fa4f8228c..ed4b6c6b0a 100644 --- a/src/gridcoin/tally.h +++ b/src/gridcoin/tally.h @@ -133,6 +133,22 @@ class Tally const int64_t payment_time, const CBlockIndex* const last_block_ptr); + //! + //! \brief A value of the accrual that is near the MaxReward for the accrual computer in context based on + //! the rate of accrual. This is defined in the implementation of the virtual method NearRewardLimit() + //! in IAccrualComputer. + //! + //! \param cpid CPID to calculate research accrual for. + //! \param payment_time Time of payment to calculate rewards at. + //! \param last_block_ptr Refers to the block for the reward. + //! + //! \return CAmount of account accrual that is near the MaxReward. + //! + static CAmount AccrualNearLimit( + const Cpid cpid, + const int64_t payment_time, + const CBlockIndex* const last_block_ptr); + //! //! \brief Compute "catch-up" accrual to correct for newbie accrual bug. //! diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 7827a69310..6e925557b6 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -7,7 +7,7 @@ 0 0 948 - 635 + 755 @@ -788,10 +788,10 @@ 12 - - + + - Status: + Magnitude: true @@ -817,18 +817,24 @@ - - + + + + + 0 + 0 + + - Magnitude: + - - true + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - + + 0 @@ -843,6 +849,16 @@ + + + + Status: + + + true + + + @@ -853,19 +869,25 @@ - - - - - 0 - 0 - + + + + + 16777215 + 16777215 + + + + You are approaching the accrual limit of 16384 GRC. If you have a relatively low balance, you should request payment via MRC so that you do not lose earned rewards. - + - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + :/icons/warning + + + true @@ -1108,6 +1130,8 @@ 1 - + + + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index cadc72d084..0a0ac45872 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -153,6 +153,10 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->stakingGridLayout->setVerticalSpacing(verticalSpacing); ui->researcherGridLayout->setVerticalSpacing(verticalSpacing); + // scale warning icon + int warning_icon_size = GRC::ScalePx(this, 21); + ui->accrualLimitWarningIconlabel->setMaximumSize(QSize(warning_icon_size, warning_icon_size)); + // Recent Transactions ui->listTransactions->setItemDelegate(txdelegate); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); @@ -285,6 +289,7 @@ void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBa bool showImmature = immatureBalance != 0; ui->immatureLabel->setVisible(showImmature); ui->immatureTextLabel->setVisible(showImmature); + } void OverviewPage::setHeight(int height, int height_of_peers, bool in_sync) @@ -501,7 +506,14 @@ void OverviewPage::updatePendingAccrual() unit = walletModel->getOptionsModel()->getDisplayUnit(); } - ui->accrualLabel->setText(researcherModel->formatAccrual(unit)); + bool near_limit = false; + + ui->accrualLabel->setText(researcherModel->formatAccrual(unit, near_limit)); + if (near_limit) { + ui->accrualLimitWarningIconlabel->show(); + } else { + ui->accrualLimitWarningIconlabel->hide(); + } } void OverviewPage::updateResearcherAlert() diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 9074e3e316..032892c9c1 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -317,6 +317,11 @@ bool ResearcherModel::needsBeaconAuth() const return m_beacon->m_public_key != m_pending_beacon->m_public_key; } +CAmount ResearcherModel::accrualNearLimit() const +{ + return m_researcher->AccrualNearLimit(); +} + CAmount ResearcherModel::getAccrual() const { return m_researcher->Accrual(); @@ -353,16 +358,23 @@ QString ResearcherModel::formatMagnitude() const return text; } -QString ResearcherModel::formatAccrual(const int display_unit) const +QString ResearcherModel::formatAccrual(const int display_unit, bool& near_limit) const { QString text; + // We only do the actual accrual calculation once. The AccrualNearLimit() is lighter. + CAmount accrual = m_researcher->Accrual(); + + near_limit = (accrual >= m_researcher->AccrualNearLimit()); + if (outOfSync()) { text = "..."; } else { - text = BitcoinUnits::formatWithPrivacy(display_unit, m_researcher->Accrual(), m_privacy_enabled); + text = BitcoinUnits::formatWithPrivacy(display_unit, accrual, m_privacy_enabled); } + + return text; } diff --git a/src/qt/researcher/researchermodel.h b/src/qt/researcher/researchermodel.h index 480fac6ee2..9e3ac12ea4 100644 --- a/src/qt/researcher/researchermodel.h +++ b/src/qt/researcher/researchermodel.h @@ -101,12 +101,13 @@ class ResearcherModel : public QObject bool hasSplitCpid() const; bool needsBeaconAuth() const; + CAmount accrualNearLimit() const; CAmount getAccrual() const; QString email() const; QString formatCpid() const; QString formatMagnitude() const; - QString formatAccrual(const int display_unit) const; + QString formatAccrual(const int display_unit, bool& near_limit) const; QString formatStatus() const; QString formatBoincPath() const; diff --git a/src/qt/researcher/researcherwizardsummarypage.cpp b/src/qt/researcher/researcherwizardsummarypage.cpp index 8d99ac4dac..6e5bd24ab6 100644 --- a/src/qt/researcher/researcherwizardsummarypage.cpp +++ b/src/qt/researcher/researcherwizardsummarypage.cpp @@ -94,7 +94,9 @@ void ResearcherWizardSummaryPage::refreshSummary() ui->cpidLabel->setText(m_researcher_model->formatCpid()); ui->statusLabel->setText(m_researcher_model->formatStatus()); ui->magnitudeLabel->setText(m_researcher_model->formatMagnitude()); - ui->accrualLabel->setText(m_researcher_model->formatAccrual(unit)); + + bool near_limit = false; + ui->accrualLabel->setText(m_researcher_model->formatAccrual(unit, near_limit)); ui->reviewBeaconAuthButton->setVisible(m_researcher_model->needsBeaconAuth()); ui->beaconDetailsIconLabel->setPixmap(