Part 2

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

Decimal precision for getting realized debt is inflated

Summary

Getting the price of getRealizedDebtUSD allows market to update the debts for markets. The accounting is incorrect due to decimal scaling.

Vulnerability Details

Within leaves/Market.sol. We know that ud60x18 types are all scaled into 1e18. The creditDepositsValueUsdX18 is calculated using the returned adjusted price of 1e18 multiplied by the value which is also in 1e18. The implementation did not divide creditDepositsValueUsdX18 by 1e18, resulting in inflated prices.getRealizedDebtUsdis called, the realized debt is wrongly scaled, resulting in incorrect PnL.

/// @notice Returns the market's net realized debt in USD.
/// @param self The market storage pointer.
/// @return realizedDebtUsdX18 The market's net realized debt in USD as SD59x18.
function getRealizedDebtUsd(Data storage self) internal view returns (SD59x18 realizedDebtUsdX18) {
// prepare the credit deposits usd value variable;
UD60x18 creditDepositsValueUsdX18;
// if the credit deposits usd value cache is up to date, return the stored value
if (block.timestamp <= self.lastCreditDepositsValueRehydration) {
creditDepositsValueUsdX18 = ud60x18(self.creditDepositsValueCacheUsd);
} else {
// otherwise, we'll need to loop over credit deposits to calculate it
creditDepositsValueUsdX18 = getCreditDepositsValueUsd(self);
}
// finally after determining the market's latest credit deposits usd value, sum it with the stored net usd
// token issuance to return the net realized debt usd value
realizedDebtUsdX18 = creditDepositsValueUsdX18.intoSD59x18().add(sd59x18(self.netUsdTokenIssuance));
}
function getCreditDepositsValueUsd(Data storage self) internal view returns (UD60x18 creditDepositsValueUsdX18) {
// load the map of credit deposits and cache length
EnumerableMap.AddressToUintMap storage creditDeposits = self.creditDeposits;
uint256 creditDepositsLength = creditDeposits.length();
for (uint256 i; i < creditDepositsLength; i++) {
// load each credit deposit data & associated collateral
(address asset, uint256 value) = creditDeposits.at(i); //@audit is this in 1e18
Collateral.Data storage collateral = Collateral.load(asset);
// update the total credit deposits value
//@audit
creditDepositsValueUsdX18 =
creditDepositsValueUsdX18.add((collateral.getAdjustedPrice().mul(ud60x18(value))));
}
}

Which inturns affects the debt distribution within Vault.sol

function _recalculateConnectedMarketsState(
Data storage self,
uint128[] memory connectedMarketsIdsCache,
bool shouldRehydrateCache
)
-- SNIP --
if (!ctx.marketUnrealizedDebtUsdX18.isZero() || !ctx.marketRealizedDebtUsdX18.isZero()) {
// distribute the market's debt to its connected vaults
market.distributeDebtToVaults(ctx.marketUnrealizedDebtUsdX18, ctx.marketRealizedDebtUsdX18);
}
-- SNIP --

Impact

Decimal precision is wrongly scaled, resulting in inflated price.

Tools Used

Recommendations

DIvide creditDepositsValueUsdX18 by 1e18.

Updates

Lead Judging Commences

inallhonesty Lead Judge
7 months ago
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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