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

Position can increase when market is disabled or settlement disabled

Summary

Users should not be able to increase their position when the market or settlement is disabled. In such situations, users should only be able to decrease their positions. For example, if a user has a position size of 500, the new value of the position should be between 0 and 499. However, due to missing checks, it is currently possible to change the position to -1000, which breaks the protocol's invariants.

Vulnerability Details

The current implementation lacks necessary checks, allowing users to increase their positions from long to short and vice versa even when the market or settlement is disabled. This can lead to violations of the protocol's intended constraints.

Below is a test that demonstrates how the position can be increased from long to short, despite the market or settlement being disabled.

function test_WhenThePositionIsBeingIncreased()
external
givenTheAccountIdExists
givenTheSenderIsAuthorized
whenTheSizeDeltaIsNotZero
givenThePerpMarketIsDisabled
{
// give naruto some tokens
uint256 marginValueUsdc = 100_000e6;
int128 userPositionSizeDelta = 10e18;
deal({ token: address(usdc), to: users.naruto.account, give: marginValueUsdc });
// naruto creates a trading account and deposits their tokens as collateral
changePrank({ msgSender: users.naruto.account });
uint128 tradingAccountId = createAccountAndDeposit(marginValueUsdc, address(usdc));
// naruto opens first position in BTC market
openManualPosition(
BTC_USD_MARKET_ID, BTC_USD_STREAM_ID, MOCK_BTC_USD_PRICE, tradingAccountId, userPositionSizeDelta
);
// protocol operator disables settlement for the BTC market
changePrank({ msgSender: users.owner.account });
SettlementConfiguration.DataStreamsStrategy memory marketOrderConfigurationData = SettlementConfiguration
.DataStreamsStrategy({ chainlinkVerifier: IVerifierProxy(mockChainlinkVerifier), streamId: BTC_USD_STREAM_ID });
SettlementConfiguration.Data memory marketOrderConfiguration = SettlementConfiguration.Data({
strategy: SettlementConfiguration.Strategy.DATA_STREAMS_DEFAULT,
isEnabled: false,
fee: DEFAULT_SETTLEMENT_FEE,
keeper: marketOrderKeepers[BTC_USD_MARKET_ID],
data: abi.encode(marketOrderConfigurationData)
});
perpsEngine.updateSettlementConfiguration(
BTC_USD_MARKET_ID, SettlementConfiguration.MARKET_ORDER_CONFIGURATION_ID, marketOrderConfiguration
);
// naruto attempts to close their position
changePrank({ msgSender: users.naruto.account });
// naruto closes their opened leverage BTC position but increasing their position
// in the beggging is 10e18, after this operation will be -20e18
openManualPosition(
BTC_USD_MARKET_ID, BTC_USD_STREAM_ID, MOCK_BTC_USD_PRICE, tradingAccountId, -(userPositionSizeDelta * 3)
);

Impact

Users can bypass restrictions by increasing their positions from long to short or vice versa when the market or settlement is disabled. This breaks the protocol's invariants and could lead to financial instability and unexpected behavior in the trading system.

Tools Used

Manual review

Recommendations

Possible solution:

function isIncreasing(
uint128 tradingAccountId,
uint128 marketId,
int128 sizeDelta
)
internal
view
returns (bool)
{
Data storage self = Position.load(tradingAccountId, marketId);
// If position is being opened (size is 0) or if position is being increased (sizeDelta direction is same as
// position size)
// For a positive position, sizeDelta should be positive to increase, and for a negative position, sizeDelta
// should be negative to increase.
// This also implicitly covers the case where if it's a new position (size is 0), it's considered an increase.
- return self.size == 0 || (self.size > 0 && sizeDelta > 0) || (self.size < 0 && sizeDelta < 0);
+ return self.size == 0 || (self.size > 0 && sizeDelta > 0) || (self.size < 0 && sizeDelta < 0) || (self.size < 0 && self.size.add(sizeDelta) > 0 && self.size.add(sizeDelta).abs().gt(self.size.abs())) || (self.size > 0 && self.size.add(sizeDelta) < 0 && self.size.add(sizeDelta).abs().gt(self.size.abs()) ) ;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Disable market limitation can be bypassed

Support

FAQs

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