Three rockets. Matched launch.

The Codebase

Is this real?
Yes it does.

Fee → server roll → stored ticket → winner launches → Match Engine buys. That architecture runs on useslot.fun today. Reels on the client only animate the server result.

FAQ

Is this real?

Yes it does.

Conceptually and in production: a paid SOL fee is verified on-chain, the server rolls published odds, the ticket is persisted, losses fund the treasury, and wins build a Pump.fun create with Slot matching an equal SOL purchase after launch confirms. The Match Engine’s private signer stays sealed — the public path above is what you can audit here.

  • ✓Server-authoritative RNG (randomInt, no modulo bias)
  • ✓Payment signature consumed once (replay lock)
  • ✓Fee payer must sign; treasury must receive ≥ fee
  • ✓Match size capped in lamports at ticket time (≤ 1 SOL)
  • ✓Match runs once per ticket (status → MATCHED)
  • ✓Demo reels ≠ live odds (show ~1/3 · live ~10%)
Win rate
…
loading
Spin fee
…
to Slot treasury
Match engine
…
equal SOL after confirm
Max match
1 SOL
server-enforced ceiling

1) Published odds

Live jackpot = WIN_RATE_BPS / 10_000. Default 1000 → exactly 10%. Uses crypto.randomInt so the selection is uniform.

Uniform roll
src/services/spin.ts → rollSpinOutcome
// src/services/spin.ts — uniform jackpot odds
import { randomInt } from "node:crypto";

export function rollSpinOutcome() {
  const n = randomInt(10_000);          // [0, 10000)
  const win = n < winRateBps();         // 1000 bps = exactly 10%

  if (win) {
    return { win: true, reels: ["🚀", "🚀", "🚀"] as const };
  }

  let reels: [string, string, string];
  do {
    reels = [
      SYMBOLS[randomInt(SYMBOLS.length)]!,
      SYMBOLS[randomInt(SYMBOLS.length)]!,
      SYMBOLS[randomInt(SYMBOLS.length)]!,
    ];
  } while (reels.every((s) => s === "🚀"));

  return { win: false, reels };
}

2) Fee → ticket

Payment signature is claimed once, then verified on-chain. Opening buy is clamped to lamports and stored on the ticket before any match can run.

Play route
src/app/api/spin/play/route.ts
// POST /api/spin/play — fee → roll → immutable ticket
const spinId = newSpinId();
const openingBuyLamports = clampOpeningBuyLamports(body.openingBuySol); // ≤ 1 SOL

const claimed = await markPaymentUsed(paymentSignature); // replay lock
if (!claimed) throw new Error("payment already used");

await verifyFeePayment({
  signature: paymentSignature,
  fromWallet: wallet,          // must be a signer
  feeWallet: quote.feeWallet,  // treasury destination
  minLamports: launchFeeLamports(),
});

const { win, reels } = rollSpinOutcome();

await saveTicket({
  id: spinId,
  wallet,
  paymentSignature,
  win,
  reels,
  openingBuyLamports,
  maxMatchLamports: openingBuyLamports,
  status: win ? "PAID_WIN" : "PAID_LOSS",
  ...
});

return { spinId, win, reels };
Payment checks
src/services/spin.ts → verifyFeePayment
// Fee verification checklist (server)
✓ tx found + meta.err === null (confirmed)
✓ fromWallet is a required signer
✓ feeWallet balance increased ≥ launchFeeLamports
✓ payer funded the transfer (sent ≥ fee)
✓ SystemProgram transfer when decodable
✓ payment age ≤ 30 minutes
✓ paymentSignature unique (KV spinpay:{sig} once)

3) Launch + equal SOL match

Slot matches your opening buy with an equal SOL purchase after launch confirms. The second buy hits a later point on the curve — we never claim “double the tokens.” Match is one-shot per ticket.

pump.fun
Match Engine · sealed · online
Match (public surface)
MatchEngine · replay-safe
// Match Engine — equal SOL after create confirms (sealed)
// Spends ticket.maxMatchLamports only — never client “100 SOL” claims.

if (ticket.matchSignature || ticket.status === "MATCHED") {
  throw new Error("already matched"); // replay lock
}

await assertCreateConfirmed(createSignature);
assertMintMatchesTicket(mint, ticket.mint);

const lamportsIn = min(ticket.maxMatchLamports, MAX_OPENING_BUY_LAMPORTS);

const result = await MatchEngine.execute({
  mint,
  lamportsIn,                 // equal SOL purchase
  venue: "pump.fun",
});

ticket.status = "MATCHED";
ticket.matchSignature = result.signature;
await saveTicket(ticket);

// Wording: Slot matches your opening buy with an equal SOL purchase
// after your launch confirms — not “double the tokens on the curve.”

Yes it does — spin when you’re ready.

Live launchpad: 10% after the 0.05 SOL treasury fee. Demo on Home is show-only.

← Back to How It Works