You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[LOW] Points are monotonically increasing with no decay or penalty — the leaderboard is a permanent, stale ranking #24
[LOW] Points are monotonically increasing with no decay or penalty — the leaderboard is a permanent, stale ranking
Summary
Points in the leaderboard only ever increase (s.points += points in reward, add_pts, reward_bonus, add_bonus_pts). There is:
No point decay over time.
No penalty for losses (losers still gain LOSE_POINTS).
No way to reduce points.
This means the leaderboard is a monotonic, permanent ranking — early adopters who accumulated points long ago can never be overtaken by newer, more active players unless the newer players out-earn them in absolute terms forever. The ranking does not reflect current skill or activity, only cumulative historical points.
Why it's unsolvable by a localized patch
Adding decay requires a time-based recompute on every read/write — a storage and gas change.
Adding penalties requires tracking loss streaks or time windows — new state.
The root problem is the points model is a cumulative counter, not a skill rating. A correct fix requires a decayed or Elo-style rating system — a fundamental redesign of the leaderboard semantics.
I’d like to take on this issue because I understand that the underlying problem is not simply that points need to be decremented. The current points model is fundamentally cumulative, which makes the leaderboard represent historical activity rather than a player’s current performance.
My approach would therefore be to redesign the ranking semantics around a rating model that can change in both directions, while preserving the integrity of the existing leaderboard and reward flows.
Proposed approach
I would first trace the complete lifecycle of the current points system, particularly:
"s.points += points"
"reward"
"add_pts"
"reward_bonus"
"add_bonus_pts"
"LOSE_POINTS"
the existing "upsert_top" logic and its min-cache invariant
all leaderboard read/write paths and any assumptions that points are monotonically increasing.
From there, I would introduce a rating model rather than attempting to patch the existing cumulative counter.
The exact model should be selected based on the game's existing mechanics and available player/match data. If the existing data supports meaningful head-to-head outcomes, an ELO-style rating would be appropriate. If the system's existing events are better represented by activity over time, a time-decayed rating can be used instead.
The important property is that a player's effective rating must be allowed to increase or decrease, while the leaderboard calculation must always be derived from the current rating rather than historical cumulative points.
Leaderboard/cache redesign
I would also address the issue's "upsert_top" constraint rather than bypassing it.
Because the current implementation assumes scores only increase, allowing ratings to decrease means the existing min-cache approach can no longer be treated as authoritative.
I would therefore redesign the leaderboard update/recompute path so that:
A player's rating can move in either direction.
A rating decrease cannot leave stale entries in the top-ranking cache.
Rankings are recomputed or invalidated when an update can affect the cached top set.
Ranking order remains deterministic for equal ratings.
Existing state is migrated safely without silently converting historical points into an invalid rating.
Migration and backwards compatibility
I would avoid destructively changing the existing points field without understanding every consumer of it.
Where necessary, I would separate the concepts of:
historical/cumulative points
current competitive rating
leaderboard rank
This allows historical rewards/activity to remain meaningful while the leaderboard reflects current performance.
For existing users, I would define and test a deterministic migration/initial-rating strategy rather than arbitrarily assigning new ratings.
Testing
I would add tests covering the actual failure mode described in the issue, including:
a player gaining rating;
a player losing rating;
repeated losses;
repeated wins;
inactive players and time-based decay, if decay is selected;
players overtaking previously active players;
equal ratings/tie handling;
leaderboard updates when a player falls out of the cached top set;
leaderboard updates when a previously lower-ranked player rises into the top set;
stale-cache prevention;
migration of existing player data;
preservation of unrelated reward functionality.
I would also verify that the new implementation does not accidentally reintroduce the monotonic-score assumption elsewhere in the contract/application.
Delivery
I would work incrementally: first understanding and documenting the existing ranking flow, then implementing the rating model, then replacing the incompatible cache assumptions, followed by migration and comprehensive tests.
I would keep the changes focused on the issue and avoid unrelated refactors so the resulting PR remains reviewable.
The main goal is not simply to make points decrease. It is to make the leaderboard actually represent current competitive performance, which is the root problem identified in this issue.
Not yet done: wiring that into prediction_market's actual loss-settlement path (LOSE_POINTS), so losers still gain positive points from prediction_market today. Blocked on prediction_market itself, which currently does not compile on main — I found three separate, unrelated defects while investigating (an unclosed get_governor_count body, four undefined-variable errors near a config_changed event at line 476, and a get_ttl call that doesn't exist on this SDK's Persistent storage type). That's independent of this issue and too large/risky to fold into fix(leaderboard+prediction_market): repair builds, add penalize() + CI (#24, blocked on referral_registry) #185.
Leaving this issue open rather than closing it via #185 — closing it should wait until prediction_market is repaired and actually wired to penalize(). Happy to pick up the prediction_market build repair as a separate PR if that's useful; the tokenomics decision (does the existing 2-token consolation prize for losing stay as-is?) is a maintainer call either way.
Activity
Dayz-tech-co commentedon Aug 16, 2026
Application for SPulse Issue #24
I’d like to take on this issue because I understand that the underlying problem is not simply that points need to be decremented. The current points model is fundamentally cumulative, which makes the leaderboard represent historical activity rather than a player’s current performance.
My approach would therefore be to redesign the ranking semantics around a rating model that can change in both directions, while preserving the integrity of the existing leaderboard and reward flows.
Proposed approach
I would first trace the complete lifecycle of the current points system, particularly:
From there, I would introduce a rating model rather than attempting to patch the existing cumulative counter.
The exact model should be selected based on the game's existing mechanics and available player/match data. If the existing data supports meaningful head-to-head outcomes, an ELO-style rating would be appropriate. If the system's existing events are better represented by activity over time, a time-decayed rating can be used instead.
The important property is that a player's effective rating must be allowed to increase or decrease, while the leaderboard calculation must always be derived from the current rating rather than historical cumulative points.
Leaderboard/cache redesign
I would also address the issue's "upsert_top" constraint rather than bypassing it.
Because the current implementation assumes scores only increase, allowing ratings to decrease means the existing min-cache approach can no longer be treated as authoritative.
I would therefore redesign the leaderboard update/recompute path so that:
Migration and backwards compatibility
I would avoid destructively changing the existing points field without understanding every consumer of it.
Where necessary, I would separate the concepts of:
This allows historical rewards/activity to remain meaningful while the leaderboard reflects current performance.
For existing users, I would define and test a deterministic migration/initial-rating strategy rather than arbitrarily assigning new ratings.
Testing
I would add tests covering the actual failure mode described in the issue, including:
I would also verify that the new implementation does not accidentally reintroduce the monotonic-score assumption elsewhere in the contract/application.
Delivery
I would work incrementally: first understanding and documenting the existing ranking flow, then implementing the rating model, then replacing the incompatible cache assumptions, followed by migration and comprehensive tests.
I would keep the changes focused on the issue and avoid unrelated refactors so the resulting PR remains reviewable.
The main goal is not simply to make points decrease. It is to make the leaderboard actually represent current competitive performance, which is the root problem identified in this issue.
grantfox-oss commentedon Aug 16, 2026
🦊 GrantFox — @Dayz-tech-co has been assigned to this issue as part of the Third Campaign campaign!
Next steps:
Closes #24)Good luck! Track your progress on GrantFox.
feat(leaderboard): add penalize() — a real way to reduce a player's p…
Dayz-tech-co commentedon Aug 24, 2026
Status update from PR #185:
main. Seeleaderboard/src/decay_tests.rs, e.g.test_idle_leader_is_overtaken_by_a_newer_player_who_earned_far_less.penalize(caller, user, pts)on the leaderboard contract. Market-gated, decay-aware, saturates at zero, 14 tests, safe against the top-list invariants (can't be used to insert an unranked player or evict a ranked one on their behalf).prediction_market's actual loss-settlement path (LOSE_POINTS), so losers still gain positive points fromprediction_markettoday. Blocked onprediction_marketitself, which currently does not compile onmain— I found three separate, unrelated defects while investigating (an unclosedget_governor_countbody, four undefined-variable errors near aconfig_changedevent at line 476, and aget_ttlcall that doesn't exist on this SDK'sPersistentstorage type). That's independent of this issue and too large/risky to fold into fix(leaderboard+prediction_market): repair builds, add penalize() + CI (#24, blocked on referral_registry) #185.Leaving this issue open rather than closing it via #185 — closing it should wait until
prediction_marketis repaired and actually wired topenalize(). Happy to pick up theprediction_marketbuild repair as a separate PR if that's useful; the tokenomics decision (does the existing 2-token consolation prize for losing stay as-is?) is a maintainer call either way.fix(referral_registry): repair build/test defects blocking issue SPul…
feat(prediction_market): wire penalize() into the loss-settlement pat…
Merge branch 'main' into fix/issue-24-leaderboard-merge-corruption
Merge upstream/main: resolve conflicts from set_config Config-struct …
fix(leaderboard): repair merge corruption blocking the crate build
grantfox-oss commentedon Aug 31, 2026
🎉 This issue has been marked as completed on GrantFox!
@Dayz-tech-co's PR #185 was approved and merged by @Muyideen-js.
🏆 @Dayz-tech-co: You earned 35 FoxPoints for this contribution! Your current tier: Explorer (357 total points). Track your full progress on GrantFox.
👏 Great work, @Dayz-tech-co! Keep contributing to SPulse-Org.
Merge pull request #185 from Dayz-tech-co/fix/issue-24-leaderboard-me…