Findings
What we measured about Somnia and dreamDEX while building on them. Stated flat, as measurements — no complaint, and no apology for the ones that cost us.
On this page
Retracted: callbacks are billed at gas used, not at the limit#
This page said the opposite until 11 September, and it was wrong — never true, rather than true once and since changed. We reported that a reactive callback is charged against its subscription’s gas limit whatever it uses: 0.07 STT a wake, a 6.7× overpay. Checked charge by charge, the chain bills gas used.
| Measure | Result |
|---|---|
| Charged blocks matched to their receipts, six engines, 1–11 September | 255 |
| Equal to gas used × price, to the wei | 255 |
| Equal to the gas limit × price | 0 |
| The 1–2 September run: 2,715 callbacks over 12.75 hours | 27.23 STT burned |
| What billing at the limit would have cost | 190.05 STT |
The 0.07 was our engine’s own costPerCallback — the gas limit times the fee, a worst-case bound — read as the bill. The receipts showing 1,479,630 – 1,497,350 gas used were real; the charge paired with them was not. Limits of 10,000,000 and 4,000,000 and priority fees of 1 and 2 gwei all billed the same way, so our own settings do not explain it either.
It mattered. On the strength of it we cut the limit to 4,000,000 to save money that was never being charged, and at that limit no purchase fits: a callback that buys cover has used up to 9,218,505 gas. What stands is the 32 STT an owner must hold to open a subscription — and changing the limit means closing and reopening, so it needs that 32 STT again.
Retracted: the price does follow the work#
This section argued that billing was flat — that the 13,213 registration wakes cost the same as a full scan of the book. It followed from the finding above and falls with it: charges vary with the work, and single callbacks in seven sampled minutes on 1 September cost between 0.0033 and 0.0101 STT.
eth_estimateGas runs roughly 4× over actual#
Settling 42 positions, estimated then executed: estimates of 2,053,708 – 2,796,559 against actual usage of 597,706 – 797,706. A ratio of 3.5 to 4.2×.
It is safe — a limit set from the estimate always succeeds — but it means the estimate is a ceiling, not a forecast. An earlier version said this compounded with limit billing to about twenty-five times the cost. It does not: billing is at gas used, so a generous limit costs nothing extra.
The latch sweep#
Prompted by a real production failure. pendingTickAt was set by one path and cleared only by an inbound reactive callback — and Phase 0 had already recorded that reactive matches can be evicted from a full queue or deferred indefinitely at low priority. The code trusted them anyway.
Every piece of state in the system was then audited with the same two questions — what clears this, and what happens if that thing never comes? Anything whose answer depended on an external arrival with no timeout and no permissionless escape was given one. Three needed fixing:
pendingTickAt— now expires after a grace period and emitsTickExpiredrather than latching forever.activeSubscriptionId— the protocol removes subscriptions on its own when the owner’s balance cannot cover the limit, and the flag stayed true, sosubscriptionHealth()reported subscribed when it was not. Now reconcilable permissionlessly.pendingList— grew without limit; 218 dead entries had accumulated. Now prunable permissionlessly.
One remains latent and documented rather than fixed — see Limitations.
Somnia charges 3,125 gas per byte of deployed bytecode#
Against Ethereum’s 200. For a 17.9 KB contract that is 56M gas for the bytecode alone, before anything else. There is also 400,000 per new account, 200,000 per new non-zero storage slot, and a 1,000,000 gas remaining requirement that is checked but not charged.
The trap is that Foundry’s simulation applies Ethereum rules even when forking Somnia. Its estimate for our vault deploy was 2,017,173; the node’s own estimate was 34,289,290; the bytecode alone required 20,809,375 (6,659 bytes × 3,125). A 17× shortfall, and the first deploy failed on it.
Documentation issues#
- The token table is mainnet-only. Using the documented addresses on testnet fails silently rather than reverting.
- Decimals differ between the two. A split that is easy to carry across from mainnet examples and get wrong.
- The explorer URL in circulation does not resolve.
testnet.somniascan.iofails to connect — not a 404, a DNS failure. The working explorer isshannon-explorer.somnia.network.
All of it, with the receipts, is written up for the chain team in somnia-feedback.md.