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

getAccountLeverage() may revert due to underflow error

Summary

getAccountLeverage() may revert due to underflow error.

Vulnerability Details

getAccountLeverage() returns the current leverage of a given account id, based on its cross margin collateral and open positions.

This function first gets the trading account's marginBalanceUsdX18, data type SD59x18:

SD59x18 marginBalanceUsdX18 = tradingAccount.getMarginBalanceUsd(tradingAccount.getAccountUnrealizedPnlUsd());

Then iterates the trading account's active markets to get totalPositionsNotionalValue, data type is UD60x18:

function getAccountLeverage(uint128 tradingAccountId) external view returns (UD60x18) {
UD60x18 totalPositionsNotionalValue;
...
for (uint256 i; i < tradingAccount.activeMarketsIds.length(); i++) {
...
totalPositionsNotionalValue = totalPositionsNotionalValue.add(positionNotionalValueX18);
}
}

At last, totalPositionsNotionalValue is converted to type SD59x18, and is divided by marginBalanceUsdX18, the result value is converted to UD60x18 and returned:

return totalPositionsNotionalValue.intoSD59x18().div(marginBalanceUsdX18).intoUD60x18();

The problem is that marginBalanceUsdX18 can be negative if the trading account is in a loss (negative unPnl), so the result value is negative, the transaction will be reverted when it tries to convert the negative SD59x18 value to UD60x18.

Please run the PoC in fillMarketOrder.t.sol to verify:

function testAudit() public {
address alice = makeAddr("Alice");
deal(address(usdc), alice, 2000e6);
vm.startPrank(alice);
usdc.approve(address(perpsEngine), 2000e6);
uint128 tradingAccountId = createAccountAndDeposit(2000e6, address(usdc));
perpsEngine.createMarketOrder(
OrderBranch.CreateMarketOrderParams({
tradingAccountId: tradingAccountId,
marketId: BTC_USD_MARKET_ID,
sizeDelta: 1e18
})
);
vm.stopPrank();
vm.startPrank(marketOrderKeepers[BTC_USD_MARKET_ID]);
bytes memory mockSignedReport = getMockedSignedReport(BTC_USD_STREAM_ID, MOCK_BTC_USD_PRICE);
perpsEngine.fillMarketOrder(tradingAccountId, BTC_USD_MARKET_ID, mockSignedReport);
vm.stopPrank();
// price drops and the account is in a loss
updateMockPriceFeed(BTC_USD_MARKET_ID, MOCK_BTC_USD_PRICE * 9 / 10);
(
SD59x18 marginBalanceUsdX18,
,
,
) = perpsEngine.getAccountMarginBreakdown(tradingAccountId);
// marginBalanceUsdX18 is less than 0
assertTrue(marginBalanceUsdX18.unwrap() < 0);
// PRBMath_SD59x18_IntoUD60x18_Underflow
vm.expectRevert();
UD60x18 leverage = perpsEngine.getAccountLeverage(tradingAccountId);
}

Impact

getAccountLeverage() reverts due to underflow error.

Tools Used

Manual Review

Recommendations

Return ud60x18(type(uint256).max) if marginBalanceUsdX18 is a negative value.

function getAccountLeverage(uint128 tradingAccountId) external view returns (UD60x18) {
TradingAccount.Data storage tradingAccount = TradingAccount.loadExisting(tradingAccountId);
SD59x18 marginBalanceUsdX18 = tradingAccount.getMarginBalanceUsd(tradingAccount.getAccountUnrealizedPnlUsd());
UD60x18 totalPositionsNotionalValue;
if (marginBalanceUsdX18.isZero()) return marginBalanceUsdX18.intoUD60x18();
+ if (marginBalanceUsdX18.lt(SD59x18_ZERO)) return ud60x18(type(uint256).max);
...
}
Updates

Lead Judging Commences

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

Appeal created

h2134 Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
h2134 Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge 12 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.