Skip to content
Live on Monad testnetProduction contracts with Agora’s testnet AUSD and Pyth prices: test funds, no real value. Unaudited. Mainnet beta: launch pending.Contracts Risks

Liquidations, auctioned.Priced by the block, not the gas race.

Paraclr is a perps clearinghouse for Monad. An underwater account doesn’t go to the fastest bot for a fixed fee: it goes to a Dutch auction whose discount rises 5 bps per block, backed by its lane’s backstop LPs. The trader keeps the equity the taker isn’t paid.

Auction clock+5 bps per block · pool takes at 75 bps, block +15
Block
+11
In auction
3 / 8 lanes
Top discount
50 bps
  1. L0auction 30
  2. L1healthy
  3. L2taker took 45
  4. L3auction 15
  5. L4healthy
  6. L5auction 50
  7. L6healthy
  8. L7healthy

discount 0pool minimum 75 bps

At a take the taker is paid P = d × notional; the trader keeps E − P.

Conflict X-raymeasured on Monad testnet · 23 txs

0Paraclr lanes
cross-lane edges

18Naive shared state
cross-lane edges

The auction readout is a schematic, slowed down (a real block is ~0.35 s, so block +15 comes about 5 s in). The X-ray figures are measured: bench/xray/10143-run-mv2za35r-mfh66.json.

The problem

Liquidation is a race. The trader pays for it.

On a typical on-chain perps venue an underwater account goes to whichever bot lands the liquidation first, for a penalty fixed in advance. Bots compete on gas and ordering rather than on price, so the penalty never falls when takers would have accepted less, and the trader can pay more than a competitive price for the takeover.

TodayFirst bot winsschematic
  1. bot Alands first
  2. bot Btoo late
  3. bot Ctoo late

priority fee bidthe winner takes the fixed penalty, in full

ParaclrA clock instead of a race. The discount starts at 0 and rises 5 bps per block on the testnet deployment (25 is the contract default), so a higher gas bid buys an earlier take at a smaller discount, never a bigger one. How the auction works

  1. 01

    Fixed penalty

    The price is set before the crash

    The liquidation fee is a constant: the same in a calm market and in a cascade. A taker who would accept less has no way to offer less.

  2. 02

    Gas bidding

    Bots compete on gas, not on price

    Whoever lands first collects the whole fee, so liquidators bid for priority and ordering. None of that competition flows back to the trader.

  3. 03

    No discovery

    Nobody learns what the takeover was worth

    One winner, one fixed price. The trader pays it whether a taker would have stepped in for a fraction of it or not.

How the auction works

A Dutch auction per account, priced by the block clock.

Every block, the sweeper starts auctions for newly underwater accounts in all 8 lanes. The discount a taker earns climbs block by block until someone takes the account, and whatever equity the taker isn’t paid goes back to the trader as free collateral.

Discount by block after the auction starts

As deployed on Monad testnet: +5 bps per block, cap 500 bps. The contract default is +25, which reaches the cap at block +20 (about 7 s); the testnet step was lowered through the timelock so keepers on a public RPC have time to react.

blocks since the auction started · discount in bps · seconds at ~0.35 s blocks

Outside taker
Any block, at the current discount: an account in the same lane takes if it stays above initial margin.
Backstop pool
Takes at its minimum discount, 75 bps by default: block +15 at this step.
Cap
500 bps at block +100. Untaken (say, an empty pool), the auction expires and closes at mark through the old fixed-fee path.

At the take: where the equity goes

E = collateral + PnL at mark. Proportions not to scale.

The taker gets P = d × notional and the positions at mark; the trader keeps max(0, E − P). If E < P, lane insurance, then lane bad debt, pays the rest.

MEV-resistant, not MEV-free. The price comes from the block clock, so out-bidding on gas can’t buy a bigger discount: it only lands a take earlier, at a smaller one. Ordering still matters when two takers want the same block, so we don’t claim there’s no MEV.

The lifecycle, in four calls

  1. 01

    Start

    startAuctions(lane, accounts)

    Anyone can put an account below maintenance margin into auction; it’s frozen.

  2. 02

    Tick

    d = min(500, 5 × blocks)

    The discount rises with the block number, never with a gas bid.

  3. 03

    Take

    backstopTakeBatch · take

    The taker gets the positions and P = d × notional; the trader keeps E − P.

  4. 04

    Cure or expire

    cancelAuction · expireBatch

    Healthy again: cancelled. Untaken at 500 bps: closed at mark.

Backstop LPs. Each lane has its own pool. LPs deposit AUSD for shares; the pool takes that lane’s auctions at 75 bps, earns the discount and carries the positions. Returns are shown as earned since deploy, never as an APY.

How the auction worksBackstop LPs in the README

Conflict X-ray

Conflict-free is a claim. Here’s the graph.

