Somnia × DreamDEX · Event Contracts · DoraHacks 2026
rampart
Order-book depth the protocol itself will not let you withdraw.
- Solidity
- Somnia Shannon · 50312
- @somnia-chain/markets-sdk
- Foundry
- zero runtime deps
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.
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.
Deployed at 0x2a09b4c474828e6895af273e51Ba8C181c91191a on Somnia Shannon, chain 50312.
05Proof on Shannon · 2026-08-19
The chain refuses its own funder.
$ 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.
- 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.
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.
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.
$ 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.
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 runs against a live Somnia binary pool with zero runtime dependencies — hand-rolled keccak-256 and raw JSON-RPC. @somnia-chain/markets-sdk is a dev dependency: CI diffs our hand-transcribed ABI against it, 35/35. Building that turned up three things written down nowhere.
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.
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.
The pool rejects cancelOrderFor from any operator with OnlyApprovedContracts — even 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.
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.
cast call 0x1b8ed5380a4741df019acf5faa0ce6ecbf6167ee \ "cancelOrder(uint128)" 129127208515966879685 \ --from 0xFbc73Ce1C0B43f87cD065f82df24697dEc653595 \ --rpc-url https://api.infra.testnet.somnia.network → execution reverted: 0xf5e39c1f → IncorrectSender(caller, expected)
- Repogithub.com/edycutjong/rampart
- ProofDEMO.md — every tx, every limit
- The txshannon-explorer.somnia.network
/tx/0x959b4770…6ddb - Contract0x2a09b4c4…191a
- Deckrampart.edycu.dev/pitch/
Every other order book asks you to trust the maker. This one doesn't have to.