> For the complete documentation index, see [llms.txt](https://docs.overtime.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.overtime.io/overtime-casino/how-the-games-are-built.md).

# How the Games are Built

Overtime Casino has twelve games built across two generations of architecture. They play the same way from a user's seat - connect a wallet, place a bet, get a verifiable result from Chainlink VRF - but under the hood there are two designs, and the differences matter if you're integrating, auditing, or just want to understand exactly what guarantees you're getting.

This page explains both. You don't need it to play. You do need it to build.

***

### The two generations at a glance

**The original five** - Roulette, Dice, Blackjack, Baccarat, Slots - are standalone contracts. Each one holds its own bankroll, talks to Chainlink VRF directly, and manages its own reservations, free bets, and referrals.

**The newer seven** - Plinko, Hi-Lo, Keno, Video Poker, Three Card Poker, Ultimate Texas Hold'em, Bonus Texas Hold'em - are built on a shared core. A single contract, `CasinoCoreV2`, holds the treasury and the shared services; each game contract is thin, containing only its own rules and bet state.

Here's the contrast, dimension by dimension:

|                     | Original five                                            | Newer seven                                                      |
| ------------------- | -------------------------------------------------------- | ---------------------------------------------------------------- |
| **Funds**           | Each game holds its own bankroll                         | One shared treasury (`CasinoCoreV2`)                             |
| **Randomness**      | Game calls VRF and receives the callback directly        | Core calls VRF and routes the callback to the game               |
| **User cancel**     | Yes - after a timeout, you can cancel your own stuck bet | No - recovery is operator-only                                   |
| **Free bets**       | Separate `…WithFreeBet` functions                        | A flag on `placeBet` (or a separate function for side-bet games) |
| **Circuit breaker** | None                                                     | Per-game auto-pause on cumulative loss                           |
| **Read aggregator** | `CasinoData`                                             | `CasinoDataV2`                                                   |

Both generations use **Chainlink VRF v2.5**, support **USDC / WETH / $OVER**, enforce a **3 USD minimum bet**, and are deployed as upgradeable proxies. Neither has a function to void a resolved win, bias the RNG, or ban a winning wallet.

***

### The original five: standalone contracts

Each of the first five games is a self-contained contract that holds its own liquidity.

**Lifecycle.** You call `placeBet` (or `placeMultiBet` for Roulette, or the per-action functions in Blackjack) directly on the game contract. It pulls your collateral via `safeTransferFrom` into its own balance, validates your selection, and reserves the worst-case payout. It requests a random word from Chainlink VRF. When the VRF coordinator calls the game's own `rawFulfillRandomWords`, the game derives the outcome, pays you if you won, pays your referrer if you lost, and emits a resolution event.

**Full reservation.** This is the defining property of the V1 design. Every game tracks `reservedProfitPerCollateral` - the sum of the worst-case payouts of *all* its pending bets. Before accepting a new bet, it checks that its entire balance covers that entire cumulative liability:

```
balanceOf(this) >= reservedProfitPerCollateral[collateral]
```

If a new bet would push reserved liability past the bankroll, it reverts with `InsufficientAvailableLiquidity`. In other words, every pending bet in a V1 game is fully backed at all times - the contract could pay every outstanding bet's maximum simultaneously and still be solvent. This is conservative and capital-heavy, but it means a V1 game can never be caught short.

**User-callable cancel.** Each V1 game lets *you* recover a stuck bet. If Chainlink fails to fulfill within `cancelTimeout` (a minimum of 30 seconds, set per game), you can call the cancel function yourself - `cancelBet` in Roulette, Dice, and Baccarat; `cancelHand` in Blackjack; `cancelSpin` in Slots - and get your stake back. You don't need to wait for an operator. There's also an `adminCancelBet`-style path (resolver-only) for emergencies, but the user path is the primary one.

**Free bets and referrals.** Free bets use dedicated entry points - `placeBetWithFreeBet`, and `placeMultiBetWithFreeBet` for Roulette - that pull from the free-bets holder instead of your wallet. Referrals are wired per-game through the shared `IReferrals` registry; a losing real-money bet pays the referrer automatically.

**Reading them.** A separate read-only aggregator, `CasinoData`, merges paginated bet history across all five games into a uniform record shape, so a frontend can fetch a page of history in one call per page rather than one call per bet.

***

### The newer seven: one shared core

The seven newer games share `CasinoCoreV2`, a singleton treasury and services contract. The game contracts hold no funds; they're pure game logic and lifecycle state.

`CasinoCoreV2` handles:

* **Funds** - pulling stakes from your wallet (`pullFromUser`) or your free-bet balance (`useFreeBet`), and paying out (`payOut`)
* **Randomness** - requesting words from Chainlink VRF (`requestRandomWords`) and routing the fulfilled callback to the right game
* **Reservations** - reserving and releasing worst-case payouts
* **Free bets and referrals** - the shared wiring, identical in spirit to V1 but centralized
* **Limits** - effective minimum bet, maximum bet, and maximum profit, per game
* **Circuit breaker** - per-game cumulative-loss accounting that auto-pauses a game that's bleeding

Each game - `Plinko`, `Keno`, `HiLo`, and the rest - implements only its rules and its bet's state machine. When a VRF word arrives, core calls the game's `onVrfFulfilled`, the game derives its outcome, and tells core how to settle.

There's also a third contract: **`CasinoDataV2`**, a read-only aggregator over core and all seven games - a treasury overview, per-game status (including live circuit-breaker state), and paginated bet records, all from one place. (See Integrate Overtime Casino for how to use it.)

**Lifecycle.** You call `placeBet` on the game contract (passing an `isFreeBet` flag, or using `placeBetWithFreeBet` for the side-bet games). The game asks core to pull your stake and reserve the worst case, then requests a VRF word. Single-shot games (Plinko, Keno) resolve on the first callback. Multi-step games (Hi-Lo, Video Poker, the poker games) take further actions via a `makeAction(betId, action)` dispatcher, each potentially triggering a new VRF request, until the bet resolves. The VRF coordinator calls **core's** `rawFulfillRandomWords`, which looks up which game owns that request and forwards to its `onVrfFulfilled`.

**Multi-step games use an action dispatcher.** The games where you make mid-hand decisions expose one `makeAction(betId, action)` function with a small integer action code, which keeps them uniform and easy to drive from a frontend, bot, or AI agent:

* **Hi-Lo:** `0` = guess Above, `1` = guess Below, `2` = cash out
* **Three Card Poker:** `0` = Play, `1` = Fold
* **Ultimate Texas Hold'em:** `0` = play pre-flop, `1` = check pre-flop, `2` = play post-flop, `3` = check post-flop, `4` = raise river, `5` = fold
* **Bonus Texas Hold'em:** `0` = play pre-flop, `1` = fold, `2` = raise flop, `3` = check flop, `4` = raise turn, `5` = check turn, `6` = raise river, `7` = check river

(Video Poker uses a dedicated `draw(holdMask)` rather than the dispatcher.) An unknown action code reverts (`InvalidAction`) rather than silently doing nothing.

***

### Staged VRF: hidden cards stay hidden

Several games - in both generations - depend on you acting before you've seen certain cards: the dealer's hand in Blackjack and the poker games, the next street in Hold'em, your draw cards in Video Poker. In every one of those cases, the unseen cards are drawn from a **separate VRF request that fires only when the game needs to reveal them.** They are never written to contract storage before you commit your decision.

This matters because contract storage is publicly readable (`eth_getStorageAt`). If a future card were sitting in storage while you decided, a sophisticated player could read it and play perfectly. Staging the VRF requests so each card is generated only at the moment of reveal closes that hole. The principle was introduced in the original Blackjack contract (the dealer's hole card isn't generated until you stand) and is applied consistently across every newer game that has hidden information.

***

### The circuit breaker (newer seven only)

`CasinoCoreV2` tracks each game's cumulative house net P\&L, in USD, as bets settle. If a game's net result falls below a configured loss threshold - default **1,000 USD**, adjustable per game - that game **auto-pauses**: no new bets until an admin resets it. Pending bets always continue to settle even while a game is paused.

This protects the shared treasury from a single game bleeding out due to a bug or an extreme run of player wins, and it's a feature the V1 games (with their isolated bankrolls) didn't need. The live state - `houseNetUsd`, `maxNetLossUsd`, `autoPaused` - is readable on-chain through `CasinoDataV2`.

***

### Cancellation: the key behavioral difference

If a VRF request stalls, how a bet recovers depends on which generation it belongs to.

**Original five:** you can cancel your own stuck bet after `cancelTimeout` (minimum 30 seconds). The operator can also admin-cancel, but you are never dependent on them.

**Newer seven:** there is **no user-callable cancel.** This was a deliberate decision - a user cancel sitting in the mempool alongside a pending VRF request is a front-run surface (a player could cancel selectively based on the pending result). The only recovery path is `adminCancelBet`, callable by the resolver role. Core still exposes a `cancelTimeout` value (minimum 30s), but for the newer games it's an **operational SLA, not an on-chain rule** - the soonest the operator commits to admin-cancelling a stalled bet, surfaced so a frontend can show a "maximum wait before refund." It is not enforced by the contract, because there's no user-callable cancel for it to gate.

A cancel - in either generation - refunds your original stake; for the multi-step newer games it forfeits any accumulated multiplier or raises and returns the base stake. Free-bet cancels route the refund back to your free-bet balance.

***

### House edge, transparently bounded

Across both generations, each game's economics are bounded in code and readable on-chain:

* **Roulette** - European single-zero, house edge fixed at \~2.7% by the wheel
* **Dice** - house edge configurable, capped at 5%; payout derived from `(1 − houseEdge) / probability`
* **Blackjack** - 3:2 naturals, dealer hits soft 17; edge depends on rules and play
* **Baccarat** - Banker payout a public parameter, bounded \[1.00×, 2.00×]
* **Slots** - pay table fully public; house edge capped at 5%
* **Plinko** - RTP capped at 98% (≥2% edge), enforced on every paytable update
* **Hi-Lo** - house edge configurable within 2%–5%; per-guess multiplier from a published formula
* **Keno** - paytables calibrated to \~1.9%–3.1% edge per pick count; multipliers capped at 300×
* **Video Poker** - 8/5 Jacks or Better, \~96.5% RTP under optimal play
* **Three Card Poker** - locked paytables with a guaranteed ≥2% edge
* **Ultimate / Bonus Hold'em** - locked Vegas-standard paytables, side-bet ceilings at 500×

In every case you can read the relevant parameters off-chain and verify the math yourself before you play. That's the whole point: the casino's edge is a number you can check, not a claim you have to trust.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.overtime.io/overtime-casino/how-the-games-are-built.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