After a crash we trace every transaction that resolved it and record which storage slots each one read and wrote. Then we run the identical crash on a deliberately naive build of the same contract, where every per-lane aggregate shares one storage key, and put the two dependency graphs side by side.

Network
Monad testnet, chain 10143
Method
live trace · debug_traceTransaction (prestateTracer)
Run
run-mv2za35r-mfh66
Paraclr lanesper-lane storage keys0x5b40…21c5 ↗
0dependency edges across lanes
Transactions
23
Dependency edges
2
Blocks
11
Longest chain
2
Independent txs
19
Slots on the most edges
  • paraclr._ledgers[3].collateral2 edges
Naive shared statesame code · every aggregate on one key0x3A15…981c ↗
18dependency edges across lanes
Transactions
24
Dependency edges
20
Blocks
12
Longest chain
4
Independent txs
6
What serializes it
  • naive._ledgers[0].collateral14 edges
  • naive._auctionCounters[0].{started|taken|cancelled|expired}10 edges
  • naive._auctionList[0].length10 edges
edge within one lane edge across lanes one lane’s transactionsrow = earlier tx that wrote a slot · column = later tx that read or wrote it

Lanes are numbered L0 to L7, as in the contract and the solvency ledgers. L0 had no transactions in this run, so it has no band.

Crash it yourselfPress one button in the Arena and watch a live -20% crash on the Monad testnet demo stack resolve lane by lane, ending with this X-ray built from that run’s transactions. Test funds, no sign-in.

What an edge means

Transaction j read or wrote a storage slot that an earlier transaction i in the same block wrote. That is the condition under which Monad’s optimistic parallel execution has to re-execute j. Across blocks, consensus already orders transactions, so edges are only drawn inside a block.

Excluded from the graph (4)
  • Each tx's own sender account (EOA) `balance` and `nonce`: written by the gas pre-payment/refund and the nonce increment of the account that signed it, not by contract code. (Same-sender txs are ordered by nonce anyway.)
  • The block coinbase (fee recipient) `balance`: every tx credits its priority fee to it; on anvil the coinbase is 0x0000000000000000000000000000000000000000.
  • Transient storage (EIP-1153, the reentrancy guard): it is cleared at the end of every tx, is not part of the state, and never appears in prestate traces.
  • Txs that are not part of the design's resolution phase (the shock, price pushes, the other design's txs) are not nodes of that design's graph. The price loop is paused for the whole crash run, so no oracle write lands between the shock and the last resolution tx.

What it doesn’t show

These are dependencies, not measured re-executions. Monad’s internal re-execution count isn’t observable from outside the node, and the X-ray says nothing about how fast either design runs. It’s a measured dependency map of one crash, labelled with how it was collected.

Checked before any chain

A Foundry test runs a lane-3 batch and a lane-5 batch from the same state, records every slot each touches with vm.accesses, and fails on any write/write or read/write overlap. Read the test.

Solvency is one on-chain check. solvency() returns the AUSD the clearinghouse holds against its liabilities summed over the 8 lane ledgers; the app reads it every block. Solvency proof in the README

Results

Measured on Monad testnet. Every number has a file.

Each figure is read from a committed JSON report when the page is built. The README has every run, including the local anvil ones, and how each was collected.

  1. 28%

    less taken from traders than the old fixed 1% fee would have taken in the same crash

    1,052.41 vs 1,464.26 AUSD, 22 accounts, −20% shock

    bench/results/10143-2026-10-10T22-40-07Z.json
  2. 0 vs 49

    cross-lane conflicts: Paraclr’s lanes against a naive shared-state build of the same code

    first public Arena crash on Monad testnet, live debug_traceTransaction

    bench/xray/10143-run-mv2ze6l6-xzyi6.json
  3. 11 blocks

    for every auction in the Monad testnet run to resolve: 18 taken by backstop pools, 4 by demo takers, 0 expired

    8.8 s from the first submit to the last receipt

    bench/results/10143-2026-10-10T22-40-07Z.json

All runs and X-rays in the README

Production contracts on Monad testnet

The contract set the mainnet beta would run, on chain 10143 with Agora’s testnet AUSD. Test funds, no real value. Nothing of ours is on Monad mainnet yet.

  • ClearinghousePositions, delayed orders, auctions, backstop pools, solvency()0xbC1fD0…b203
  • TimelockOwner of the protocol. Every parameter, oracle or role change waits out its delay0xdD66E8…47Ab
  • Owner multisigA Safe. Proposes and executes changes through the timelock0xCC6224…e4c3
  • GuardianCan pause deposits and new increase orders. Cannot unpause or change parameters0x35b9f4…BFC8
Page 1 / 4, addresses 1 to 4 of 15

All 15 addresses in the READMEFrom deployments/10143.json, deploy block #69,041,758.

Built on

Public infrastructure, used as published.

Paraclr is contracts, a keeper and an app on top of five projects. Each row says what it does here and one fact you can check; the README has the code paths and addresses.

