open and an add_collateral carry no price. Each one takes the stake when it lands and becomes
an order that waits for the venue’s next price. A close is a request that carries no price,
and anyone may complete it with a priced settle.
This page is the contract a client or a bot builds against. A bot needs a wallet and an RPC
endpoint. It needs no access to the price feed, no token and no key from us.
Build an open with
ix.open(accounts, args, priceAccountsFor(market, config, 'open')), as
opening a position shows, and an add with
addCollateralForMode. Both read the governed config: decode it on every request. Never build
an open with openForMode: it leaves out the market’s fee-terms account, so the position freezes
the legal maximum of the exit fee and no rung check runs.The timing settings
Every wait on this page is a count of slots, and every count is a governed setting. Read them through the SDK on each request. Do not compile one into a client.
Each setting can change by a governed write with no program upgrade. The SDK is the source:
Opening: an order, then a fill
- Send
open. It carries no quote. Its remaining accounts are the market’s fee-terms account when the market has terms (priceAccountsFor(market, config, 'open')). When it lands, the stake is taken, the slot holds the order withentryPrice0, andPositionOpenedcarries the sequence number. It is an order, not yet a position. - The entry price is the first price of the venue’s price feed at or after
dslots from the slot in which theopenlanded. A listed filler writes it. Read it from the eventEntryFilled(entryPrice,quoteSlot), or from a non-zeroentryPriceon your own book. - Or the order is returned. The event is
OpenVoided:cause,refundTotal,feeKeptanddelivered. An order is returned when the first price came too late or did not meet the venue’s quality rule, when the market closed, or when no filler landed insided + T_fillslots of the order.refundTotalis the whole stake, andfeeKeptreads 0.deliveredfalse means the payout destination could not receive the refund, and it is held on the book: claim it withclaimPayout. - You cannot fill your own order (
NotAListedFiller, 6205). You need not return it either: afterd + T_fillslots anyone may sendvoid_open, and the venue’s keepers do.
voidReasonOf(cause) names the cause, and VOID_REASON_TEXT has the words for a trader. The fill
is the step that judges the price. So the price rows of openGates do not decide an order, and the
quote refusals of quotes and freshness cannot refuse one.
Build with ix.open and priceAccountsFor(market, config, 'open'), send, and then watch the book. The EntryFilled or OpenVoided event is
in the filler’s or the keeper’s transaction, not in yours:
Doubling down
Sendadd_collateral with no quote. CollateralCommitted is in your own transaction:
the added stake is taken (addedCollateral, totalDebit), the timer extension is written at once
(timerAfterS), and addTarget is the slot d after the add. Then one of two:
CollateralAdded. A filler priced the add at the first price at or afteraddTarget. It carries the new entry (entryPrice) and the entry before it (entryPriceBefore).AddReversed. The added stake is returned in full (refundTotal), as for a returned order.causeis one of the open’s causes, or 8 (the price was outside the position’s limits: at the add’s price the position was already at its barrier or its target) or 9 (the position was knocked out first).
- Until one of the two arrives, and then for
M_addslots after the price of a filled add, a close request, a retarget, an expiry mark and a second add returnAddPending(6211).closableFromSlotOfincludes that time. - A reversed add keeps the time it added to the timer. Its stake comes back.
- A knockout price from before the add still counts in that time. A price that breached the
barrier of the entry in force at its slot can still prove the knockout.
KnockoutBeforeAddreports it, the position is knocked out on its old stake, and the added stake comes back (AddReversed, cause 9). - A touch of the take-profit target while an add waits for its price cannot be stamped, and it is lost if the add is then filled.
addCollateralForModerefuses to build an add while adds are off (AddsDisabled, 6212).
Closing: a request, then a settle
- Send
request_close, signed by the owner or a session key. It carries no quote, so it cannot fail on a stale price.CloseRequested.exitTargetis the slotdafter the request. - The exit price is the first price of the venue’s price feed at or after
exitTarget. The close is done whenSettlementPricedandSettlementReceiptarrive. - Anyone may send that settle, and the venue’s keepers do.
isPermissionless('voluntary', phase)is true once the phase isExitRequested. - The window rule. If nobody settles inside
Wslots of the target, the price becomes the first price of the next window ofWslots, and so on.SettlementReceipt.windowis the count of windows that rolled: 0 for a close settled in its first window. - A request cannot be withdrawn. A second request returns
ExitPending(6213): treat it as success. SettlementPriced.unverifiedreadstruewhile a market lists one price feed. A price from a single feed isunverifiedby construction; it does not mean the price was wrong. Do not show a warning or a badge from it.
request_close, mark_expiry and open need no quote instruction: send each one with the
compute-budget pair. computeUnitLimit(built.name) gives the declared limit, and for the
open computeUnitLimit('open', { quotePost: false }).
The minimum hold
A position cannot be closed by its owner in the first slots after its entry price.- The venue has one hold for every market:
resolveMinHoldSlots(config.minHoldSlots). A market can have a longer one:market.closeHoldSlots(0 is none). The hold in force is the larger of the two (holdInForce). - It counts from the entry price:
EntryFilled.quoteSlot, and after a filled double-down from the add’s price, which is the position’sopenedSloton the book. - A close request inside it returns
MinimumHoldNotMet(6103) for the venue’s hold orMarketHoldNotMet(6236) for the market’s. Neither one is a failure of the position. - “Can close in N seconds”, with no guess:
closableFromSlotOf(position, config, market)is the first slot at which a request can land, from three public accounts. It isnullwhile the position is an order or an add waits.slotsUntil(slot, nowSlot)counts the slots, andestimatedSecondsUntil(slots, slotMs)turns them into an estimate that you label as one. - The request must land at or after that slot.
- A take-profit proof must also clear both holds, on the slot of its own price.
- A knockout is never held back. A market’s own hold never holds back a timer end.
Timers
- From the deadline, anyone may send
mark_expirywith reason 3 (userExpiry) or 4 (tenorExpiry).ExpiryMarked.exitTargetis the slotdafter the mark, and the exit price is the first price at or after it, by the same window rule as a close. - A settle with an expiry reason on a position that has no exit target returns
ExitNotAnchored(6214): mark it first. - A wrapper such as Minute sends the mark and then its own settle from slot
exitTargeton: two transactions, at leastdslots apart. - A timer is 0 or at least 26 seconds, at the open and after an add (
TimerBelowFloor, 6225).
Knockouts and take-profits
- Anyone may prove a level with any signed price that is newer than the entry and at most
W_koslots old. So a position can be knocked out by a price up toW_koslots old. - After a close request or an expiry mark, only a price that is not later than the exit price can
prove a level (
ExitPendingotherwise). - While an add waits for its price, a touch of the take-profit target cannot be stamped, and it is lost if the add is then filled.
- After a take-profit retarget, only a price at least
dslots after the retarget proves the new target (TakeProfitProofTooEarly, 6220). stamp_bookrecords the level on the book; the settle that pays it follows. It is the only stamp that can prove a knockout with a price from before a filled double-down.
Settling or stamping for others
This is optional. The venue’s keepers send every settle and every stamp.- A settle of a requested or marked exit needs the window quote: the first price of the venue’s
price feed at or after the start of the current window,
exitTarget + K × W, whereKis the count of whole windows since the target. Any other quote returnsNotWindowQuote(6217). stamp_bookneeds any signed price that breaches the level, inside theW_kolook-back.- To obtain a quote, see Getting prices.
Three more rules
- Show “side full” before the tap when a limit is set. A market can carry a limit on the stake
of one side: an amount, a share of the pool, or both, and the smaller one is in force. With no
limit set, nothing refuses.
sideRoom(direction, market, vault, vaultBalance)reads the room from the market’s two stake counters, its two limits and the pool’s assets. On a full side whoever lands first gets the room: there is no waiting list for it. A full side returnsSideStakeCapReached(6224). (The queue of how a win is paid is a different thing: it orders payouts, not room.) - A client may settle its own requested close in the first window, with the window quote. That
keeps the first price. If nobody settles in
Wslots, the next window’s price applies, and a knockout price in between counts. The count of rolled exits is public:SettlementReceipt.windowis not 0 for them. - Read a position through the SDK’s accessors:
phase,isUnfilled,pendingAdd,addTargetSlot,addMemo,memoIsLive,exitTargetSlot,exitReasonandtpFloorSlot. The same raw bytes of a position mean different things in different states. See reading positions.
What a client must never assume
- That the price on screen at the tap is the entry or the exit. It is the next price after the delay.
- That a landed
openis a position. It is an order untilEntryFilled. - That a landed
request_closeis a close. It is a close atSettlementPriced. - That
PositionOpenedcarries the entry. It has no price field. A position’sentryPriceon the book is 0 untilEntryFilled. Take entries fromEntryFilledandCollateralAdded, and returns fromOpenVoidedandAddReversed. - That a price in one of Minute’s round events is a price. See integrating Minute.
- That a second transaction is safe after a timeout. Never sign a second order for the same intent inside one blockhash life. Send the same signed bytes again.
- That adds are on.
addCollateralForModerefuses to build one while they are off. - That a void or a reversal is an error. It is a normal outcome, and the stake comes back
(
refundTotal). - That two events in one slot are in time order. Order by
quoteSlot.
openerTag.
Events
Each has a decoder:
decodeEntryFilled, decodeOpenVoided, decodeCollateralCommitted,
decodeAddReversed, decodeCloseRequested, decodeExpiryMarked, decodeKnockoutBeforeAdd and
decodeSettlementReceipt. On a landed transaction, receiptOf and
venueEventsNamed(receipt, 'EntryFilled') read them all. Every event starts with owner,
market, seq and openerTag.
Refusals a client can get
The codes are fixed.retryClassOf(code) gives the class of each one, and RETRY_CLASS_LABEL the
words: see error handling.
When the venue stops taking orders,
open and add_collateral return ProtocolPaused (6046) and
config.paused is true, as for any pause. Closes, timer ends and knockouts keep working, and an
order already placed is filled or returned as usual. See risk.