Part 2

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

The `creditDepositsValueCacheUsd` of the market.sol's struct is never been written to leading to higher gas costs and inefficiency in protocol

Summary

The creditDepositsValueCacheUsd param in the market's Data struct is the last USD value that is used to account for all of the credit deposited to the market by its engine and it is never updated that could lead to unnecassary inefficiencies and waste of gas.

Vulnerability Details

The creditDepositsValueCacheUsd param in the market's Data struct is the last USD value that is used to account for all of the credit deposited to the market by its engine. The withdrawUsdTokenFromMarket function in the CreditDelegationBranch.sol is called for minting usd tokens and updating the market's debt state. it uses the marketTotalDebtUsdX18 and delegatedCreditUsdX18 to calculate for the market's creditCapacityUsdX18. Now to get the market's total debt the getTotalDebt function is called in the market.sol and that takes the market's unrealized debt and the realized debt, for getting the realized debt the getRealizedDebtUsd function is called. Now this function here is for returning the realized debt of the market by using the creditDeposits of the market. Now firstly it checks that if the lastCreditDepositsValueRehydration >= block.timestamp and if it is it will simply return the creditDepositValueUsd as self.creditDepositsValueCacheUsd.

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

And these creditDepositsValueCacheUsd and lastCreditDepositsValueRehydration are updated via this internal function:

function rehydrateCreditDepositsValueCache(Data storage self)
internal
returns (UD60x18 creditDepositsValueUsdX18)
{
creditDepositsValueUsdX18 = getCreditDepositsValueUsd(self);
self.creditDepositsValueCacheUsd = creditDepositsValueUsdX18.intoUint128();
self.lastCreditDepositsValueRehydration = block.timestamp.toUint128();
}

Now the issue is that those params are never updated when the credit is deposited in the market and the above function is never being called and because of this getRealizedDebtUsd will always skip to the else block

else {
// otherwise, we'll need to loop over credit deposits to calculate it
creditDepositsValueUsdX18 = getCreditDepositsValueUsd(self);
}

which just iterates through all the credit deposits map. This can be really expensive for the the caller if the creditDeposits map contains a lot of entries and to iterate through all of them can lead to a very large amount of gas being spent for no reason as compared to if the cache was being updated and simply returning the creditDepositsValueCacheUsd as the creditDepositValueUsd

Impact

Due to lack of state updates in the market's struct and some functions like rehydrateCreditDepositsValueCache being implemented but never getting used or called and some functions will consume a lot more gas than intended because of the iterations in the creditDeposit enumerable map which will result in inefficiencies and overspending of gas for the protocol

Tools Used

Manual Review

Recommendation

Update the creditDepositsValueCacheUsd state in the market.depositCredit everytime the depositCreditForMarket function is called to store the lastDepositedCredit cache avoid going through the iterations whenever possible.

Updates

Lead Judging Commences

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

Appeal created

hard1k Submitter
4 months ago
inallhonesty Lead Judge
4 months ago
hard1k Submitter
4 months ago
inallhonesty Lead Judge 4 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.