web3wagmi
Perp Desk
Monad mainnet · Perpl

Methodology

Every number on Perp Desk comes from a public source, carries its unit and its block or timestamp, and can be reproduced with the formula below. When a source fails, the panel says so and shows the reason; a failed read never renders as zero.

Sources

SourceWhat it givesRefresh
Perpl REST app.perpl.xyz/api/v1Market context (ids, decimals, fees, margins, funding interval), ticker, order book (up to 100 levels a side), funding history, candlesContext 60s, ticker 3s, book 2s, funding 60s
Perpl Exchange contract on Monad (chain 143)Account, position, liquidation, deposit, withdrawal and fill events from block 54,773,010; open positions and account balances from contract stateIndex snapshot plus a live tail every 15s
Hyperliquid info APImetaAndAssetCtxs, l2Book (20 exact levels, plus 4 and 3 significant-figure books), fundingHistory, userFees2s to 60s
Envio HyperSyncLog reads when ENVIO_API_TOKEN is set; otherwise the archive RPC rpc1.monad.xyzOn demand

The Exchange address is 0x34b6552d57a35a1d042ccae1951bd1c370112a6f, deployed at block 54,773,010 (2026-02-11 23:02:27 UTC). Collateral is AUSD with 6 decimals. Market ids, decimals, fees and tick sizes are loaded from the context endpoint on every run and never hard-coded.

Units on the wire

Each unit below was checked against live data before any panel used it.

FieldUnitCheck
Pricesraw / 10^price_decimalsAll 10 Perpl marks within 20 bps of Hyperliquid
Sizes, open interestraw / 10^size_decimals; OI is one sideEquals on-chain long and short open interest exactly
Volume, TVL, candle volumeAUSD, 6 decimalsBase volume x mark reproduces dollar volume within 3%
Feesmillionths of notional; 100 = 1 bpsBase tier taker 3.45 bps, maker 0.45 bps; decoded fills paid 3.45, 2.50 and 2.10 bps (tiers 0, 2, 3)
Initial and maintenance marginleverage in hundredths; 1500 = 15xA 3x market posted exactly 1/3 of notional as collateral
Funding rate (API)millionths per funding interval (2,580s)Payment per lot = rate x index x 10^exp / 1e6 on every market
Funding rate (chain)1/100,000ths per intervalMatches the API rate and payment
On-chain enumsone below the API (0 = long, 1 = short)Taker fills tied to their orders and to the maker price

TVL is Perpl's own figure. It sits within 1% of the on-chain position balance on BTC and ETH and 17% below it on MON, so panels label it as Perpl-reported.

Markets board

basis (bps)        = (Perpl mark / HL mark - 1) x 10,000
volume share (%)   = Perpl 24h USD volume / (Perpl + HL 24h USD volume)
OI share (%)       = Perpl OI USD / (Perpl + HL OI USD), both one side at mark
funding spread     = Perpl annualised funding - HL annualised funding

Perpl state is stamped per market by block. The Hyperliquid leg is fetched at the time shown; if it fails, the Perpl columns still render and every HL column is blank with the reason.

Execution cost and depth

A market order of a given USD size is walked through each venue's live book, level by level. Buys consume asks, sells consume bids.

slippage (bps) = (avg fill price / mid - 1) x 10,000   (sign flipped for sells, so cost is positive)
total (bps)    = slippage + base-tier taker fee
cost (USD)     = total / 10,000 x filled notional
depth band     = sum of price x size for levels within N bps of mid, N in 10, 25, 50, 100

Perpl taker fee is 3.45 bps from the context. Hyperliquid's base taker fee is read live from userFees (4.5 bps); if that read fails the documented 4.5 bps is used and labelled as such. Both venues have lower tiers for high-volume accounts.

