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.
- Block
- +11
- In auction
- 3 / 8 lanes
- Top discount
- 50 bps
- L0auction 30
- L1healthy
- L2taker took 45
- L3auction 15
- L4healthy
- L5auction 50
- L6healthy
- L7healthy
discount 0pool minimum 75 bps
At a take the taker is paid P = d × notional; the trader keeps E − P.
0Paraclr lanes
cross-lane edges
18Naive shared state
cross-lane edges
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.
- bot Alands first
- bot Btoo late
- 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
- 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.
- 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.
- 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
- 01
Start
startAuctions(lane, accounts)Anyone can put an account below maintenance margin into auction; it’s frozen.
- 02
Tick
d = min(500, 5 × blocks)The discount rises with the block number, never with a gas bid.
- 03
Take
backstopTakeBatch · takeThe taker gets the positions and P = d × notional; the trader keeps E − P.
- 04
Cure or expire
cancelAuction · expireBatchHealthy 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.
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.
- Transactions
- 23
- Dependency edges
- 2
- Blocks
- 11
- Longest chain
- 2
- Independent txs
- 19
paraclr._ledgers[3].collateral2 edges
- Transactions
- 24
- Dependency edges
- 20
- Blocks
- 12
- Longest chain
- 4
- Independent txs
- 6
naive._ledgers[0].collateral14 edgesnaive._auctionCounters[0].{started|taken|cancelled|expired}10 edgesnaive._auctionList[0].length10 edges
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.
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.
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.json0 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.json11 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
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.
MonadLive on testnet · chain 10143
The chain. Each of the 8 lanes keeps its auction state in its own storage, so in a crash Monad's parallel executor has no reason to re-execute one lane's resolution because of another's. Discounts are timed in blocks, not seconds.
Agora AUSDCollateral · testnet AUSD
The single collateral and quote asset: margin, PnL, fees, lane insurance and backstop deposits, 6 decimals. Claim test AUSD calls Agora's faucet contract from your own wallet.
PythPrices · testnet, data trialEvery order and auction carries a signed Pyth update the contract verifies for age, confidence and a positive price. An order fills at the first Pyth price after its delay, so neither the trader nor the executor picks it.
SOL/USD and BTC/USD · stored price ≤ 15 s old · confidence ≤ 2% · Hermes data trial until ~Oct 20
PrivySign-in + walletSign in with a passkey, email, Google, X, Discord, GitHub or your own wallet. Everyone without a wallet gets one embedded wallet, which signs their trades; a reload keeps the session.
No seed phrase · the demo burner key is never offered on mainnet
EnvioIndexer · live on testnet
HyperIndex reads the clearinghouse through HyperSync: every order, fill, auction and backstop flow. Order history, your past auctions and activity since deploy come from its GraphQL API on Envio Cloud.
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?
What does a trader actually lose?
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?
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?
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?
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?
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.
