Somnia × DreamDEX · Event Contracts · DoraHacks 2026

rampart

Order-book depth the protocol itself will not let you withdraw.

0xf5e39c1f the revert that proves it
  • Solidity
  • Somnia Shannon · 50312
  • @somnia-chain/markets-sdk
  • Foundry
  • viem
rampart mark — a two-rung order-book depth ladder: the upper amber rung, owned by a wallet, fragments and drifts away; the lower cyan rung holds and the strike that hits it bounces back.

two rungs · one cancel · one axis PULLABLE dissolves  ·  FIRM repels

02The hero moment

He could not take his own order back.

The wallet that paid for the resting order called cancelOrder. The pool refused it and named both parties in its own revert data.

0xf5e39c1f
IncorrectSender(address,address) Transaction 0x959b4770…6ddb · status 0x0 · mined and permanent on Somnia Shannon. A failed transaction cannot be mocked, staged, or replayed.

03The problem

Displayed depth is only a promise.

On every order book that exists, the size at a level is size the maker can cancel in one block. Takers price that uncertainty in, and no venue can tell them which depth is real.

Wallet-owned depth

Can vanish in one block.

One cancelOrder from the owner and the level is gone — after you decided to trade against it.

Contract-owned depth

Stays until filled or expired.

Only a taker fill or the mandatory expireTimestampNs removes it. There is no third path.

Polymarket, Kalshi and every CEX show you the first bar. Nobody has ever shown you the second.

04The mechanism

Make the contract the owner.

FirmQuote holds the collateral, approves the Somnia BinaryPool, and calls placeBinaryOrder — so Order.owner is the contract, not a wallet. It exposes no cancel path, no reduce path, and grants no operator. Every withdrawal route is therefore closed by the pool's own rules.

cancelOrder(uint128)
owner only — and the owner is bytecode with no code path to ask
CLOSED
cancelOrderFor(owner, id)
an operator the owner approved — the contract never grants one
CLOSED
reduceOrderFor(owner, id, qty)
per-user approval only, no system allowlist — never granted
CLOSED

Deployed at 0x2a09b4c474828e6895af273e51Ba8C181c91191a on Somnia Shannon, chain 50312.

05Proof on Shannon · 2026-08-19

The chain refuses its own funder.

cast — Somnia Shannon · no wallet, no funds, no gas
$ cast call 0x1b8ed538…67ee "cancelOrder(uint128)" \
    129127208515966879685 --from 0xFbc73Ce1…3595

execution reverted, data:
  0xf5e39c1f
  …fbc73ce1c0b43f87cd065f82df24697dec653595   ← caller
  …2a09b4c474828e6895af273e51ba8c181c91191a   ← expected

$ cast 4byte 0xf5e39c1f
IncorrectSender(address,address)

# control — the same call, from the contract itself:
$ cast call … --from 0x2a09b4c4…191a
0x                                ← no revert. It WOULD succeed.
shannon-explorer.somnia.network/tx/0x959b4770…6ddb · status 0x0
  • 1The caller is the wallet that funded and paid for the order.
  • 2The expected address is FirmQuote. The pool names both parties itself.
  • 3The control makes it rigorous: the order is not uncancellable in general. The pool would let its owner cancel — the owner just has no way to ask.

Escrow verified still locked across both attempts: tUSDC 4,827,000 before and after, YES balance unchanged — the order rested, it did not fill.

06The product

Three classes, not two.

Order.owner is readable on-chain, so every price level can be typed. The third class is the one that makes the metric honest.

FIRM
owner is a contract whose EXTCODEHASH is attested, and it is still inside its lock window
This depth cannot be withdrawn.
PULLABLE
owner is a wallet — the empty code hash, keccak256("")
This depth can vanish in one block.
UNVERIFIED
owner is a contract whose code hash we have not attested
No claim is made.

UNVERIFIED is load-bearing. Every escape in our corpus mints UNVERIFIED depth, not FIRM — the book renders it as exactly what it is. The metric errs low by construction.

07The headline number

A naive check is fooled six times out of six.

Typing a level FIRM on EXTCODESIZE > 0 is forgeable. Rampart attests EXTCODEHASH under a static bytecode policy instead.

Attested EXTCODEHASH8 / 8
Naive EXTCODESIZE2 / 8

Corpus: the real FirmQuote, six attacker contracts that each look firm to a naive check, and a plain wallet. All eight are deployed on Shannon; five of the six escapes executed on-chain as real withdrawals.

one command, deterministic
$ node script/headline.mjs --live
attested 8/8   ·   naive 2/8

