DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: high
Invalid

A User's order will be closed prematurely when ever they set a TP/SL offchain order based of an error in implementation.

Summary

The current implementation of the fill price validation logic for Limit/TP/SL offchain orders is flawed. It does not correctly differentiate between the conditions for limit orders, take profit (TP), and stop-loss (SL) orders. This can lead to premature order closures, causing users to incur losses. This report outlines the issue and provides a recommended fix to ensure proper handling of these orders.

Vulnerability Details

The existing logic checks if the order is a buy or sell order and validates the fill price accordingly. However, it fails to consider the different behaviors for limit orders, TP, and SL for both long and short positions.

**Developer's Comment:**

@audit>> assumption is wrong >>> // if the order increases the trading account's position (buy order), the fill price must be less than or
@audit>> assumption is wrong >>> // equal to the target price, if it decreases the trading account's position (sell order), the fill price
@audit>> assumption is wrong >>> // must be greater than or equal to the target price.
ctx.isFillPriceValid = (ctx.isBuyOrder && ctx.offchainOrder.targetPrice <= ctx.fillPriceX18.intoUint256())
|| (!ctx.isBuyOrder && ctx.offchainOrder.targetPrice >= ctx.fillPriceX18.intoUint256());
// we don't revert here because we want to continue filling other orders.
if (!ctx.isFillPriceValid) {
continue;
}

Not ALL BUY increases positions (LONG/SHORT) - WRONG >> // if the order increases the trading account's position (buy order), the fill price must be less than or equal to the target price. ONLY for now NEW POSITION/INCREASING POSITION note this was not check also

Not ALL SELL reduces positions (LONG/SHORT) - WRONG >> // if it decreases the trading account's position (sell order), the fill price must be greater than or equal to the target price. ONLY for now NEW POSITION/INCREASING POSITION note this was not check also

**correct Assumptions:**

1. A buy order for a long position increases the long position.

2. A buy order for a short position decreases the short position.

3. A sell order for a short position increases the short position.

4. A sell order for a long position decreases the long position.

Hence the wrong assumptionby the protocol that all buys increases the position is wrong creates a flaw price validation

@audit >>> This raises alot of issue>>>> ctx.isFillPriceValid = (ctx.isBuyOrder && ctx.offchainOrder.targetPrice <= ctx.fillPriceX18.intoUint256())\
|| (!ctx.isBuyOrder && ctx.offchainOrder.targetPrice >= ctx.fillPriceX18.intoUint256());
// we don't revert here because we want to continue filling other orders.
if (!ctx.isFillPriceValid) {
continue;
}

Flawed Implementation Example:

Example.

For a long position,

USER A sets

  1. TP at 80,000

  2. SL at 61,000

  3. after buying BTC at 67,000.

  4. If the current price is 66,800,

  5. the existing logic may prematurely execute the sell order

  6. because the target price (80,000)-TP

  7. is greater than the fill price (66,800),

  8. even though the market has not reached the TP.

BASED on assumption 4 above.

. A sell order for a long position decreases the long position.

USER A wants to sell his BTC at 80,000. present price 66,800

|| (!ctx.isBuyOrder && ctx.offchainOrder.targetPrice >= ctx.fillPriceX18.intoUint256());

will be executed successfully at 66800 eventhough we are far away from 80,000.

conditon 1.

  1. sell order check

  2. targetPrice >= ctx.fillPrice (80,000 > = 66,800)

This flaw allow this to pass and execute. This exists both ways. Kindly review other assumption flaws also.

Impact

Incorrect validation of TP/SL orders can lead to premature closures of user positions, causing unexpected losses and undermining user trust in the trading platform.

Note:

when closing a Position

  1. LONG

    1. TP - Sell at higher Price

    2. SL - Sell at Lower Price

  2. SHORT

    I. TP - Buyback at lower Price

    II. SL - Buyback at higher Price

Tools Used

- Solidity code analysis

- Logical verification

Recommendations

The fix for this is not straight forward and i would admonish the sponsor to also look in ward for a better fix. but i was able to come up with one i believe is ok also. kindly review the fix below

To correctly handle TP/SL orders and prevent premature closures, the validation logic should differentiate between limit orders, TP, and SL orders. The proposed solution involves updating the data structure and implementing a conditional validation check based on the order type.

**Proposed Data Structure:**

  1. 1 update offchain struct

struct Data {
uint128 tradingAccountId;
uint128 marketId;
int128 sizeDelta;
uint128 targetprice;
+ bool isLimitOrder;
+ bool isTP;
bool shouldIncreaseNonce;
uint120 nonce;
bytes32 salt;
uint8 v;
bytes32 r;
bytes32 s;
}
  1. 2 Fix Price Validation Logic in offchainorder settlement

**Proposed Solution:**

-- ctx.isFillPriceValid = (ctx.isBuyOrder && ctx.offchainOrder.targetPrice <= ctx.fillPriceX18.intoUint256())
-- || (!ctx.isBuyOrder && ctx.offchainOrder.targetPrice >= ctx.fillPriceX18.intoUint256());
++ bool isIncreasing = Position.isIncreasing(params.tradingAccountId, params.marketId, params.sizeDelta)
++ && ctx.isMarketWithActivePosition;
++ if (ctx.offchainOrder.isLimitOrder) {
++ ctx.isFillPriceValid = (isIncreasing && ctx.isBuyOrder && ctx.offchainOrder.targetPrice <= ctx.fillPriceX18.intoUint256())
++ || (!isIncreasing && !ctx.isBuyOrder && ctx.offchainOrder.targetPrice >= ctx.fillPriceX18.intoUint256());
++ } else {
++ if (ctx.offchainOrder.isTP) {
++ ctx.isFillPriceValid = (!isIncreasing && ctx.isBuyOrder && ctx.offchainOrder.targetPrice >=ctx.fillPriceX18.intoUint256())
++ || (!isIncreasing && !ctx.isBuyOrder && ctx.offchainOrder.targetPrice <= ctx.fillPriceX18.intoUint256());
++ } else {
++ ctx.isFillPriceValid = (!isIncreasing && !ctx.isTP && ctx.isBuyOrder && ctx.offchainOrder.targetPrice <= ctx.fillPriceX18.intoUint256())
++ || (!isIncreasing && !ctx.isTP && !ctx.isBuyOrder && ctx.offchainOrder.targetPrice >= ctx.fillPriceX18.intoUint256());
++ }
++ }
Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!