Q1 2026 Security & Compliance Report44 incidents, $482M in losses, insights from 11 industry leaders.
Read the report

Audit name:

[SCA] Somnia dreamDEX | Dream-Dex | Apr2026

Date:

May 20, 2026

Table of Content

Introduction
Audit Summary
System Overview
Potential Risks
Findings
Appendix 1. Definitions
Appendix 2. Scope
Appendix 3. Additional Valuables
Disclaimer

Want a comprehensive audit report like this?

Introduction

We express our gratitude to the Somnia team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.

The Somnia Spot DEX is a central limit order book (CLOB) spot trading protocol deployed on the Somnia blockchain. It enables users to place, match, and settle limit and market orders for ERC-20 token pairs, with conditional stop-loss and take-profit orders triggered automatically via Somnia's native on-chain reactivity precompile.

Document

NameSmart Contract Code Review and Security Analysis Report for Somnia
Audited ByIvan Bondar; Seher Saylık
Approved ByKerem Solmaz
Websitehttps://somnia.network/
Changelog01/05/2026 - Preliminary Report
20/05/2026 - Final Report
PlatformSomnia
LanguageSolidity
TagsOrder Book DEX; Vault; Upgradable; Fungible Token; Proxy
Methodologyhttps://docs.hacken.io/methodologies/smart-contracts

Review Scope

Repositoryhttps://github.com/somnia-chain/dream-dex-spot-audit
Commit8667425
Final Commit9015ac4

Audit Summary

31Total Findings
22Resolved
7Accepted
2Mitigated

The system users should acknowledge all the risks summed up in the risks section of the report

{Finding_Table?columns=title,severity,status&setting.filter.type=Vulnerability}

Documentation quality

  • Functional requirements are detailed.

    • Project overview is detailed

    • All roles in the system are described.

    • Use cases are described and detailed.

    • For each contract, all futures are described.

    • All interactions are described.

  • Technical description is detailed.

    • Run instructions are provided.

    • Technical specification is provided.

    • The NatSpec documentation is sufficient.

Code quality

  • The development environment is configured.

Test coverage

Code coverage of the project is 81.94 (branch coverage).

  • Deployment and core user interactions are thoroughly covered with high line and statement coverage.

  • Mixed-decimal and upgrade scenarios are well tested.

  • Negative-path and edge-case branch coverage has gaps in several contracts.

  • Reentrancy and concurrent multi-user interaction scenarios lack dedicated tests.

System Overview

