@somnia-chain/markets-sdk


@somnia-chain/markets-sdk / index / PerpOrderMarginPreview

Type Alias: PerpOrderMarginPreview

PerpOrderMarginPreview = { priceable: false; asOfBlock: bigint; } | { priceable: true; asOfBlock: bigint; increasingQuantity: bigint; reducingQuantity: bigint; lockAmount: bigint; initialMarginPortion: bigint; adverseGapPortion: bigint; leverageSurcharge: bigint; effectiveImfBps: bigint; markPrice: bigint; unlockedCollateral: bigint; equity: bigint; imRequirement: bigint; feeHeadroom: bigint; topUpRequired: bigint; wallet: { balance: bigint; allowance: bigint; } | null; fundingPayer: Address | null; mainWalletCapacity: bigint | null; mainFundingBlocked: boolean; walletCoversTopUp: boolean; ownWalletPull: bigint; mainWalletPull: bigint; hasCollateralForLock: boolean; meetsInitialMargin: boolean; voucherBlocked: boolean; restrictedBlocked: boolean; isolationBlocked: boolean; sufficient: boolean; }

Defined in: packages/sdk/src/perp/margin.ts:1515

What a perp order will cost and whether it will be accepted, computed BEFORE sending it.

A discriminated union: an unpriceable market yields no preview at all, because every component below needs the mark. Narrow on priceable first.

Union Members

Type Literal

{ priceable: false; asOfBlock: bigint; }

priceable

priceable: false

The pool's mark feed is stale or zero. Placement of any order with an increasing leg would revert on the contract's own freshness gate, so there is nothing to preview — and no field here could be trusted if there were.

asOfBlock

asOfBlock: bigint

The block every read was pinned to.


Type Literal

{ priceable: true; asOfBlock: bigint; increasingQuantity: bigint; reducingQuantity: bigint; lockAmount: bigint; initialMarginPortion: bigint; adverseGapPortion: bigint; leverageSurcharge: bigint; effectiveImfBps: bigint; markPrice: bigint; unlockedCollateral: bigint; equity: bigint; imRequirement: bigint; feeHeadroom: bigint; topUpRequired: bigint; wallet: { balance: bigint; allowance: bigint; } | null; fundingPayer: Address | null; mainWalletCapacity: bigint | null; mainFundingBlocked: boolean; walletCoversTopUp: boolean; ownWalletPull: bigint; mainWalletPull: bigint; hasCollateralForLock: boolean; meetsInitialMargin: boolean; voucherBlocked: boolean; restrictedBlocked: boolean; isolationBlocked: boolean; sufficient: boolean; }

priceable

priceable: true

asOfBlock

asOfBlock: bigint

The block every read was pinned to.

A preview is a statement about THIS block, not about the block the order lands in. The adverse-gap component moves one-for-one with the mark, so a limit bid above a falling mark locks more than quoted and either gate can flip. Re-quote near send time for anything close to the edge.

increasingQuantity

increasingQuantity: bigint

The part of the order that increases the position — the only part that locks.

reducingQuantity

reducingQuantity: bigint

The part absorbed by existing exposure. Locks nothing.

lockAmount

lockAmount: bigint

Total collateral the pool will lock (raw quote units) — the honest "margin required".

initialMarginPortion

initialMarginPortion: bigint

The initial-margin component of lockAmount, at the effective (OI-scaled) IMF.

adverseGapPortion

adverseGapPortion: bigint

The adverse mark-to-entry component of lockAmount, zero on a favourable entry.

A position opens at the order's price but is marked at the current mark, so a buy above mark (or sell below) is born underwater by that gap; the pool reserves it on top of initial margin. This is the term a naive notional × IMF estimate misses, and the usual reason a "max" order sized that way gets rejected.

leverageSurcharge

leverageSurcharge: bigint

Extra margin demanded because the account set a per-market leverage cap STRICTER than the market's effective IMF. Zero when unset or looser.

Charged on post-fill notional, and unlike the lock it comes out of free equity rather than being reserved.

effectiveImfBps

effectiveImfBps: bigint

The OI-scaled IMF actually applied, bps — not the static initialMarginBps.

markPrice

markPrice: bigint

Mark price the adverse gap was measured against.

unlockedCollateral

unlockedCollateral: bigint

Free collateral available to be locked.

equity

equity: bigint

