openGates tells you
first: every gate that can refuse the action, with its measured value and its threshold, in
the program’s order for an open. On a Minute book, MinuteDisabled comes first, then the
guardian freeze. The table below lists the rest in that order. An open carries no quote, and the
fill judges the price: see the other gate reports and
orders and closes.
CollateralOutOfRangeteaches nothing.stake $0.10 is below the minimum $0.20teaches everything.
What a gate is
scale is absent on categorical gates — paused or not, above the take-profit floor or below it —
because a switch has no headroom and inventing one would put a half-full bar under a fact that is
simply true or false.
Building the context for an open
openGates needs the state that the refusals are judged against. Everything in it is a read you
already know how to do.
OpenRequest is { collateral: bigint; direction: 'long' | 'short'; rungBps: number; userTimerS: number; tier?: number }.
rungBps is basis points of stake — 20_000 is 2.00x, the floor — not a whole multiple. Set
userTimerS to 0 for no timer of your own.tier is optional and 0 or absent means the market’s own. It matters because a market’s
tier is a CEILING rather than the only leverage it sells: every distance the quote returns is something / L, so a
report derived from market.tier while the request names 250x on a 1000x market shows a barrier
four times too tight and calls every gate green. Pass the same tier to openPreview — it takes
one as its seventh argument — or use client.openGates, which threads the whole request through
both.The refusals, in the order the program reaches them
Most of these are states, not faults. A frozen oracle, a paused market, a shut session, a market
inside an event window, a pool at capacity and a trader whose book is full are all the protocol
working. Show the
sentence, not a stack trace — and see error handling for which ones retrying can ever
fix.
openGates also reports open.balance last, which is not a refusal of this program at all: it is
the trader’s SPL balance against the total debit, and the token program raises SplInsufficientFunds.
It is in the report because a client that shows twelve green protocol gates and then fails on funds
has explained nothing.
A market with no session calendar always passes the session row. On a market that has one,
a position must be dead before the close, with the market’s own settlement buffer to spare, so
no position is ever live while the market is shut. An open near the close is given a shorter
tenor rather than refused, until less than a whole minimum is left.
MarketClosed means there is
no session open now; SessionEndsBeforeTenor means there is one, but not enough of it left.open.timer comes before the collateral rows. A timer past the tenor and a zero
minCollateralOut together are refused TimerExceedsTenor, which is the program’s order.Rendering the whole table
Show every gate, not only the failure. A user who can see that every gate but one passes, and that the one still passing is close to binding, understands the system; a user who sees a single red error learns nothing. Renderreport.gates rather than a count of your own — the row set depends on
the market (a single-feed market has no quorum row).
The other gate reports
The same shape covers everything the program can refuse:
Settlement’s gates are covered in settling a position; the redemption three in
providing liquidity; the two session reports in session keys.
An open and an add read no price. The rows that judge a quote (
quote.*, price.*) and the
forecast rows (open.forecast, add.forecast) do not refuse an order: the fill judges the first
price, and a price that fails returns the order. Pass sideRoom in the context, and openGates
and addGates add the side-limit row. Orders and closes has the rest.addGates is the report for add_collateral. An add takes open’s account list, open’s
entry fee and open’s side limit, so it is refused by nearly everything openGates covers, plus its own rows: the position must be live and on this market,
the engine bounds the increment against the position’s total after the blend, and add.forecast
compares the position’s remaining life with the oracle’s forecast of the close.Why this shape and not exceptions
A refusal here is data, never a thrown string. Three reasons, all of them learned the hard way:- An interface renders several gates at once, and a user deliberately provoking one — trying a stake above the cap to see where the cap is — is the normal case, not the exceptional one.
- The measured value is the whole explanation. An exception carrying only a name throws away the half of the message that helps.
- Predicting the refusal client-side costs nothing and saves a transaction fee, a round trip, and a confusing failure at the worst moment.
