Part 2

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

Market's totalDelegatedCreditUsd may be outdated when credit is added by registered engine

Summary

Market's totalDelegatedCreditUsd may be outdated when credit is added by registered engine.

Vulnerability Details

When a registered engine calls depositCreditForMarket() to add credit in form of a registered collateral type, if the collateral is USDC, settleCreditDeposit() is called to update the amount of usdc available to be distributed to vaults delegating credit to this market.

CreditDelegationBranch::depositCreditForMarket():

if (collateralAddr == usdc) {
market.settleCreditDeposit(address(0), amountX18);
} else {

In settleCreditDeposit(), market's totalDelegatedCreditUsd is used to calculate the usdc that has been accumulated per usd of credit delegated to the market.

Market::settleCreditDeposit():

// calculate the usdc that has been accumulated per usd of credit delegated to the market
UD60x18 addedUsdcPerCreditShareX18 = netUsdcReceivedX18.div(ud60x18(self.totalDelegatedCreditUsd));

Market's totalDelegatedCreditUsd is calculated based on the vault's total credit capacity and is updated in recalculateVaultsCreditCapacity().

Vault::_updateCreditDelegations():

// stores the vault's total credit capacity to be returned
@> vaultCreditCapacityUsdX18 = getTotalCreditCapacityUsd(self);
// if the vault's credit capacity went to zero or below, we set its credit delegation to that market
// to zero
UD60x18 newCreditDelegationUsdX18 = vaultCreditCapacityUsdX18.gt(SD59x18_ZERO)
@> ? vaultCreditCapacityUsdX18.intoUD60x18().mul(creditDelegationShareX18)
: UD60x18_ZERO;
// calculate the delta applied to the market's total delegated credit
@> UD60x18 creditDeltaUsdX18 = newCreditDelegationUsdX18.sub(previousCreditDelegationUsdX18);
// loads the market's storage pointer and update total delegated credit
Market.Data storage market = Market.load(connectedMarketId);
@> market.updateTotalDelegatedCredit(creditDeltaUsdX18);

The problem is that recalculateVaultsCreditCapacity() is not called when the registered engine adds credit in depositCreditForMarket(), it is possible that the market's totalDelegatedCreditUsd is outdated by then, due to the vault's total credit capacity which can also be outdated:

  1. depositCreditForMarket() is called by other engine before, market's usdcCreditPerVaultShare changed;

  2. Vault's total credit capacity depends on market's usdcCreditPerVaultShare change, but becaues recalculateVaultsCreditCapacity() is not called, the credit capacity is not up to date, therefore it is outdated.

Impact

Outdated market's totalDelegatedCreditUsd is used when registered engines add credit, results in the credit deposit is not properly settled.

Tools Used

Manual Review

Recommendations

Call recalculateVaultsCreditCapacity() when the registered engine adds credit.

if (collateralAddr == usdc) {
+ Market.Data storage market = Market.loadLive(marketId);
+ uint256[] memory connectedVaults = market.getConnectedVaultsIds();
+ Vault.recalculateVaultsCreditCapacity(connectedVaults);
market.settleCreditDeposit(address(0), amountX18);
} else {
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.