Perpetual markets¶
Kuru perpetuals separate order intent from financial accounting. PerpOrderBook owns one market's FIFO state and produces journals. AccountCore owns custody, authorization, routing, and fee policy. A quote-specific PerpEngine owns margin, positions, funding, exposure, open interest, health, and liquidation state.
Quote-specific Engines¶
Each Engine has exactly one canonical quote asset and quoteScale. Cross margin is shared only among markets registered in that Engine; positions in different quote Engines do not net.
Allocating margin is an internal custody reclassification:
AccountCore free quote decreases
PerpEngine margin ledger increases
physical token remains in AccountCore
Free AccountCore balance is not automatically pledged. Spot reserves, passive inventory, cross margin, and isolated margin remain exclusive claims.
AccountCore accepts at most one Engine per quote asset and verifies the Engine binding, enabled quote token, token decimals, and quote scale. Perpetual accounts must already be registered; unlike a Spot orderbook, PerpOrderBook does not auto-register a direct caller.
Market registration and state¶
PerpRouter allocates globally unique nonzero market IDs. ID 0 is reserved inside every Engine for its cross-margin accounting namespace and is never an orderbook market.
Registration fixes structural fields: Engine, quote asset/scale, price and size precision, and tick size. Governance may later change risk parameters, funding interval, Engine status/flags, AccountCore enablement, and orderbook execution state through their separate paths.
Engine market status is ACTIVE or PAUSED. Policy flags are CLOSE_ONLY and REDUCE_ONLY; at this revision both have the same enforcement effect—risk-increasing admission is rejected and fills are constrained to reduction. Resting incompatible orders must be cleared before enabling either flag.
Margin modes¶
One account-market pair is exactly one of:
NONE— no position or order exposure;CROSS— risk contributes to the account's Engine-wide cross namespace; orISOLATED— risk and margin are confined to this market namespace.
Cross storage supports at most 79 active market positions per account per Engine. The Engine also has a configurable account-wide cap on cross resting orders so liquidation cancellation is bounded.
Moving margin and positions¶
- Cross deposit/withdraw selects the Engine by quote asset.
- Isolated add/remove selects the Engine by market ID.
- Direct isolated-to-isolated margin transfer is not supported in V0.
- Isolated-to-cross moves the full isolated position and margin.
- Cross-to-isolated moves the position and a caller-selected nonzero margin amount; idle isolated margin may supplement it.
- Position migration requires zero bid, ask, and reduce-only order exposure. Isolated-to-cross also requires zero isolated maker-fee reserve.
- Margin removal validates the resulting source namespace. Isolated-to-cross moves the entire isolated namespace and validates the resulting cross namespace; cross-to-isolated validates both resulting cross and isolated namespaces.
Margin movement requires INTERNAL_TRANSFER; trading and manual funding settlement require TRADE.
Order admission¶
Orders use base lots and include side, price, GTC/IOC/FOK, optional post-only, optional minSizeAfterBlock, and reduceOnly. An action also supplies the requested position mode, client correlation ID, and optional builder context.
New risk requires:
- orderbook
ACTIVE; - AccountCore unpaused and market enabled;
- Engine market
ACTIVEwithout close/reduce-only policy; - tick/notional validity and sane current prices;
- projected account-position and market open-interest caps; and
- projected initial-margin health.
That admission preview checks initial margin only. On the mutating path, non-reduce-only order actions and fills that leave risk active must also pass the stricter transfer-health floor after journals are applied: the greater of initial margin and 10% of position notional. A fill that clears the resulting mode as flat skips that final health check. Reduce-only exposure/fills use their dedicated closing-capacity and market-state rules.
A reduce-only order must oppose the position and remain within true closing capacity:
long: reduceOnlyLots + openAskLots + newReduceLots <= baseLots
short: reduceOnlyLots + openBidLots + newReduceLots <= baseLots
This is stronger than checking reduce-only size against the position alone. A stale price may still allow a resting reduce-only admission, but an actual taker fill requires sane prices.
Position-dependent chains are intentionally split across actions. A fill that opens a position cannot make a later reduce-only/take-profit placement valid inside the same batch because exposure journals are applied before fill journals.
Atomic settlement¶
For an orderbook action that produces exposure or fill journals:
- The book mutates local queues while recording final exposure deltas and maker fills in transient storage.
- It calls
AccountCore.executePerpOrderActionexactly once. - AccountCore verifies the market route and resolves taker/builder fees.
- The Engine applies exposure and maker-fee reserve deltas, settles maker and taker funding/PnL, debits action fees, updates OI, clears flat modes, and checks final health.
- AccountCore accrues protocol and builder fees; the book emits packed market-data events.
Any failure reverts FIFO mutation, IDs, Engine state, and fee accounting together.
A pure no-op, such as a fresh crossing post-only skip with no other journaled change, makes no AccountCore action call.
Fee accounting¶
Perpetual maker/taker fees start from one global AccountCore pair. Active root-account tiers and live Perp referral tiers can only reduce that pair. Spot fee/referral policy is separate.
When a maker order rests, its effective maker PPS is snapshotted and collateral is reserved:
R(size) = ceil(Qdown(size, orderPrice) * makerFeePps / 10,000,000)
Placement removes R from available margin. A fill consumes the filled fee; cancellation returns the unconsumed reserve. Deltas are computed from the full before/after reserve so repeated partial fills telescope without fee-rounding drift.
Taker and builder fees are each rounded up once from total fill quote for the action. Protocol fees credit the AccountCore fee collector; builder fees accrue in the quote asset. Liquidation uses existing maker snapshots and the target account's ordinary effective taker fee, with no builder fee.
Price and funding inputs¶
A configured price updater or ops writes mark, index, and oracle prices through AccountCore. A configured funding updater or ops writes a signed funding-accumulator delta. The on-chain Engine checks update ordering, configured interval, nonzero values where required, mark freshness, and index/oracle divergence in trading/liquidation paths.
The final mark-price and funding-rate formulas are not implemented on-chain at this revision. The Engine bounds index against oracle but does not bound the supplied mark against either. Updater methodology and availability are therefore part of the trusted system and require separate service documentation.
Margin removal and position migration require a fresh mark, but their transfer-health path does not apply the index/oracle divergence check used by trading and liquidation. Raw threshold views enforce neither freshness nor divergence.
Funding is a signed cumulative per-size accumulator. On settlement:
funding = abs(currentAccumulator - positionCheckpoint) * baseLots / sizePrecision
Credits round down and liabilities round up. A positive accumulator delta pays longs and charges shorts; a negative delta does the reverse. Funding settles before every fill or through explicit single/batch AccountCore calls. A new position starts at the current checkpoint.
Health model¶
Define the Engine's nested quote conversions:
Qdown(lots, price)
= floor(floor(price * lots / sizePrecision) * quoteScale / pricePrecision)
Qup(lots, price)
= ceil(ceil(price * lots / sizePrecision) * quoteScale / pricePrecision)
Position initial margin is:
positionIM(lots) = ceil(Qup(lots, markPrice) * 1,000,000 / maxInitialLeverageE6)
Opening bid and ask exposure are projected independently from the signed position. Each incremental requirement is multiplied by limitOrderRiskFactorPps; limitOrderMargin is the larger side, not their sum. initialMargin = positionIM + limitOrderMargin. Reduce-only exposure is excluded because capacity is bounded to the current position.
effectiveCollateral = deposited margin
+ discounted positive unrealized PnL
(or full negative unrealized PnL)
+ unsettled funding
Maker-fee reserve is reported separately because it has already been removed from available margin.
Each market derives:
cancelMargin = ceil(initialMargin * cancelFactorPps / 10,000,000)
maintenanceMargin = ceil(initialMargin * maintenanceFactorPps / 10,000,000)
takeoverMargin = ceil(initialMargin * takeoverFactorPps / 10,000,000)
with takeoverFactor <= maintenanceFactor <= cancelFactor. Cross health sums independently rounded market values. Raw threshold views report state without freshness enforcement; trading and liquidation paths apply their own price-sanity gates.
Margin removal, migrations, and final non-reduce-only order actions/fills that leave risk active apply the transfer-health floor:
transferRequired = max(initialMargin, ceil(totalPositionNotional * 10%))
withdrawable = min(deposited margin, max(effectiveCollateral - transferRequired, 0))
Positive unrealized PnL may release deposited collateral but cannot itself be withdrawn as tokens beyond the deposited balance.
The indexer health recipe spells out every projected-side branch, funding sign, intermediate ceil/floor, cross-market sum, and freshness/divergence gate used by these getters and actions.
Primary views¶
- AccountCore: route config, quote-to-Engine, admission preview, fee policy.
- Engine: cross/isolated balances, maker-fee reserves, modes and positions, market state, thresholds, transfer requirement, withdrawable margin, liquidation locks, cross resting-order count.
- Orderbook: BBO, L2, per-slot order/order ID, last trade, market state.
- Router: quote-to-Engine, market-to-Engine/orderbook, verified proxies, next market ID.
Current scope limits¶
- No cross-Engine or multi-asset portfolio margin.
- No automatic allocation of free AccountCore quote balance.
- No Aave/ERC-4626 collateral or debt accounting.
- No signed deficit, insurance fund, bankruptcy waterfall, or ADL path.
- Realized loss, funding debit, or action fee beyond unsigned stored margin reverts.