<!-- canonical: https://0xsimao.com/reports/ostium -->

# Ostium audit

Three Sigma · RWA perpetuals DEX · 19th February 2024

A private security review of Ostium (RWA perpetuals DEX), conducted with Three Sigma and completed on 19th February 2024 over 33 days. 0xSimao disclosed 57 findings: 8 high severity, 11 medium, 13 low and 25 informational.

## What Ostium is

Ostium is a perpetuals DEX purpose-built for real-world assets: a stablecoin-settled trading engine with a dynamic fee structure and an in-house oracle lets traders take leveraged synthetic positions on commodities, FX and other traditional markets, fully on-chain.

## Scope

The Three Sigma review covered the trading storage and lifecycle: position opening and closing, funding-fee accrual on both sides of a pair, liquidation timeouts, oracle fee handling, and the pair-update and blacklist administration paths.

## High severity findings

- [In OstiumTradingStorage, firstEmptyTradeIndex() and firstEmptyOpenLimitIndex() overwrite index 0 if not found](https://0xsimao.com/findings/ostium-storage-index-overwrite-found)
- [Liquidations can be prevented by updating the SL timeout before it expires](https://0xsimao.com/findings/ostium-liquidations-prevented-timeout-expires)
- [Uneven getPendingAccFundingFees() leading to wrong funding rate update](https://0xsimao.com/findings/ostium-uneven-acc-funding-fees)
- [Inability to Close Positions with Significant Positive PnL](https://0xsimao.com/findings/ostium-inability-close-positions-positive)
- [Oracle fees should be payed upfront to protect the protocol from failed performeUpkeep() calls](https://0xsimao.com/findings/ostium-oracle-fees-upfront-performe)
- [updatePair() is missing the pairOk() modifier](https://0xsimao.com/findings/ostium-update-pair-missing-modifier)
- [USDC Blacklisting Prevents Trader Liquidation](https://0xsimao.com/findings/ostium-blacklisting-prevents-trader-liquidation)
- [Casting from int256 to uint256 won't revert if the number is negative, possibly leading to issues](https://0xsimao.com/findings/ostium-casting-int256-won-revert)

## Medium severity findings

- [utilizationThresholdP of 10_000 will make OstiumPairInfos::_getUtilizationOpeningFee() divide by 0](https://0xsimao.com/findings/ostium-000-infos-opening-fee)
- [OstiumVault::lockDiscount() may revert due to division by 0](https://0xsimao.com/findings/ostium-lock-discount-revert-division)
- [OstiumTradingCallbacks::executeAutomationOpenOrderCallback() reverts if it can not find the openLimitOrder](https://0xsimao.com/findings/ostium-callbacks-callback-reverts-find)
- [OstiumTrading::executeAutomationOrder() and _getLimitOrdersToTrigger() should check isPaused for open limit orders](https://0xsimao.com/findings/ostium-automation-orders-trigger-paused)
- [Missing Pausability Check in OstiumTrading::executeAutomationOrder() for Opening Orders](https://0xsimao.com/findings/ostium-pausability-automation-opening-orders)
- [OstiumTrading::topUpCollateral() is missing pairsStored.groupMaxCollateral(pairIndex) check](https://0xsimao.com/findings/ostium-collateral-pairs-stored-index)
- [OstiumTrading::closeTradeMarket() and OstiumTrading::topUpCollateral() are missing pending trigger checks](https://0xsimao.com/findings/ostium-close-trade-top-collateral)
- [Error in OstiumPairsStorage::groupMaxCollateral() calculation](https://0xsimao.com/findings/ostium-pairs-storage-group-collateral)
- [OstiumPriceUpKeep::performUpKeep() does not correctly handle possible reverts](https://0xsimao.com/findings/ostium-price-perform-correctly-reverts)
- [Trader Can Set Wrong TP and SL](https://0xsimao.com/findings/ostium-trader-set-wrong)
- [Setting maxFundingFeePerBlock to a lower value than abs(lastFundingRate) will brick getPendingAccFundingFees()](https://0xsimao.com/findings/ostium-fee-abs-brick-fees)

## Low severity findings

- [OstiumTradesUpKeep Triggers Automation for Pending Orders Expected to Fail](https://0xsimao.com/findings/ostium-trades-triggers-pending-expected)
- [Limitations for Users Employing Multi-sig or Account Abstraction Wallets in Setting Delegate Address](https://0xsimao.com/findings/ostium-limitations-employing-abstraction-delegate)
- [executeAutomationOpenOrderCallback Executes STOP Orders at a Worse Price](https://0xsimao.com/findings/ostium-callback-executes-worse-price)
- [reqID_pendingAutomationOrder stores the index of the trade, which could point to a different trade since the request was created](https://0xsimao.com/findings/ostium-req-index-point-since)
- [OstiumTradingCallbacks::executeAutomationOpenOrderCallback() opens limit orders at market price instead of the limit price](https://0xsimao.com/findings/ostium-callbacks-callback-opens-price)
- [OstiumPairsStorage::getAllPairsMaxLeverage() reverts if enough pairs are created](https://0xsimao.com/findings/ostium-pairs-storage-reverts-created)
- [OstiumRegistry should disable renouncing ownership if it is never intended](https://0xsimao.com/findings/ostium-registry-renouncing-ownership-intended)
- [OstiumLinkUpKeep:topUp() is missing a length check for registryAddresses](https://0xsimao.com/findings/ostium-link-top-registry-addresses)
- [.values() may revert when calling getWatchList() due to OOG](https://0xsimao.com/findings/ostium-revert-watch-list-oog)
- [In OstiumLinkUpKeep, empty watchlist or keeperIds arguments are not handled](https://0xsimao.com/findings/ostium-empty-watchlist-keeper-handled)
- [Using transfer() instead of call() may revert](https://0xsimao.com/findings/ostium-transfer-instead-call-revert)
- [Missing disableInitializers() call in the constructor](https://0xsimao.com/findings/ostium-disable-initializers-call-constructor)
- [Ownable2Step is recommended over Ownable](https://0xsimao.com/findings/ostium-ownable2-step-recommended-ownable)

## Informational findings

- [Implement Storage Gap in Delegatable Contract](https://0xsimao.com/findings/ostium-implement-storage-gap-delegatable)
- [Unused Trade Size Variable in OstiumTrading::executeAutomationOrder()](https://0xsimao.com/findings/ostium-trade-size-variable-automation)
- [Implement Custom Errors Instead of require Statements](https://0xsimao.com/findings/ostium-implement-custom-require-statements)
- [Inconsistent Precision of Percentage Variables](https://0xsimao.com/findings/ostium-inconsistent-precision-percentage-variables)
- [Unnecessary Typecasting of msg.sender](https://0xsimao.com/findings/ostium-unnecessary-typecasting-msg-sender)
- [Use of Magic Numbers](https://0xsimao.com/findings/ostium-magic-numbers)
- [Unused and Wrong Imports](https://0xsimao.com/findings/ostium-unused-wrong-imports)
- [Redundant Calculations in OstiumTrading::openTrade()](https://0xsimao.com/findings/ostium-redundant-calculations-trading-trade)
- [OstiumTrading::closeTradeMarket() Makes Redundant Call to OstiumTradingStorage::getOpenTradeInfo()](https://0xsimao.com/findings/ostium-close-redundant-storage-info)
- [OstiumLinkUpKeep Config Not Set In initialize()](https://0xsimao.com/findings/ostium-link-keep-config-initialize)
- [OstiumPriceUpKeep Verifies Reports Using Native Token Instead of LINK](https://0xsimao.com/findings/ostium-price-verifies-reports-native)
- [OstiumTrading::updateOpenLimitOrder() Makes Unnecessary Field Updates](https://0xsimao.com/findings/ostium-trading-unnecessary-field-updates)
- [OstiumTrading::canExecute() should also be checked in OstiumTradesUpKeep::checkCallback()](https://0xsimao.com/findings/ostium-trading-checked-trades-callback)
- [Unused UPDATE_SL OstiumPriceUpKeep::OrderType](https://0xsimao.com/findings/ostium-unused-price-keep-type)
- [In OstiumTradesUpKeep, _getLimitOrdersToTrigger() and _getOpenOrdersToTrigger() return tradesToTrigger with 1 extra length](https://0xsimao.com/findings/ostium-trades-orders-extra-length)
- [Wrong decoding of verifierResponse in OstiumPriceUpKeep::performUpkeep()](https://0xsimao.com/findings/ostium-decoding-verifier-response-price)
- [updateGroupCollateral() should revert if the _pairIndex does not exist](https://0xsimao.com/findings/ostium-collateral-revert-index-exist)
- [Error parameters should be more descriptive](https://0xsimao.com/findings/ostium-error-parameters-descriptive)
- [Faulty revert reason decoding in Delegatable](https://0xsimao.com/findings/ostium-faulty-revert-decoding-delegatable)
- [OstiumOpenPnl::average() can be simplified by adding a state variable that tracks the cumulative sum of nextEpochValues](https://0xsimao.com/findings/ostium-average-tracks-sum-epoch)
- [OstiumOpenPnl::nextEpochValuesRequestCount stores the same information as nextEpochValues.length](https://0xsimao.com/findings/ostium-epoch-count-stores-information)
- [Redundant abi.decode() in OstiumTradesUpKeep::checkCallback()](https://0xsimao.com/findings/ostium-redundant-abi-decode-callback)
- [Consider using forceApprove instead of safeApprove in OstiumTraidingCallbacks](https://0xsimao.com/findings/ostium-force-approve-traiding-callbacks)
- [OstiumLinkUpKeep:removeFromWatchList() complexity could be reduced](https://0xsimao.com/findings/ostium-watch-list-complexity-reduced)
- [Typos in OstiumLinkUpKeep](https://0xsimao.com/findings/ostium-typos-ostium-link-keep)

## The report

[Download the report (PDF)](https://0xsimao.com/reports/2024-02-19-ostium.pdf)
