• Somnia Shannon · chain 50312
  • mined 2026-08-19 · permanent
  • status 0x0 — reverted on purpose

The wallet that paid for this order tried to cancel it.
The chain said no.

rampart makes order-book depth the protocol itself will not let you withdraw. A FirmQuote contract owns the resting order and has no cancel function — so the pool refuses everyone, including its own funder.

one cancel · two kinds of depththe bounce
pool.cancelOrder(orderId) PULLABLE · owner = wallet PULLED FIRM · owner = contract HELD the pool refuses its own funder 0xf5e39c1f
A cancel strikes the resting quote and bounces; pullable amber hardens into held cyan and never returns.

classifier accuracy

8/8

naive EXTCODESIZE check: 2/8

forge test

70

0 failures · 87.97% line coverage

full-book retype p95

0.25 ms

against a 100 ms block budget

adversarial corpus

6

deployed attacker contracts · 4 escapes executed on-chain

01The refusal

A failed transaction is the only proof that cannot be staged

It failed, it was mined, and it is permanently public. Nothing here is mocked, replayed, or reconstructed.

tx 0x959b4770…6ddb · the funder calls cancelOrder on the contract's resting order REVERTED
$ cast call 0x1b8ed538…67ee "cancelOrder(uint128)" 129127208515966879685 \
    --from 0xFbc73Ce1…3595            # the funder's own wallet

execution reverted, data:

  0xf5e39c1f  IncorrectSender(
    caller    0xFbc73Ce1C0B43f87cD065f82df24697dEc653595   ← the wallet that PAID for the order
    expected  0x2a09b4c474828e6895af273e51Ba8C181c91191a   ← FirmQuote — a contract with no cancel function
  )

The pool names both parties in its own revert data. It knows exactly who is asking — the wallet that funded the order — and it refuses, because the order's owner is the contract. That error message is the product. Open the transaction on the explorer ↗

from the funder wallet

0xFbc73Ce1…3595 (EOA)

execution reverted · 0xf5e39c1f IncorrectSender

from FirmQuote itself

0x2a09b4c4…191a (contract)

0x — no revert. The pool would let its owner cancel.

Same pool, same order, same calldata. So this is not "orders can't be cancelled here" — the pool would honour a cancel from the owner. The owner is a contract with no code path to ask. That distinction is the entire mechanism.

02The lock

Three moves close every exit

The trick is not what the contract does — it is what it cannot do.

Hold & approve

FirmQuote holds the collateral and approves the Somnia BinaryPool. The funder's wallet only ever funds the contract.

Rest as the owner

The contract calls placeBinaryOrder, so the order's owner is the contract — not any wallet.

No way to ask

No cancel path, no reduce path, no operator ever granted. The pool will only take instructions from an owner that cannot give them.

cancelOrder(uint128)

Owner-only. Any other caller — including the funder — reverts IncorrectSender 0xf5e39c1f. Proven above, on-chain.

cancelOrderFor(owner, id)

Needs an operator the owner approved. FirmQuote never grants one — there is no code path that could.

reduceOrderFor(owner, id, qty)

Per-user approval only, no system allowlist. Same closed door.

The quote stands until a taker fills it or its mandatory expireTimestampNs lapses. Two exits — neither of them is "the maker changed their mind." Seven of the 70 tests assert the absence of every withdrawal selector from FirmQuote's ABI: the lock is tested as a missing feature, because it is one.

03What it makes possible

A book where every level tells you if it can run away

Order.owner is readable on-chain, so every price level can be typed by who owns it.

FIRM

Owner is a contract whose EXTCODEHASH is attested, still inside its lock window.

Claim: this depth cannot be withdrawn.

PULLABLE

Owner is a wallet.

Claim: this depth can vanish in one block.

UNVERIFIED

Owner is a contract we have not attested.

Claim: none. No claim is made — and that refusal is the point.

can vanish in one blockthe pool will not release it

UNVERIFIED sits off this axis on purpose. Every escape in our corpus mints UNVERIFIED depth, not FIRM — it renders as exactly what it is. FIRM means attested and locked, and attestation is a human-reviewed list: the static filter rejects the known escapes, it does not prove irrevocability.

Why UNVERIFIED is load-bearing

A naive "owner is a contract → firm" check is forgeable: a hidden cancel, an upgradeable proxy, a DELEGATECALL, or an operator granted after resting would all fake FIRM. Attestation binds to exact runtime bytecode, so every one of those mints depth that carries no claim. The honest limit: a static scan cannot see a selector built at runtime, so attestation is human-reviewed rather than automatic.

An observable that exists nowhere else

Percent of the book that cannot be withdrawn is a liquidity-quality signal no other exchange can publish. Off-chain books — Polymarket, Kalshi, every CEX — keep resting orders as signed messages inside a private matching engine, with no on-chain owner to inspect.

