Sync round countdown across clients and enforce the bet cutoff on deadline, not scheduler tick

The round timer relied on each client's own wall clock, so two browsers with
skewed local clocks showed different countdowns for the same round; the
server now also returns server_time so the frontend can correct for clock
skew. Also drop out-of-order /rounds/current responses (multiple independent
triggers could resolve late and revert the UI to a stale drawing/result
state) and prune per-round bookkeeping maps on round transitions.

Separately, place_bet only checked status == "open", leaving a window (up to
the scheduler's 5s tick interval) after a round's timer hit zero where a new
bet could still be accepted. place_bet now checks the round's own deadline
directly (round_accepts_bets), acting as an immediate "yellow light" for new
entries while still letting already-broadcast bets confirm before the round
closes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 14:07:09 +02:00
co-authored by Claude Sonnet 5
parent f27fe6243c
commit 7dcf6d2756
8 changed files with 87 additions and 17 deletions
+4 -1
View File
@@ -1,4 +1,4 @@
from datetime import timedelta, timezone
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, Request
from pydantic import BaseModel
@@ -14,6 +14,7 @@ router = APIRouter(prefix="/rounds", tags=["rounds"])
class CurrentRoundResponse(BaseModel):
server_time: str
round_id: int | None = None
status: str | None = None
opened_at: str | None = None
@@ -37,6 +38,7 @@ async def current_round(request: Request, session: AsyncSession = Depends(get_se
if round_ is None:
await session.commit()
return CurrentRoundResponse(
server_time=datetime.now(timezone.utc).isoformat(),
bet_amount_sats=config.bet_amount_sats,
draw_animation_seconds=config.draw_animation_seconds,
chain_tip_height=chain_tip_height,
@@ -51,6 +53,7 @@ async def current_round(request: Request, session: AsyncSession = Depends(get_se
await session.commit()
return CurrentRoundResponse(
server_time=datetime.now(timezone.utc).isoformat(),
round_id=round_.id,
status=round_.status,
opened_at=opened_at.isoformat(),