Problem
SpellBlocLeaderboard.updatePlayerStats() can revert for ordinary early-session data and becomes increasingly expensive as leaderboards fill. A child whose first update occurs before 60 seconds cannot be ranked, and each update repeatedly scans and bubble-sorts multiple storage arrays.
Evidence
The speed score is calculated as:
stats.totalPlayTime > 0
? (stats.wordsLearned * 60) / (stats.totalPlayTime / 60)
: 0;
For 1 <= totalPlayTime < 60, integer division makes the denominator zero and the transaction reverts. A stats update then calls _updateLeaderboard() for five categories plus the age leaderboard. Each call performs a linear search and an O(n²) storage bubble sort (up to 100 entries), making gas cost and liveness dependent on leaderboard size.
There are related consistency gaps: trimmed players retain a non-zero playerRanks value, inactive players remain in arrays, and lastGlobalUpdate is initialized but not updated on leaderboard changes.
Proposed implementation
- Fix speed units with multiplication-before-division using a clearly documented metric, e.g. words per minute based on seconds, and define zero-duration behavior.
- Avoid on-chain full sorting. Store canonical player stats/scores and compute pages/ranks off-chain from events, or use a bounded top-N structure with predictable insertion cost.
- Make rank semantics explicit for players outside top N.
- Cleanly handle inactive/removed players and stale entries.
- Validate monotonic/cumulative stats so updates cannot accidentally reduce totals or create impossible accuracy.
- Add gas budgets and invariant tests at maximum expected population.
Acceptance criteria
- updates at 0, 1, 59, 60, and large session durations do not divide by zero and produce documented scores.
- update gas stays below an agreed ceiling independent of historical player count (or within a proven bound for bounded top N).
- a trimmed/inactive player never reports a stale rank.
- events contain enough information for a deterministic off-chain leaderboard/indexer.
- fuzz tests cover score arithmetic without overflow/revert for valid inputs.
- tests cover ties, repeat updates, top-N entry/exit, inactivity, and the maximum configured board size.
Problem
SpellBlocLeaderboard.updatePlayerStats()can revert for ordinary early-session data and becomes increasingly expensive as leaderboards fill. A child whose first update occurs before 60 seconds cannot be ranked, and each update repeatedly scans and bubble-sorts multiple storage arrays.Evidence
The speed score is calculated as:
For
1 <= totalPlayTime < 60, integer division makes the denominator zero and the transaction reverts. A stats update then calls_updateLeaderboard()for five categories plus the age leaderboard. Each call performs a linear search and an O(n²) storage bubble sort (up to 100 entries), making gas cost and liveness dependent on leaderboard size.There are related consistency gaps: trimmed players retain a non-zero
playerRanksvalue, inactive players remain in arrays, andlastGlobalUpdateis initialized but not updated on leaderboard changes.Proposed implementation
Acceptance criteria