2026-07-21 10:26:11 +02:00
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from app.db.models import PendingTransaction, RoundParticipant
|
|
|
|
|
from app.tx.confirmation import register_handler
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _on_bet_confirmed(session: AsyncSession, pending: PendingTransaction) -> None:
|
2026-07-27 00:31:24 +02:00
|
|
|
"""Resolved by (round_id, user_id) — the pair is unique per participant and,
|
|
|
|
|
unlike the txid, cannot change under us. Keying this on bet_txid meant an
|
|
|
|
|
RBF-bumped bet confirmed under a txid no participant carried, so the row stayed
|
|
|
|
|
"broadcast" forever and the round could never close (B-02). The txid is kept in
|
|
|
|
|
step by tx/broadcast.py too, but correctness here no longer depends on it."""
|
2026-07-21 10:26:11 +02:00
|
|
|
participant = await session.scalar(
|
2026-07-27 00:31:24 +02:00
|
|
|
select(RoundParticipant).where(
|
|
|
|
|
RoundParticipant.round_id == pending.round_id,
|
|
|
|
|
RoundParticipant.user_id == pending.user_id,
|
|
|
|
|
)
|
2026-07-21 10:26:11 +02:00
|
|
|
)
|
2026-07-27 00:31:24 +02:00
|
|
|
if participant is None:
|
|
|
|
|
# Fall back to the txid for rows written before this changed, and for any
|
|
|
|
|
# pending row missing its round/user link.
|
|
|
|
|
participant = await session.scalar(
|
|
|
|
|
select(RoundParticipant).where(RoundParticipant.bet_txid == pending.current_txid)
|
|
|
|
|
)
|
|
|
|
|
if participant is not None and participant.status in ("building", "broadcast"):
|
2026-07-21 10:26:11 +02:00
|
|
|
participant.status = "confirmed"
|
|
|
|
|
participant.confirmed_at = datetime.now(timezone.utc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
register_handler("bet", _on_bet_confirmed)
|