Part 2

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

Critical Underflow inside of `Vault::_updateCreditDelegations`

Summary

The credit delegation system fails catastrophically when a market's new credit delegation value is less than its previous value. This occurs because Vault::_updateCreditDelegations incorrectly handles decreases in credit delegation using unsigned arithmetic (UD60x18), causing underflows that break core protocol operations like deposits and redemptions.

Vulnerability Details

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/35deb3e92b2a32cd304bf61d27e6071ef36e446d/src/market-making/leaves/Vault.sol#L601C1-L605C70

The _updateCreditDelegations function calculates credit delegation changes using unsigned arithmetic:

UD60x18 creditDeltaUsdX18 = newCreditDelegationUsdX18.sub(previousCreditDelegationUsdX18);

However, Market.sol has two distinct functions for handling credit updates:

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/35deb3e92b2a32cd304bf61d27e6071ef36e446d/src/market-making/leaves/Market.sol#L492C1-L495C6

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/35deb3e92b2a32cd304bf61d27e6071ef36e446d/src/market-making/leaves/Market.sol#L471C1-L473C6

// For positive deltas
function updateTotalDelegatedCredit(Data storage self, UD60x18 creditDeltaUsdX18)
// For negative deltas
function updateTotalDelegatedCredit(Data storage self, SD59x18 creditDeltaUsdX18)

Here's an example to showcase the issue without checking if negative delta and always using UD60x18:

newCreditDelegationUsdX18 = 316800000
previousCreditDelegationUsdX18 = 396000000
// Attempts: 316800000 - 396000000 = UNDERFLOW!

Impact

This breaks critical functions that depend on recalculateVaultsCreditCapacity, including:

  • Deposits failing

  • Redemptions failing

  • Credit capacity updates

  • Market debt calculations

Tools Used

Foundry

Recommendations

Inside of Vault::_updateCreditDelegations create an If statement logic to handle if it's going to be a negative delta to handle proper assignment of SD59x18 or UD60x18 and call the right Market::updateTotalDelegatedCredit this way.

function _updateCreditDelegations(
...
)
private
returns (...)
{
.... start of function
+ bool isNegativeDelta = newCreditDelegationUsdX18.lt(previousCreditDelegationUsdX18);
+ Market.Data storage market = Market.load(connectedMarketId);
+ if (isNegativeDelta) {
+ SD59x18 creditDeltaUsdX18 = newCreditDelegationUsdX18.intoSD59x18().sub(previousCreditDelegationUsdX18.intoSD59x18());
+ market.updateTotalDelegatedCredit(creditDeltaUsdX18);
+ } else {
+ UD60x18 creditDeltaUsdX18 = newCreditDelegationUsdX18.sub(previousCreditDelegationUsdX18);
+ market.updateTotalDelegatedCredit(creditDeltaUsdX18);
}
- 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);
... rest of function
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Vault::_updateCreditDelegations uses unsigned UD60x18 for credit delegation delta calculation which will underflow on any decrease in credit delegation amount

Support

FAQs

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