---
name: super402-casino
description: Play the Super402 on-chain lottery on super402.casino — a provably-fair casino built for autonomous AI agents, paid per ticket in USDC on Solana via the x402 payment protocol (HTTP 402). Buy weighted jackpot tickets, get 50% back every time you don't win, poll the async draw, and verify every outcome. Triggers when the user wants to "play super402", "buy a casino ticket with USDC on Solana", "bet on the x402 jackpot", "settle an x402 paywall on super402.casino", or programmatically call any `https://super402.casino/api/*` endpoint.
license: MIT
---

# Super402.casino — Agent Skill

A self-contained guide for autonomous agents (and the LLMs driving them) to play
**[super402.casino](https://super402.casino)** — a provably-fair **progressive
jackpot lottery** paid in USDC on **Solana** via the [x402 protocol](https://x402.org).

> **Production base URL:** `https://super402.casino`
> **Devnet base URL:** `https://devnet.super402.casino` (network `solana-devnet`)
> **Chain:** Solana. **Asset:** Circle USDC — mainnet mint `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, devnet mint `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU`.
> Never hardcode the treasury address — read it from `GET /api/solana/payment-info`.

---

## TL;DR — four things to know

1. **This is a lottery, not a per-spin casino.** A ticket does not win or lose immediately. It joins the current **round**; tickets accumulate a **prize pool**; when the pool crosses a threshold the round "arms" and a background worker draws **one weighted winner who takes the whole pool**. Everyone who didn't win gets **50% of their bet back automatically**.
2. **Authentication = payment.** Every priced endpoint is gated by HTTP 402. On Solana the simplest path is: send a real USDC transfer to the treasury, then submit its signature in the `X-Wallet-Tx` header. **Your wallet is recovered from the on-chain transaction — never send a private key, and you never need to prove your address separately.**
3. **Async settlement.** `POST /api/casino/play/:tier` returns `{ entered: true, roundId, armed, ... }` immediately — there is **no** win field. Poll `GET /api/casino/jackpot-status` and `GET /api/casino/player/:address` to learn the result. Never treat the POST response as a win.
4. **Provably fair, and you can contribute entropy.** Each round commits a server-seed hash before any ticket. Pass an optional `client_seed` with your bet and it is folded into the draw, so neither the house nor any single player can steer the winner. Verify with `GET /api/casino/verify-round/:roundId`.

---

## Endpoints

All paid endpoints accept payment via **either** the wallet path (`X-Wallet-Tx`, below) **or** the standard x402 `X-PAYMENT` header.

| Method & path | Price | Purpose |
| --- | --- | --- |
| `GET /api/casino/tiers` | free | Tiers, prices, entry weights, revenue split, token gate |
| `GET /api/solana/payment-info` | free | Treasury address, USDC mint, network — **read before paying** |
| `GET /api/casino/commitment` | free | Current round's committed server-seed hash (snapshot before betting) |
| `POST /api/casino/play/:tier` | tier price | Buy a ticket (`tier` = `bronze\|silver\|gold\|platinum`) |
| `GET /api/casino/jackpot-status` | free | Pool progress, threshold, `armed`, rough ETA to the draw |
| `GET /api/casino/player/:address` | free | Your totals, wins, and tickets in the current round |
| `GET /api/casino/verify-round/:roundId` | free | Reveal the seed + recompute the winner of a drawn round |
| `GET /api/casino/daily-status` | free | Daily $SUPER402 holder-draw pool + last winner |
| `GET /api/casino/verify-daily/:id` | free | Verify a daily holder draw |
| `GET /api/casino/eligibility/:wallet` | free | Whether a wallet meets the token gate |
| `GET /health` | free | Liveness + network + reconcile status |

### Tiers

| Tier | Price | Entries (weight) |
| --- | --- | --- |
| `bronze` | $0.10 | 1 |
| `silver` | $0.50 | 5 |
| `gold` | $1.00 | 10 |
| `platinum` | $5.00 | 50 |

Entries scale with price (`entries = price / $0.10`), so a platinum ticket has 50× a bronze ticket's odds in the weighted draw. Live values: `GET /api/casino/tiers`.

### Per-bet allocation

Every ticket splits: **50% rebate** (paid back to you if you don't win), **40% prize pool**, **5% daily $SUPER402 holder draw**, **4% token buyback**, **1% house**. The round winner takes the whole 40%-fed pool.

### Token gate

When enabled, you must hold at least a threshold of **$SUPER402** to play. An ineligible wallet that pays is **automatically refunded 100%** and rejected — you never lose money for being ineligible. Check first with `GET /api/casino/eligibility/:wallet`.

---

## Paying — the wallet path (recommended on Solana)

There is no EIP-3009 pull on Solana, so you *push* the funds: send a normal USDC SPL transfer to the treasury, then hand the server its signature.

```js
import {
  Connection, Keypair, PublicKey, Transaction, sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
  getAssociatedTokenAddress, getOrCreateAssociatedTokenAccount, createTransferInstruction,
} from "@solana/spl-token";
import bs58 from "bs58";

const BASE = "https://super402.casino";
const conn = new Connection(process.env.SOLANA_RPC_URL, "confirmed");
const agent = Keypair.fromSecretKey(bs58.decode(process.env.AGENT_PRIVATE_KEY)); // stays local — never sent

// 1. Discover treasury + mint + the tier price (never hardcode).
const info  = await fetch(`${BASE}/api/solana/payment-info`).then(r => r.json());
const tiers = await fetch(`${BASE}/api/casino/tiers`).then(r => r.json());
const tier  = tiers.tiers.find(t => t.id === "gold");
const USDC  = new PublicKey(info.usdcMint);
const treasury = new PublicKey(info.treasury);
const priceMicro = Number(tier.priceUSDC);

// Optional safety clamp — refuse to overpay a spoofed/compromised server.
const MAX_TICKET_PRICE_USDC = 5_000_000;
if (priceMicro > MAX_TICKET_PRICE_USDC) throw new Error("tier price above my cap");

// 2. Push the USDC transfer to the treasury.
const from = await getAssociatedTokenAddress(USDC, agent.publicKey);
const to   = (await getOrCreateAssociatedTokenAccount(conn, agent, USDC, treasury)).address;
const sig  = await sendAndConfirmTransaction(
  conn,
  new Transaction().add(createTransferInstruction(from, to, agent.publicKey, priceMicro)),
  [agent]
);

// 3. Submit the bet. The payer is recovered from `sig` on-chain; X-Wallet-Payer
//    is an optional hint only. Add an optional client_seed for provable fairness.
const placed = await fetch(`${BASE}/api/casino/play/gold`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Wallet-Tx": sig },
  body: JSON.stringify({ client_seed: bs58.encode(agent.publicKey.toBytes()).slice(0, 16) }),
}).then(r => r.json());
// → { success:true, entered:true, roundId, ticketId, tier, entries, yourTicketsThisRound,
//     poolBalance, threshold, armed, message }
// NOTE: no win/lose here — this is a lottery entry. Poll for the result (below).
```

Notes:
- **Retry on 425.** If the transfer isn't finalized yet the server returns HTTP 425 (`retryable: true`). Wait a few seconds and re-POST the *same* `X-Wallet-Tx` — it's idempotent (one ticket per signature).
- **One signature = one ticket.** Replays are rejected. Log `sig` before POSTing so a paid-but-interrupted bet is recoverable.
- **Amount** must be ≥ the tier price; recency window is ~15 minutes.

### Paying — the x402 facilitator path

Alternatively, call the endpoint with no payment; the server replies `402` with a `PAYMENT-REQUIRED` challenge. Sign it with the x402-solana facilitator and retry with the `X-PAYMENT` header. Use the `x402` npm package or the `x402-pay` skill to build the header. (The server emits only `PAYMENT-REQUIRED` — there are no `X-Payment-Scheme/Amount/...` headers.)

---

## Polling the result

```js
const BASE = "https://super402.casino";
const me = "<your wallet address>";

// Poll until the round you entered has drawn.
async function awaitDraw(myRoundId) {
  for (let i = 0; i < 30; i++) {
    const status = await fetch(`${BASE}/api/casino/jackpot-status`).then(r => r.json());
    const player = await fetch(`${BASE}/api/casino/player/${me}`).then(r => r.json());
    // Your win shows up in player stats once the round draws.
    if (Number(status.currentRoundId) > myRoundId) return player; // round rolled over → drawn
    await new Promise(r => setTimeout(r, 5_000 * Math.min(i + 1, 6)));
  }
  throw new Error("draw did not resolve in time");
}
```

Read `GET /api/casino/player/:address` for `gamesWon` / `totalWon` and your current-round tickets. A win is the whole prize pool, paid on-chain treasury→winner. If you didn't win, your 50% rebate is sent automatically.

---

## Verifying fairness

After a round draws, its server seed is revealed:

```js
const v = await fetch(`${BASE}/api/casino/verify-round/${roundId}`).then(r => r.json());
// → { serverSeed, serverSeedHash, clientSeedsDigest, winningEntry, recomputedWinningEntry,
//     commitmentValid, winnerMatches, verified }
```

The draw is `sha256(serverSeed + ":" + roundId [+ ":" + clientSeedsDigest]) mod totalEntries`, where `clientSeedsDigest = sha256(join(",", players' client_seeds in ticket order))` (omitted if nobody supplied one). `commitmentValid` proves the seed matches the hash committed *before* the round; `winnerMatches` proves the winning entry recomputes. Both true ⇒ `verified: true`.

---

## Errors and limits

| HTTP | Meaning / agent action |
| --- | --- |
| 402 | Pay-required handshake (x402 path). Read the challenge, attach `X-PAYMENT`, retry. |
| 425 | Transfer not yet finalized (wallet path). Wait and re-POST the same `X-Wallet-Tx`. |
| 400 `Invalid payment` | Signature malformed, amount too low, no transfer to treasury, or payer not derivable/not a signer. |
| 403 `token_holding_required` | You don't hold enough $SUPER402. If you paid via the wallet path you were auto-refunded 100%. |
| 429 | Rate limited (per-IP; play endpoint is throttled). Back off. |
| 500 | Server error — safe to retry idempotently (same `X-Wallet-Tx`). |

---

## Operating notes for agents

- **Never transmit a private key.** The server recovers your address from the on-chain transaction; no endpoint ever wants your key. Keep `AGENT_PRIVATE_KEY` local.
- **Test on devnet first.** Use `https://devnet.super402.casino` and Circle's Solana devnet USDC faucet before mainnet.
- **Clamp your spend.** Take the tier price from `/api/casino/tiers`, refuse anything above your own cap, and require https for non-localhost.
- **This is a lottery.** You can't lose your whole stake on one bet — non-winners always get 50% back. Odds scale with the tier's entry weight, not a per-spin probability.
- **Idempotency.** One ticket per payment signature. Log the signature before the POST so an interrupted bet is recoverable.
- **Provably fair is cheap.** Snapshot `GET /api/casino/commitment` before a session and pass a fresh `client_seed` per bet; re-verify a sample of rounds via `/verify-round/:id`.

---

## Quick reference — copy-pasteable agent prompt

> You have the **super402-casino** skill. Play the lottery on `https://super402.casino` by (1) reading `GET /api/solana/payment-info` and `GET /api/casino/tiers`, (2) sending a USDC SPL transfer of the tier price to the treasury on Solana, (3) POSTing `/api/casino/play/<tier>` with header `X-Wallet-Tx: <signature>` and optional body `{ "client_seed": "<hex>" }`. The POST returns a lottery entry, **not** a win — poll `GET /api/casino/jackpot-status` and `GET /api/casino/player/<wallet>` for the outcome, and verify with `GET /api/casino/verify-round/<roundId>`. Non-winners get 50% back automatically. The wallet private key is in `AGENT_PRIVATE_KEY` and must never be sent anywhere. Tiers: bronze $0.10, silver $0.50, gold $1.00, platinum $5.00.

---

## Resources

- Live tiers: `https://super402.casino/api/casino/tiers`
- Payment info: `https://super402.casino/api/solana/payment-info`
- Full API reference: `API.md` in this toolkit
- Reference agent: `examples/agent.js`
- x402 spec: <https://x402.org>
