2026-07-22 23:20:55 +02:00
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
2026-07-21 10:26:18 +02:00
|
|
|
from sqlalchemy import select
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from app.db.models import PendingTransaction, Round
|
|
|
|
|
from app.tx.confirmation import register_handler
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _on_payout_confirmed(session: AsyncSession, pending: PendingTransaction) -> None:
|
2026-07-27 00:31:24 +02:00
|
|
|
# Resolved by round_id rather than by payout_txid: an RBF-bumped payout confirms
|
|
|
|
|
# under a different txid than the one first recorded (B-02).
|
|
|
|
|
round_ = None
|
|
|
|
|
if pending.round_id is not None:
|
|
|
|
|
round_ = await session.get(Round, pending.round_id)
|
|
|
|
|
if round_ is None:
|
|
|
|
|
round_ = await session.scalar(select(Round).where(Round.payout_txid == pending.current_txid))
|
2026-07-21 10:26:18 +02:00
|
|
|
if round_ is not None and round_.status == "paying_out":
|
|
|
|
|
round_.status = "closed"
|
2026-07-22 23:20:55 +02:00
|
|
|
# closed_at is what round_cooldown_seconds counts from (service.py's
|
|
|
|
|
# open_new_round_if_needed) — re-stamp it here at actual payout
|
|
|
|
|
# confirmation time rather than leaving it at the earlier "closing"
|
|
|
|
|
# timestamp, so a short cooldown (e.g. 20s) is a real pause after the
|
|
|
|
|
# winner's tx confirms, not swallowed by the ~2 block-time draw+payout wait.
|
|
|
|
|
round_.closed_at = datetime.now(timezone.utc)
|
2026-07-21 10:26:18 +02:00
|
|
|
# The winner's own address is already watched by the Electrum listener, so
|
|
|
|
|
# their balance is credited by the normal deposit path once this confirms.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
register_handler("payout", _on_payout_confirmed)
|