Add Democratic fundraising platform.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-05-16 00:47:44 +00:00
parent 89f9b8dc83
commit 391d90a754
141 changed files with 14989 additions and 914 deletions

View File

@@ -0,0 +1,97 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";
import { generateServerSeed, deriveInt } from "@/lib/provably-fair";
import { creditWalletCredits, debitWalletCredits, INSUFFICIENT_CREDITS } from "@/lib/wallet-safety";
export const dynamic = "force-dynamic";
const SYMBOLS = ["🍒", "🍋", "🍊", "🍇", "💎", "7⃣"];
const SYMBOL_WEIGHTS = [30, 25, 20, 15, 7, 3]; // out of 100
// Paytable: 3 of a kind multipliers
const PAYTABLE: Record<string, number> = {
"🍒": 2,
"🍋": 3,
"🍊": 5,
"🍇": 10,
"💎": 25,
"7⃣": 50,
};
function weightedSymbol(roll: number): string {
let cumulative = 0;
for (let i = 0; i < SYMBOLS.length; i++) {
cumulative += SYMBOL_WEIGHTS[i];
if (roll < cumulative) return SYMBOLS[i];
}
return SYMBOLS[SYMBOLS.length - 1];
}
export async function POST(req: NextRequest) {
const session = await auth();
if (!session?.user?.id) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
const body = await req.json();
const { wageBLW, clientSeed } = body as { wageBLW: number; clientSeed?: string };
if (!Number.isInteger(wageBLW) || wageBLW < 1) return NextResponse.json({ error: "Invalid wager" }, { status: 400 });
const serverSeed = generateServerSeed();
const cs = clientSeed ?? "default";
const reels = [
weightedSymbol(deriveInt(serverSeed, cs, 0, 0, 99)),
weightedSymbol(deriveInt(serverSeed, cs, 1, 0, 99)),
weightedSymbol(deriveInt(serverSeed, cs, 2, 0, 99)),
];
let multiplier = 0;
let outcome = "loss";
if (reels[0] === reels[1] && reels[1] === reels[2]) {
multiplier = PAYTABLE[reels[0]] ?? 2;
outcome = "win";
} else if (reels[0] === reels[1] || reels[1] === reels[2] || reels[0] === reels[2]) {
multiplier = 1.5;
outcome = "win";
}
const payout = outcome === "win" ? Math.floor(wageBLW * multiplier) : 0;
try {
await prisma.$transaction(async (tx) => {
await debitWalletCredits(tx, session.user.id, wageBLW);
await tx.ledgerEntry.create({
data: { userId: session.user.id, delta: -wageBLW, type: "DEBIT_GAME_BET", memo: "SLOTS bet" },
});
if (payout > 0) {
await creditWalletCredits(tx, session.user.id, payout);
await tx.ledgerEntry.create({
data: { userId: session.user.id, delta: payout, type: "CREDIT_GAME_WIN", memo: "SLOTS payout" },
});
}
await tx.gameSession.create({
data: {
userId: session.user.id,
gameType: "SLOTS",
wageredBLW: wageBLW,
payoutBLW: payout,
multiplier,
outcome,
serverSeed,
clientSeed: cs,
resultData: { reels, multiplier },
},
});
});
} catch (e) {
if (e instanceof Error && e.message === INSUFFICIENT_CREDITS) {
return NextResponse.json({ error: "Insufficient balance" }, { status: 402 });
}
console.error(e);
return NextResponse.json({ error: "Could not settle slots game" }, { status: 500 });
}
return NextResponse.json({ reels, multiplier, payout, outcome, serverSeed });
}