The viewer renders pinned chain state from block 468,201,000 — 11 resting orders, 2 FIRM, 0.1% of the book unwithdrawable — with the reason behind every label, and a live re-check that runs the same classification in your browser.

04The headline number

Six contracts that all look firm. One classifier catches every one.

The corpus is FirmQuote, six deployed attacker contracts that each look firm to a naive check, and a plain wallet. Four of the six escapes were executed on-chain, not just unit-tested.

attested EXTCODEHASH

8/8

Every owner typed correctly — including all six contracts engineered to look firm.

naive EXTCODESIZE > 0

2/8

Fooled by all six contract attacks. It only gets the two trivial ends right.

Each corpus member, its escape, and how each classifier types it
# Owner The escape it hides Attested Naive
S0 FirmQuote none — no cancel path exists FIRM FIRM
S1 HiddenCancel a private cancelOrder behind an innocuous method UNVERIFIED FIRM
S2 Erc1967Proxy upgrade the implementation after resting UNVERIFIED FIRM
S3 DelegateEscape DELEGATECALL into attacker code UNVERIFIED FIRM
S4 OperatorGranter grant an operator after the order rests UNVERIFIED FIRM
S5 QuietReduce shrink the depth with reduceOrder UNVERIFIED FIRM
S6 BatchCancel cancel through an alternate batch selector UNVERIFIED FIRM
S7 plain wallet cancels trivially, any time PULLABLE PULLABLE

The naive column is computed the same way, from the same chain state, in the same run — so the comparison is arithmetic, not rhetoric. Reproduce it with node script/headline.mjs.

0.13 ms

p95 to retype the full book, against a 100 ms block budget — three orders of magnitude of headroom.

70 / 0

forge test — 70 tests passing, 0 failures, at 87.97% line coverage.

5 of 6

attacker escapes executed as real transactions on Somnia Shannon. The other two are documented, not hidden — see honest status.

05Verify it yourself

One read-only call. No wallet, no funds, no gas.

The RPC answers anyone. Ask the pool to cancel the resting order as the wallet that paid for it, and watch the chain refuse.

anyone, anywhere — foundry's cast
cast call 0x1b8ed5380a4741df019acf5faa0ce6ecbf6167ee "cancelOrder(uint128)" \
  129127208515966879685 \
  --from 0xFbc73Ce1C0B43f87cD065f82df24697dEc653595 \
  --rpc-url https://api.infra.testnet.somnia.network

what comes back
execution reverted, data:
  0xf5e39c1f
  000000000000000000000000fbc73ce1c0b43f87cd065f82df24697dec653595   ← caller   (the funder)
  0000000000000000000000002a09b4c474828e6895af273e51ba8c181c91191a   ← expected (FirmQuote)

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

The control

Swap --from to the contract's own address — 0x2a09b4c4…191a — and the same call returns 0x. The pool would let its owner cancel; the owner just has no way to ask.

Replay the whole thing

./gate.sh runs deploy → rest → refuse end-to-end with your own key. forge test runs all 70 tests. Every command and its expected output is in DEMO.md.

06Honest status

What is here is real; what is not here is named

Calibrated honesty is part of the engineering, not a disclaimer bolted to the end of it.

Real today

  • FirmQuote — deployed to Shannon at 0x2a09b4c4…191a, chain 50312
  • The on-chain refusal — the funder's cancel reverted 0xf5e39c1f, mined and permanent
  • The control — the same call from the contract's address returns 0x
  • The classifier — attested 8/8 vs naive 2/8 over the deployed corpus
  • 6 attacker contracts deployed — 5 of the 6 escapes executed on-chain
  • 70 tests, 0 failures · 87.97% line coverage · retype p95 0.13 ms
  • DEMO.md — every transaction, the control, and exact reproduce steps

Named, not claimed

  • Testnet only — nothing is on Somnia mainnet, and nothing has been audited
  • No firmness percentage on this page. It is a live measurement over a live market; a static page cannot honestly freeze one. script/firmness.mjs computes it on demand
  • Sell-side quoting — deliberately out: it needs an ERC-6909 operator grant, and granting none is what keeps the lock airtight
  • 2 of the 6 escapes did not execute — one is blocked by the pool's own unwired operator path (a genuine SDK finding), one by a dry native faucet. Both are proven in unit tests and written up, not hidden
  • The registry is a transparency list, not a trustless oracle — anyone can re-derive it from the same bytes
  • Demo video · external users — none yet
07The evidence trail

The whole story is on the explorer

All on Somnia Shannon (chain 50312), all mined 2026-08-19.

rampart locks a resting quote into the book: a cancel strikes the resting quote and bounces; pullable amber hardens into held cyan and never returns.
The same mechanism as the book sees it — two levels under identical pressure, one of which the pool will not release.