# Getting started: build your first casino game Go from an empty directory to a coinflip round on your machine, then make the game your own. No account, backend access, or testnet funds required. :::note[Current SDK checkout] - `@chain-protocol/casino-sdk@0.7.0` This guidance reflects this checkout and may include unreleased changes. [Casino changelog](/sdk/casino/changelog/) ::: This page takes you from an empty directory to a coinflip round played on your machine. Then you replace the coinflip game and frontend with your own. You need no Chain.wtf account, no backend access and no testnet funds. You need [Node.js](https://nodejs.org) 22 or newer, or [Bun](https://bun.sh) 1.4.2 or newer. Nothing else: the SDK simulator brings its own chain, VRF node and Solidity compiler. ## What you are building A Chain.wtf casino game is two deliverables plus a small metadata file: 1. **The game's rules**, which settle bets against the house liquidity pool. Write them for one of the two engines, or as your own Solidity contract. See [Choose how to write your game](#choose-how-to-write-your-game). 2. **A static web frontend** (any framework, or none) that the Chain.wtf app embeds as a sandboxed iframe. It contains no wallet code. It talks to the host page through the `@chain-protocol/casino-sdk` bridge, and the host signs everything. 3. **A `game.manifest.json`** served next to your frontend, describing the game to the host. The flow of a bet: the player configures a wager in your UI, your UI asks the host to open a session, and the host signs the transaction. The game contract requests verifiable randomness (VRF) and settles the outcome. Your UI animates the result and reveals the payout. ## Choose how to write your game **Write your game for the slot engine or the graph engine whenever it fits.** An engine is a shared game contract that the chain already trusts. You describe your game's math in a file or a TypeScript module, and the engine plays it. You write no Solidity. An engine game reaches players faster: - **Only your frontend is reviewed.** The chain derives the RTP, the maximum payout and the risk figures from your game's math when it deploys, and refuses math that breaks a rule. Nobody reviews or audits a contract, because you ship none. - **It joins the offering right away.** A game whose highest RTP is at or below the approval ceiling (98% at genesis) is whitelisted as soon as it deploys. A game above the ceiling waits for the security council. Pick the engine that fits your game: | Your game | Write it with | | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | One draw settles the bet: slots, scratch cards, Limbo, dice over or under a target. | [Slot engine](/sdk/casino/slot-engine/) | | Several steps or player choices: Hilo, a tower climb, blackjack, double-or-nothing, roulette, Plinko. | [Graph engine](/sdk/casino/graph-engine/) | | Neither engine can express it. | Your own `ICasinoGameV2` contract (Steps 4 and 5) | **Write your own contract only when your game fits neither engine.** You then own the contract's math, its risk quotes and its security. The platform reviews the contract as well as the frontend, and the contract needs an audit. Nothing on chain proves its RTP, so it always waits as a pending game until the security council approves it. Steps 1 to 3 below are the same for every game. With an engine, follow the engine's page in place of Steps 4 and 5, then come back for Step 6. ## Step 1: create the game Create a game from the coinflip example with the `chain` command from `@chain-protocol/cli`: ```sh npx @chain-protocol/cli init casino my-game ``` With Bun: ```sh bunx @chain-protocol/cli init casino my-game ``` Expect `Created my-game from the Coinflip example.` followed by the next steps. `my-game/` holds: | Path | What it is | | -------------------------------- | ------------------------------------------------------------------------------- | | `contracts/src/CoinflipGame.sol` | The game contract. | | `deployment.ts` | Names the contract the SDK simulator compiles and deploys. | | `src/` | The game frontend (React and Vite). | | `public/game.manifest.json` | The manifest the host fetches from your frontend's origin. | | `package.json` | Depends on `@chain-protocol/casino-sdk` and `@chain-protocol/cli`, one version. | ## Step 2: install the dependencies ```sh cd my-game npm install ``` With Bun: ```sh cd my-game bun install ``` ## Step 3: play a round Start the frontend in one terminal: ```sh npm run dev ``` With Bun, run `bun run dev`. Expect Vite to serve the game at `http://localhost:3100/`. Start the SDK simulator in a second terminal: ```sh npm run simulator ``` With Bun, run `bun run simulator`. The script runs `chain dev casino --frontend-url http://localhost:3100`. It starts the dev chain, the VRF node and a minimal casino host, compiles and deploys the contract `deployment.ts` names, and serves the simulator page. Expect these lines when it is ready: ```text [simulator] Deployed CoinflipGame at 0x… Play your game at http://127.0.0.1:3300/?game=http%3A%2F%2Flocalhost%3A3100&rpc=… Press Ctrl-C to stop. ``` Open the URL it prints. The simulator page mounts your frontend in its iframe, and the player account holds 1,000,000 test chUSD. Pick **Heads** or **Tails** and press **BET**. Expect the coin to land within a few seconds, the result to show in the top-right rail, and `session 1 SETTLED` in the log under the frame. The SDK simulator behaves like the production host: optimistic sessions, a lagging indexer feed and balance guarding, against a real VRF node on your machine. Everything you do from here on replaces the coinflip contract and UI with your own. ## Step 4: write your game contract Skip this step and Step 5 if your game uses an engine. The [Slot Engine](/sdk/casino/slot-engine/) and [Graph Engine](/sdk/casino/graph-engine/) pages show how to write and play an engine game. Implement [`ICasinoGameV2`](/sdk/casino/building-games/#21-icasinogamev2-full-solidity). Import it from the SDK, as `contracts/src/CoinflipGame.sol` does: ```solidity import { ICasinoGameV2 } from '@chain-protocol/casino-sdk/contracts/ICasinoGameV2.sol'; ``` The functions that matter most: | Function | What it does | | ----------------- | ------------------------------------------------------------------------------------------- | | `onSessionStart` | Validates the bet (`gameData`), sets up session state, usually requests randomness. | | `onRandomness` | Receives the VRF result, computes the outcome, settles (or advances a multi-step game). | | `onPlayerAction` | Optional. Mid-session moves for multi-action games (hit or stand, reveal a mine, cash out). | | `quoteCaps` | The most stake and the largest net win one session of a bet configuration can reach. | | `quoteRiskParams` | Expected payout, maximum payout, its probability and the body variance of a bet. | **Your quotes set your fees and your maximum wager.** `openSession` computes every session's fees from the edge you quote (`wager − expectedPayout`), and works out the largest wager it accepts from what `quoteRiskParams` returns. Get these right before anything else: - `expectedPayout` is RTP × wager, under the player's best policy when your game has choices. - `probabilityWad` is the probability of exactly `maxPayout`, not of any win. - `bodyVarianceScaled` is the variance of the payout with the top tier excluded, in wei² × 1e18. A quote that fails a guardrail reverts `openSession`. What each figure must mean, the errors, the fee split and the bet-size cap are in [Contract Constraints → What your quotes decide](/sdk/casino/contract-constraints/#what-your-quotes-decide). Two shapes cover almost every game: - **Instant** (coinflip, dice, slots): `onSessionStart` requests randomness, `onRandomness` settles. One transaction from the player's point of view. - **Multi-action** (blackjack, mines): the session stays open across several `onPlayerAction` calls, each optionally requesting more randomness. Both patterns, with full contract examples, are in [Building Casino Games §2.3](/sdk/casino/building-games/#23-contract-patterns-instant-vs-multi-action). Two rules worth knowing before you write a line of code: - **Deriving dice or cards from random bytes**: use rejection sampling, never `byte % n`. See [Randomness → Dice](/sdk/casino/randomness-dice/). A game gets one random value per request and derives every other value it needs from it; `onRandomness` may not request randomness again. - **The facet enforces phase rules, timeouts and payout caps** on top of your contract. Read [Contract Constraints](/sdk/casino/contract-constraints/) so your state machine doesn't fight it. Slots-like games with rare huge payouts also need the tiered reserve model in [Slots Risk & Reserves](/sdk/casino/slots-risk-and-reserves/). ## Step 5: deploy it in the SDK simulator `deployment.ts` tells `chain dev` which contract to compile and deploy: ```ts import type { DeploymentInput } from '@chain-protocol/casino-sdk'; export default { kind: 'casino', contract: { source: 'src/MyGame.sol', name: 'MyGame' }, } satisfies DeploymentInput; ``` - `contract.source` is relative to `contracts/`, and `contract.name` is the contract in that file. - If the constructor takes arguments, list them in `constructorArgs`. `{{diamond}}` and `{{token}}` stand for the host and the token addresses of the chain you deploy to. - Keep `deployment.ts` self-contained: import only types. `chain dev` loads it on its own, and the platform reads the same file when it deploys your submission. While `npm run simulator` runs, it watches `deployment.ts` and every Solidity file the build read. Save a change and it recompiles, deploys the contract at a fresh address and lists it first in the simulator page's game picker. You do not restart anything. A compile error prints in the simulator's terminal, and the previous deployment keeps running. To compile the contract with your own toolchain as well, Hardhat resolves `@chain-protocol/casino-sdk/contracts/…` from `node_modules`. Foundry needs the remapping `@chain-protocol/=node_modules/@chain-protocol/`. ## Step 6: build the guest UI Start from the example's `src/` or from scratch. The whole bridge is small: ```typescript import { connectGameToHost } from '@chain-protocol/casino-sdk/guest'; import type { HostApi, HostSnapshot } from '@chain-protocol/casino-sdk/guest'; const connection = connectGameToHost({ setState: async (snapshot: HostSnapshot | null) => { // called on every host update: wallet status, balances, session rows render(snapshot); }, }); const hostApi: HostApi = await connection.promise; // Place a bet: ABI-encode your bet config as gameData const { sessionKey } = await hostApi.openSession({ wager: parseUnits('10', snapshot.token.decimals).toString(), gameData: encodeAbiParameters(...), }); // ...watch snapshot.sessions.items for the row with your sessionKey to reach // a terminal phase, decode raw.gameState, play your result animation, then: await hostApi.revealOutcome({ sessionId }); ``` The essential guest rules: - Only enable betting when `snapshot.wallet.status === 'ready'`. - Clamp the bet input to what the platform accepts right now: `hostApi.getMaxWager({ gameData })` answers the maximum wager for the bet the player selected. Ask again when the player changes the bet, and never compute the maximum yourself. See [Building Casino Games §4](/sdk/casino/building-games/#asking-the-host-for-the-maximum-wager). - Match your bet to a session row via the returned `sessionKey`. The row's `phaseName` turning terminal (`SETTLED`, `FORFEITED` or `CANCELLED`) is your settle signal. - **Always call `revealOutcome` when your win animation finishes.** Until then the host hides the payout from its balance displays so the top bar doesn't spoil the result. - Call `connection.destroy()` on unmount. The full lifecycle, snapshot reference, and per-game frontend patterns (coinflip, blackjack, mines) are in [Building Casino Games §3–6](/sdk/casino/building-games/#3-bridge-sdk-guest--host). If your frontend runs on another port, pass it to the simulator: `npx chain dev casino --frontend-url http://localhost:5173` (`bunx chain dev casino …` with Bun), or change the `simulator` script in `package.json`. The setup panel lets you raise the indexer lag, force wallet states and change the maximum-wager answer. See [Local Simulator](/sdk/casino/local-simulator/) for the full tour. ## Step 7: add the manifest Serve a `game.manifest.json` from the **same origin** as your game. The example serves it from `public/game.manifest.json`; adjust it to your game. The `gameId` must follow the canonical id rules, and the host validates `presentation` and `capabilities`. Validate yours locally with `validateCasinoGameManifest` from the SDK ([details](/sdk/casino/building-games/#5-gamemanifestjson--host-validation)). ## Step 8: test the unhappy paths Before shipping, use the simulator to make sure your game survives production reality: - **Slow indexer**: raise the indexed-feed lag in the setup panel. Your game should ride the optimistic session rows without flicker or duplicate results. - **Wallet not ready**: force `disconnected` or `setup-required` and check your non-ready screens. - **Maximum wager**: set the setup panel's max-wager answer to a low `limit`, then to `unknown`. Your bet input should clamp to the limit and fall back to your own limits, never to unlimited. - **Refresh mid-round**: reload the iframe during an open session. Your UI should recover the session from the snapshot instead of starting blank. ## Step 9: ship it Build your frontend with `npm run build` or `bun run build`, which writes `dist/`, and host it at a stable HTTPS URL with `game.manifest.json` at the same origin. What else you deliver depends on how you wrote the game: - **With an engine,** submit the repository with its `deployment.ts` through the developer platform. The platform builds your title, the chain checks its math when it deploys, and the review covers your frontend only. A title at or below the approval ceiling is whitelisted at once. - **With your own contract,** you also deliver the audited game contract. The review covers the contract and the frontend, and the security council approves the game before it takes bets. The Chain.wtf maintainers wire the rest: indexer registration and the catalog entry. ## Keep up with SDK releases The host tells your game its SDK version in `snapshot.sdkVersion`. A breaking release reaches staging as a canary before it reaches production, and your game handles both hosts during that notice period. Read [Building Casino Games → SDK versions and breaking releases](/sdk/casino/building-games/#sdk-versions-and-breaking-releases). ## Where to next - [Building Casino Games](/sdk/casino/building-games/): the complete reference this page summarizes. - [Local Simulator](/sdk/casino/local-simulator/): everything `chain dev casino` runs and replicates. - [Contract Constraints](/sdk/casino/contract-constraints/): the rules the facet enforces on your contract.