Also used: Safe, OpenZeppelin, Foundry, viem, Slither and Next.js. Supporting stack in the README

Built for the Monad Metropolis hackathon. Not affiliated with or endorsed by Monad, Agora, Pyth, Privy, Envio, Safe or OpenZeppelin.

FAQ

The honest answers.

Every claim on this page is meant to match the repo. If something here is wrong, the code, the committed bench and X-ray JSON and deployments/143.json win.

Is it MEV-free?
No, and we don’t claim it. Pricing is MEV-resistant: the discount is set by the block clock, not by gas bidding, so paying more gas can’t buy a bigger discount, only an earlier take at a smaller one. Ordering still matters when two takers want the same account in the same block, and the backstop pool is a known taker at a known threshold that others can try to step in front of. That competition pushes takes earlier, which is the direction that helps the trader.
What does a trader actually lose?
The payment to the taker, P = discount × notional, at the discount of the block the account is taken in. Everything else, E − P, comes back as free collateral. If equity is below P and the lane’s backstop pool is the taker, the lane’s insurance fund covers the gap and then lane bad debt does (an outside taker is paid only up to the trader’s equity, so the lane never subsidizes self-liquidation); a trader never loses more than their equity. An account that gets healthy again before it’s taken (top-up or price recovery) can be cured and keeps everything. One nobody takes by the 500 bps cap expires through the old fixed-fee path (1% of notional).
Who are the backstop LPs?
Anyone who deposits AUSD into a lane’s backstop pool. The pool takes that lane’s auctions at its minimum discount (75 bps by default, below the old 1% fixed fee), earns the discount and inherits the positions, so its NAV moves with the market until that exposure is reduced. Returns are shown as earned since deploy, never as an APY. On testnet the operator seeds each pool with test AUSD.
What does the Conflict X-ray prove, and what doesn't it?

It shows which transactions in one measured crash read or wrote a storage slot that an earlier one wrote, for Paraclr and for a naive build of the same code with every per-lane aggregate on one key. That read-after-write dependency is the condition under which Monad’s optimistic execution re-executes a transaction in the same block.

It doesn’t show Monad’s internal re-execution count (not observable from outside the node) or speed. Each X-ray says how it was collected: a live debug_traceTransaction trace, or a replay on a local anvil fork.

Are the 8 lanes Monad's execution threads?
No. A contract can’t choose which of Monad’s threads runs a transaction. Lanes are a storage layout: each account’s risk state lives in lane uint160(account) % 8, so the 8 lanes’ auction transactions have disjoint write sets and give Monad’s optimistic executor no reason to re-execute one because of another. 8 is a design choice, not a Monad limit.
Is it audited?
No. Not audited: internal review and Slither only. The mainnet beta would run unaudited code under hard caps, so use only what you can afford to lose. The risk page spells out what that means.
Is this live on mainnet?

Not yet. A capped beta with real AUSD launches on Monad mainnet only if every check in the launch gate passes. Until deployments/143.json is committed, Paraclr has no contracts on mainnet: anything there that claims to be Paraclr isn’t ours.

The production contracts are live on Monad testnet (chain 10143) with Agora’s testnet AUSD and Pyth prices: test funds, no real value. The measured runs on this page come from the demo stack (on Monad testnet, labelled), whose AUSD is MockAUSD, a 6-decimal stand-in, not Agora’s token.

What happens if the keepers stop?
Keepers execute delayed orders, update funding and run auctions, but they aren’t gatekeepers: anyone can execute a pending order or start an auction by supplying a Pyth price update. If nobody does, a committed order can be cancelled once its window has passed and its margin returns, and liquidations run late, which can leave a lane with more bad debt. Keeper liveness risk.
What's real, and what's simulated?

The app on Monad testnet: the production contracts with real Pyth prices verified on chain and Agora’s testnet AUSD. Orders, positions, funding, auctions, backstop pools and the solvency ledgers are all on chain. There is no crash button: prices follow the market, so auctions run when the market moves. Test funds, no real value.

The measured crash runs (demo stack, Monad testnet and local anvil): the demo stack, where the price feed is simulated (the keeper pushes it on chain and the flash crash is one oracle transaction), the hundreds of crash-run accounts are synthetic (created by a demo-only seedAccounts), and the collateral is MockAUSD. The app’s Simulated mode is an in-browser model, labelled as such.

What doesn't it do yet?

Both stacks: one position per market per account, backstop exposure is reduced by an operator/keeper role rather than a market, and there are no scoped session keys.

Demo stack only: no funding payments or trading fees, a simulated price feed, and orders that fill immediately. The production set adds Pyth, delayed orders, fees, funding and caps, and an order can’t flip a position from long to short in one step.

Trade it on testnet. Watch the books.

Sign in with a passkey, email, a social account or your own wallet and trade SOL and BTC perps on Monad testnet: real Pyth prices, Agora’s testnet AUSD, per-lane auctions and a solvency check every block. Test funds, no real value.