Track Casino SDK releases, breaking changes, and unreleased work. Check what changed before you move your game to another version.
Versions follow semver from 0.5.0, and each heading carries the release date. In 0.x a minor
bump is breaking and a patch is not. Releases before 0.5.0 used date versions, YYYY.MM.DD-N.
0.7.0 (2026-10-28)
Section titled “0.7.0 (2026-10-28)”Released in lockstep with @chain-protocol/pvp-sdk and @chain-protocol/cli 0.7.0.
-
Optional logical guest windows via
openWindow, callable only after the guest advertises the exact target. Public exports includeWINDOW_TARGETS,supportsWindowTarget,WindowTarget,WindowCapabilitiesandWindowOpenResult. LegacyopenPanelbehavior is unchanged. -
SteeringDeployercreates a graph title’s Steering contract at a predictable address.deploySteering(initCode)runsCREATE2with a zero salt and emitsSteeringDeployed(steering, initCodeHash);steeringAddress(initCodeHash)predicts it. The salt is not bound to the sender, so a build can name the address before it knows who sends the deploy, and titles with the same init code share one Steering contract. -
steeringRefusal({ maxIndependentDraws, configurationChunks, nodeChunks })returns why derive orGraphTitleDeployerwould refuse a packed title under a Steering contract (a ladder, or a table bundle), or null.deployGraphGamethrows the same reason as before. -
A graph deployment’s
steeringtakesconstructorArgs, with the same{{diamond}}and{{token}}placeholders as a contract deployment.defineDeploymentdefaults it to[], and the local node passes it to the Steering contract’s constructor. -
defineDeploymenttakes an optionalfrontendfor every kind:directory,framework(vite,nextjsortanstack-start),installCommand,buildCommandandoutputDirectory. It tells the platform where the frontend lives and how it builds; without it the platform detects the framework.
- Slot and graph titles name only chunks their own deployer created.
SlotTitleDeployerandGraphTitleDeployerrecord every pointerdeployChunkcreates inisChunk, and refuse any other pointer withSlotTitleDeployer__NotAChunk(pointer)orGraphTitleDeployer__NotAChunk(pointer). A contract that only looks like a chunk could otherwise change its code after the deployer derived the title’s RTP from it. The deploy helpers already upload through the deployer they create the title with. The fix ships as a new version of both deployers inslotGameDeployerArtifactandgraphGameDeployerArtifact. - Graph titles: a chance cannot reach a stake it never escrowed. Derive now refuses a chance
any of whose outcomes holds a stake other than the chance’s own, with
GraphDerive__ChanceStakeMismatch(id), as the compiler always has. A decision holds its action 0’s stake. A forfeit at a decision pays the default value less any part of that stake the session has not escrowed, as a settlement does. Engines and titles are immutable, so the fix ships as a newGraphTitleDeployer:graphGameDeployerArtifactcarries it, and titles from the previous deployer keep the previous engine. Inline derive costs about 2% more gas and batched derive under 0.5% more; the inline-scale graph still derives in one transaction. GraphNode’schancekind takes an optionalstake, the stake a packed chance records.unpackGraphsets it only where it differs from the stake the chance’s first outcome holds, which the compiler never packs, andderiveGraphrefuses such a chance.graphGameAbilists the new error.
Changed
Section titled “Changed”- Breaking: the player’s contract is called the Smart Vault again.
HostSnapshotV1reports it aswallet.smartVaultAddressandbalances.smartVaultBalance, replacingsmartWalletAddressandsmartWalletBalance. The docs, the simulator and the examples use the new names. - Breaking: every creation call names its game developer.
SlotTitleDeployer.deployTitleanddeployThresholdTitle,GraphTitleDeployer.deployTitleanddeployBatchedTitleandGameBytecodeDeployer.createGametakeaddress developerbeforeauthorshipId, and the registry’sregisterEngineTitleandregisterBytecodeGametake it aftercreator. Anyone may name any developer, since the name only directs the game dev fee. A zero developer reverts withGameRegistryFacet__ZeroDeveloper(LocalGameRegistry__ZeroDeveloperon the simulator), and with an authorship the named developer must be its writer, elseAuthorship__DeveloperMismatch(authorshipId, developer).underivedTitlecarriesdeveloperaftercreator. The SDK’sdeployTableGame,deployThresholdGame,deploySlotGame,deployGraphGameandcreateBatchedGraphGametake an optionaldeveloperand name the wallet client’s account without one. Against a deployer from before this change the wrappers simulate the new call first and, when the deployer has no function for it, send the call without a developer, whose registry makes the sender the developer; that fallback goes once every registered deployer is redeployed. - Breaking: casino randomness settles whenever it lands. A word delivered after the deadline
reaches
onRandomnesslike any other, instead of refunding the stake. The randomness timeout is now 45 seconds and cannot be set lower, socancelStuckRandomnessrefunds only after that.LocalCasinoHost.RANDOMNESS_TIMEOUT_SECONDSis 45. See ADR 0026. - The Title names listed under
0.6.0-canary.3are no longer deprecated. They stay as aliases of the Game names, and the deployed results’titlestays besidegame.
0.6.0 (2026-10-20)
Section titled “0.6.0 (2026-10-20)”0.6.0 publishes the 0.6.0 canaries under the latest dist-tag, together with the changes of
0.5.2. Coming from 0.5.x, read the three canary entries below: they list every breaking change
and every rename.
Documentation
Section titled “Documentation”- Engines first. Getting Started and the overview tell game devs to write a game for the slot
engine or the graph engine whenever it fits: the chain checks the math when it deploys, the
review covers the frontend only, and a game at or below the approval ceiling is whitelisted when
it deploys. An own
ICasinoGameV2contract is for games neither engine can express. - A Graph Engine guide: writing a title, checking its figures, playing it locally, submitting
it, the frontend, fair ladders, table bundles, Steering contracts and deploy costs. Its example,
Climb, is a tower and a scratch card with a full frontend:
bun run start:graphin a checkout plays it on the SDK simulator. - The Slot Engine page covers the Game names, the browser-safe
slot-engine/thresholdsubpath and how to deploy a threshold Game.
0.6.0-canary.3 (2026-10-20)
Section titled “0.6.0-canary.3 (2026-10-20)”- A browser-safe quote of a plain graph configuration.
quoteConfiguration(table, configuration, wager)from@chain-protocol/casino-sdk/graph-enginereturns, bit for bit, what the engine’squoteCaps,quoteRiskParamsandonSessionStartreserve giveencodePlainGameData(configuration):maxEscrowStake,maxReservedProfit,openingReservedProfit,maxPayout,probabilityWad,expectedPayoutandbodyVarianceScaled(the typeGraphQuote).graphQuoteTable(compiled, { steered })builds itsGraphQuoteTablefrom a compiled Game; store its figures and quote in the browser without deriving there. A Game deployed with a Steering contract issteeredand reserves its whole cap when a session opens. Out-of-range configurations throw aRangeError. @chain-protocol/casino-sdk/slot-engine/threshold, a browser-safe subpath with no Node imports:thresholdGameData,decodeThresholdGameData,rollResult,rollThreshold,decodeThresholdGameState,quoteThreshold,thresholdRtpWadRange,thresholdGameInputsHash,ThresholdTargetOutOfRangeErrorand the types.quoteThresholdnow also returnsmaxEscrowStake,maxReservedProfitandopeningReservedProfit, so it answers whatquoteCaps,quoteRiskParamsand the opening reserve give.@chain-protocol/casino-sdk/local-host, for host-path tests on EDR or any dev chain:deployLocalCasinoHost,whitelistOnLocalHost,decodeCasinoSession, and the ABIs and bytecode ofLocalCasinoHost,LocalTestTokenandTestRandomnessRouter, whosefulfill(requestId, word)delivers the word a test picks.- Slot threshold Games from a TS module.
deployment.tstakes{ kind: 'threshold', name, title }, wheretitlenames a module whose default export lists the Game’sThresholdConfigurations by index.chain dev casinodeploys it through the simulator’sSlotTitleDeployer. - Game IDs (ADR 0025, proposed). The simulator’s registry gives each registered game the next
Game ID from 1, with
gameIdOf,gameOfId,gameCountand aGameIdAssignedevent, and/__local-contracts.jsonlists every game with itsgameId. The production diamond gains the same once the security council cuts theGameRegistryFacet. runCasinoPropertiesandrunCasinoConformance(./conformance) take{ address }of a Game already on the chain as well as runtime code, so they run an engine clone whose implementation is there.
Changed
Section titled “Changed”quoteBundlereturnsopeningReservedProfittoo, the whole cap, and its result is aGraphQuote.thresholdGameDatathrows aRangeError(still anError) for an index or target out of range.- Smaller guests. The engine and deployer ABIs are marked pure and tree-shake away: a guest that walks and encodes a graph Game drops from 46.3 KB to 18.8 KB minified (12.5 KB to 6.2 KB gzip).
- Faster derive. A Game’s max net is derived once, not per configuration: Dice’s 9,799 configurations compile in 0.2 s instead of 2.6 s, with the same figures.
chain dev casinowatches every module a Game module imports, workspace packages outside the project included, and redeploys when one changes. Installed packages undernode_modulesstay unwatched.chain dev casinoleaves its referenceCoinflipGameout of the game picker when the project’s Game registers under that name.LocalCasinoHost’s bytecode changes for the Game IDs. Read it from./local-hostrather than a copy.
Deprecated
Section titled “Deprecated”-
Title is now Game in the TypeScript API (original-games spec U2). Every old name below still works and is the same value, and goes in
0.7.0. Deployed results carry the address asgamebeside the deprecatedtitle. The Solidity contracts, their functions, events and errors, thedeployment.tsfieldtitleand the simulator’s contract keys (slotTitleDeployer,graphTitleDeployer) keep their names.Subpath Old name New name graph-enginecompileGraphTitlecompileGraphGamegraph-engineGraphTitleDefinitionGraphGameDefinitiongraph-engineCompiledGraphTitleCompiledGraphGamegraph-enginedeployableTitledeployableGamegraph-enginedeployGraphTitledeployGraphGamegraph-engineDeployGraphTitleParamsDeployGraphGameParamsgraph-engineDeployedGraphTitleDeployedGraphGamegraph-enginecreateBatchedGraphTitlecreateBatchedGraphGamegraph-enginederiveGraphTitlederiveGraphGamegraph-enginegraphTitleInputsHashgraphGameInputsHashgraph-enginegraphTitleAbigraphGameAbigraph-enginegraphTitleDeployerAbigraphGameDeployerAbigraph-enginegraphTitleDeployerArtifactgraphGameDeployerArtifactgraph-engineMAX_TITLE_CHUNKSMAX_GAME_CHUNKSslot-engineslotTitleAbislotGameAbislot-engineslotTitleDeployerAbislotGameDeployerAbislot-engineslotTitleDeployerArtifactslotGameDeployerArtifactslot-enginereadTitlereadTableGameslot-enginereadThresholdTitlereadThresholdGameslot-enginedeployTitledeployTableGameslot-engineDeployTitleParamsDeployTableGameParamsslot-engineDeployedTitleDeployedSlotGameslot-enginedeployThresholdTitledeployThresholdGameslot-engineDeployThresholdTitleParamsDeployThresholdGameParamsslot-enginedeploySlotTitledeploySlotGameslot-engineDeploySlotTitleParamsDeploySlotGameParamsslot-enginecompileSlotTitleFilecompileSlotGameFileslot-enginecompileTitleFilecompileTableGameFileslot-engineTitleDefinitionSlotGameDefinitionslot-engineCompiledTitleCompiledTableGameslot-engineThresholdTitleThresholdGameslot-engineSlotTitleSlotGameboth slot subpaths thresholdTitleInputsHashthresholdGameInputsHash
Compatibility
Section titled “Compatibility”- Nothing on chain changes for Games: engine titles deployed with canary.1 or canary.2 keep working, and these quotes match them.
- Code that compares a whole
quoteThresholdorquoteBundleresult, or spreads it into an array, sees the added fields. - The old Title names compile with a deprecation warning. Move to the new names before
0.7.0.
0.6.0-canary.2 (2026-10-20)
Section titled “0.6.0-canary.2 (2026-10-20)”- Committed ladder rounds (ADR 0023). Game data
0x03 ‖ uint16 configuration ‖ uint8 roundCount ‖ roundCount × uint8 option ‖ tailopens a session at a fair-ladder root that plays every listed option in one randomness callback and settles. Auto-bet can now drive a ladder such as Hilo.@chain-protocol/casino-sdk/graph-engineaddsencodeCommittedLadderGameData,decodeCommittedLadderGameData,checkCommittedLadder,walkCommittedLadder,committedLadderReserve,InvalidCommittedRoundsErrorand the typeCommittedLadderRounds. A configuration takes at most itscommittedRoundsoptions, which derive sets from the callback gas and the ladder’s widest option: 93 for Hilo, 16 for options of 256 outcomes. More rounds, none, or a root that is not a ladder revertGraphEngine__InvalidCommittedRounds. raw.actionson session items: everyactionDatasubmitted to the session, in step order, asSubmittedAction { step, actionData, transactionHash? }.stepis the step the action produced, so a randomness request that step made has it as itsnonce. The list includes the action that settled the session and is empty before the first action. A multi-step game rebuilds a session fromrandomnessRequestsandactionsafter a reload or on another device. A host that does not report actions omits the field, so treatundefinedas unknown.LocalCasinoHost, and the production casino facet, emitCasinoSessionActionSubmitted(uint256 indexed sessionId, uint32 indexed step, bytes actionData)for every player action, before the step’sCasinoSessionAdvancedorCasinoSessionSettled. The simulator reportsraw.actionsfrom it.
Changed
Section titled “Changed”- Breaking: graph titles need the new
GraphTitleDeployer.ConfigurationFigures, whichconfigurationFiguresandderivereturn, ends withuint8 committedRounds, andDerivedGraph’s configurations carry it. Titles of the canary.1 engine reject tag0x03withGraphEngine__InvalidGameData. Redeploy a game, and regenerate any artefact that stores derived figures.
0.6.0-canary.1 (2026-10-19)
Section titled “0.6.0-canary.1 (2026-10-19)”Changed
Section titled “Changed”-
Breaking:
SlotTitleDeployerandGraphTitleDeployertake the diamond’s game registry (IGameRegistry) as their only constructor argument and register every title on it with the creator, an authorship id, the title inputs hash and the lowest and highest RTP of its bet configurations.deployTitle,deployThresholdTitleanddeployBatchedTitletake anauthorshipId, zero for none, and the SDK’s deploy functions an optionalauthorshipId. The deployers’ RTP band,setRtpBandand owner are gone: they still refuse an RTP of 100% or more, and the diamond checks the RTP band.titleAuthoris nowtitleCreator, andGraphTitleDeployer.pendingTitleAuthoris nowunderivedTitle, andGraphTitleDeployer__NotPendingis nowGraphTitleDeployer__NotDeriving. -
Breaking: casino deadlines are block timestamps.
CasinoSession.deadlineBlockis nowdeadline, a session has 30 seconds to receive its randomness and 86,400 seconds (24 hours) to act, andLocalCasinoHostexposesACTION_TIMEOUT_SECONDSandRANDOMNESS_TIMEOUT_SECONDS. The conformance suite expires a session withadvanceTime. -
Breaking: randomness delivered after its deadline no longer reaches
onRandomness. The host cancels the session and refunds the stake, ascancelStuckRandomnessdoes, and reports a zero randomness inCasinoSessionSettled. -
Breaking: a game gets one random value per request. A step reached from
onRandomnessmay not move toWAITING_RANDOMNESS. The casino facet reverts the callback withCasinoGameFacet__RandomnessRequestedFromCallback, and once the randomness deadline passescancelStuckRandomnessrefunds the session’s whole remaining escrow. Derive further values from the one you received. -
openSessionandsubmitActionarepayable. The call whose step requests randomness carries its fee, and a call whose step requests none gets its ETH back. -
The simulator pays for randomness the same way.
LocalCasinoHoststarts with an empty router balance, the simulator page attaches the host’srequiredPaymentto every open and action, and a step reached fromonRandomnessthat requests randomness fails delivery withLocalCasinoHost__RandomnessRequestedFromCallback.
0.5.2 (2026-10-20)
Section titled “0.5.2 (2026-10-20)”- Optional Host capability discovery on guest connections and
playAccessopt-in forrequestPlayAccess(). Casino gains the access intent; PvP keeps its existing optional method and legacy callers. An acknowledgment means the existing Host UI was accepted, never that the player is authenticated or ready. Pending duplicate requests coalesce, and connection destruction invalidates pending/queued requests. Simulator requests record explicit fixture intent without automatically changing readiness. See “Requesting play access” in the README.
Changed
Section titled “Changed”- Breaking: casino deadlines are block timestamps.
CasinoSession.deadlineBlockis nowdeadline, a session has 30 seconds to receive its randomness and 86,400 seconds (24 hours) to act, andLocalCasinoHostexposesACTION_TIMEOUT_SECONDSandRANDOMNESS_TIMEOUT_SECONDS. The conformance suite expires a session withadvanceTime. - Breaking: randomness delivered after its deadline no longer reaches
onRandomness. The host cancels the session and refunds the stake, ascancelStuckRandomnessdoes, and reports a zero randomness inCasinoSessionSettled. - Breaking: a game gets one random value per request. A step reached from
onRandomnessmay not move toWAITING_RANDOMNESS. The casino facet reverts the callback withCasinoGameFacet__RandomnessRequestedFromCallback, and once the randomness deadline passescancelStuckRandomnessrefunds the session’s whole remaining escrow. Derive further values from the one you received. - Breaking: a step reached from
onRandomnessmay not change the escrow. The casino facet reverts the callback withCasinoGameFacet__EscrowChangedFromCallback, which replacesCasinoGameFacet__EscrowIncreaseNotAllowed, and once the randomness deadline passescancelStuckRandomnessrefunds the session’s whole remaining escrow. Return escrow fromonPlayerActioninstead. The simulator refuses it withLocalCasinoHost__EscrowChangedFromCallback. openSessionandsubmitActionarepayable. The call whose step requests randomness carries its fee, and a call whose step requests none gets its ETH back.- The simulator pays for randomness the same way.
LocalCasinoHoststarts with an empty router balance, the simulator page attaches the host’srequiredPaymentto every open and action, and a step reached fromonRandomnessthat requests randomness fails delivery withLocalCasinoHost__RandomnessRequestedFromCallback.
0.5.1 (2026-10-17)
Section titled “0.5.1 (2026-10-17)”- Guest panels. A game can declare panels the host may open from its toolbar:
connectGameToHost(methods, { capabilities: { panels: [...] } })with anopenPanel({ panel })method. Casino panels areinfo,historyandaudio. The host reads the declaration fromconnection.capabilitieson the connectionconnectHostToGamereturns. A game that declares nothing keeps working as before: the host gets an empty capability set and shows no panel controls. See “Host capabilities and panels” in the README.
Changed
Section titled “Changed”- The bridge runs on penpal 8, which fixes listener and timer leaks on failed or destroyed
handshakes. A host on penpal 8 still connects to games built on SDK
0.5.0with penpal 7.
0.5.0 (2026-10-15)
Section titled “0.5.0 (2026-10-15)”sdkVersionin the snapshot.HostSnapshot.sdkVersionis the version of the SDK the host was built with, for example0.5.0or0.6.0-canary.3. The production host and the simulator set it from the SDK’spackage.jsonwhen they are built.hostSupports(snapshot, version)answers whether the host’s SDK is atversionor newer. It compares major, minor and patch and ignores the prerelease tag, so a canary host answers as the version it previews. It answersfalsefor a snapshot withoutsdkVersion, which comes from a host older than0.5.0.
Changed
Section titled “Changed”-
Breaking: the package is
@chain-protocol/casino-sdk. It was@chain/casino-sdk. -
Breaking:
apiVersionis removed from the snapshot and fromgame.manifest.json.schemaVersionstays as the manifest file’s format version. A manifest that still carriesapiVersion: 1validates, and the validated manifest leaves it out. -
Breaking: the TypeScript type names lose their version suffixes, with no aliases left behind. The Solidity names, such as
ICasinoGameV2, keep theirs.Before After CasinoGameManifestV1CasinoGameManifestHostSnapshotV1HostSnapshotHostApiV1HostApiGuestApiV1GuestApiRandomnessRequestV1RandomnessRequestRandomnessRequestEchoV1RandomnessRequestEchoRandomnessRequestVerificationV1RandomnessRequestVerificationRandomnessVerificationV1RandomnessVerificationVrfVerificationChecksV1VrfVerificationChecks -
The slot engine is part of the SDK. Import its library from
@chain-protocol/casino-sdk/slot-engineand its contracts from@chain-protocol/casino-sdk/slot-engine/contracts/*. Its title commands move to thechainCLI:chain slots <command>from@chain-protocol/clireplacesbun run cli -- <command>inslot-engine/, and theslot-enginebin is gone. From the SDK root,slots:e2e,slots:exampleandslots:testreplacee2e:simulator,example:generateandtest. -
Breaking: the game interface and the session codec moved from
simulator/contracts/tocontracts/. Import them as@chain-protocol/casino-sdk/contracts/ICasinoGameV2.sol,.../CasinoSession.soland.../CasinoSessionCodec.sol. -
The package publishes to npm as ESM with type declarations, MIT, for Node 22+ and Bun 1.4.2+. It publishes
.,./guest,./contracts/*,./slot-engineand./slot-engine/contracts/*for games, and./hostand./conformanceas internal subpaths outside semver../scripts/*is gone. -
Versions follow semver, in lockstep across both SDKs and the
chainCLI. Breaking releases publish asX.Y.Z-canary.Nunder the npmcanarytag before production switches.
Removed
Section titled “Removed”- The SDK ZIP on the docs site and the
shadcn add @chain/casino-sdkregistry item. Install@chain-protocol/casino-sdkfrom npm.
Compatibility
Section titled “Compatibility”- Replace
@chain/casino-sdkwith@chain-protocol/casino-sdkin your imports andpackage.json. - Rename the types in the table above.
- Import
ICasinoGameV2.solfrom@chain-protocol/casino-sdk/contracts/in Hardhat, or remap@chain-protocol/=node_modules/@chain-protocol/in Foundry. - Stop reading
snapshot.apiVersion. To branch on the host, callhostSupports(snapshot, '0.6.0'); never parsesdkVersionyourself. - Remove
apiVersionfrom yourgame.manifest.json. A manifest that keeps it still validates.
2026.10.11-1
Section titled “2026.10.11-1”hostApi.getMaxWager({ gameData })onHostApiV1. A game asks the host for its maximum wager and passes the game data it is about to open the session with. The answer is aMaxWagerResult:{ kind: 'limit', maxWager },{ kind: 'no-limit' }or{ kind: 'unknown' }. Without game data (hostApi.getMaxWager()) the host answers for empty game data, the game’s base configuration. The production host reads the vault’s maximum wager for the game it serves and answersunknownwhen it cannot be worked out.- “Max-wager answer” in the simulator’s setup panel. The harness answers
getMaxWagerwith a fixed cap in whole tokens, 1000 by default, or withno-limitorunknown.
Changed
Section titled “Changed”- Breaking:
computeMaxWageris removed. A vault’s bet-size cap follows from the game’s quotes and the vault’s state, so a game can no longer compute it from a multiplier. - Breaking: the snapshot’s
casinoblock is removed, with its four fieldsavailableLiquidity,maxBetRiskBps,maxAllowedReservedProfitandmaxBetAmount. - Breaking:
maxWagerinMaxWagerResultis a decimal string in token base units, like thewagerofopenSession. It was abigint. - Breaking for hosts:
getMaxWageris a required method ofHostApiV1. - The coinflip and Lucky Reels examples ask the host for their maximum wager. The coinflip example passes the game data of the selected pick.
- A game’s quotes set its fees and its bet-size cap.
openSessioncomputes every fee of a session from its edge,wager − expectedPayout, and works the largest wager it accepts out of the fourquoteRiskParamsfigures andmaxReservedProfit. The game dev fee is credited when the session settles or is forfeited, whether the player won or lost. A cancelled session pays no fee.CONTRACT_CONSTRAINTS.mdhas a new section, “What your quotes decide”, with what each figure must mean, the fee split and the bet-size cap. - Quotes are checked against guardrails at whitelisting and at every
openSession. A quote outside the RTP band, withmaxPayoutbelowexpectedPayout, with a body variance of0while outcomes belowmaxPayoutpay, or with a plain variance that is not positive reverts withQuoteFiguresLib__RtpOutsideBand,QuoteFiguresLib__MaxPayoutBelowExpectedPayout,QuoteFiguresLib__BodyVarianceMissingorQuoteFiguresLib__VarianceNotPositive. - A wager above its bet-size cap reverts with
CasinoGameFacet__WagerAboveRiskBudget,CasinoGameFacet__WagerAboveBreakEvenRuleorCasinoGameFacet__NetWinAboveSingleWinCap. These replaceCasinoGameFacet__BetRiskExceedsLimit. - The reserve reads
expectedPayoutand holds the exact count of jackpots. It expects the vault to keep a session’s LP income and nothing else of its edge, and for a heavy-tail game it holds the smallest number of jackpots that covers 99.99% of outcomes, one or two more than before.SLOTS_RISK_AND_RESERVES.mdhas the rule and a worked example. - Breaking for hosts that decode session bytes: the fixed part of the encoded session grew
from 255 to 295 bytes. The session’s edge and the three rates its fees are computed from
follow
operator. - Docs corrected:
StepResulthas nooutcomefield, the default randomness timeout is 15 blocks, andquoteCapsreturns a session’s largest stake and net win, not a wager range.
Compatibility
Section titled “Compatibility”- Replace
computeMaxWager(snapshot, { maxMultiplierX })withawait hostApi.getMaxWager({ gameData }), wheregameDatais what you pass toopenSession. A game with one bet configuration callshostApi.getMaxWager(). - Read the limit as
answer.kind === 'limit' ? BigInt(answer.maxWager) : undefined. - The call is asynchronous and the answer belongs to one bet configuration. Ask again when the
player changes the configuration, and on an interval. The coinflip example’s
src/lib/useMaxWager.tsis a hook you can copy. - Delete every read of
snapshot.casino. A check of your reserved profit againstmaxAllowedReservedProfitbecomeswager > BigInt(answer.maxWager). - Treat
unknown, and a rejected call, as “fall back to your own limits”, never as unlimited. ICasinoGameV2is unchanged, so a game compiles and deploys as before. Check its quotes against “What each return must mean” inCONTRACT_CONSTRAINTS.md:expectedPayoutunder the player’s best policy,probabilityWadfor exactlymaxPayout, body variance in wei² × 1e18.openSessionreverts for a game whose quote fails a guardrail.- A host that decodes
CasinoSessionAdvanced.sessionitself must update its codec; snapshots from the previous layout no longer decode.
2026.10.02-2
Section titled “2026.10.02-2”Changed
Section titled “Changed”- The simulator’s chUSD has six decimals, like USDC on mainnet. The local node funds the player and the vault in six-decimal amounts.
- The coinflip and Lucky Reels examples fall back to USDC and six decimals when the
snapshot’s
tokencarries no symbol or decimals. The guide’s snippets fall back to 6, and the slot engine’s simulator end-to-end run sizes its wagers with the token’s decimals.
Compatibility
Section titled “Compatibility”- Parse and format amounts with
snapshot.token.decimals. A game that assumes 18 decimals shows amounts a trillion times too small.
2026.10.02-1
Section titled “2026.10.02-1”Changed
Section titled “Changed”- The player’s contract is called the Smart Wallet.
HostSnapshotV1reports it aswallet.smartWalletAddressandbalances.smartWalletBalance; the docs, the simulator and the coinflip example use the new names.
Compatibility
Section titled “Compatibility”- Games that read the player’s contract address or balance from the snapshot must switch to the new field names.
2026.09.18-1
Section titled “2026.09.18-1”- Slot engine add-on in
slot-engine/: ship a slot as a settlement table on one shared engine contract, with no per-title Solidity.bun run start:slotsruns the local stack with the Lucky Reels example. SeeSLOT_ENGINE.md. - Stake Engine importer.
bun run cli -- import-stakeconverts a Stake Engine math export into a slot engine title, keeping each mode’s RTP and a sample of its books for the frontend. SeeMIGRATING_FROM_STAKE_ENGINE.md.
Changed
Section titled “Changed”- The SDK’s
startscripts run workspace members throughscripts/run-in.ts, which uses the package manager that started them, sobun,npm,pnpmandyarnall work. Apnpm-workspace.yamlships alongsidepackage.json’sworkspaces.
Compatibility
Section titled “Compatibility”- Nothing changes for existing games; the add-on is a separate workspace.
2026.09.17-2
Section titled “2026.09.17-2”- Operators (not open yet).
CasinoGameFacetgainedopenSession(game, vault, wager, gameData, operator), which attributes a session to the frontend that served it. Operator registration is closed for now: every session resolves to the protocol’s default operator, and a missing or unregistered code never reverts. See §7.1 ofCHAIN_WTF_CASINO_GAMES.md. - The simulator’s
LocalCasinoHostmirrors the overload, with permissionlesssetOperator/setDefaultOperatorstand-ins for the council-controlled registry.
Compatibility
Section titled “Compatibility”- Games and guests need no change: the four-argument
openSessionstill works and the operator never reachesSessionContextor the iframe. - The encoded session grew from 235 to 255 fixed bytes (
operatorsits after the risk flags). A host that decodesCasinoSessionAdvanced.sessionitself must update its codec; snapshots from the previous layout no longer decode.
2026.09.17-1
Section titled “2026.09.17-1”Changed
Section titled “Changed”getRandomnessVerificationfollows the current Verify Network router. The router now keeps only the id of an open request and stores nothing after fulfillment, so the host verifies from the fulfillment transaction alone: theRandomnessFulfilledlog, the request echoed in the calldata (hashed back torequestId, whose derivation gained the lockedgasPriceand a service tag), and the enclave signature, whose EIP-712 domain is now version2. The signing key is recovered from that signature — nodes register under the address of their key — and the ECVRF proof is checked under it; no router view is read.checksare now the four that remain meaningful:vrfProofValid,vrfBetaMatchesRandomness,enclaveSignatureValid,signerMatchesFulfiller.validrequires all four.requestcarries the router’s current request fields:consumer,sequence,fulfiller,assignedAt,fee,gasPrice,callbackGasLimit,callbackData.- The simulator’s bundled
local-verify-networkdeploys the current router and fulfills with an RFC 9381 prover on@noble/curves;getRandomnessVerificationruns the same checks against it as production.
Compatibility
Section titled “Compatibility”- Removed from verification results: the
fulfillmentCommittedandfastVerifyComponentsMatchchecks, theartifacts.uPoint/artifacts.vComponentshints, andfulfilledAt. The echoed request’srequester,paymentandclientDataare nowconsumer,feeandcallbackData. Verify views that render checks by name should drop the two removed rows;validkeeps its meaning.
2026.09.15-1
Section titled “2026.09.15-1”SessionPhaseis now exported from the package root and./guest. It mirrors the Solidity enum, so games can comparephase === SessionPhase.SETTLEDinstead of hardcoding3.sessions.items[].phaseNameis typed as the matchingSessionPhaseNameunion.- Payout cap section in
CONTRACT_CONSTRAINTS.md, covering two traps that revert large wins withInvalidPayout: a payout computed separately from the reserve and ending up a few base units above it, and releasing reserved profit on the settling step.
Changed
Section titled “Changed”- Breaking:
computeMaxWagerreturns aMaxWagerResultinstead ofbigint | undefined:{ kind: 'limit', maxWager },{ kind: 'no-limit' }(limits are published but none binds this bet: ≤1x multiplier and nomaxBetAmount), or{ kind: 'unknown' }(the host publishes nothing to derive a limit from). Previously the last two both returnedundefined. - The game interface moved from
solidity/ICasinoGameV2.soltosimulator/contracts/ICasinoGameV2.sol, so drop-in games import it as./ICasinoGameV2.sol. The local node also resolves any other import path ending inICasinoGameV2.solto that file, so a contract can keep its own repo’s import path. CONTRACT_CONSTRAINTS.mdno longer saysopenSessioncallsonSessionStarttwice. The facet has called it once since the stateless-session release.
Compatibility
Section titled “Compatibility”- Update
computeMaxWagercallers:result.kind === 'limit' ? result.maxWager : undefinedrestores the old value. Contracts are unaffected. - Games importing
../../solidity/ICasinoGameV2.solkeep compiling in the simulator. The import falls back to the new file by name.
2026.09.05-1
Section titled “2026.09.05-1”Changed
Section titled “Changed”- Stateless casino sessions. The facet no longer stores sessions: it keeps
keccak256(encodedSession)per id, emits the encoded snapshot inCasinoSessionAdvanced(sessionId, step, requestId, randomness, session)on every non-terminal step, and takes it back as calldata —openSession(game, vault, wager, gameData),submitAction(encodedSession, actionData),cancelStuckRandomness(encodedSession),forfeitExpiredSession(s)(bytes…).CasinoSessionOpenedshrank to(sessionId, game, player, vault, wager)andCasinoSessionSettlednow carries the fulfillingrandomnessinstead ofgameName;CasinoSessionPhaseAdvanced,CasinoSessionEscrowUpdated,CasinoSessionRandomnessRequestedandCasinoSessionRandomnessFulfilledare gone. The game-facing bridge is unchanged — the host resolves the snapshot bytes itself. - Casino randomness provider. Like PvP, the casino facet requests through a swappable
IRandomnessProvider(CasinoConfigFacet.setRandomnessProvider/getRandomnessProvider, eventRandomnessProviderUpdated) and receivesonCasinoRandomnessFulfilled(requestId, randomness, clientData)from it. The production provider isVerifyNetworkCasinoRandomnessProvider, a stateless adapter that echoes the session snapshot through the router’sclientData; it holds the prepaid VRF balance instead of the diamond. randomnessRequests[].nonceis now the session step that made the request (strictly increasing, not consecutive: a coinflip’s only request is"1").getRandomnessVerificationverifies against the redesigned Verify Network router, which stores onlykeccak256(proofCommitment ‖ fulfilledAt)per request: artifacts come from the fulfillment transaction (theRandomnessFulfilledlog and the request echoed in the calldata).artifacts.uPoint/artifacts.vComponentsare now optional derived hints; new fieldsrequest,fulfilledAt; new checkfulfillmentCommitted(the router still commits to this proof — a successful challenge clears it).validrequires all six checks.- The simulator’s
LocalCasinoHostmirrors the stateless facet (simulator/contracts/casino/holds the session struct + codec) and itsgetRandomnessVerificationnow runs the same checks as production against the local router.
Deprecated
Section titled “Deprecated”randomnessRequestDataonHostApiV1.openSession/submitActionis ignored; the facet requests randomness itself. Omit it.
2026.08.30-1
Section titled “2026.08.30-1”sessions.items[].stakeinHostSnapshotV1: the session’s total escrowed stake — the wager plus mid-session increases (e.g. a blackjack double/split), sourced from theCasinoSessionEscrowUpdatedevent. Updates flow through the flashblock layer, so an increase reaches the snapshot without waiting for the indexed feed. Games no longer need to reconstruct total escrow from their owngameState.
Compatibility
Section titled “Compatibility”- The field is optional — feature-detect it. It is absent on hosts that predate this release
and on sessions from before
CasinoSessionEscrowUpdatedexisted; fall back towager(or your own reconstruction). On the live platform it stays absent until the facet upgrade that emits the event ships; the simulator publishes it already.
2026.08.29-1
Section titled “2026.08.29-1”- Forfeit cash-out quote —
ICasinoGameV2.quoteForfeitPayout(ctx). New view on the game interface returning the session’s current cash-out value (stake + accrued winnings) decoded fromctx.gameState. When a session is abandoned pastactionDeadlineBlock, forfeiture now pays the playermin(quote, escrowedStake + reservedProfit)minus a 10% cut (FORFEIT_WINNINGS_CUT_BPS = 1000) instead of confiscating the whole stake. The facet calls it defensively (gas-cappedstaticcall, exactly 32 return bytes); any revert, malformed return, or absence of the method pays0— the pre-change behavior. - Who should quote: only games with a true anytime cash-out whose value is fully determined
by already-revealed state (mines-style). Games whose mid-round value depends on unresolved
randomness or hidden state (blackjack-style) must return
0— any concrete quote there is an adverse-selection exploit (players abandon exactly when their real continuation value is below the quote). SeeCHAIN_WTF_CASINO_GAMES.md§quoteForfeitPayoutsemantics. - The simulator’s
LocalCasinoHostmirrors the facet logic, so the forfeit payout path is testable locally.
Compatibility
Section titled “Compatibility”- Backwards compatible: already-deployed games lack the method and keep paying
0on forfeit. The method is part ofICasinoGameV2, so newly compiled games must implement it — returning0unconditionally is a valid implementation (and the required one for blackjack-style games). On-chain activation on the live platform awaits the facet upgrade; the simulator enforces the new behavior already.
2026.08.08-1
Section titled “2026.08.08-1”- Live platform bet limits. The Chain.wtf host (and the simulator) now populate the
snapshot’s
casinoblock on every refresh:availableLiquidity,maxBetRiskBps,maxAllowedReservedProfit(the per-bet reserved-profit capopenSessionenforces viaCasinoGameFacet__BetRiskExceedsLimit), and the newcasino.maxBetAmount— the absolute per-game wager ceiling when the platform configures one (unset or'0'= none). computeMaxWager(snapshot, { maxMultiplierX })guest helper (exported from the package root and./guest): converts thecasinoblock into the largest wager the facet accepts for a game whose worst-case payout iswager * maxMultiplierX, accounting formaxBetAmount. Rounds conservatively (never returns a wager the facet would still reject) and returnsundefinedon hosts that don’t publish limits. Use it to clamp bet inputs so players can’t submit bets the chain will bounce; games withgameData-dependent multipliers should pass the multiplier of the current selection, or invert their exactquoteCapsmath againstmaxAllowedReservedProfit(the bundled coinflip example shows both the exact gate and the helper-driven “Max” clamp).
Compatibility
Section titled “Compatibility”- Additive and optional — existing games keep working. An absent
casinoblock means “limits unknown” (older host), not “unlimited”.
2026.08.03-1
Section titled “2026.08.03-1”simulator/— a fully standalone local test setup, needing nothing outside the SDK package. The package is now an npm workspace: onenpm installat the SDK root covers the simulator, the bundled VRF node and the example game, and onenpm startruns the whole local stack (chain + VRF node + casino deployment + harness + example). Individually,npm run local-nodeboots a chain (spawns anvil if none is running), deploys the real Verify Network VRF router + fulfilling node (via the bundledlocal-verify-network), and deploys a minimal casino: a freely mintable test token,LocalCasinoHost— a stand-in for the productionCasinoGameFacetthat runs the fullICasinoGameV2lifecycle and emits byte-identical events — and the realCoinflipGamecontract (vendored compiled artifact) so the bundled example works out of the box.npm run devstarts the harness (Vite + React + Tailwind): it mounts any game iframe by URL and drives it through the same bridge as the Chain.wtf host, replicating the production host’s behavior so games face the real integration quirks: optimisticpending:session rows, a near-instant flashblock push layer plus a lagged (tunable) indexed session feed, atomic settled flips with monotonic results, the game-steered balance ledger, and the stuck-randomness cancel path. The collapsible setup panel can also forcewallet.statusto exercise non-ready game states. Seesimulator/README.md.
Compatibility
Section titled “Compatibility”- Tooling only — no bridge/type changes.
getRandomnessVerificationis not implemented by the harness (the call rejects, as it may on any host that doesn’t support it).
2026.07.26-1
Section titled “2026.07.26-1”- Added transaction hashes to session items so games can render real block-explorer
/tx/links instead of falling back to the game contract’s address page:raw.openTransactionHash— the transaction that opened the session (the bet). The Chain.wtf host populates it on the optimistic row as soon as the relayer confirms, so it is available mid-round, before the indexer projection catches up.raw.settleTransactionHash— the transaction that settled the session; absent until settled.raw.randomnessRequests[].transactionHash— the VRF fulfillment transaction, per request; absent until that request is fulfilled. Because thegetRandomnessVerificationresult extends the request shape, the same hash also appears on each verification entry.
- Explorer links prove a transaction exists, not that the randomness is fair — keep the client-side
getRandomnessVerificationverdict as the primary fairness signal and treat/tx/links as secondary. SeeRANDOMNESS_VERIFICATION.md.
Compatibility
Section titled “Compatibility”- Additive and optional: older hosts omit all three fields, and sessions projected before this version have no hashes (only a re-indexed environment backfills them). Keep an address-link (or no-link) fallback when a hash is absent.
2026.07.20-1
Section titled “2026.07.20-1”- Added
raw.randomnessRequeststo session items — every VRF request of the session in request order ({ nonce, requestId, randomness?, fulfilled }). Multi-step games (blackjack, pvp-dice) see one entry per step; single-shot games see exactly one.raw.randomnessis now populated with the first fulfilled word. - Added optional
HostApiV1.getRandomnessVerification({ sessionId })— the host reads the ECVRF fulfillment artifacts from the Verify Network router and verifies them client-side (proof, output, EIP-712 enclave signature, signer identity). Returns per-request verdicts plus the raw artifacts so the result can be re-verified independently. SeeRANDOMNESS_VERIFICATION.mdfor the full contract and rendering rules.
Compatibility
Section titled “Compatibility”- Additive and optional: feature-detect
host.getRandomnessVerificationbefore calling; older hosts also omitraw.randomnessRequests. On environments without Verify Network (local dev) the method resolves withsupported: false.
2026.07.19-1
Section titled “2026.07.19-1”- Added optional
HostSnapshotV1.ui.viewport.availableHeight— the height in px from the top of the game’s iframe to the bottom edge of the visible screen, before the user scrolls. Games that want their primary action (e.g. the bet button) to land exactly at the screen edge should size the content above it toavailableHeightminus the action’s own height. The value is measured against the small viewport (svhsemantics), so it stays stable while scrolling collapses mobile browser chrome and only changes on real resizes or orientation changes. - CSS viewport units cannot replace this value: the host grows the iframe to fit the game’s
reported content height, so inside the iframe
100vhequals the full content height, not the visible screen.
Compatibility
Section titled “Compatibility”- Additive and optional: games running against older hosts simply receive no
ui.viewportand should fall back to their existing layout.
2026.07.18-1
Section titled “2026.07.18-1”- Added optional
HostSnapshotV1.token.iconUrl— an absolute URL of the wager token’s icon so games can render it next to balances and wagers.
Compatibility
Section titled “Compatibility”- Additive and optional: games running against older hosts simply receive no
iconUrland should fall back to a text symbol.
2026.07.03-1
Section titled “2026.07.03-1”- Added optional
HostApiV1.reportContentSize({ minHeight })so casino iframe games can report their current required content height to the host after the bridge connects. - Added
reportGameContentSize(hostApi)andobserveGameContentSize(hostApi)helpers in@chain/casino-sdk/guest. Games can use the observer to report initial height and subsequent layout changes without wiring their ownResizeObserver.
Changed
Section titled “Changed”- Removed static
presentation.minHeightfrom casino game manifest validation and examples. Height is now a runtime concern reported by the child iframe whencapabilities.resizeis supported.
Compatibility
Section titled “Compatibility”- Dynamic sizing is additive: older games that do not call
reportContentSizecontinue to render using the host’s existing frame fallback. reportContentSizeis optional on the host API so games can safely run against older hosts by checking for the method or usingobserveGameContentSize, which no-ops when unsupported.
2026.07.02-1
Section titled “2026.07.02-1”HostApiV1.revealOutcome({ sessionId })(required) — games must call this when their result presentation has finished. Sessions settle on-chain long before a result animation completes (bonus buys can run past a minute), so from the moment a bet is placed until the game reveals the outcome, the Chain.wtf host clamps its balance displays (navbar, Smart Wallet modal, and thebalances.smartWalletBalancepushed in the snapshot): the balance may move down (wager debit, extra escrow pulls) but never up. Guarding from bet placement — rather than subtracting the payout once it is known — means even a settlement that lands on-chain within a block can never flash the result. This is the reverse of an optimistic update: the payout is final, only its display is delayed. The guard exists only while the game is mounted — refreshing the page or navigating away from the game shows the real balance immediately.- Only credits are withheld: the wager debit still shows the moment a session opens, and a lost session moves no balance at settlement (the host releases the guard itself on a lost round), so games only need to reveal after wins — calling it after losses is a harmless no-op.
Breaking
Section titled “Breaking”revealOutcomeis a mandatory part of the game contract: a game that never calls it after a win leaves the host’s displayed balance stale until the player leaves or reloads the game page. Update existing games to callhostApi.revealOutcome({ sessionId })at the end of their win presentation.
2026.06.18-1
Section titled “2026.06.18-1”CasinoGameFacetevents now carry opaque game bytes, so hosts can populateHostSnapshotV1.sessions.items[].rawfrom the session indexer without supplementary on-chain reads:CasinoSessionOpened— addedbytes gameData(the wager-time payload passed toopenSession).CasinoSessionPhaseAdvanced— addedbytes gameState(post-step opaque state after each facet transition, including the first step after open).CasinoSessionSettled— addedbytes gameState(final opaque state at settlement).
- Documented that the casino_sessions projection ingests these fields and exposes them on each
session row; the Chain.wtf host maps them to
raw.gameData/raw.gameStatein the Penpal snapshot pushed viaguestApi.setState.
Changed
Section titled “Changed”- Host snapshot freshness: integrators should treat indexer session rows as the source of truth
for
raw.gameDataandraw.gameState. On-chaingetSession(sessionId)is optional for recovery or debugging — it is no longer required on every phase advance to keep the iframe in sync.
Compatibility
Section titled “Compatibility”- The new event fields are additive. Indexers and hosts that already merge projection rows into
HostSnapshotV1gain automatic opaque-state updates; games that decoderaw.gameStatefrom the snapshot require no bridge API changes.