DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Incorrect Handling of Negative PnL Leading to Net Value Underflow

Summary

The net value calculation in VaultReader.getPositionInfo() function does not properly handle negative PnL, leading to an underflow that falsely inflates a position’s net value.

Vulnerability Details

In VaultReader.getPositionInfo, net value is computed by adding collateral and claimable amounts and then adjusting for the position’s PnL.

/**
* @notice Retrieves the position information for a given key and market prices.
* @param key The key representing the position.
* @param prices The current market prices for the relevant tokens.
* @return PositionData The data structure containing detailed information about the position.
*/
function getPositionInfo(
bytes32 key,
MarketPrices memory prices
) external view returns (PositionData memory) {
uint256 sizeInTokens = getPositionSizeInUsd(key);
if (sizeInTokens == 0) {
return PositionData({
sizeInUsd: 0,
sizeInTokens: 0,
collateralAmount: 0,
netValue: 0,
pnl: 0,
isLong: true
});
}
PositionInfo memory positionInfo = gmxReader.getPositionInfo(
address(dataStore),
referralStorage,
key,
prices,
uint256(0),
address(0),
true
);
uint256 netValue =
positionInfo.position.numbers.collateralAmount * prices.shortTokenPrice.min +
positionInfo.fees.funding.claimableLongTokenAmount * prices.longTokenPrice.min +
positionInfo.fees.funding.claimableShortTokenAmount * prices.shortTokenPrice.min -
positionInfo.fees.borrowing.borrowingFeeUsd -
positionInfo.fees.funding.fundingFeeAmount * prices.shortTokenPrice.min -
positionInfo.fees.positionFeeAmount * prices.shortTokenPrice.min;
if (positionInfo.basePnlUsd >= 0) {
netValue = netValue + uint256(positionInfo.basePnlUsd);
} else {
netValue = netValue - uint256(-positionInfo.basePnlUsd);
}
return PositionData({
sizeInUsd: positionInfo.position.numbers.sizeInUsd,
sizeInTokens: positionInfo.position.numbers.sizeInTokens,
collateralAmount: positionInfo.position.numbers.collateralAmount,
netValue: netValue,
pnl: positionInfo.basePnlUsd,
isLong: positionInfo.position.flags.isLong
});
}

The code differentiates between positive and negative PnL as follows:

if (positionInfo.basePnlUsd >= 0) {
netValue = netValue + uint256(positionInfo.basePnlUsd);
} else {
netValue = netValue - uint256(-positionInfo.basePnlUsd);
}

If the absolute value of the negative PnL exceeds the combined collateral and fee amounts, subtracting it in unsigned arithmetic will underflow, wrapping around to an extremely high value. This miscalculation misrepresents the true economic state of the position and violates the invariant that a depositor’s share value should not be reduced by others’ actions.

Affected Lines of Code:-

netValue = netValue - uint256(-positionInfo.basePnlUsd);
  • A trader opens a position with collateral worth $100.

  • Due to adverse price movements, the position incurs a negative PnL of $150.

  • When getPositionInfo() is called, the net value is calculated as: netValue = 100 - 150
    Because the subtraction is performed using unsigned arithmetic, the result underflows and wraps around to a very large number (e.g., near 2^256-50).

Therefore, net value underflow, creating an artificial profits, enabling users to drain the vault.

Impact

Underflow in net value calculations can lead to users being able to withdraw more than their fair share even when they are in a loss state. This directly jeopardizes the fund’s accounting, leading to systematic financial losses for the protocol and its depositors.

Tools Used

Manual Review

Recommendations

Change the net value computation to use a signed integer throughout, then ensure the final value is nonnegative before converting to uint256. Also, introduce explicit checks that prevent subtraction from causing an underflow, reverting the transaction if a negative net value is detected.

int256 netValueSigned = ...;
require(netValueSigned >= 0, "Negative net value");
netValue = uint256(netValueSigned);
Updates

Lead Judging Commences

n0kto Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

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