> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chance.money/llms.txt
> Use this file to discover all available pages before exploring further.

# Bet constraints

> Every gate a place call must pass — size limits, liability caps, the EV identity, and commit freshness.

A place call reverts unless it clears **all** of these gates. Quote them client-side before sending the transaction — every value below is readable on-chain.

## Constraint table

| Constraint              | Rule                                                                                                        | Where enforced | Revert                            |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | -------------- | --------------------------------- |
| Min bet                 | `stake ≥ minBet`                                                                                            | `place*`       | `BetTooSmall`                     |
| Max bet                 | `stake ≤ maxBetAmount`                                                                                      | `place*`       | `BetTooLarge`                     |
| Max stake vs bankroll   | `stake ≤ maxStakeCap()` — `availableBankroll × maxStakeBps / 10000` (default 50%)                           | `place*`       | `BetTooLarge`                     |
| Max bet liability (MBL) | `liability − stake ≤ maxBetLiabilityCap()` — `availableBankroll × maxBetLiabilityBps / 10000` (default 10%) | `place*`       | `BetLiabilityTooHigh`             |
| Max payout multiple     | `payout ≤ stake × effectiveMaxMultiplierBps() / 10000`                                                      | `place*`       | `InvalidParam` / `InvalidOdds`    |
| EV floor (Strike)       | `W ≥ EV × B` (win probability ≤ 1)                                                                          | `placeStrike`  | `InvalidParam`                    |
| EV band (Range)         | `P_min ≤ EV × B ≤ P_max`                                                                                    | `placeRange`   | `InvalidOdds`                     |
| EV identity (Ladder)    | `Σ massWad[i] × payouts[i] / WAD == EV × B` exactly, `Σ massWad = WAD`, 1–16 buckets, no zero-mass bucket   | `placeLadder`  | `InvalidOdds`                     |
| Epoch freshness         | `expectedCommitId == currentCommitId` and the epoch is open                                                 | `place*`       | `CommitMismatch` / `EpochNotOpen` |
| Client seed             | `clientSeed != 0`                                                                                           | `place*`       | `InvalidParam`                    |
| Attribution length      | `platform` / `game` strings ≤ 64 bytes                                                                      | `place*`       | `InvalidParam`                    |
| Mode enabled            | Strike / Range / Ladder can be paused independently                                                         | `place*`       | `ModeDisabled`                    |

**Liability** is the maximum the pool might have to pay: Strike `W`, Range `P_max`, Ladder `max(payouts)`. It is reserved against the bankroll at place time and released at settle.

## The effective multiplier

`effectiveMaxMultiplierBps()` is a **live** gate that scales linearly with bankroll utilization:

```text Formula theme={"theme":"css-variables"}
liabilityUtilizationBps = pendingPayoutLiability × 10000 / (availableBankroll + pendingPayoutLiability)
effective = maxMultiplierBps (100× at idle)  →  minMultiplierBps (2× at full utilization)
```

A 100× Strike that quotes fine on an idle pool can revert minutes later when other bets fill the liability budget. Re-read `effectiveMaxMultiplierBps()` (or consume the `BankrollSync` event, which carries all the precomputed caps) right before placing.

## Ladder mass snapping

The contract checks the Ladder EV identity in **exact integer math** — a float-solved probability set almost never lands on it after rounding to WAD. The reference client rounds masses with a largest-remainder allocation, then transfers single units of mass between buckets until the on-chain check passes (see `snapMassToChain` in the web app's `ladderQuotes`). If you build Ladder args yourself, do the same: quote with the exact contract formula, and adjust masses (not payouts) until `Σ massWad × payouts / WAD == EV × stake`.

## expectedCommitId freshness

Every place call pins the epoch it bets under. Read `currentCommitId()` immediately before sending; if the keeper rotates the epoch between your read and your transaction, the call reverts `CommitMismatch` — just re-read and retry. There is no max-age gate on an open epoch, so an open epoch never goes "stale" on its own.

<Tip>
  All caps in one read: subscribe to the `BankrollSync` event, which emits `availableBankroll`, `effectiveMaxMultiplierBps`, `maxStakeCap`, and `maxBetLiabilityCap` after every place, settle, and stake movement — no `eth_call` polling needed. See [Events](/protocol/events).
</Tip>
