Part 2

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

`getRealizedDebtUsd()` Can Return Stale or Incorrect Debt Values Due to Cache Invalidation Issue

Bug Description:

The getRealizedDebtUsd() function is designed to compute the realized debt of a market by adding the latest credit deposits value to the net USD token issuance. To optimize gas costs, it uses a cached credit deposit value (creditDepositsValueCacheUsd), which is only updated when the cache is deemed outdated based on lastCreditDepositsValueRehydration:

if (block.timestamp <= self.lastCreditDepositsValueRehydration) {
creditDepositsValueUsdX18 = ud60x18(self.creditDepositsValueCacheUsd);
} else {
creditDepositsValueUsdX18 = getCreditDepositsValueUsd(self);
}

However, this cache invalidation logic is flawed because:

  1. It only checks block.timestamp, not actual changes in credit deposits. If deposits or withdrawals occur within the same block, the function may return outdated values, leading to incorrect debt calculations.

  2. getCreditDepositsValueUsd() iterates over all credit deposits, but the function is only called if block.timestamp is greater than lastCreditDepositsValueRehydration, meaning it does not trigger when credit deposits change within the same block.

  3. If the protocol allows flash loans or instant credit deposits and withdrawals, the cached value may not represent the true deposits, leading to incorrect realized debt calculations, potentially allowing vaults to manipulate their debt exposure.

Impact:

The market can underestimate or overestimate realized debt due to stale credit deposit values, leading to incorrect debt distributions to vaults. This could allow malicious actors to deposit and withdraw credit within the same block, manipulating their debt obligations and gaming the system.

Mitigation:

Instead of relying solely on block.timestamp, invalidate the cache whenever a credit deposit is modified by updating lastCreditDepositsValueRehydration inside deposit and withdrawal functions:

function depositCredit(Data storage self, address asset, UD60x18 amountX18) internal {
AssetToAmountMap.update(self.creditDeposits, asset, amountX18, true);
self.lastCreditDepositsValueRehydration = block.timestamp.toUint128(); // Ensure cache is invalidated on update
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
6 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.