Fix interest earned calculation to measure realized profit only - #2315
Open
MusabShakeel576 wants to merge 1 commit into
Open
Fix interest earned calculation to measure realized profit only#2315MusabShakeel576 wants to merge 1 commit into
MusabShakeel576 wants to merge 1 commit into
Conversation
…rojection "Interest earned" showed +1.50 at a 14% headline rate; the rate moved to 13.59% and it dropped to +0.75 — a ~2x fall from a ~3% rate change. The counter was a hybrid of a measurement and a forecast, so a rate change re-priced the entire holding period. - calculateYield's interest modes (CURRENT / INTEREST_ONLY) now return realized profit only (balance x rate - deposited). They previously added `balance x APY x (now - firstDeposit)` on top, double-counting the period the realized figure already covered and making a measurement scale with the APY. - When deposit history is unavailable, the interest modes return the new INTEREST_UNAVAILABLE sentinel instead of a bare APY projection. That silent fallback returned roughly half of realized+projection, so the number swung by ~2x whenever the subgraph query flickered. Callers hold the last known value. - The interest modes no longer require a valid APY or start timestamp: a failed APY fetch must not blank out interest already sitting on-chain. - Route interest earned through /savings/summary for every vault, not just soFUSE. It measures against a high-water-mark rate, so the figure can't step backwards on a transient NAV dip. - Tick the live counters at the APY the screen displays as "Current Yield" (maxAPY) rather than the all-time APY, so the headline rate and the rate the numbers move at agree. Per-second smoothing still projects forward only, from the instant the measurement was taken. - Convert soFUSE interest to USD on the redesigned savings card. It was left in FUSE units, rendered with a $ sign and subtracted from a USD total. - Label the month-to-date stat "This month (est.)" — it is an APY projection, not a measurement. Adds unit coverage asserting interest is APY-invariant, survives a failed APY fetch, clamps underwater positions to zero, and reports unavailability instead of guessing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uPvndf13ix7roj8jJcdLX
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes a critical bug in the "Interest earned" counter that caused it to swing by ~2x when vault APY rates changed or deposit history became temporarily unavailable. The counter now correctly reports realized profit only (total value - total deposited) instead of layering an APY projection on top of it.
Key Changes
Redefined interest modes as measurements, not forecasts:
CURRENTandINTEREST_ONLYmodes now return only realized profit from deposit history, eliminating the double-counting of the holding period that occurred when APY projections were added on top.Introduced
INTEREST_UNAVAILABLEsentinel: When deposit history cannot be established (subgraph unavailable, no deposits yet), the interest modes now return-1instead of guessing with an APY projection. This prevents the ~2x swing that occurred when the fallback calculation was triggered.Separated APY projection to per-second tick: The
useSavingsYieldhook now applies APY projections only for elapsed time since the last measurement, keeping the counter smooth without retroactively re-pricing the entire holding period when rates change.Fixed APY source for live counters: Changed from using all-time APY (
apys.allTime) to the current max APY (maxAPY) for ticking the live interest counter, ensuring the displayed rate matches the headline rate advertised on the card.Improved backend summary usage: The
/savings/summaryendpoint is now the preferred source for interest earned on all vaults (not just FUSE), as it measures against a high-water-mark exchange rate and prevents the figure from stepping backwards on transient NAV dips.Updated interest conversion logic: Fixed ETH vault interest conversion to properly denominate in USD (was previously left in ETH while being displayed with a $ sign).
Added comprehensive regression tests: New test suite validates that interest earned is stable across APY changes, handles missing deposit history gracefully, and correctly reports realized profit.
Notable Implementation Details
TOTAL_USD,TOTAL, etc.), but interest modes now signalINTEREST_UNAVAILABLEinstead.calculateYieldin interest modes must check forINTEREST_UNAVAILABLEand preserve the last known value rather than updating to the sentinel.useSavingsYieldnow only projects forward from the anchor measurement time, not from the deposit start time, preventing rate changes from affecting historical periods.https://claude.ai/code/session_011uPvndf13ix7roj8jJcdLX