Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

Type Constraint in Position Size Calculations Causes Unnecessary Trade Reverts

Summary

An implementation issue has been identified where large but valid trades could be unnecessarily rejected due to an overly restrictive type constraint in the position size calculations.

ctx.isNotionalValueIncreasing =
Position.isNotionalValueIncreasing(tradingAccountId, marketId, sizeDeltaX18.intoInt256().toInt128());

The function performs a conversion from SD59x18 to int128 through an intermediate int256. Under Solidity 0.8.25, which includes built-in overflow checks, this creates a hard limitation where trades will revert if the position size doesn't fit within int128 bounds (approximately ±1.7e38).

// Position.sol
function isNotionalValueIncreasing(
uint128 tradingAccountId,
uint128 marketId,
int128 sizeDelta // Unnecessarily restrictive type
)
internal
view
returns (bool)
{
Data storage self = load(tradingAccountId, marketId);
SD59x18 sizeX18 = sd59x18(self.size);
SD59x18 sizeDeltaX18 = sd59x18(sizeDelta);
...
}

Impact

The int128 constraint creates a functional limitation rather than a security risk. Large but otherwise valid trades will revert due to the type conversion, not because they exceed any intended business limits. The reversion occurs at the type conversion level rather than at a meaningful business logic check. This creates an artificial ceiling on position sizes that isn't driven by market requirements.

Proof of Concept

Given a large but valid trade size:

SD59x18 largePosition = // Value larger than int128 max but valid for the market
// The following will revert due to overflow check, even if the position size
// is within market limits
Position.isNotionalValueIncreasing(accountId, marketId, largePosition.intoInt256().toInt128());

Proposed Solution

Modify the Position library to accept SD59x18 directly:

function isNotionalValueIncreasing(
uint128 tradingAccountId,
uint128 marketId,
SD59x18 sizeDeltaX18
)
internal
view
returns (bool)
{
Data storage self = load(tradingAccountId, marketId);
SD59x18 sizeX18 = sd59x18(self.size);
return sizeX18.isZero() || (sizeDeltaX18.add(sizeX18).abs() > sizeX18.abs());
}

This allows position sizes to be limited by actual business rules rather than type constraints:

ctx.isNotionalValueIncreasing = Position.isNotionalValueIncreasing(tradingAccountId, marketId, sizeDeltaX18);
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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