feat(economy): persist interactive-game stakes so a restart can't eat them

Blackjack and RPS PvP deduct a stake up front and held it only in an in-memory
View - a restart mid-hand lost the coins. Now the stake is escrowed on the user
record (new pending_wager JSON field) in the SAME commit as the deduction, and:

- do_blackjack_bet accumulates the escrow (covers double/split); do_blackjack_payout
  clears it on settlement (incl. the 0-payout loss/timeout paths).
- do_rps_pvp_deposit records it; do_rps_pvp_payout/refund clear it, and a new
  do_rps_pvp_forfeit clears the loser's marker (their stake went to the winner).
- reconcile_pending_wagers() runs on startup (on_ready) and refunds any stake left
  escrowed by an interrupted game. It's idempotent and locks per user.

Schema: pending_wager auto-types as json via sync_pb_schema. Tests cover the full
escrow lifecycle, loser forfeit, and idempotent reconciliation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VbAVsrZuYesea99mPMmPT
This commit is contained in:
Rene Arumetsa
2026-09-04 02:37:43 +03:00
parent f72d72b355
commit 8481003edf
5 changed files with 202 additions and 1 deletions

View File

@@ -290,10 +290,12 @@ def register_economy_games_commands(
if self.bet > 0:
if winner == "a":
await economy.do_rps_pvp_payout(self.player_a.id, self.bet)
await economy.do_rps_pvp_forfeit(self.player_b.id)
bet_line_a = f"\n+{coin(self.bet)}"
bet_line_b = f"\n-{coin(self.bet)}"
elif winner == "b":
await economy.do_rps_pvp_payout(self.player_b.id, self.bet)
await economy.do_rps_pvp_forfeit(self.player_a.id)
bet_line_a = f"\n-{coin(self.bet)}"
bet_line_b = f"\n+{coin(self.bet)}"
else: