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

# Integrating Minute

> The typed dependency, the governed dials, the fee model, who pays, who settles, the per-market gate, and the transaction a round needs.

**Minute to Win It** is a separate on-chain program that wraps this venue. A player commits a stake,
a sixty-second clock starts, and the round ends when the clock runs out or the barrier is hit.
Minute opens a real position here through CPI, handing the venue a fee-inclusive `budget`; the venue
does not know it is being wrapped.

Program: `MinXAXaPZezFMc8mm1uZnK8NcYt7Fhbr7Eifdnw7cfH` · Config PDA:
`3WQ7QsWJJhqa1REGjUJFzCUNPSonbaXKcZoJbDwQnYq4`

## Decode with the SDK. Do not transcribe the layouts.

The SDK decodes Minute's accounts and events, so no layout needs to be copied into your repository:

```ts theme={"dark"}
import {
  decodeMinuteConfig,
  decodePlayerAccount,
  decodeMinuteEvent,
  minuteConfigPda,
} from '@rekt/1000x';
```

A decoder turns a layout change into a compile error or a refused read, where a transcribed offset
drifts silently. If you decode bytes off the wire instead (an indexer has no choice: the bytes *are*
the interface), use `decodeMinuteEvent` and the event decoders next to it rather than deriving your
own.

<Warning>
  **On `RoundOpened`, `gross_stake − stake` is Minute's fee plus the venue's.** `stake` is the
  collateral the *venue* resolved. Minute's own fee is `floor(gross_stake × entry_fee_bps / 10_000)`,
  from the event alone; subtract it from the difference to get the venue's cut.
  `RoundExtended.added_collateral` is the **measured** position delta, not the amount the player
  typed.
</Warning>

## Read the governed dials from the chain. Every time.

`min_stake`, `max_stake`, `entry_fee_bps`, `exit_fee_bps` and `initial_timer_s` are **governed**.
They move by a transaction, with no release on your side and no warning.

<Warning>
  An integrator who copies today's rate into their own config ships a display that is wrong the day
  it moves — **and every test passes**, because their number and their display agree with each other
  perfectly. Self-consistency is the failure mode, not the defence.
</Warning>

Read them with `decodeMinuteConfig(bytes)` from the account at `minuteConfigPda()`, and prefer
failing closed when the config is unreadable over falling back to a remembered value.

## The fee model: fee-inclusive, top to bottom

Both layers take their fee from INSIDE the number handed to them, and neither adds anything on top.
**The number the player types is the number that leaves the wallet.**

The two money instructions:

```rust theme={"dark"}
open_round(game_kind: u8, direction: u8, total_in: u64, min_collateral_out: u64)
double_down(seq: u64, expected_add_count: u16, added_total_in: u64, min_added_collateral_out: u64)
```

