Math / Kit Integration Boundary
This package computes deterministic values from a frozen plain-data snapshot. Hyperliquid Kit owns transport, identity resolution, freshness, policy, and execution. The split is intentional:
| Concern | Kit provides | Math returns |
|---|---|---|
| Precision | market kind, szDecimals, raw user value, explicit direction | canonical protocol-valid candidate or objective precision check |
| Asset identity | network, official dex name, metadata index, market kind | canonical asset key and numeric asset-ID arithmetic |
| Orderbook | ordered L2 snapshot and chosen reference price | mid/spread, deterministic book walk, VWAP/slippage |
| Positions | ordered fills, normalized fee convention, starting cost basis | state transitions, entry, realized/unrealized/closed PnL |
| Fees/funding | current schedule/rate rules, oracle/impact prices, settlement interval | tier arithmetic, fee amount, premium/rate/payment/annualization |
| Margin/liquidation | same-snapshot positions, marks, cross account value, isolated values, official tiers, unified DEX/token join | current/projected margin facts, unified monitoring ratio, and tier-consistent liquidation roots |
| Order preview | current position/collateral, protocol rule availability, caller target assumptions | objective checks, local max bound, reduce-only, trigger, scale, continuous TWAP target |
| Replay | ordered/deduplicated events, completeness evidence, observed snapshot | ledger projection, residuals, objective reconciliation checks |
| Spot/HIP | token metadata, marks, normalized fee token value, account-abstraction and DEX facts | units, cost basis, dust predicates/projection, HIP-1/HIP-3 deterministic constraints |
Two rules that apply to every mapping
- Every money-like Math input is a decimal string. Official responses mix strings (
szi,entryPx,lowerBound) and JSON numbers (universe[].maxLeverage,marginTiers[].maxLeverage,leverage.value). Number fields must be converted withString()before they enter Math; a raw number is rejected withinvalid-decimal-string. The only numeric Math inputs are integer counts and indices (index,szDecimals,weiDecimals,timestampMs, tier indices). - Official response objects are rebuilt, not passed through. Math validates exact key sets, so extra server fields (
cumFunding,returnOnEquity,coin,time, …) are rejected asinvalid-input-shape. Kit rebuilds each input object field by field. This is deliberate: it makes every mapping explicit and auditable. The one intentional exception: officiall2Booklevel rows{ px, sz, n }are already the exact input shape and pass through unchanged (the response's outercoin/timewrapper is still stripped).
Official API field mapping
Asset identity
{ network, marketKind: 'perp', dex, index }:dexis the official dex name fromperpDexs('xyz','flx', …) and isnullfor the first-party dex — the officialperpDexsentry for it isnull, and an empty-stringdexrequest parameter means the same thing, so Kit passesnull(Math normalizes''tonull).indexis the market's position in that dex'smeta.universe.- Fill/market
coinstrings route by syntax: a bare name ("BTC") is a first-party perp,"@N"is spot pair indexN, and"dex:NAME"is a HIP-3 perp on that dex. Kit performs this routing; Math's perp functions accept perp rows only.
Margin tiers and market metadata
meta.marginTablesis an array of[marginTableId, { description, marginTiers }]pairs. Kit joinsuniverse[].marginTableIdto the matching pair and maps each innermarginTiers[]entry as{ lowerBound, maxLeverage: String(maxLeverage) }, preserving order.- Not every
marginTableIdappears inmarginTables. Low-numbered IDs (observed: IDs below 50, equal to the market'smaxLeverage) denote an implicit single-tier schedule; map them as[{ lowerBound: '0', maxLeverage: String(universe[i].maxLeverage) }]. This fallback is verified by dated live liquidation-price comparisons on markets using such IDs. meta.universe[].szDecimals(or Spot token metadata) suppliesszDecimals.meta.universe[].marginMode === "strictIsolated"maps tomarginRemoval: 'strict'; every other market maps tomarginRemoval: 'allowed'. Standard first-party perps are'allowed'.
Account snapshots (clearinghouseState)
- Establish the user's official account abstraction mode before mapping the snapshot. The direct mappings below are for standard mode, where each DEX has separate cross collateral.
- Unified account is recommended for most users; the official documentation does not establish that every new account defaults to it. In unified and portfolio-margin modes, balances and holds are reported through spot clearinghouse state and individual perp-DEX user states are not meaningful. Unified mode requires collateral-token aggregation across DEXs; portfolio margin uses a separate portfolio-maintenance and liquidation-value formula. Neither mode may be represented by passing one DEX's
crossMarginSummary.accountValueinto the standard per-DEX formulas. - Account mode is consumer-owned configuration and is not safely inferred from a
clearinghouseStateshape. An arbitrary public address is therefore not a valid standard-account oracle unless its mode is independently established. position.szi→signedSize(already signed; negative is short).position.entryPx→entryPrice.position.leverage.value→leverageviaString().crossAccountValue←crossMarginSummary.accountValue— notmarginSummary.accountValue; for a standard-mode DEX the two differ whenever isolated positions exist.isolatedMarginValue: dated fixture evidence (fixtures/live/2026-07-19-mainnet-m3.jsonmappingAssertions) proves that with one isolated position it equalsmarginSummary.accountValue − crossMarginSummary.accountValue, unrealized PnL included. Official responses do not publish a per-position isolated equity field, so with multiple isolated positions this mapping is unevidenced and must be treated as unmapped until a fixture proves a per-position derivation.- Observed
liquidationPxandmarginUsedare replay/verification evidence, not Math inputs.
Unified account ratio
calculateUnifiedAccountRatioaccepts a normalized join, not raw API responses and not an inventedmultiverseinfo request.- For each DEX, Kit obtains
collateralTokenfrom the correspondingmetaresponse. The first-party DEX is index0and uses token0; builder DEX indexes follow theperpDexsordering. crossMaintenanceMarginUsedcomes from that DEX'sclearinghouseStatetop-level field.isolatedMarginUsedis a package-side normalized field: sumassetPositions[].position.marginUsedonly whereposition.leverage.type === "isolated".spotBalancescomes fromspotClearinghouseState.balances, mapped field by field as{ token, total }. Under unified mode this endpoint is the trading-balance source of truth.- The official statement that individual perp DEX user states are not meaningful concerns their balance summaries. The official unified-ratio reference still reads per-DEX cross maintenance and isolated position margin fields.
- The official reference uses
?? 0for a missing spot row. Math deliberately requires every DEX-referenced collateral token to have an explicit spot row; synthesize{ total: "0" }only when the same snapshot proves the API omitted a true zero balance. Otherwise Math returnsinvalid-input. - The official reference skips tokens whose
available = total - isolatedMarginUsedis non-positive. Math deliberately returnsindeterminatewhen such a token has cross or isolated occupation, preventing an at-risk or inconsistent snapshot from appearing safe. A zero-occupation token contributes ratio"0"without division even when its available balance is zero or negative. - The returned ratio is a monitoring fact. Official documentation does not define
ratio >= 1as a package-safe liquidation threshold, and Kit must not present it as one.
Outcome metadata and sides
- Outcome asset IDs use the numeric
outcomeand binarysidefrom the sameoutcomeMetasnapshot. - HIP-4 settlement math uses semantic
tokenSide: "yes" | "no". Official docs saysideSpecslabels are often Yes/No but do not define a package-safe numeric-side-to-label mapping. - Kit must resolve the token label from the same dated
outcomeMeta.sideSpecssnapshot and pass the semantic payout side explicitly. Math never converts numeric side0 | 1to Yes/No. evaluateRecurringOutcomeaccepts caller-selected mark updates around the settlement timestamp. Kit owns update provenance and bucket-index-to-outcome mapping.settledOutcomeis comparison evidence, not formula authority.
Prices and funding (metaAndAssetCtxs)
- The response is a
[meta, assetCtxs]pair aligned byuniverseindex.assetCtx.oraclePx→oraclePrice,markPx→markPrice,impactPxs[0]→impactBidPrice,impactPxs[1]→impactAskPrice. assetCtx.fundingis already the hourly funding rate as a decimal fraction ("0.0000125"means 0.00125%/hour). It is directly usable asfundingRateforcalculateFundingPayment— do not rescale it by the 8-hour base interval; that convention is internal to the rate formula inputs.
Fee schedule (userFees)
- Official naming:
crossis the taker rate andaddis the maker rate.feeSchedule.basemaps asbaseRates: { makerRate: add, takerRate: cross }; eachfeeSchedule.tiers.vip[]entry maps as{ minimumWeightedVolume: ntlCutoff, makerRate: add, takerRate: cross }. userFees.userCrossRate/userAddRateare the account's effective rates and can be passed directly asratetocalculateTradeFee.feeSchedule.tiers.mm[](maker-fraction-cutoff market-maker rebate tiers) is not expressible in the volume-threshold tier model and isnot-supported; referral and staking discounts remain Kit policy inputs applied to the rates before Math sees them.
Fills and replay events (userFills)
side: "B"→'buy',side: "A"→'sell'.px→price,sz→size.- Server
feesign matches Math's signed user-cost convention (positive charge, negative rebate) as observed on dated fills (takercrossed: truepositive, maker rebate negative), so it may be passed asfee: { kind: 'explicit', amount }once Kit has verified thefeeTokenis the quote currency. startPosition,closedPnl, andfeepass verbatim intoserverFillEvidence; Math reports neutral residuals rather than assuming a server formula.userFills, funding updates, transfers, and snapshots become ordered replay events only after Kit has backfilled, deduplicated, routed by market kind, and established completeness.cashBalance(replay/reconcile snapshots) is the account's USDC cash ledger excluding unrealized PnL. The recommended mapping ismarginSummary.accountValue − Σ position.unrealizedPnlcomputed from one snapshot; whichever convention Kit picks must be applied identically to the base and observed snapshots, because replay projects cash asbase cash + realized PnL + fees + funding + transfers.- For Spot fees paid in base token, Kit must not assume raw fill
sizeis gross or net. It may callprojectSpotPositionEventonly when it has independently established the actual base inventory delta and quote-valued fee; otherwise the mapping is not-supported until an explicit base-fee input contract is sourced.
TWAP execution target
calculatePerpTwapExecutionTargetreceives the native TWAP total base size astotalSize, the caller-selected total duration asdurationMs, and one caller-observed elapsed time aselapsedMs. Kit owns the start-time evidence and elapsed-time convention; Math only checks0 <= elapsedMs <= durationMsand computes the continuous target.- The official 5-minute-to-7-day duration window and minimum total order value are mutable admission rules enforced by Kit/server policy. They are not Math arithmetic preconditions.
- The server derives child count and interval from total size and running time. Kit must not use the Math target to invent native child orders, interval timing, randomization, catch-up size, slippage, or fill behavior.
What Kit must not infer from an ok result
An ok Math result is not an execution authorization. Kit still owns warnings, severity, blocking, freshness, wallet/signature/nonce handling, API errors, rate limits, mutable server limits, order submission, and reconciliation after execution. In particular:
- local max size is not the server-accepted maximum;
- a liquidation root is not a prediction of partial liquidation, backstop fill, ADL, or scheduling;
- a continuous TWAP target or local Scale projection is not a fill forecast;
- HIP-1/HIP-3 experimental results do not prove deployment, collateral, or cross-margin eligibility;
not-evaluatedconstraints must remain visible to product policy rather than being treated as satisfied.
Math has no network, subscription, clock, persistence, mutable cache, signing, nonce, or wire-action mapping. Its only runtime dependency is decimal.js.