Skip to content

About

Worked example for HyperEVM developers: a perp pool that hedges traders' net position on HyperCore through CoreWriter, with every order outcome and its timing measured on testnet. Not audited.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

31 Commits

Folders and files

Repository files navigation

hyperevm-perp

Learning HyperEVM by building a perp whose pool takes the other side of every trade and hedges traders' net position with its own position on HyperCore, sent through CoreWriter. The third build after hyperevm-amm (an exchange that trades on HyperCore) and hyperevm-lend (a lending market that reads HyperCore prices).

Status (2026-10-19): a learning project, not audited, not for real funds. It has only been run on testnet; HedgedPerp itself is not restricted to any chain (only the testnet copy is), so do not deploy it with real money.

  • Codex reviewed it adversarially six times (r6 on the testnet-only copy, r6b APPROVE). The hedge's "is my order done?" took four rounds and ended as a keeper acknowledgement, below.
  • One hedged round trip ran on testnet with real prices: open, hedge on Core, close, unwind the hedge, every USDC back (docs/step3-testnet.md).
  • Liquidation, a margin-rejected hedge and an IOC that found nothing ran on testnet with a testnet-only copy whose price the keeper sets (docs/step4-liquidation-and-rejections.md).
  • An order recorder (recorder/orders.py, reviews rec1–rec3 APPROVE) is built but has not been run on testnet yet.
  • Step 5 is specified and built, and testnet session 1 ran (2026-10-29; spec s5h APPROVE, implementation s5i5, amendments s5i7b APPROVE): G1 failed (historical 0x800 reads return the current state), and session 1 stopped at cycle 22 of 120 when the testnet book drifted over 1% from the oracle: how stale a contract's view of its own Core position is, and how quickly and reliably it can flatten that position with reduce-only orders sent through CoreWriter (docs/step5-close-from-evm-spec.md). The testnet copy gained rawIocReduceOnly; the recorder's unwind is now reduce-only.

What you can take from this repo

For a library that wraps CoreWriter and the precompiles, use hyper-evm-lib; to read HL prices safely, hl-price-guard. This repo is a worked example: code to copy from, and what happened when it ran on testnet.

I want to… Look at
hedge a pool's net exposure on HyperCore from a contract HedgedPerp.hedge: read the Core position, send one IOC for the gap
know whether a CoreWriter order is done "Why the hedge's acknowledgement is off-chain" below: three on-chain answers that broke, and acknowledge(cloid) from the info API
see every outcome a CoreWriter perp order can have, with timings docs/step4-liquidation-and-rejections.md (filled, iocCancelRejected, perpMarginRejected)
settle trades so neither trader nor keeper gets a free option requestOpen / settleOpen: anyone settles 2–15 s later at the checked oracle
liquidate without letting a wide book block it HedgedPerp.liquidationPrice: the price better for the trader of oracle and mark
record CoreWriter orders on testnet and fail closed recorder/orders.py
deploy a contract over the 2M small-block gas limit script/big_blocks.py (big blocks on, deploy, off)

Run

git clone --recurse-submodules https://github.com/psyto/hyperevm-perp && cd hyperevm-perp
forge test

46 tests: HyperCore precompiles and CoreWriter are mocked at their real addresses, so nothing touches a network.

Measured on testnet (2026-10-19)

  • A CoreWriter perp order is found in orderStatus by its cloid: unknownOid for under a second, then filled, iocCancelRejected or perpMarginRejected. A margin-rejected order is registered, not missing.
  • A contract's Core account starts with no perp margin; an order then is rejected (perpMarginRejected), not dropped silently.
  • Deploying a ~14 KB contract needs big blocks (evmUserModify); turn them off afterwards, since every transaction from the address then waits for a big block.
  • Basis shows up even on a quiet book: trader entry at the oracle 82,874.0, hedge fill 82,904.0. Core's taker fee was 4.5 bp a side, against the 10 bp charged to traders.
  • USDC EVM → Core through CoreDepositWallet did not arrive for an EOA either on 2026-10-19 (it did on 2026-10-17). HYPE's bridge worked, so USDC was made on Core by selling HYPE on spot.

How it works

  • One market, BTC on testnet. Traders post USDC margin (up to 20x) and go long or short. The pool is every trader's counterparty.
  • Two-step trades, settled by anyone. The trader requests; anyone settles at the checked oracle 2–15 s later. Within one block the price is one value, so when the trader and the keeper are both live, whoever likes that block's price settles it: neither gets a free option. After 15 s the trader can cancel.
  • The hedge. hedge() reads the pool's Core position (0x800) and sends one IOC for the gap to traders' net position, with a fresh cloid. Nothing else goes out until the keeper acknowledges that cloid after reading a final status from the info API (filled, iocCancelRejected, perpMarginRejected, …), never on unknownOid. The contract keeps no record of what the hedge holds; it reads Core each time.
  • Liquidation never refuses a price. Liquidatable when equity is below 2.5% of notional at the price better for the trader: the higher of oracle and mark for a long, the lower for a short. One feed moving alone liquidates no one, and a wide or empty book does not block it; a move counts only once both show it (see Known limits). The equity left goes to the caller; a shortfall is the pool's.
  • Payouts never depend on Core. Profits are capped at 5× margin, and a position opens only while the pool's EVM USDC alone covers every open position's cap. Core money is the hedge's and is not counted. Payouts are credits, claimed from the EVM balance.
Parameter Value
Max leverage 20x
Fee 10 bp of notional at open and at close
Liquidatable below 2.5% of notional (−2.5% at 20x)
Profit cap 5× margin
Settle window 2–15 s after the request
Hedge order one IOC, limit 30 bp beyond the touch and within 1% of the oracle; at most maxHedgeLots; at least $12
Price checks for settles constructor bounds on mark/oracle, mid/oracle, spread