Account equity (signed) before the lock.

imRequirement

imRequirement: bigint

Initial-margin requirement of EXISTING positions.

feeHeadroom

feeHeadroom: bigint

The pool's worst-case fee reserve for this order — an auto-pull addend, never part of lockAmount. See PerpOrderMarginQuote.feeHeadroom.

topUpRequired

topUpRequired: bigint

What auto-pull would REQUEST in total, 0n unless autoPull was passed.

Not the wallet spend once a main is in play — that is ownWalletPull for the owner and mainWalletPull for the main, and an order form showing this figure as the child's debit over-states it by the main's leg. With no main the two coincide.

See PerpOrderMarginQuote.topUpRequired, including the three cases where the pool declines and this reads 0n for a reason other than "nothing needed".

wallet

wallet: { balance: bigint; allowance: bigint; } | null

The owner's collateral-token balance and MarginBank allowance, null unless autoPull was passed. Both bind on topUpRequired, and which one is short decides whether the fix is "approve more" or "fund the wallet".

fundingPayer

fundingPayer: Address | null

The linked MAIN ELIGIBLE to fund what the owner's wallet cannot, or null when the account funds itself (unlinked, a main itself, or the rail is dormant) or autoPull was not passed.

Eligibility, not a debit. This is resolved for every linked account on the autoPull path, including an order that needs no top-up at all and one whose own wallet covers the whole pull. Read mainWalletPull > 0n for the wallet that actually moves, and show THAT before the trader signs.

mainWalletCapacity

mainWalletCapacity: bigint | null

What fundingPayer can spend — MarginBank.quoteWalletCapacity(payer), i.e. min(balance, allowance). null whenever fundingPayer is.

mainFundingBlocked

mainFundingBlocked: boolean

The main's leg is withheld because the account still owes a PRIOR payer, so MarginBank.depositForFromMain would revert PriorFundingPayerOutstanding. mainWalletPull is 0n while this holds, whatever mainWalletCapacity says. Clear the old claim with trader.repayPerpMainFunding.

walletCoversTopUp

walletCoversTopUp: boolean

Both funding wallets together cover topUpRequired. See PerpOrderMarginQuote.walletCoversTopUp.

ownWalletPull

ownWalletPull: bigint

The share of topUpRequired the owner's own wallet would supply — it pays first. See PerpOrderMarginQuote.ownWalletPull.

mainWalletPull

mainWalletPull: bigint

The share fundingPayer would supply, 0n with no main. See PerpOrderMarginQuote.mainWalletPull.

hasCollateralForLock

hasCollateralForLock: boolean

Gate 1 — the unlocked balance covers the lock, after any auto-pull. Failing it reverts InsufficientCollateral before margin is even checked.

Vacuously true when nothing is locked: the pool calls lockCollateral only if (lockAmount > 0), so a purely reducing order never touches this gate even from a negative unlocked balance. Without autoPull it is measured against unlockedCollateral alone — see PerpOrderMarginQuote.hasCollateralForLock.

meetsInitialMargin

meetsInitialMargin: boolean

Gate 2 — post-lock equity still covers the requirement: equity + topUpRequired - lockAmount >= imRequirement + leverageSurcharge.

Vacuously true for a purely reducing order, which the pool exempts outright (if (increasingQuantity > 0)) on the grounds that closing can only improve account health. An account below initial margin can therefore always reduce.

voucherBlocked

voucherBlocked: boolean

The account holds a credit-voucher floor and this increasing order is barred outright — the market is not on the voucher allowlist, or the protocol's voucher leverage cap is unset. Placement reverts VoucherMarketNotAllowed / VoucherLeverageCapNotSet, whatever the margin numbers say.

Distinct from the margin gates: when the market IS allowlisted, the voucher cap instead feeds the ordinary leverage path and shows up in leverageSurcharge rather than here.

restrictedBlocked

restrictedBlocked: boolean

The market is close-only and this order has an increasing leg, so placement reverts MarketRestricted. See PerpOrderMarginQuote.restrictedBlocked.

isolationBlocked

isolationBlocked: boolean

Isolated margin bars this market for this account, so placement reverts IsolatedMarketBlocked — the one gate here that blocks a reduce too. See PerpOrderMarginQuote.isolationBlocked.

sufficient

sufficient: boolean

Every gate. The order should be accepted.