Hyperliquid's l2Book returns 20 exact levels per side, which covers only a few bps on BTC. Perp Desk adds its 4 and 3 significant-figure books, keeps the exact levels, and adds only what each coarse bucket holds beyond them, priced at the bucket's far edge. A walk that reaches those buckets is therefore an upper bound on HL cost, and a depth band that reaches them is a lower bound (shown with ≥). The exact and coarse books are read together and cached only once stitched, and a coarse residual is kept only past the last exact level, so a bucket from a moving book can never land inside the spread. The Levels line counts the 20 exact levels and the bucket entries separately. Perpl's REST book is capped at 100 levels: a side at the cap is marked "100+ (capped)", and a walk that uses every served level reports what those levels hold as a lower bound with the book continuing past them. Perpl limits market orders to order_max_market_slippage_bps (100 bps on every market today); Perp Desk measures that limit from mid, and a walk past it is marked as needing a limit order.

Funding

Perpl annualised = rate per interval x (31,536,000 / funding_interval_sec)
HL annualised     = hourly rate x 8,760
spread per day    = (Perpl annualised - HL annualised) / 365

Positive funding means longs pay shorts on both venues. Perpl's current rate is its newest funding event; Hyperliquid's is its predicted rate for the current hour. Seven-day averages are simple means over each venue's events; both settle on a regular grid, so this equals a time-weighted average. For a market listed inside the window, both the Perpl and the Hyperliquid averages (and so the average spread) run from its first Perpl event, and the row says how many hours that is.

Perpl's rate is quantised: the contract stores it as a whole number of 1/100,000ths of notional per interval, one step being 12.2% a year at 2,580s. In the data seen so far the rate has not gone past ±4 steps (±48.9% a year) on any market, while the onchain clamp (absFundingClampPctPer100K, read 2026-10-01) is ±10 steps. The panel reports the largest step count seen in the window and marks a current rate sitting at it "at bound"; such a rate may understate the premium it stands for.

Liquidation prices and the map

Liquidation prices follow Perpl's published formula (docs.perpl.xyz, exchange/liquidation):

P_liq = P_entry + s x (P_entry x L x mmf - deposit - fundingPnl) / L
s = +1 for a long, -1 for a short; L = size in base units
mmf = maintenance margin fraction from the context (BTC 2500 -> 4%)

The backfill checked this on the 25 newest liquidations, reading each position at the block before: the event's mark had crossed the computed price in 25 of 25, median distance 4.7 bps. Funding owed to the account counts as collateral; treating it as debt fails on a real SOL short. The map sums size x liquidation price for every open position into price buckets around the mark. Longs liquidate below the mark and plot red; shorts above and plot green.

Distance to liquidation is signed: a long is (mark - liq) / mark, a short is (liq - mark) / mark, and a negative value means the mark is already past it.

Stress test and fragility

For each move m from -20% to +20% in 1% steps, a long liquidates when mark x (1 + m) ≤ its liquidation price and a short when mark x (1 + m) ≥ it. Perpl closes liquidated positions on the book, so the liquidated longs are walked into today's bids by base size and the shorts into the asks. This is a worst case: today's book, with all forced flow arriving at once. Knock-on is one cascade step, the notional whose liquidation price lies inside the band the forced flow walks through. Past-bankruptcy is what the fills lose beyond each position's bankruptcy price. Partial liquidation and the PLP vault's buy-to-liquidate path are left out.

Fragility ranks the open markets on five factors, 1 = most fragile:

FactorDefinition
OI to depthone-side OI / the thinner side's depth within 50 bps
Concentrationtop five accounts' share of gross OI
Effective leveragenotional / equity across the market's positions
Near liquidationshare of OI that liquidates within a 10% move
Forced-flow slippageslippage of pushing the 10% liquidation flow through the book
fragility = (n - mean rank) / (n - 1) x 100      n = ranked markets

It is a relative rank: the most fragile of the listed markets always scores high.

crowd skew = sum(net_i x w_i) / sum(gross_i x w_i)
w_i        = min(1, cap / gross_i),  cap = 5% of the market's gross notional

