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

Reducing trade size causes trader position to move to loss

Summary

When a trader reduces their trade size, the unrealizedPnlUsdX18 moves to a loss position even when the market price has not changed.

Vulnerability Details

When a trader reduces the size of their open position, the protocol incorrectly starts with a loss even when the market price has not changed. This problem stems from the fact that the trader position is closed and a new position is created.

POC

function testReducePositionSizeLeadToInitialLoss() public {
changePrank({ msgSender: users.sasuke.account });
// $10,000
uint256 amount = 10000 * 10 ** 6;
deal({ token: address(usdc), to: users.sasuke.account, give: amount });
uint128 tradingAccountId = createAccountAndDeposit(amount, address(usdc));
// With this size, the trade is on 10x leverage
int128 size = 1 * 10 ** 18;
// Create a LONG market order on BTC-USD with size 1
perpsEngine.createMarketOrder(
OrderBranch.CreateMarketOrderParams({
tradingAccountId: tradingAccountId,
marketId : BTC_USD_MARKET_ID,
sizeDelta: size
})
);
// Pick up market order by marketorderkeepers
changePrank({ msgSender: marketOrderKeepers[1] });
bytes memory firstMockSignedReport =
getMockedSignedReport(0x00037da06d56d083fe599397a4769a042d63aa73dc4ef57709d31e9971a5b439, MOCK_BTC_USD_PRICE);
perpsEngine.fillMarketOrder(tradingAccountId, BTC_USD_MARKET_ID, firstMockSignedReport);
changePrank({ msgSender: users.sasuke.account });
// Get current trading account position
Position.State memory position = perpsEngine.getPositionState(tradingAccountId,
BTC_USD_MARKET_ID,
MOCK_BTC_USD_PRICE
);
//Entry Price
UD60x18 initialEntryPrice = position.entryPriceX18;
//Entry Position Size
SD59x18 positionSize = position.sizeX18;
// Reduce Trade Size
size = 0.5 * 10 ** 18;
perpsEngine.createMarketOrder(
OrderBranch.CreateMarketOrderParams({
tradingAccountId: tradingAccountId,
marketId: BTC_USD_MARKET_ID,
sizeDelta: -size
})
);
changePrank({ msgSender: marketOrderKeepers[1] });
bytes memory firstMockSignedReport2 =
getMockedSignedReport(0x00037da06d56d083fe599397a4769a042d63aa73dc4ef57709d31e9971a5b439, MOCK_BTC_USD_PRICE);
perpsEngine.fillMarketOrder(tradingAccountId, BTC_USD_MARKET_ID, firstMockSignedReport2);
changePrank({ msgSender: users.sasuke.account });
Position.State memory position3 = perpsEngine.getPositionState(
tradingAccountId,
BTC_USD_MARKET_ID,
MOCK_BTC_USD_PRICE
);
//Updated Position Size
SD59x18 updatedPositionSize1 = position3.sizeX18;
// unrealizedPnlUsdX18
SD59x18 unrealizedPnlUsdX181 = position3.unrealizedPnlUsdX18;
// asserts that the unrealizedPnlUsdX18 after reducing trade is less than zero
assert(unrealizedPnlUsdX181.intoInt256() < 0);
}

Impact

  1. Reduction in trade starts with a loss

Tools Used

Manual Review

Recommendations

Update the calculation logic to ensure that when trader reduces their trader, the position does not start with a loss.

Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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

Give us feedback!