Six escapes, six distinct policy catches

  • S1HiddenCancelhidden cancelOrder
  • S2Erc1967Proxyupgrade after resting
  • S3DelegateEscapeDELEGATECALL out
  • S4OperatorGranterlate operator grant
  • S5QuietReducereduceOrder shrink
  • S6BatchCancelalternate selector

Every one of these reads FIRM to an EXTCODESIZE check. Every one is typed UNVERIFIED by the attested classifier, each caught by a different check in the policy.

08Architecture

Owner in, typed book out.

The pool is the source of truth for who owns each resting order. Everything Rampart adds is a read — no indexer to trust, no off-chain state to keep in sync, nothing a maker can influence.

FirmQuote.sol holds collateral no cancel · no reduce Somnia BinaryPool placeBinaryOrder() Order.owner = contract resting book getAllOpenOrders OffChain() EXTCODEHASH attested set + static bytecode policy typed book FIRM / PULLABLE / UNVERIFIED ESCROW RESTS OWNERS CLASSIFY @somnia-chain/markets-sdk + viem
0.13 msfull-book retype · p95
0.13 ms
100 ms block budget

Re-typing the entire book and re-aggregating the percentage fits inside one Somnia block with three orders of magnitude to spare — so the metric can be live, not batch. Measured over a deterministic 2,000-order book by script/bench.mjs — fixed owners, fixed seed, so the number reproduces on any machine.

09Sponsor SDK — the engine, not the decoration

Three findings the docs do not have.

Rampart is built on @somnia-chain/markets-sdk and viem against a live Somnia binary pool. Integrating it turned up three things that are not written down anywhere.

01

The generic placeOrder reverts UseBinaryPlacement on a binary pool. Binary markets need placeBinaryOrder, with the YES/NO side as an explicit kind param — read out of markets-sdk/src/tradeAbi.ts, not the documentation.

02

The prose docs document SpotPool. BinaryPool differs, and the difference is fatal if missed — including builderFeeBpsTimes1k, which must be uint96 or the selector changes and the call reverts with nothing decodable.

03

The pool rejects cancelOrderFor from any operator with OnlyApprovedContractseven for a valid grant. We found this by deploying an attacker whose whole escape was a late operator grant, and watching the pool block it anyway.

Written up as a 261-line feedback report — a required deliverable for this event, produced from the build rather than from a read-through. Rampart's entire mechanism is the pool's own ownership rule; there is no version of this project on another chain.

10Ecosystem impact

An observable no off-chain book can produce.

“Percent of book that cannot be withdrawn” needs an on-chain Order.owner to inspect. That requirement is the whole argument.

Off-chain book

Polymarket · Kalshi · every CEX

Resting orders are signed messages inside a private matching engine. There is no on-chain owner to inspect, so the metric is not hard to build — it is impossible to build. Even the venue itself cannot prove it without being believed.

On-chain order book

Somnia · DreamDEX

The owner is an address, its code is a public hash, and both are a free read. Liquidity quality stops being a marketing claim and becomes a number anyone can re-derive from chain state alone.

Firm depth is what a maker can charge for and a taker can price. This is a reason to put an order book on-chain that has nothing to do with decentralisation — it is a product capability that only exists here.

11What this is not

The limits, stated up front.

Testnet only
Somnia Shannon, chain 50312. Nothing is deployed to mainnet and nothing has been audited.
Buy side only
Selling firm needs an ERC-6909 operator grant — and granting no operator is exactly what keeps the lock airtight. v1 quotes the buy side and says so.
Sound, not complete
The static analyzer is sound for the six known escape classes. It is not a general proof of irrevocability, and we do not present it as one.
List, not oracle
The attestation registry is a transparency list anyone can re-derive from the same bytes. It is not a trustless oracle, and it never will be without governance.
No traction
No users, no volume, no adoption to report. A live firmness read returns 0% right now — the deployed lock window has lapsed, and we would rather show you that than a stale screenshot.

Each of these is in DEMO.md under Honest limits, with the exact reproduce command for the two escapes that did not complete on-chain.

12The ask

Don't take our word. Take the chain's.

no wallet · no funds · no gas · ~4 seconds
cast call 0x1b8ed5380a4741df019acf5faa0ce6ecbf6167ee \
  "cancelOrder(uint128)" 129127208515966879685 \
  --from 0xFbc73Ce1C0B43f87cD065f82df24697dEc653595 \
  --rpc-url https://api.infra.testnet.somnia.network

 execution reverted: 0xf5e39c1f
 IncorrectSender(caller, expected)
70 / 0
forge tests · failures
87.97%
line coverage
6
attacker contracts deployed
261
line SDK feedback report

Every other order book asks you to trust the maker. This one doesn't have to.

rampart · somnia × dreamdex
01 / 12
← → navigate · esc overview · p notes

Speaker notes — slide 1