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

# Payout tables

> The invariant in exact integers, how to size a payout against the vault, and the common refusals.

A payout table is an array of 1 to 32 outcomes `{ p, m }`:

* `p`: probability as a `u64` in billionths. Every `p` is greater than zero, and they sum to **exactly** 1 000 000 000.
* `m`: multiplier as a `u64` in basis points of the ticket price. 10 000 is 1×, 0 pays nothing, and the ceiling is 100 000 000 (10 000×).

An outcome's payout is `price × m / 10 000`, rounded down, in USDC base units.

## The invariant

`open_ticket` computes, in 128-bit integers with no rounding:

```
lhs = Σ p_i · m_i
```

and admits the payout table only if, for the app share `f` it passes:

```
lhs ≤ (10 000 − 100 − 100 − f) · 1 000 000 000
```

That is the **margin floor**: the declared margin must cover the 100 bps vault margin floor, the 100 bps protocol share and the app's own share. The program also derives the declared margin it stores in the ticket:

```
margin_bps = 10 000 − ceil(lhs / 1 000 000 000)
vault_margin_bps     = margin_bps − f − 100
```

No floating-point value is involved anywhere, and your client should not use one either. Build payout tables in `bigint`.

> [!TIP]
>
> Round in the vault's favour: round probabilities down, and if you round a multiplier up, derive the probability from the rounded multiplier. Put the remainder of 1 000 000 000 on the zero outcome so the sum is exact.

## Examples

**Win or nothing.** For a target payout `T` on ticket price `s` at margin `e` bps:

```ts theme={"dark"}
const m = (T * 10_000n + s - 1n) / s;          // round up so the payout is at least T
const p = ((10_000n - e) * 1_000_000_000n) / m; // round down to keep the margin
const payoutTable = [{ p, m }, { p: 1_000_000_000n - p, m: 0n }];
```

**Three tiers at a 2 % margin.**

| Outcome | `p` | `m` | `p · m` |
| - | - | - | - |
| 10× | 50 000 000 | 100 000 | 5.000 × 10¹² |
| 0.9× | 450 000 000 | 9 000 | 4.050 × 10¹² |
| 0.15× | 500 000 000 | 1 500 | 0.750 × 10¹² |
| | 1 000 000 000 | | 9 800 × 10⁹ |

**Refused: pays out more than it takes in.** `[{ p: 500_000_000n, m: 21_000n }, { p: 500_000_000n, m: 0n }]` returns 105 % on average. `open_ticket` fails with `PayoutTableOverpays`.

**Refused: fair, but the margin does not cover the split.** A payout table with a 1.5 % margin and no app share is fair but leaves the vault less than its 100 bps floor plus the 100 bps protocol share. `open_ticket` fails with `MarginFloor`.

## How much a vault can back

The vault reserves each ticket's **liability**, its worst case net of what the ticket price brought in:

```
vault_credit = price − protocol_share − app_share
payout_max  = price × max(m) / 10 000
liability   = payout_max − vault_credit
```

Admission then checks, against the vault as it stands at open:

| Check | Rule | Refusal |
| - | - | - |
| Per-ticket cap | `liability ≤ min(0.5 × vault_margin × free_balance, 150 bps × assets)` | `PerTicketCap` |
| App ceiling | the app's open liability, this ticket included, `≤ 20 % × assets` | `IntegratorCap` |
| Utilisation | `(reserved + open credits) ≤ 70 % × assets`, after this ticket | `UtilisationBreaker` |
| Global exposure | `reserved ≤ 80 % × assets`, after this ticket | `GlobalExposure` |
| Gross solvency | `reserved + open credits ≤ assets` | `Insolvent` |

`free_balance = assets − reserved liability − open vault credits`. The per-ticket cap uses the **vault margin**, net of shares, so inflating the declared margin does not widen it, and every basis point of app share narrows it. After a drawdown trip the cap is halved for 48 hours.

### Sizing a payout

Two consequences matter for an interface:

1. **Only the top multiplier costs capital.** A payout table that pays something 90 % of the time can be trivial to back; a single large payout decides what the vault must hold.
2. **The cap bounds the liability, not the multiplier.** On a fixed payout table, a cap `C` admits a ticket price of about `C / (max(m) − 1)`. The same vault that refuses 25 USDC at 1 000× accepts a few cents at 1 000×.

