From 005a7942fbe28056b5a8f8d5d308881a004aff03 Mon Sep 17 00:00:00 2001 From: Dorian Date: Sun, 8 Mar 2026 19:57:00 +0000 Subject: [PATCH] feat: wire bet settlement into fight finalization Lock bets when fight goes live, settle all bets atomically when fight ends. Winners get Cashu payout tokens, losers forfeit. Draws refund all bets. Settlement results persisted to bets table. Co-Authored-By: Claude Opus 4.6 --- server/src/engine/orchestrator.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server/src/engine/orchestrator.ts b/server/src/engine/orchestrator.ts index eebb765..20723e0 100644 --- a/server/src/engine/orchestrator.ts +++ b/server/src/engine/orchestrator.ts @@ -10,6 +10,7 @@ import { generateMockBotResponse, isClassicBot, generateClassicBotResponse } fro import { setCooldown } from './queue.js' import { isHumanPlayer, waitForHumanResponse } from './human-responses.js' import { payWinner, refundEntry, ENTRY_FEE_SATS } from './payments.js' +import { settleBets, lockBets } from './betting.js' interface BotRecord { id: string @@ -295,6 +296,9 @@ async function createFightRecord(botA: BotRecord, botB: BotRecord, arena: Arena, startedAt: now, createdAt: now, }) + // Lock bets when fight starts + lockBets(fightId) + emit(fightId, 'fight_start', { botA: { id: botA.id, name: botA.name, elo: botA.eloRating }, botB: { id: botB.id, name: botB.name, elo: botB.eloRating }, @@ -506,6 +510,21 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec potSats: mode === 'ranked' ? 42 : 0, }) + // Settle bets + try { + const settlements = await settleBets(fightId, winnerId) + for (const s of settlements) { + db.update(schema.bets).set({ + status: s.won ? 'won' : winnerId ? 'lost' : 'refunded', + payoutSats: s.payoutSats, + payoutToken: s.payoutToken, + settledAt: new Date().toISOString(), + }).where(eq(schema.bets.id, s.betId)).run() + } + } catch (err) { + console.error(`[betting] settlement failed for fight ${fightId}:`, err) + } + // Ranked fight payout if (mode === 'ranked') { // Dev mode: always pay the human bot (not mock), regardless of win/loss