`total_in` / `added_total_in` is the fee-inclusive total — **the exact number that leaves the
wallet**, and what `min_stake` / `max_stake` bound. `min_collateral_out` /
`min_added_collateral_out` is a **floor on the resolved position**: forwarded to the venue and
re-checked by Minute against what the venue actually minted, so a venue fee that moves between your
quote and execution **reverts** instead of quietly buying less. **`0` is refused by name**
(Minute's `MinCollateralOutRequired`; Minute refuses the zero before the CPI) — it is the
absence of a floor, not a floor.

<Warning>
  **There is no `max_total_debit` argument.** `min_collateral_out` pins what the player GETS, not
  what they pay.
</Warning>

An add is always exactly the round's opening total: `added_total_in == round.opening_gross`, which
is also the open's `RoundOpened.gross_stake`. One field, read off the round or off the event — not a
range, and not `round.stake` or `round.opening_stake`, both of which hold the venue-resolved
collateral and are refused by name (`AddMustEqualOpeningStake`, 6042 — Minute's code; the venue's 6042 is `RedemptionExpired`).

<Steps>
  <Step title="Minute's fee is ABSORBED">
    `entry_fee_bps` comes **out of** the stake, not on top. A player who commits \$1.00 has exactly
    \$1.00 leave their wallet toward Minute, of which the fee is a part. Minute never adds to what
    the player typed. `min_stake` and `max_stake` bound this **gross** figure, so a typed \$0.25 is
    a legal 250,000 stake — **do not gross down for Minute's fee.**
  </Step>

  <Step title="The venue is ALSO fee-inclusive: Minute is a pass-through">
    The venue takes its own fee from inside the budget it is handed, exactly as Minute does. Minute
    takes its cut off the top and hands the remainder DOWN to the venue as a `budget`; the venue
    takes its own `entry_fee_bps` from INSIDE that budget and opens the largest position the
    remainder buys. **Nothing is added on top at either layer**, and a round's timer costs nothing
    extra. So the wallet debit **is** the number the player typed: one SPL transfer, of exactly
    `total_in`, and both fees are paid out of it.
  </Step>
</Steps>

<Note>
  **The wallet debit has no rounding gap.** It is a single transfer of the typed amount, so there is
  no floor division on the wallet side to lose a unit to. The fees still round, and they round the
  POSITION, which is why `min_collateral_out` exists.
</Note>

<Note>
  The venue's entry fee is 0, so the venue takes nothing from the budget it is handed. A round's close pays the venue's exit fee on the same terms as a Knockout position on that
  market: see [Fees and limits](/limits). Read both programs' fees from their config accounts and the
  market's fee-terms account.
</Note>

Here it is as arithmetic. The number you type is the number that leaves the wallet. Only the last
line depends on the two entry fees:

```
typed total 250,000 (Minute's min_stake)
  -> Minute absorbs its entry fee;  the remainder goes to the venue as a `budget`
  -> the venue absorbs its own entry fee from INSIDE that budget and opens what it buys
  -> TOTAL WALLET DEBIT  =  250,000   exactly.  Not "at most". Measured: zero dust.
  -> RESOLVED POSITION   =  250,000   <- equal to the typed total at Minute 0 bps + venue 0 bps; smaller whenever either fee is above 0
```

**A typed \$0.25 total debits \$0.25.** Any fee comes out of it, not on top of it. When a fee is
above zero, the POSITION is smaller than the typed number, since both fees are absorbed from inside it.
The binding floor is therefore the venue's `min_collateral` on the RESOLVED position, not
a grossed-up stake: read it from the chain, and set the CPI's `min_collateral_out` to the collateral
you predicted for the budget — never a token value like `1`, which satisfies the check and protects
nothing. If you need a specific minimum position, that is a governance question about `min_stake` and
`min_collateral`, re-derived whenever the *venue* moves its own fee, which Minute does not control.

<Warning>
  **Do not gross the player's input down.** Shrinking the typed number is a silent under-spend: it
  opens a smaller round than the player was shown. Send the typed number to `total_in` verbatim, and
  read both programs' fees from their config accounts when you predict the position.
</Warning>

### The ceiling is not `max_stake`

`max_stake` bounds the **opening** stake. A double-down adds to a live round, so total exposure runs
past it.

`max_adds = 0` means **uncapped**, not "no adds". The binding constraint is the venue's maximum
collateral per position, `config.params.maxCollateral` on the venue's config, and the refusal is
**Minute's** `AddExceedsVenueCapacity` (6046): Minute checks the venue's ceiling before attempting
the add. So the ceiling is the venue's number and the error is Minute's; do not look for this code in
the venue's table.

## Markets: the venue's per-market gate

Minute keeps no market list of its own: `Round.market` may point at any market **the venue has
listed**, and an unlisted asset has no `Market` PDA to pass. The venue then applies one gate of its
own: a Minute player's book may **open or add** only on a market whose `minuteEnabled` flag is true.
Anywhere else the venue refuses with `MinuteDisabled` (6201). Settles are never gated, so a live round
is never trapped by it. Offer a round only on a market whose `minuteEnabled` is true and whose `paused` is false (both on `decodeMarket(bytes)`). Only the venue's config authority changes either. A round on a market with trading hours is refused outside them, with the venue's own codes. See [markets](/markets).

## Who pays, and who may sponsor

Three roles, and they are separate on purpose:

| Slot | What it does | Constrained? |
| - | - | - |
| `payer` | pays lamports | **`init_player` only:** must equal the player |
| `player_wallet` | the player's identity | **not a signer** on `open_round` |
| `funder` | authorises the USDC | SPL decides — owner **or an approved delegate** |

**`open_round` and `double_down` are sponsorable.** Your backend can be `payer`, and a delegate the
player approved can be `funder`, so the player never signs the open and never needs SOL.

**`init_player` is the exception:** it requires `payer == player_wallet`, and refuses anything else
with `PayerMustBeThePlayer` (6038).

<Note>
  **The shape that works:** put a lamport transfer to the player and `init_player` in **one
  transaction**, with the player as `payer` and signer. You carry the cost and the player never sees
  a fund-your-wallet step. It happens once per player.
</Note>

Rent is a **deposit, not a fee**: it returns to the rent payer when the player's accounts are closed
with `close_player`.

## Settlement is permissionless, and a redundant settle is not a no-op

Anyone may settle a round's venue position once its clock has expired. If you run your own closer
alongside Minute's keeper, that is fine and encouraged — but know the shape of the race:

**A second settle of an already-settled position FAILS with the venue's `PositionNotFound` (6057).**
It is an error, not a silent success. There is no double-payout and there cannot be, because the
state transition gates the money rather than following it — the loser of the race gets a reverted
transaction, not a second payout.

<Warning>
  Treat `PositionNotFound` on a settle as **success by someone else**, never as an incident. It is the
  ordinary case: the venue's own keepers close Minute's positions without invoking Minute at all, and
  a `reconcile_round` then records the round. A closer that pages on this pages continuously.
</Warning>

## Error codes are meaningless without their program

Anchor numbers **every** program's errors from 6000 — so even 6000 itself collides (Minute's `NotAuthority` vs the venue's `RateOutOfRange`). So:

* `6027` is Minute's `RoundNotLive` **and** the venue's `ReserveUnderflow`
* `6007` is Minute's `WrongMint` **and** the venue's `NoFreshPrice`

<Warning>
  A bare-number match reads a real vault accounting failure as "already settled" and writes a
  terminal record while the position is still live. **Key your classification by program**: use the
  Anchor error *name* when present, else prove which program refused from the logs — Minute's
  failure line present and the venue's absent means the refusal happened before the CPI.
</Warning>

Take codes from the IDL, never from arithmetic on the enum. A code quoted in prose without its
program is a number waiting to be matched on wrongly.

## Opening, and the venue's price

`open_round` and `double_down` reach the venue's `open` and `add_collateral` by CPI. Those read no
price, so neither Minute instruction needs a quote instruction in front of it. The round's venue
position is an **order** until a filler writes its entry at the venue's next price after a short
delay, and a double-down is priced the same way or returned. A double-down the pool has no room to
cover is returned in full, and the round's total stays within the venue's maximum position. Read the
entry from the venue's events, not from Minute's: see [events of an order](#events-of-an-order).

Minute forwards its remaining accounts to the venue. The SDK's `minuteIx.openRound` and
`minuteIx.doubleDown` take them as their last argument and refuse an empty list:

```ts theme={"dark"}
import { pricingRemainingAccounts, withFeeTermsTable } from '@rekt/1000x';

// open_round: the sysvar, then the market's fee-terms account when the market has terms. The venue
// freezes the terms of the round from it. Without the account the round freezes the legal maximum
// of the exit fee and no rung check, so always pass it.
const openRemaining = withFeeTermsTable(pricingRemainingAccounts(market), market, config, 'open');

// double_down: the venue's add reads no price and no terms. The sysvar satisfies the builder.
const addRemaining = pricingRemainingAccounts(market);
```

Never pass `market.feeds`: `feeds[0]` is a feed id, not an account.

**Closing a round is the venue's `settle` on the player's book, followed by `reconcile_round`.**
`settle_round` is refused while the venue runs its liquidity pool (`CapitalRequired`, 6265, the
venue's code), because the pool settles every position directly so that part of a win can be queued.
A knockout, take-profit or expiry close carries the quote's ed25519 instruction immediately before the
venue's `settle`, and `priceAccountsFor(market, config, 'settle')` gives the whole list of remaining
accounts. An expiry takes two transactions: anyone marks the venue position's expiry (`mark_expiry`)
from the bell, and the settle follows from the mark's `exitTarget` on, with the window quote. See
[settling a position](/sdk/settling#request-or-mark-then-settle). Anyone may send the settle, and the
venue's keepers do. `reconcile_round` then records the round on Minute's side. If no price can be
obtained at all, the venue's no-price close, `lastResort`, applies to the round's venue position
directly (see [settling a position](/sdk/settling#the-no-price-close)), and `reconcile_round` then
records the round.

**Do not gate a close on a pause.** `Market.paused` refuses opens and adds (and a take-profit stamp
or retarget), never `settle`: the program pauses new positions, never the exit. A consumer that refuses a close because
the market is paused re-creates the very trap the program designs out: a trader who cannot get out.
Pause your OPEN button if you must; never your close.

## Events of an order

Minute's events are `RoundOpened`, `RoundExtended`, `RoundSettled`, `RoundReconciled`, `Swept`,
`PlayerInitialized`, `PlayerResized`, `PlayerClosed`, `ConfigUpdated`, `PauseChanged` and
`LookupTableSet`. It has no event named `DoubledDown`.

Minute's two round events are emitted when the order or the add lands, before the venue has priced
it:

* **`RoundOpened.entry_price` is 0** on every open.
* **`RoundExtended` is emitted at the add's commit**, also for a double-down that is later returned.
  Its `entry_price` equals its `entry_price_before`.
* **`opened_slot` in both events is the slot of the commit**, not the slot of the entry price. The
  holds count from the venue's `EntryFilled.quote_slot`, and after a filled double-down from the
  venue position's `opened_slot` on the book.

So do not read an entry, a blend or "the add is done" from either event. The round's entry is the
venue's `EntryFilled.entry_price`. The entry after a double-down is the venue's
`CollateralAdded.entry_price`, with `entry_price_before`. A returned double-down is the venue's
`AddReversed`. Join them to the round on `opener_tag`.

## The transaction is tight

A wrapped open names the Minute accounts **and** the venue accounts it reaches, against Solana's
1,232-byte packet limit. Compile a v0 message against two lookup tables. The first is the table
Minute's config names (`decodeMinuteConfig(bytes).lookupTable`); read it from the config rather than
from your own settings. The second is the venue table for the market, from `chooseMarketTable`
(see [session keys](/sdk/sessions#the-byte-wall)). Both are frozen, so cache their contents by address. Measure the signed packet before you send it, and refuse one that does not
fit rather than letting the RPC reject it.


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