Crowd skew is the market's net lean with every account's weight capped, so a whale counts no more than 5% of gross notional and the rest of the book shows. It moves continuously as positions change, so two similar accounts swapping places cannot swing it. The panel shows the capped gross and the account count beside it, and states the counterparty sentence only with at least 20 accounts and a lean of 25% or more.

Trader PnL

account value = balanceCNS + sum over open positions (deposit + deltaPnlCNS + premiumPnlCNS)
net flows     = deposits - withdrawals + protocol transfers in - protocol transfers out
window PnL    = value(now) - value(window start) - net flows inside the window
all-time PnL  = value(now) - all net flows

Values are read from Exchange contract state at both ends of each window on every perpetual, hidden ids included. The locked balance already sits inside balanceCNS and is not added again. The change in value minus flows matched realised PnL plus funding minus fill fees plus change in unrealised PnL on sampled accounts, within the insurance and protocol fees charged on opens. Realised PnL, win rate and closes per trader come from a rolling 24h trade log the server reads live from the archive node: every PositionClosed, PositionDecreased and PositionInverted event, plus the liquidations inside the same span (the leaderboard and the trader page state its block range). A PositionClosed carries no size, so its size is the position size after the last open, increase, partial close or flip seen for that account and market. Volume alone comes from the index's fills window, which is fixed when the index is built; the leaderboard says how far it trails the PnL span and marks the column stale past an hour.

Activity and fees

Daily volume comes from Perpl's 1d candles. Accounts, liquidations, deposits and withdrawals come from Exchange events indexed from the deploy block. A liquidation is a PositionLiquidated event. PositionDeleveraged and PositionUnwound (forced closes, such as the delisted perpetual 30 wound down at mark, mostly in profit) are counted apart and labelled by kind. A liquidation on a perpetual the context does not list has no USD price: it is counted, left out of the USD total, and the count left out is shown. Fees are maker plus taker fees from decoded fills over the recent window, with builder fees left out. A taker fill carries no account or market, so it is attributed to the position event directly before it in the same transaction; this agreed with order-request stitching on 167 of 167 and 126 of 126 fills in two samples.

Index coverage

DataCoverageSource
Accounts, liquidations, deposits, withdrawalsBlocks 54,773,010 to 109,614,355, no gapsHyperSync
Open positionsBlock 109,606,926 (snapshot)Contract state, getPositionsV2
Fills, fees, fill volumeBlocks 109,320,929 to 109,606,926, 2026-09-30 12:22:54 UTC to 2026-10-01 12:22:41 UTCDecoded fill and position events
Daily volume2026-02-12 to 2026-10-01Perpl 1d candles

Current caveats: Fees and fill volume in the index cover blocks 109,320,929 to 109,606,926 (24.0h) only

Checks run by the last backfill (2026-10-01 12:25:14 UTC):

  • pass accounts: numberOfAccounts() = 5374 at block 109606926; AccountCreated events indexed = 5374, highest id 5374
  • fail liquidation-price: P_liq = P_entry + s(C_MMR - C_deposit - C_funding)/L with C_MMR = P_entry x L x maintenance fraction (docs.perpl.xyz/exchange/liquidation.md). Position read at block - 1 for the 25 newest liquidations: the event mark had crossed the computed price in 24 of 25; median distance 3.4 bps. Misses: MON short mark 0.033057 vs 0.03305713 at 109523991
  • pass taker-attribution: Blocks 109606327..109606926: the position event before each TakerOrderFilled named the same account and perpetual as the tx's OrderRequest in 111 of 111 taker fills
  • pass fill-volume: 23 whole hours: maker fill notional $19,107,441 vs Perpl 1h candle volume $19,107,454 (-0.00%); maker $20,979,101 vs taker $20,903,049 over the window

Source code and the backfill script are in the repository. Run npm run backfill to extend the index from its last snapshot. Back to the markets.