On an idle vault with a 2 % declared margin, the smallest a payout table can declare, and no app share (vault margin 100 bps), the Kelly term binds first: 0.5 × 100 bps is 50 bps of the free balance, below the 150 bps bound on assets. The smallest vault that carries a win-or-nothing ticket is roughly `liability / 0.005`:

| Ticket price | Payout | Liability | Vault needed, idle (USDC) |
| - | - | - | - |
| 1 USDC | 10 USDC | 9.01 | about 1 850 |
| 2 USDC | 19.60 USDC | 17.62 | about 3 550 |
| 1.90 USDC | 185.90 USDC | 184.02 | about 36 850 |
| 6.45 USDC | 622.44 USDC | 616.05 | about 123 250 |
| 7.5 USDC | 7 500 USDC (1 000×) | 7 492.58 | about 1.5 million |

> [!NOTE]
>
> The 2 % minimum returns the most reward per unit of budget and is the most demanding margin for capacity. Its vault margin is 100 bps, so the Kelly term of the per-ticket cap is 50 bps of the free balance, against 125 bps at a 3.5 % margin (vault margin 250 bps). For the same liability, every vault size here is 2.5 times what a 3.5 % payout table needs (Kelly term 50 bps against 125 bps), and a given vault backs a top payout 0.4 times as large: large top payouts at the minimum margin need a larger vault. Above a 4 % margin with no app share, the 150 bps bound on assets binds first on an idle vault.

As the vault fills up, the free balance shrinks with it, so the largest payout the vault accepts falls as utilisation rises.

### Show the limit, do not hit it

Read `Config` (for the parameters) and `Vault` (for `totalAssets`, `totalReservedLiability`, `openVaultCredits`) and apply the checks above in integers to show users the largest payout, or the largest ticket price, the vault accepts right now. Then simulate the transaction before asking for a signature: the program is the authority, and the vault can move between your read and the user's click.

```ts theme={"dark"}
import { fetchConfig, fetchVault, findConfigPda, findVaultPda } from "./generated/fade";

const params = (await fetchConfig(rpc, (await findConfigPda(cfg))[0])).data.params;
const vault = (await fetchVault(rpc, (await findVaultPda(cfg))[0])).data;

const free = vault.totalAssets - vault.totalReservedLiability - vault.openVaultCredits;
const marginBps = 200n; // the payout table's declared margin
const appShareBps = 0n;
const vaultMarginBps = marginBps - appShareBps - params.protocolShareBps;
const kelly = (((free * vaultMarginBps) / 10_000n) * params.kellyFractionBps) / 10_000n;
const flat = (vault.totalAssets * params.censorshipBps) / 10_000n;
const cap = kelly < flat ? kelly : flat; // largest liability one ticket may take now
```

## Common refusals

| Error | Cause | Fix |
| - | - | - |
| `OutcomeCount` | 0 or more than 32 outcomes | Group outcomes that share a multiplier |
| `ProbabilityRange` | A `p` of 0, or above 1e9 | Remove unreachable outcomes |
| `ProbabilitySum` | Probabilities do not sum to exactly 1e9 | Put the remainder on the zero outcome |
| `MultiplierCeiling` | A multiplier above 10 000× | Lower the top payout |
| `PayoutTableOverpays` | Expected return above 100 % | Lower probabilities or multipliers |
| `MarginFloor` | Margin below 200 bps + app share | Raise the margin or lower the app share |
| `AppShareTooHigh` | App share above 500 bps | Lower it |
| `PriceBounds` | Ticket price below `min_price` or above `max_price` | See [Devnet addresses and parameters](https://docs.fade.finance/build/devnet.md) |
| `PerTicketCap` | Liability above the cap | Smaller top payout, lower ticket price, lower app share, or a larger vault |
| `IntegratorCap` | Your open liability above 20 % of assets | Wait for your open tickets to settle |
| `UtilisationBreaker` | The vault is more than 70 % committed | Wait; new tickets resume as tickets settle |

The full list is on [Errors](https://docs.fade.finance/build/errors.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.