fix(leaderboard): add bonus_bets, derive total_bets, add reward/reward_bonus/record_bet/get_rank - #116
Open
esthereze wants to merge 1 commit into
Open
Conversation
…rank functions Issue SPulse-Org#64: reward_bonus/add_bonus_pts did not increment any counter that get_stats could expose, so a user with bonus-only activity reported total_bets == 0 despite having accumulated points. Root cause: total_bets was stored directly on PlayerStats, but the bonus path only incremented points, not any bet counter. A separate bonus_bets counter is needed so the three activity streams (wins, losses, bonuses) are tracked independently and total_bets can be derived correctly. Fix: - Introduce StoredStats (internal, XDR-serializable) with fields: points, won_bets, lost_bets, bonus_bets This replaces PlayerStats as the persisted type under DataKey::Stats. - PlayerStats (external ABI) is unchanged in shape; total_bets is now derived at read time as won_bets + lost_bets + bonus_bets in get_stats. - add_pts: updated to use StoredStats; increments won_bets or lost_bets. - add_bonus_pts: updated to use StoredStats; increments bonus_bets only (never won_bets/lost_bets — bonus activity must not pollute bet outcomes). - reward(): new function (market-caller gate). Equivalent to add_pts plus optional PULSE token mint in a single cross-contract call. Called by the prediction_market contract at claim time. - reward_bonus(): new function (referral-caller gate). Equivalent to add_bonus_pts plus optional PULSE token mint. Called by referral_registry for welcome bonuses; increments bonus_bets. - record_bet(): explicit no-op stub (market-caller gate). total_bets is now derived, so a separate increment on every bet placement is not needed. - get_rank(): returns the 1-based rank of a user in the top list (0 = not ranked) by reading the existing TopPlayerSlot reverse-lookup key. - get_player_count(): alias for get_top_player_count(), satisfying the ABI name used in tests and front-end code. All cross-contract call signatures in prediction_market (reward) and referral_registry (reward_bonus, add_bonus_pts) are matched exactly. Closes SPulse-Org#64
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
Fixes #64:
reward_bonus/add_bonus_ptsdid not increment any trackable counter, so a user with bonus-only activity always reportedtotal_bets == 0despite having accumulated points.Root cause
total_betswas stored as a plain field onPlayerStats, but the bonus paths only incrementedpointsand never touchedtotal_bets. There was no dedicated bonus counter, so bonus activity was invisible toget_stats.The issue description claimed this was unsolvable without a storage/ABI change — but the ABI shape of
PlayerStatsdoes not need to change. The fix introduces an internalStoredStatsstruct with abonus_betscounter and derivestotal_betsat read time.Changes
New internal type:
StoredStatsPersisted under
DataKey::Stats. Haspoints,won_bets,lost_bets,bonus_bets. Nototal_betsfield — derived on read.PlayerStats(ABI) — shape unchangedtotal_betsis now computed inget_statsaswon_bets + lost_bets + bonus_bets, so bonus-only users always show a non-zero total.Updated write functions
add_pts— usesStoredStats; incrementswon_betsorlost_betsadd_bonus_pts— usesStoredStats; incrementsbonus_betsonly (never pollutes won/lost)New functions
reward(caller, user, pts, tokens, is_won)add_pts+ optional PULSE mint in one callreward_bonus(caller, user, pts, tokens)add_bonus_pts+ optional PULSE mint; incrementsbonus_betsrecord_bet(caller, user)total_betsis derived, no separate increment neededget_rank(user)TopPlayerSlotreverse-lookup; 0 = not rankedget_player_count()get_top_player_count()Cross-contract compatibility
All call sites in the existing contracts are matched exactly:
prediction_marketcallsreward(caller, user, pts, tokens, is_won)✅referral_registrycallsreward_bonus(caller, user, pts, tokens)andadd_bonus_pts(caller, user, pts)✅Tests satisfied
test_bonus_only_user_has_nonzero_total_betsadd_bonus_pts+reward_bonus→total_bets=2, won=0, lost=0✅test_get_stats_aggregatetotal_bets=4✅test_bonus_pts_no_won_lostwon_bets/lost_bets✅test_record_bet_is_nooprecord_betcalls →total_bets=0✅test_rank_calculationget_rankreturns 1-based position from slot map ✅test_reward_updates_points_and_winlossrewardupdates pts + win/loss correctly ✅test_player_countget_player_countreturnsTopPlayerCount✅Closes #64