Known limits

  • The keeper is trusted for the hedge. It is also the only liquidity provider. An early or wrong acknowledgement can put two orders on Core; that risks only Core money, never what traders are owed.
  • The hedge lags. Between two hedges the Core position trails traders' exposure by whatever settled, with no bound in size or time but the keeper's pace. On top of that, the one outstanding order can run after exposure moved and point the wrong way, by at most its size. Core liquidations or ADL of the hedge move the Core position on their own; the next hedge orders the gap from what Core shows.
  • Liquidation can be held back. A move against a trader counts only once both the oracle and the mark show it. The mark follows HL's book, so a trader (or anyone) who pushes the BTC book their way keeps the liquidation price on their side for as long as they pay to hold the push; a real move the mark lags waits the same way. Meanwhile the loss can pass the margin, and the pool pays the shortfall. Neither delay is measured or bounded here (hl-price-guard docs/safe-limits.md section 5).
  • Capital-hungry. Because payouts do not count Core money, the EVM must hold 5× every open margin.
  • Not handled: funding (traders pay none; the hedge pays or receives Core's), basis (traders settle at the oracle, the hedge fills at the book: 3.6 bp in step 3), gaps under $12, other liquidity providers (needs share pricing against open PnL), Core HYPE (gas) cannot be sent back to the EVM.

Why the hedge's acknowledgement is off-chain

A CoreWriter action runs after the EVM block, and the EVM is never told how it ended. Reviews r3–r5 broke three on-chain answers to "is my order done?":

Tried Broke because
a timer between orders (r3) while Core was slow, three calls sent three identical orders
"the Core position moved" (r4) Core liquidations and ADL move it too; an order can net back to its start; a timeout does not cancel a queued action
acknowledging "never registered" orders (r5) orderStatus answers unknownOid for those and for an order still inside CoreWriter's delay alike

On testnet every order then showed unknownOid for under a second and a final status within about 2 s.

Layout

Path What
src/HedgedPerp.sol the market
src/testing/TestnetPricedPerp.sol testnet only (refuses any chain but 998): keeper-set price for liquidation, settles within 1% of the real oracle, a raw IOC (and its reduce-only twin) for measuring orderStatus and closes
test/ 46 tests; precompiles and CoreWriter mocked at their real addresses
docs/design.md the design and what is left
docs/step3-testnet.md the hedged round trip on testnet
docs/step4-liquidation-and-rejections.md liquidation, perpMarginRejected, iocCancelRejected; all four order outcomes and their timings
docs/step5-close-from-evm-spec.md spec only: precompile visibility of a contract's Core position, reduce-only close latency and outcomes, a read-only mainnet book estimate
recorder/orders.py testnet order recorder: sends raw IOCs one at a time, records every orderStatus change by cloid, fails closed; step 5 commands g1, step5, lag, and book (mainnet, read only)
docs/reviews/ Codex review rounds: payloads, outputs, PoCs, and what was fixed (r1.md–r6.md; recorder rec1–rec3; step 5 spec s5–s5h)
script/big_blocks.py big blocks on/off for the deployer (deployment needs ~2.8M gas, over the 2M small block)
lib/hyperevm-amm, lib/hl-price-guard pinned git submodules: CoreWriter/precompile helpers, guarded price reads (hl-price-guard v0.3.1; tests set prices with its HLTest)

Reviews

Codex, adversarial, a failing test for every finding (docs/reviews/).

Round Scope Findings Outcome
1 whole market 2 high, 1 medium capital is EVM USDC only; anyone settles within 2–15 s; no hedge without a Core account
2 r1 fixes 1 high the documented hedge bound was wrong; corrected
3 r2 fixes 1 high, 1 medium a timer let orders pile up; rebuilt as one unconfirmed order
4 the rebuild 1 high, 1 medium a position change is no acknowledgement; keeper acknowledges by cloid; settles never wait
5 acknowledgement 1 high (procedure) never acknowledge on unknownOid
6, 6b the testnet-only copy 1 medium, 1 low; then APPROVE the raw IOC takes the one order slot; settle price within 1% of the oracle
rec1–rec3 the order recorder CHANGES twice; then APPROVE fails closed on any surprise; status and unwind for manual recovery
s5–s5h step 5 spec 4 high, 16 medium, 3 low over s5–s5g; s5h APPROVE reduce-only for every close and recovery; a gate on whether historical 0x800 reads work; local-clock brackets only; per-order acknowledge table
s5i1–s5i5 step 5 implementation 5 medium, 5 low over s5i1–s5i4; s5i5 APPROVE G1 strict per block and bound to its pool; exact price and size conversion; read pace with degraded brackets; slot re-read before every acknowledgement; base fee schedule recorded
s5i6, s5r1, s5i7–s5i7b signing fix; testnet session 1 s5i6 APPROVE; s5r1 and s5i7 CHANGES, s5i7b APPROVE --private-key for cast 1.7; opens 2% through (the testnet book drifted >1%); ackopen for a stopped open; first-read matches are upper bounds; every session ends on record

Next

  • Step 5 on testnet: sessions 2 and 3 of step5 on other days (98 cycles left), lag; the week of mainnet book runs until 2026-11-05 (read only); write it up.
  • A full arithmetic audit round, as hyperevm-lend's r8 did.
  • Funding passed through to traders, accrued per second.
  • A keeper that polls orderStatus and acknowledges by itself (by hand on testnet so far).
  • A way to send the pool's Core HYPE back to the EVM.

License

Apache-2.0

About

Worked example for HyperEVM developers: a perp pool that hedges traders' net position on HyperCore through CoreWriter, with every order outcome and its timing measured on testnet. Not audited.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages