A strategic counterpart to /rob: coins moved to the bank are safe from /rob and
/heist (which only touch liquid balance) but earn no Bot Farm interest and can't
be spent, gambled or given until withdrawn - the deliberate trade-off against
keeping coins liquid.
- New bank.py (do_deposit/do_withdraw), bank_balance schema field.
- /bank (view), /deposit, /withdraw commands ('all' supported), with db_error
handling; /balance shows the vault when non-zero.
- Coins leaderboard and /status money-supply now count net worth
(balance + bank_balance), so banking never hides you from the board or the
supply metric.
- Season reset wipes bank_balance too (no cross-season wealth hiding).
10 tests: deposit/withdraw math, guards, coin conservation, rob cannot touch the
vault, and net-worth accounting on the leaderboard + stats.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VbAVsrZuYesea99mPMmPT
64 lines
2.5 KiB
Python
64 lines
2.5 KiB
Python
"""Bank vault: rob-proof coin storage.
|
|
|
|
Coins moved to the bank are safe from /rob and /heist (those target the liquid
|
|
`balance` only), but they earn no interest and cannot be spent, gambled or given
|
|
until withdrawn. This is the deliberate trade-off against keeping coins liquid,
|
|
where Bot Farm can earn interest but a robber can take a cut. Net worth
|
|
(balance + bank_balance) is what the coins leaderboard ranks, so banking never
|
|
hides you from the leaderboard.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from ..pb_client import DatabaseError
|
|
from .store import _commit, _locked_by, _txn, get_user
|
|
|
|
__all__ = ["do_deposit", "do_withdraw"]
|
|
|
|
|
|
@_locked_by(0)
|
|
async def do_deposit(user_id: int, amount: int) -> dict:
|
|
"""Move `amount` coins from liquid balance into the bank vault."""
|
|
try:
|
|
user = await get_user(user_id)
|
|
except DatabaseError:
|
|
return {"ok": False, "reason": "db_error"}
|
|
if user.get("eco_banned"):
|
|
return {"ok": False, "reason": "banned"}
|
|
if amount <= 0:
|
|
return {"ok": False, "reason": "invalid"}
|
|
if user["balance"] < amount:
|
|
return {"ok": False, "reason": "insufficient", "balance": user["balance"]}
|
|
user["balance"] -= amount
|
|
user["bank_balance"] = user.get("bank_balance", 0) + amount
|
|
try:
|
|
await _commit(user_id, user)
|
|
except DatabaseError:
|
|
return {"ok": False, "reason": "db_error"}
|
|
_txn("BANK_DEPOSIT", user=user_id, amount=amount, bal=user["balance"], bank=user["bank_balance"])
|
|
return {"ok": True, "amount": amount, "balance": user["balance"], "bank": user["bank_balance"]}
|
|
|
|
|
|
@_locked_by(0)
|
|
async def do_withdraw(user_id: int, amount: int) -> dict:
|
|
"""Move `amount` coins from the bank vault back to liquid balance."""
|
|
try:
|
|
user = await get_user(user_id)
|
|
except DatabaseError:
|
|
return {"ok": False, "reason": "db_error"}
|
|
if user.get("eco_banned"):
|
|
return {"ok": False, "reason": "banned"}
|
|
if amount <= 0:
|
|
return {"ok": False, "reason": "invalid"}
|
|
bank = user.get("bank_balance", 0)
|
|
if bank < amount:
|
|
return {"ok": False, "reason": "insufficient", "bank": bank}
|
|
user["bank_balance"] = bank - amount
|
|
user["balance"] += amount
|
|
try:
|
|
await _commit(user_id, user)
|
|
except DatabaseError:
|
|
return {"ok": False, "reason": "db_error"}
|
|
_txn("BANK_WITHDRAW", user=user_id, amount=amount, bal=user["balance"], bank=user["bank_balance"])
|
|
return {"ok": True, "amount": amount, "balance": user["balance"], "bank": user["bank_balance"]}
|