The protocol is composed of two deployable contracts, SpotPool and SpotStopOrderRegistry, both designed for deployment behind upgradeable proxies using ERC-7201 namespaced storage. The out-of-scope deployer libraries support both UpgradeableBeacon + BeaconProxy and TransparentUpgradeableProxy patterns. SpotPool inherits from the abstract OrderBook matching engine and the abstract ERC20Vault, forming a self-contained spot market for a single base/quote token pair. ERC-20 tokens (or the chain's native token via a sentinel address) are deposited into the vault, which tracks internal balances. When an order is placed, the required tokens (plus maker fees for the on-book portion, rounded up via ceiling division) are locked from the user's internal balance. The matching engine performs a dry-run/real-run two-pass fill against the opposite side of the book, transfers tokens between maker and taker internal balances on each fill, and charges fees (floor-rounded) to a configurable fee recipient. Fees use a sub-basis-point precision unit (BPS_TIMES_1K, where 1 BPS = 1,000 units). Supported order types include NormalOrder, FillOrKill, ImmediateOrCancel, and PostOnly, with self-matching prevention via CancelTaker or CancelMaker options.

Price-time priority within the order book is maintained by a composite data structure: a Red-Black Tree of unique price-priority levels combined with an intrusive doubly-linked list for FIFO ordering within each level and efficient full-book traversal. Order indices are allocated via a LIFO free-list (OrderIndexAllocator) to minimize new storage slot creation, and each OrderId encodes both a reusable slot index and a monotonically increasing unique counter to prevent stale-reference confusion. Per-user open order tracking is handled through a separate linked-list index (PerUserOrderIndex), and expired orders are lazily cleaned up during order placement and book traversal.

SpotStopOrderRegistry is a standalone upgradeable contract that holds pending stop orders with GTE (greater-than-or-equal) or LTE (less-than-or-equal) trigger conditions on the midpoint price. It subscribes to the SpotPool's MarkPriceUpdated event via the Somnia reactivity precompile at 0x0100. When the precompile invokes the registry's onEvent callback, all orders whose trigger conditions are satisfied are processed: each is submitted as an IOC order to the SpotPool via placeOrderFor, with failures captured by try-catch to avoid blocking sibling orders. Users pay SOMI (the chain's native token) per order creation to fund handler invocation gas; SOMI is refunded on cancellation and consumed on trigger. An admin (via OwnableUpgradeable) manages subscription lifecycle, fee parameters, slippage tolerance, and excess SOMI withdrawals.

Files in Scope

  • Common.sol: Defines the OrderIndex and OrderId user-defined value types, their packing/unpacking helpers, the NATIVE_TOKEN sentinel address used by the vault for native token accounting, timestamp conversion to nanoseconds, ceiling/floor division utilities, absolute-value helpers, BPS-based fee calculation functions, and the OrderIndexAllocator library that manages a LIFO free-list for reusable order slot indices.

  • IERC20Vault.sol: Declares the interface for the internal-balance vault, specifying deposit, depositNative, withdraw, and getWithdrawableBalance, along with associated error types for insufficient balance, zero amounts, and native token transfer failures.

  • IOrderBook.sol: Declares the interface for the central limit order book, defining the Order, OrderBookParameters, and OrderBookLevel structs, the OrderType and SelfMatchingOption enums, and the external functions for placeOrder, placeOrderFor, cancelOrder, reduceOrder, getBookLevels, and paginated off-chain order iteration.

  • ISpotPool.sol: Extends IOrderBook and IERC20Vault to define the spot pool interface, adding updateFeeRecipient, placeTakerOrderWithoutVault (a combined deposit-trade-withdraw convenience function), convertToQuoteAtPriceCeil, and getPoolParams for reading all pool configuration in a single call.

  • ISpotStopOrderRegistry.sol: Declares the interface for the stop order registry, defining the PendingOrderType, Operator enums, the PendingOrderWithTrigger and StoredPendingOrder structs, and external functions for createPendingOrder, cancelPendingOrder, claimSomi, subscription management, and admin configuration setters.

  • ISomniaReactivity.sol: Declares the minimal interface for the Somnia reactivity precompile at 0x0100, specifying the SubscriptionData struct and the subscribe/unsubscribe functions used by the stop order registry to create and remove event subscriptions.

  • LinkedList.sol: Implements an intrusive doubly-linked list library keyed by uint64 values, providing insertNodeBefore, insertNodeAfter, eraseNode, getNextKey, and getPreviousKey operations. Multiple independent logical lists are supported within a single storage mapping, using zero as the empty sentinel.

  • OrderBook.sol: Abstract contract implementing the core CLOB matching engine. It manages order storage, the dry-run/real-run two-pass matching loop against the opposite book side, expired-order cleanup, per-user order tracking, and order lifecycle hooks (_onOrderPlaced, _onOrderAddedToBook, _onOrderFilled, _onOrderRemoved, _onOrderReduced) that concrete subclasses override to implement token locking and settlement.

  • OrderIndexManager.sol: Abstract contract that wraps OrderIndexAllocator with a monotonically increasing unique counter to produce globally unique OrderId values, providing _getNextOrderId and _freeOrderIndex for subclasses.

  • PerUserOrderIndex.sol: Library that maintains a per-address linked list of open order indices, supporting O(1) addOrder (prepend), removeOrder, getLatestOrderForUser, and getNextUserOrder traversal.

  • PriorityIndex.sol: Library that combines a Red-Black Tree of unique price-priority levels with a doubly-linked list of order indices to maintain price-time priority ordering. It provides insertOrder, eraseOrder, getBestOrder, and getNextBestOrder, with price-to-priority conversion that inverts bid prices so higher bids map to lower (better) priority keys.

  • SpotStopOrderRegistry.sol: Concrete upgradeable contract that stores pending stop orders with GTE/LTE trigger conditions, manages a Somnia reactivity subscription to MarkPriceUpdated events, processes triggered orders as IOC submissions to the associated SpotPool via placeOrderFor, handles SOMI payment collection and refund (with an unclaimed fallback for contracts that cannot receive native tokens), and exposes admin functions for subscription lifecycle and configuration.

  • SpotPool.sol: Concrete upgradeable contract that combines the OrderBook matching engine with the ERC20Vault to form a complete spot trading market. It implements all order lifecycle hooks to lock/unlock base and quote tokens, charges maker and taker fees on fills, computes and emits the midpoint mark price, validates order book parameters against zero-quote-fill dust attacks, and provides placeTakerOrderWithoutVault for atomic deposit-trade-withdraw execution.

  • ERC20Vault.sol: Abstract contract providing internal-balance accounting for ERC-20 tokens and the chain's native token (via a sentinel address). It exposes deposit, depositNative, and withdraw with nonReentrant guards, and internal helpers _depositFor, _withdrawFor, _transfer, and _useBalance used by SpotPool for token locking and settlement.

  • RedBlackTree.sol: Vendored implementation of BokkyPooBah's Red-Black Tree library, providing O(log n) insert, remove, first, last, next, and prev operations on a self-balancing binary search tree of uint256 keys. It is used by PriorityIndex to maintain sorted unique price-priority levels.

Privileged roles

SpotStopOrderRegistry.sol

  • owner (inherited from OwnableUpgradeable): Administrative control over the registry's configuration, subscriptions, and excess SOMI funds.

    • Can call createSubscription to create a Somnia reactivity subscription for automated order triggering.

    • Can call removeSubscription to remove the active reactivity subscription, rendering all existing pending orders inert.

    • Can call setSomiPaymentPerOrder to update the SOMI payment required per pending order creation.

    • Can call setSlippageToleranceBps to update the slippage tolerance applied to triggered market orders.

    • Can call withdrawSomi to withdraw excess SOMI (not reserved for pending-order refunds or unclaimed balances) to an arbitrary recipient.

    • Can call transferOwnership to transfer the owner role to a new address.

    • Can call renounceOwnership to irrevocably renounce the owner role.

  • Somnia Reactivity Precompile (address(0x0100)): Sole caller authorized to deliver on-chain event callbacks.

    • Can call onEvent to trigger pending stop orders when the SpotPool emits a MarkPriceUpdated event, causing matched orders to be placed into the spot order book.

SpotPool.sol

  • owner (inherited from OrderBook, which inherits OwnableUpgradeable): Administrative control over order book parameters and the approved-contracts allow-list.

    • Can call updateOrderBookParameters to update tick size, minimum quantity, and lot size.

    • Can call updateIsApprovedContractToPlaceOrders to approve or revoke contracts authorized to place orders on behalf of users.

    • Can call transferOwnership to transfer the owner role to a new address.

    • Can call renounceOwnership to irrevocably renounce the owner role.

  • Approved contracts (managed by owner via updateIsApprovedContractToPlaceOrders): Contracts on the allow-list that may act on behalf of arbitrary users.

    • Can call placeOrderFor to place orders in the order book on behalf of any specified owner address.

  • feeRecipient (set at initialization, self-managed): The address that accumulates trading fees and controls its own succession.

    • Can call updateFeeRecipient to nominate a new fee recipient address (cannot be the zero address or the current recipient).

Potential Risks

External Precompile and Infrastructure Dependency: SpotStopOrderRegistry depends on Somnia's native reactivity precompile at hardcoded address 0x0100 for subscription creation (subscribe), removal (unsubscribe), and event-driven callback delivery to onEvent. The precompile's availability, callback delivery guarantees, and gas-deduction behavior are controlled entirely by the Somnia validator set and are outside the protocol's control. If the precompile ceases to deliver callbacks (due to validator changes, precompile upgrades, or insufficient SOMI balance on the subscription owner), all pending stop orders become inert until the subscription is re-established.

Incompatible Token Support Without Enforcement: The protocol does not support rebasing, fee-on-transfer, or hook-bearing (ERC-777) tokens, yet there is no on-chain enforcement to prevent their use. While currently mitigated through permissioned deployment, if pool creation is opened to third parties, unsupported tokens could be introduced, leading to incorrect accounting, unexpected behavior, or loss of funds.

Off-Chain Keeper Dependency for Expired-Order Cleanup: Expired entries are skipped via getNextBestOrder and remain in the priority index until evicted by the owner's next placement (_cleanupUserExpiredOrders) or by a permissionless call to the cancelExpiredOrders(OrderId[]) / sweepExpiredAtLevel(bool, uint256, uint256) entry points. Without continuous off-chain keepers invoking these sweepers, expired orders accumulate at busy price levels, increasing per-match cost linearly with the number of stale entries and degrading both _matchOrderToBook and _checkAndEmitMidpointChange performance. Deployment of dedicated keeper infrastructure is an operational prerequisite for production health and is outside the protocol's on-chain control surface.

Approved Contract Authority Over User Vault Balances: The placeOrderFor function in OrderBook allows any address present in the isApprovedContractToPlaceOrders mapping to place orders on behalf of arbitrary users, directly consuming those users' deposited vault balances without per-order user consent. The owner populates this mapping via updateIsApprovedContractToPlaceOrders. If a compromised or malicious contract is approved, it can place unfavorable orders that drain any user's internal vault balance.

Unrestricted State Modification by Owner: The owner of SpotPool can modify order book parameters (tickSize, minQuantity, and lotSize) at any time via updateOrderBookParameters with no upper-bound constraints beyond the zero-quote-fill check and minQuantity != 0. The owner of SpotStopOrderRegistry can change somiPaymentPerOrder and slippageToleranceBps via setSomiPaymentPerOrder and setSlippageToleranceBps respectively, with immediate effect. Parameter changes take effect on all subsequent orders and may invalidate assumptions under which existing resting or pending orders were placed.

Absence of Timelock Mechanisms for Critical Operations: All administrative functions across SpotPool and SpotStopOrderRegistry (including updateOrderBookParameters, updateIsApprovedContractToPlaceOrders, setSomiPaymentPerOrder, setSlippageToleranceBps, createSubscription, removeSubscription, and withdrawSomi) execute immediately upon the owner's call without any enforced delay or review period. There is no on-chain buffer for users or external monitors to observe and react to potentially harmful parameter changes before they take effect.

Treasury Withdrawal Authority: The withdrawSomi function in SpotStopOrderRegistry allows the owner to withdraw SOMI from the contract up to the amount exceeding reservedSomi + totalUnclaimedSomi. While user refund balances are arithmetically protected, the withdrawn excess originates from SOMI consumed by triggered stop orders and is intended to fund the reactivity subscription's gas costs. Owner withdrawal of this operational surplus could deplete the contract's SOMI balance below the 32 SOMI minimum required to maintain the active subscription.

Single Points of Failure and Control: The owner role across both SpotPool and SpotStopOrderRegistry concentrates trading parameter configuration, contract approval, subscription management, SOMI withdrawal, and (indirectly through the proxy admin) implementation upgrades under a single address. Both contracts derive access control from OwnableUpgradeable, gating all privileged operations to this single owner with no on-chain enforcement of multi-signature approval or separation of duties. If the owner is an externally owned account, compromise of a single private key would grant full control over trading parameters, approved contracts, fund withdrawals, and implementation upgrades.

Flexibility and Risk in Proxy Upgrades: Both SpotPool and SpotStopOrderRegistry are designed for deployment behind OpenZeppelin v5.5.0 upgradeable proxies with ERC-7201 namespaced storage across multiple distinct storage namespaces (somnia.storage.OrderBook, somnia.storage.OrderIndexManager, somnia.storage.SpotPool, somnia.storage.ERC20Vault for SpotPool; somnia.storage.OrderIndexManager and somnia.storage.SpotStopOrderRegistry for the registry). While namespaced storage reduces collision risk, future upgrades must maintain layout compatibility across all namespaces simultaneously, and an incorrectly structured upgrade can corrupt any of these storage regions.

Absence of Upgrade Window Constraints: The proxy admin can invoke an implementation upgrade at any time without a mandatory waiting period, cooling-off window, or governance approval step. A malicious or erroneous upgrade takes effect immediately for all affected proxies upon the single transaction's confirmation, leaving no opportunity for users to withdraw funds or cancel orders in response to announced changes.

Findings

Code
Title
Status
Severity
F-2026-1647Unbounded Loop in onEvent Combined With No-Retry Precompile Semantics Enables Griefing of Stop-Order Execution
fixed

High
F-2026-1618Unchecked Addition in _checkAndEmitMidpointChange Leads to Permanent Denial of Service on Bid Operations
fixed

High
F-2026-1659Unsmoothed SpotPool Midpoint Enables Midpoint-Manipulation Sandwich Attacks
fixed

Medium
F-2026-1656Reactivity Precompile Drains Contract Balance Without On‑Chain Accounting
mitigated

Medium
F-2026-1616Tiny Partial Fills Can Truncate Quote To Zero — Taker Receives Base Without Paying
fixed

Medium
F-2026-1662Market Pending Orders Accept Arbitrary limitPrice Which Is Silently Ignored on Trigger
fixed

Low
F-2026-1662createPendingOrder Silently Accepts Overpayment of SOMI, Forfeiting Excess to Admin on Trigger
fixed

Low
F-2026-1662onEvent Does Not Gate on activeSubscriptionId
fixed

Low
F-2026-1662placeOrderFor Accepts an Arbitrary Owner With No Per-User Consent or Opt-In Mechanism
accepted

Low
F-2026-1661Incomplete Storage Cleanup on Pending-Order Cancellation
mitigated

Low
1-10 of 31 findings

Identify vulnerabilities in your smart contracts.

Appendix 1. Definitions

Severities

When auditing smart contracts, Hacken is using a risk-based approach that considers Likelihood, Impact, Exploitability and Complexity metrics to evaluate findings and score severities.

Reference on how risk scoring is done is available through the repository in our Github organization:

Severity

Description

Critical
Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.

High
High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.

Medium
Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.

Low
Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
  • Severity

    Critical

    Description

    Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.

    Severity

    High

    Description

    High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.

    Severity

    Medium

    Description

    Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.

    Severity

    Low

    Description

    Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.

Potential Risks

The "Potential Risks" section identifies issues that are not direct security vulnerabilities but could still affect the project’s performance, reliability, or user trust. These risks arise from design choices, architectural decisions, or operational practices that, while not immediately exploitable, may lead to problems under certain conditions. Additionally, potential risks can impact the quality of the audit itself, as they may involve external factors or components beyond the scope of the audit, leading to incomplete assessments or oversight of key areas. This section aims to provide a broader perspective on factors that could affect the project's long-term security, functionality, and the comprehensiveness of the audit findings.

Appendix 2. Scope

The scope of the project includes the following smart contracts from the provided repository:

Scope Details

Repositoryhttps://github.com/somnia-chain/dream-dex-spot-audit
Commit8667425efd52f68a94f0d6cae27f98bdd1b7c333
Final Commit9015ac43a5234ddf7ecdabb35b85e4f3acbbd93b
WhitepaperN/A
Requirementshttps://github.com/somnia-chain/dream-dex-spot-audit/tree/main/docs;
Technical RequirementsREADME.md

Assets in Scope

Common.sol - Common.sol
ERC20Vault.sol - ERC20Vault.sol
LinkedList.sol - LinkedList.sol
OrderBook.sol - OrderBook.sol
OrderIndexManager.sol - OrderIndexManager.sol
PerUserOrderIndex.sol - PerUserOrderIndex.sol
PriorityIndex.sol - PriorityIndex.sol
RedBlackTree.sol - RedBlackTree.sol
SpotPool.sol - SpotPool.sol
SpotPoolDeployer.sol - SpotPoolDeployer.sol
SpotStopOrderRegistry.sol - SpotStopOrderRegistry.sol
SpotStopOrderRegistryDeployer.sol - SpotStopOrderRegistryDeployer.sol

Appendix 3. Additional Valuables

Additional Recommendations

The smart contracts in the scope of this audit could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like ownership changes or proxy upgrades, as well as unexpected fund manipulations, including large withdrawals or minting events. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended.

To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:

  • Detecting changes to ownership or critical permissions.

  • Monitoring large or unexpected transactions and minting events.

  • Pausing operations when irregularities are identified.

These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations while maintaining smooth operations.

Frameworks and Methodologies

This security assessment was conducted in alignment with recognised penetration testing standards, methodologies and guidelines, including the NIST SP 800-115 – Technical Guide to Information Security Testing and Assessment , and the Penetration Testing Execution Standard (PTES) , These assets provide a structured foundation for planning, executing, and documenting technical evaluations such as vulnerability assessments, exploitation activities, and security code reviews. Hacken’s internal penetration testing methodology extends these principles to Web2 and Web3 environments to ensure consistency, repeatability, and verifiable outcomes.

Disclaimer