Instant Open
Instant Open is the delegated flow for opening a position — on both solver kinds (Enigma lowcap and Rasa majors); instantOpen dispatches on the resolved solver’s kind (see Per-solver behavior). The user signs an EIP-712 message, the SDK submits it to the solver, and the solver returns a tempQuoteId immediately — the position is tradeable before the on-chain anchor lands.
The SDK splits the flow into three functions on purpose:
prepareInstantOpenParams— resolve every derivable input (market metadata, mark price, locked params, fee rates) into the exact wire-shapedInstantOpenParametersbag.instantOpen— sign + submit. Requires the fully-resolved bag.instantOpenAuto— thin wrapper that callsprepareInstantOpenParamstheninstantOpen. One line for callers who don’t care about the intermediate state.
The split lets you access, cache, or override any step. It also has a latency implication: instantOpenAuto runs the prepare fetches inline, so the wall-clock from “user clicks” to “signature dialog” is prepare fetches + sign + POST. If you already have the pre-fetched data (from a preview screen, a quote review, an earlier order), call instantOpen directly and you get straight to sign + POST — faster fills, tighter price windows.
Plus read helpers for the pending queue: getInstantOpens (paginated list), getInstantOpenQuoteId (resolve on-chain id for a given tempQuoteId).
Import
import {
instantOpen,
instantOpenAuto,
limitOpenAuto,
prepareLimitOpenParams,
prepareInstantOpenParams,
getInstantOpenFees,
getInstantOpens,
getInstantOpenQuoteId,
type PrepareInstantOpenParameters,
type PrepareLimitOpenParameters,
type InstantOpenParameters,
type InstantOpenReturnType,
type PendingInstantOpen,
} from "@symmio/trading-core";The three-function split
Under the hood, instantOpenAuto is:
export async function instantOpenAuto(config, parameters) {
const resolved = await prepareInstantOpenParams(config, parameters);
return instantOpen(config, resolved);
}That’s it. The wrapper exists so simple call sites stay one line; the underlying two-function shape exists so:
- You can cache the prepared bag (e.g. render a review screen with the resolved trade math and only submit after user confirmation).
- You can override any resolved field (e.g. pin a mark price you fetched elsewhere, skip a locked-params fetch by supplying it directly).
- You can run prepare early — kick off the fetches when the user opens the trade panel, then when they click Open you only sign + POST.
- You can inspect the exact numbers being signed before the network hop.
instantOpen on its own is the “I already have everything, just submit” primitive. Pair it with a manual prepareInstantOpenParams call staged earlier when open latency matters.
prepareInstantOpenParams
Pure resolver. Takes the minimal UI-shaped inputs (wallet, market, side, margin, leverage, slippage) and returns the full InstantOpenParameters bag needed by instantOpen.
Steps (in order):
- Validate the user’s
slippage— a non-finite value or one outside[0, 100)throwsINVALID_SLIPPAGEbefore any network work. On a lowcap (Enigma) solverslippagemay be omitted and is auto-derived: the SDK dry-runs the mark-sized order and sets the price bound to the estimated fill price plus 4% headroom (flat 4% off mark when the estimate is unavailable). On majors an omittedslippagethrowsSLIPPAGE_REQUIRED— the estimate-driven behavior in steps 4–5 is lowcap-only, and a majors prepare is unchanged from the classic flow (locks + platform fee, no estimate fetch). - Resolve market metadata, mark price, locked params, and fee rates — fetched concurrently, with any caller-supplied field short-circuiting its fetch.
- Run
calculateTradeParamsto deriverequestedOpenPrice, the mark-sizedquantity(initialMargin × leverage / markPrice),cva,lf,partyAmm,partyBmm,notional. - Dry-run the sized order via
GET /estimated-price: when the expected fill deviates from mark beyond the slippage tolerance (|estimated − mark| / mark > slippage/100), the call rejects withSLIPPAGE_EXCEEDEDinstead of submitting an order the solver would refuse. Best-effort — an unavailable estimate (solver down, non-Enigma) skips the gate. Pass a pre-fetchedestimatedOpenPriceto skip the round-trip on latency-sensitive submits, ornullwhen an estimate was already attempted but unavailable. - Derive the
addMarginamount viacalculateMargin:locks + platformOpenFee + openSolverFee + staticSolverFeeOpen + expectedSettlementLoss— the open-side legs only. Close fees are not funded here; the contract charges them at close from the position (see Fees at close). Lowcap only — the open funds the solver’s open fee (hedgerFeeOpen × leveraged notional), its flat static open fee (a USD amount fromgetSolverInfo, size-independent) and the open-price settlement (side-awaremax(0, adverse fill − mark) × quantityvs the dry-run estimate) from the VA balance, so the SubAccount → VA transfer must fund every open-side leg. A lowcap SHORT additionally funds its locks atmark × 1.01(SHORT_FUNDING_BUFFER_PERCENT) — the request price is a contract floor, so a fill above it rescales the signed locks up and the prefund needs that headroom. On majors the margin stayslocks + platformFeeat the classic basis. - Convert every value to 18-decimal-wei
bigint.
const resolved = await prepareInstantOpenParams(config, {
from: sessionKey,
subAccountAddress,
market: { id: 1 },
positionType: PositionType.LONG,
initialMargin: "100",
leverage: 5,
slippage: 1,
});
// `resolved` is now inspectable, hashable, and ready to sign.
console.log(resolved.quantity, resolved.requestedOpenPrice, resolved.addMargin);Full-balance funding (lowcap)
Fund with a typed initialMargin or with fund — never both. fund deploys the user’s entire selected collateral into the position’s Virtual Account and sizes the quantity to fit it. It is lowcap (Enigma) only.
With a typed initialMargin, the SubAccount keeps back whatever the position did not consume (locks + open-side fees + settlement). A user who deposited $100 and pressed “Max” therefore saw a smaller spendable figure and left dust behind. In full-balance mode the whole $100 moves to the VA, and the SDK shrinks the quantity instead so its locks + open-side fees + worst-case settlement fit inside it — nothing is stranded, and the figure deployed is the figure picked. Close fees are not sized into the budget; the contract charges them at close from the position (see Fees at close), so the same balance sizes a slightly larger position than it did when close fees were pre-funded. Any tiny remainder rides as free VA margin.
How it sizes:
- Static-fee carve — the solver’s flat static open fee (
getSolverInfo) is size-independent, so it would break the linear solve below. It is subtracted from the balance first and the position is sized against the remainder; a balance that cannot cover it throwsINVALID_TRADE_PARAMETERS. (The static close fee is not carved off — it is charged at close, not funded here.) - Probe — run the trade math with the remaining balance as margin input and price its cost legs. Every leg is linear in the sizing input at a fixed fill price, so
balance / probeMarginis the exact factor that makes locks + open-side fees + settlement equal the balance. - Rescale by that factor, less a
0.1%safety epsilon that (together with the maintenance-marginpartyAmmlock) absorbs a fill landing a hair worse than the estimate. - Lot snap — the rescaled quantity is snapped down to the market’s lot grid (falling back to the quantity-precision quantum when the market publishes no lot size), and the locked values are rebuilt from the exact snapped quantity. Down is the safe direction: less quantity always needs less margin.
- Cost invariant — the costs are recomputed and, while
locks + open-side fees + settlement > balance, the quantity steps down one grid step. The linear factor only mispredicts through precision rounding — worth at most a few quanta — so the loop is short and guarantees the funded VA is never smaller than what the signed quote locks. - Constraint validation — the final quantity is validated against the market’s published quote constraints (lot multiple, min/max notional, max quantity, min quote value, LF portion). A violation throws
QUOTE_CONSTRAINT_VIOLATED.
Settlement is budgeted at the expected fill when the dry-run estimate exists — and at the slippage bound (the signed request price vs mark) when it does not. Never at zero: a full-balance open leaves nothing behind in the SubAccount to absorb an optimistic estimate, so an unavailable estimate must fail safe.
Note the sizing factor can exceed 1: when the market’s locked-param percents sum below 100, a position’s locks + open-side fees cost less than the margin figure that sized it, so deploying everything can open a larger position than typing the same number as initialMargin. Full-balance sizes the quantity to fit the balance — in either direction. The estimate is only trusted at or below the probe size it was quoted for, though: when the factor would upsize past the probe, the sizing re-solves with settlement at the slippage bound (and when even the bound model no longer upsizes, it clamps to the probe size, where the estimate is still valid).
const resolved = await prepareInstantOpenParams(config, {
from: sessionKey,
subAccountAddress,
market: { id: 1 },
positionType: PositionType.LONG,
fund: { mode: "full-balance", balance: "100" }, // deploy all $100 — no initialMargin
leverage: 5,
// slippage optional on lowcap — auto-derived from the dry-run estimate
});
// addMargin === the full 100; quantity is sized so locks + open-side fees + settlement fit inside it (close fees charged at close).Automatic fallback from typed margin
For lowcap market opens, pass availableBalance alongside initialMargin to automatically fit an oversized position into the available collateral:
const prepared = await prepareInstantOpenParams(config, {
from: sessionKey,
subAccountAddress,
market: { id: 1 },
positionType: PositionType.LONG,
initialMargin: "100",
availableBalance: "100", // raw available collateral, before fee deductions
leverage: 5,
slippage: 1,
});
// prepared.fundingMode: "initial-margin" | "full-balance"
// prepared.order.quantity: final quantity, including any automatic reduction
await instantOpen(config, prepared);The SDK first rejects a typed initialMargin greater than availableBalance with INVALID_TRADE_PARAMETERS (“Initial margin exceeds available balance.”). For example, typing $101 with a $100 balance produces an error.
When the typed amount is within the balance, the SDK calculates the position’s required funding: locked margin, open-side fees, settlement provision, and the existing SHORT funding buffer. If this amount is greater than availableBalance, it uses the full-balance formula above and transfers the whole balance. At equality, or below it, typed sizing stays unchanged. For example, typing $100 with a $100 balance can produce a position requiring $102 after costs; that position is resized to fit $100.
availableBalance is a positive, finite decimal string supplied by the caller; the SDK does not fetch or reserve this balance. Use the raw SubAccount available balance, not the fee-adjusted maximum initial margin. Keep it current and pass the same inputs to preview and submission. The decision is recalculated on each call, so reducing the typed amount or increasing the balance can restore "initial-margin" mode.
This option is lowcap-only. It cannot accompany fund; explicit full-balance funding already supplies its own balance. Omitting it preserves existing typed-margin behavior. Market minimums, invalid inputs, slippage checks, and balances unable to cover the static open fee still produce errors. Close fees are not added to the open budget.
getInstantOpenFees accepts the same availableBalance and returns the same effective fundingMode and sized quantity. Even with includeSettlementInTotalFee: false, settlement remains part of the sizing budget; that option changes only the displayed fee total. Both query keys include availableBalance.
validateInstantOpenBalanceFunding
Run the same balance validation synchronously before fetching a price estimate or other trade inputs:
import { validateInstantOpenBalanceFunding } from "@symmio/trading-core";
validateInstantOpenBalanceFunding({
initialMargin: "100",
availableBalance: "100",
isLowcap: true,
});Pass optional initialMargin, availableBalance, and fund, plus required isLowcap (whether the selected solver supports lowcap opens). The function returns nothing when validation passes and does nothing when availableBalance is omitted. With a balance supplied, it throws INVALID_TRADE_PARAMETERS for nonpositive/nonfinite amounts or a typed margin above balance, AMBIGUOUS_FUNDING for fund plus availableBalance, and FULL_BALANCE_UNSUPPORTED when isLowcap is false. Preparation and fee preview also call it internally. It validates balance funding only; market constraints and complete order validation still run during preparation.
Quote preview — prepareInstantOpenParamsQueryOptions
prepareInstantOpenParams neither signs nor submits — it only resolves inputs and builds the params — so it is safe to run live as a read. prepareInstantOpenParamsQueryOptions(config, options) wraps it as a TanStack Query, so a UI can render a “quote preview” that is bit-for-bit the InstantOpenParameters the submit will sign: the same order (price + quantity), lockedParam, margin.amount, and solverFeeCaps. No separate local recomputation to drift, and the full-balance rescale + lot snap are included for free.
The query is disabled until a funding source exists (a non-empty initialMargin, or a fund with a balance). Pre-fill the optional inputs (markPrice, feeRates, lockedParamPercent, estimatedOpenPrice, market metadata) so the query function is pure and does no per-render network. The slippage gate still runs, so an out-of-tolerance estimate surfaces as the query error — the honest preview of a quote the submit would also reject.
For both preparation and fee preview, estimatedOpenPrice accepts a decimal string, null (explicitly unavailable; skip the fetch), or undefined/omission (fetch if needed). An unavailable estimate uses the existing fallback: auto slippage uses 4%, the slippage gate is skipped, and full-balance sizing provisions settlement at the price bound. Pass the same estimatedOpenPrice to preview and submission, preserving null. With automatic or explicit full-balance sizing, settlement affects the funding decision and final quantity as well as margin.amount. Keep the balance, mark price, fee inputs, and market metadata consistent too. In React, usePrepareInstantOpenParams wraps this and returns the estimate it used for exactly that.
import { prepareInstantOpenParamsQueryOptions, PositionType } from "@symmio/trading-core";
useQuery(
prepareInstantOpenParamsQueryOptions(config, {
subAccountAddress,
from: sessionKey,
market: { id: 1 },
positionType: PositionType.LONG,
initialMargin: "100",
leverage: 5,
slippage: 1,
}),
);
// data.order.quantity / data.lockedParam.cva / data.margin.amount — exactly what the send signs.Fee preview — getInstantOpenFees
Preview every fee a new quote pays at open, separated by leg plus the total, without signing or submitting anything. Same parameters shape as the wizard (minus the signer), same resolution and math — the preview equals what prepareInstantOpenParams charges for the same inputs. The result is a kind-discriminated union of the open-side legs only:
| Fee leg | Formula | Enigma (lowcap) | Rasa (majors) |
|---|---|---|---|
platformOpenFee | getFeeForUser.openFee × notional | ✔ | ✔ |
openSolverFee | hedgerFeeOpen × notional | ✔ | — |
staticSolverFeeOpen | flat USD (solver /info) | ✔ | — |
expectedSettlementLoss | max(0, adverse fill − mark) × quantity | ✔ | — |
Close fees are not part of this preview — the contract charges them at close from the position, and they are previewed in the close flow with getInstantCloseFees, where the close notional and the holding-time solver rate are real (concept: Fees at close).
totalFee sums the open legs. It includes the expectedSettlementLoss provision by default — the provision is part of what the open funds, even though it is a provision rather than a fee the solver keeps; pass includeSettlementInTotalFee: false to sum the fee legs only (the provision stays available as its own field). On majors totalFee is just platformOpenFee. notional (the leveraged notional the rates were applied to) is returned alongside for rate displays, together with quantity — the leveraged base-asset quantity the legs were computed on.
staticSolverFeeOpen is a flat USD amount from the solver’s getSolverInfo config — it does not scale with the notional, and is "0" when the solver publishes none; pre-fill solverInfo (the getSolverInfo result) to skip the fetch. The fetch is fail-soft: an unreachable /info prices the leg at "0" instead of failing the preview.
The funding one-of applies here too: pass a typed initialMargin or fund (see Full-balance funding). A fund preview runs the exact same sizing as the open — probe, linear rescale, lot snap, cost invariant, slippage-bound settlement fallback — so the legs and quantity equal what the open will actually charge and submit. Feed that quantity to TP/SL and constraint previews instead of re-deriving it from a typed figure. Full-balance sizing additionally consults the solver’s locked-param percents; pre-fill lockedParamPercent to skip that fetch.
const fees = await getInstantOpenFees(config, {
subAccountAddress,
market: { id: 1 },
positionType: PositionType.LONG,
initialMargin: "100",
leverage: 5,
// slippage — required on majors; omit on lowcap to auto-derive
});
console.log(fees.totalFee); // open legs summed (settlement provision included) — gate the open budget on this
if (fees.kind === "enigma") console.log(fees.openSolverFee, fees.expectedSettlementLoss);getInstantOpenFeesQueryOptions / getInstantOpenFeesQueryKey ship alongside for TanStack Query consumers; the React layer wraps them as useInstantOpenFees. Pass the preview’s markPrice / estimatedOpenPrice / feeRates into the subsequent prepareInstantOpenParams call to make the actual open zero-fetch and guarantee the charged fees match the previewed ones.
Parameters
Required = inputs only the caller can know (wallet, session key, trade intent). Optional = anything derivable from solver / price-service / on-chain reads.
Every optional field you pre-fill skips its network fetch. If you already have markPrice from a live price stream, lockedParamPercent from a cached solver read, or feeRates from an earlier getFeeForUser call, pass them in — prepareInstantOpenParams short-circuits each field and only fetches what’s still missing. Pass all four optional derivable fields and the function becomes pure math with zero network hops — the fastest possible path from UI inputs to a signed message.
| Name | Type | Required | Notes |
|---|---|---|---|
from | Address | ✔ | Session-key signer. |
subAccountAddress | Address | ✔ | SubAccount that owns the trade. |
market | InstantOpenMarketData | ✔ | { id }, plus optional pre-fetched metadata (precisions, minOpenSolverFeeCap / minCloseSolverFeeCap). |
positionType | PositionType | ✔ | LONG / SHORT. |
initialMargin | string | ✔ † | Decimal collateral amount. Provide this or fund. |
availableBalance | string? | Raw positive collateral budget enabling automatic fallback with initialMargin. Lowcap only; cannot accompany fund. | |
fund | FullBalanceFunding? | ✔ † | { mode: "full-balance", balance } — deploy the whole balance, size quantity to fit. Lowcap only. Or initialMargin. |
leverage | number | ✔ | Positive integer / decimal. |
slippage | number? | Percent (e.g. 1 for 1%). Required on majors; on lowcap, omit to auto-derive from the dry-run estimate + 4%. | |
markPrice | string? | Pre-fetched mark price (decimal). Omit to fetch via Enigma. | |
estimatedOpenPrice | string | null | Pre-fetched estimated fill price (decimal). Lowcap/Enigma only — ignored elsewhere. Omit to fetch via dry-run; null skips the fetch and uses the unavailable-estimate fallback. | |
lockedParamPercent | ApiLockedParamsBySymbolIdResponse? | Pre-fetched solver locked params. Omit to fetch via getLockedParams. | |
feeRates | FeeForUser? | Pre-fetched on-chain fee rates. Omit to fetch via getFeeForUser. | |
solverInfo | EnigmaSolverInfo? | Pre-fetched solver static fees. Lowcap/Enigma only — ignored elsewhere. Omit to fetch via getSolverInfo (fail-soft). | |
uuid | string? | Forwarded to InstantOpenParameters. | |
addMarginSalt | Hex? | Forwarded. | |
sendQuoteSalt | Hex? | Forwarded. | |
deadline | bigint? | Forwarded. | |
chainId | number? | Optional chain override. |
† Funding is one-of: pass exactly one of initialMargin or fund. Passing both throws AMBIGUOUS_FUNDING; passing neither throws INITIAL_MARGIN_REQUIRED.
Returns
PrepareInstantOpenParamsReturnType — the full InstantOpenParameters bag plus fundingMode: "initial-margin" | "full-balance". Pass it directly to instantOpen. The mode reports the effective sizing, including automatic fallback; it is preview metadata, not a signed order field.
PrepareInstantOpenParamsData uses this same return type for query consumers. Fee results (BaseInstantOpenFees and its solver variants) also expose fundingMode.
Errors
RESOLVE_MARKET_NOT_FOUND— solver has no market for that id.RESOLVE_MARKET_METADATA_INCOMPLETE— market response missing precision fields.RESOLVE_MARK_PRICE_NOT_FOUND— price service returned no tick for the symbol.INVALID_TRADE_PARAMETERS— invalid trade inputs, including a typedinitialMarginaboveavailableBalance, or a balance unable to cover the static open fee.AMBIGUOUS_FUNDING— bothinitialMarginandfund, or bothavailableBalanceandfund, were passed.INITIAL_MARGIN_REQUIRED— neitherinitialMarginnorfundwas passed.FULL_BALANCE_UNSUPPORTED—fundoravailableBalancewas used on a non-lowcap (majors) solver.QUOTE_CONSTRAINT_VIOLATED— the full-balance-sized quantity violates a published market constraint (lot multiple, min/max notional, max quantity, min quote value, LF portion).
instantOpen
Sign the EIP-712 message with config.getWalletClient({ from }) and POST it to the solver. This is the low-latency primitive — everything above (prepareInstantOpenParams) has already run; this step is just sign + network.
const result = await instantOpen(config, resolved);
console.log(result.tempQuoteId);Use direct calls to instantOpen when open speed matters — typical pattern:
// on trade-panel mount, run prepare early:
const prepared = await prepareInstantOpenParams(config, uiInputs);
// user clicks Open — no fetch on the critical path:
const result = await instantOpen(config, prepared);Per-solver behavior
The two solver kinds open positions differently, and instantOpen dispatches on the resolved solver’s kind:
| Enigma (lowcap) | Rasa (majors) | |
|---|---|---|
| Operations signed | two — addMarginToNextVA on the AccountLayer, then sendQuote on the diamond | one — sendQuote on the diamond |
| Margin model | funds a fresh virtual account in the same batch | cross-margin — no VA, no addMargin |
margin parameter | required | ignored |
| Muon signature | a placeholder, with its calldata region delegated to the solver via a FlexField | a live uPnl_A_withSymbolPrice attestation, fetched immediately before signing |
| Endpoint | /instant_trade/instant_open | /instant_trade/open |
Rasa’s attestation is encoded inside the signed calldata, so it cannot be cached or fetched afterwards — instantOpen fetches it for you on that path.
Parameters
InstantOpenParameters — the resolved bag from prepareInstantOpenParams. Every field is a concrete value; no fetching happens here, apart from the Rasa Muon signature noted above.
On a contractsVersion: "0.8.6" chain the Enigma flow signs sendQuote carrying solverFeeCaps (SolverFeeCaps — 18-decimal rate caps the quote authorizes for the solver’s open/close fees; the wizard fills the market minimums, falling back to a 1% default when the market publishes none and throwing INVALID_SOLVER_FEE_CAP on a malformed vendor string; omitting it on the primitive means zero caps, which a fee-charging solver rejects). On a "0.8.5" chain the flow signs the legacy sendQuoteWithAffiliateAndData and solverFeeCaps is ignored.
Passing a literal solverId binds the generic and narrows the return type. Omitting it lets the chain’s defaultSolverId decide at runtime — in which case a missing margin on an Enigma chain throws INSTANT_OPEN_MARGIN_REQUIRED rather than silently under-funding the trade.
Returns
InstantOpenReturnType — a union discriminated on kind, sharing { success, tempQuoteId?, partyBmm? }:
EnigmaInstantOpenResult—{ kind: "enigma", … }.RasaInstantOpenResult—{ kind: "rasa", …, rfq }, whererfqis the accepted request-for-quote echoed by Rasa, normalized to the same shapegetInstantOpensreturns.
tempQuoteId identifies the pending trade until it anchors on-chain. Rarely undefined on the Enigma path (synchronous accept-without-id paths).
Errors
SET_TPSL_REJECTED(misleadingly named — same code family) or a solver-specificSymmApiErrorwhen the solver rejects the payload.- Viem
UserRejectedRequestErrorwhen the user dismisses the signature dialog. SymmError(kind: "config") when chain config is missing a solver URL.
instantOpenAuto
Convenience wrapper: prepareInstantOpenParams + instantOpen in one call. Same parameter shape as prepareInstantOpenParams.
const result = await instantOpenAuto(config, {
from: sessionKey,
subAccountAddress,
market: { id: 1 },
positionType: PositionType.LONG,
initialMargin: "100",
leverage: 5,
slippage: 1,
});Latency: this variant does the prepare fetches inline, so the total wall-clock is prepare fetches + sign + POST. Fine for most flows; use prepareInstantOpenParams + instantOpen split when open time is critical (e.g. reacting to a live price change).
limitOpenAuto
The LIMIT-order variant. Same shape and split as instant open, but the caller passes a resting price and the order is signed with orderType = LIMIT, so the solver writes a pending on-chain quote at that price instead of filling immediately. Majors / Rasa only — the Enigma (lowcap) solver has no limit support, and limitOpenAuto throws UNSUPPORTED_BY_SOLVER on a solver that lacks the limitOrder capability. The send deadline defaults to 15 minutes (vs. 5 minutes for a market instant open).
const { tempQuoteId } = await limitOpenAuto(config, {
from: sessionKey,
subAccountAddress,
market: { id: 1 },
positionType: PositionType.LONG,
initialMargin: "100",
leverage: 5,
price: "64000", // resting limit price
});Under the hood it mirrors instantOpenAuto — prepareLimitOpenParams then the shared instantOpen primitive (which honors orderType = LIMIT):
export async function limitOpenAuto(config, parameters) {
const resolved = await prepareLimitOpenParams(config, parameters);
return instantOpen(config, resolved);
}The returned tempQuoteId identifies the resting order off-chain. It appears in getInstantOpens (off-chain) until it anchors, at which point it is a pending on-chain quote — read the reconciled list with the React useLimitOrders hook, and cancel it with requestToCancelQuote / forceCancelQuote (SYMMIO Contract).
prepareLimitOpenParams
The LIMIT-order resolver — same steps and inputs as prepareInstantOpenParams but takes the resting price (instead of slippage) and stamps orderType = LIMIT plus the 15-minute default deadline into the resolved InstantOpenParameters bag. Returns the inspectable bag instantOpen consumes.
getInstantOpens
Paginated list of the solver’s pending instant-opens for a SubAccount.
const page = await getInstantOpens(config, {
account: subAccount,
offset: 0n,
size: 20n,
});The reconciler (reconcileQuotes) consumes this list, so consumers rarely call it directly. Combine with resolveQuoteAccounts to know which predicted VAs to fetch on-chain reads across.
getInstantOpenQuoteId
Resolve the on-chain quoteId for a given tempQuoteId — the anchor mapping the solver reveals once the quote lands on-chain.
const quoteId = await getInstantOpenQuoteId(config, { tempQuoteId: -1001 });The reconciler and notification handlers use this to link temp ↔ on-chain rows automatically; consumers rarely need it manually.
calculateAvailableInstantOpenMargin
Pure function — the maximum initial margin an instant open can spend, shaving the raw available balance for the open fee (on the leveraged notional) and, for SHORT, a worst-case slippage fill. The close fee is not shaved — the contract charges it at close from the position (see Fees at close), not at open. All BigInt; clamps to 0n; returns 18-decimal wei. Feed the Max chip.
import { calculateAvailableInstantOpenMargin, PositionType } from "@symmio/trading-core";
const max = calculateAvailableInstantOpenMargin({
balance, // getAccountBalanceOf, 1e18
openFee, // getFeeForUser, 18-dec fixed-point
slippageFractionWei: 5n * 10n ** 16n, // 5%
leverage: 10,
positionType: PositionType.SHORT,
});available = balance
× max(0, 1 − slippageFactor) // SHORT: slippage, LONG: 0
× max(0, 1 − leverage × openFee)- Fee is charged on the leveraged notional — subtract
leverage × openFee. Only the open fee is shaved; the close fee is charged at close. - Slippage (SHORT only) — quantity is sized off
markPrice × (1 − s); a worse fill inflates notional by up to1 / (1 − s), so cap usable balance atbalance × (1 − s). LONG sets the request above mark, so fills deflate notional — no cap.
The closeFee parameter is deprecated: it is now optional and ignored (kept so existing callers still compile). Pass it or omit it — the shave is the same.
The React layer wraps this in useAvailableInstantOpenMargin, fed by useAccountBalanceOf + useFeeForUser.
Query / Mutation options
instantOpenMutationOptions(config)— TanStack mutation bag for theinstantOpenprimitive.instantOpenAutoMutationOptions(config)— TanStack mutation bag for theAutowrapper.getInstantOpensQueryOptions(config, options)— TanStack query bag for the pending list.getInstantOpenQuoteIdQueryOptions(config, options)— TanStack query bag for the temp-id resolver.
Related
- InstantLayer — the underlying delegated-write contract.
- Instant Close — the close-side analog.
- Unified Quotes —
PendingInstantOpenfeeds intoreconcileQuotes. - TP/SL — attach a TP/SL leg via
useInstantOpenWithTpSlin the React layer.