Part 2

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

Distribution of debt to vault can lead to zero division

Summary

Distributes the market's unrealized and realized debt to the connected vaults is implementation of Zaros. The debt distribution to vaults calculation is incorrect.

Vulnerability Details

Debt is the amount taken from the liquidity pool used by traders for leverage.

Simple Elaboration of what debt is:

Consider a market maker who deposits $10,000 worth of Ethereum into a liquidity pool:

  • Liquidity Provision: The market maker receives LP tokens representing their share of the pool.

  • Trader Borrowing: A trader uses $2,000 of their own funds as collateral and borrows $8,000 from the liquidity pool to open a leveraged position worth $10,000.

  • Debt for Trader: The trader's debt is $8,000, which they must repay regardless of the outcome of their trade.

  • Market Maker's Exposure: If the trader's position incurs losses, the market maker's liquidity may be impacted, and they must be prepared for potential losses due to the volatility of the assets in the pool.

The problem with the calculation is looking at unrealized PNL e.g is -10e18 divided by the 100e18 of credit delegated. It will lead to truncated value of 0, which will affect unrealizedDebtUsdPerVaultShare.

/// @notice Distributes the market's unrealized and realized debt to the connected vaults.
/// @dev `Market::getVaultAccumulatedValues` must be called after this function to return the vault's latest
/// rewards, debt and
/// credit values, so vaults can update their accounting state variables accordingly.
/// @param self The market storage pointer.
/// @param newUnrealizedDebtUsdX18 The latest unrealized debt in USD.
/// @param newRealizedDebtUsdX18 The latest realized debt in USD.
function distributeDebtToVaults(
Data storage self,
SD59x18 newUnrealizedDebtUsdX18,
SD59x18 newRealizedDebtUsdX18
)
internal
{
// cache the total vault's shares as SD59x18
// @audit is assets not shares
SD59x18 totalVaultSharesX18 = ud60x18(self.totalDelegatedCreditUsd).intoSD59x18();
// if there is zero delegated credit and we're trying to distribute debt to vaults, we should revert and the
// market is considered to be in a panic state
if (totalVaultSharesX18.isZero()) {
revert Errors.NoDelegatedCredit(self.id); // here
}
// update storage values
//@audit is using assets
self.realizedDebtUsdPerVaultShare = newRealizedDebtUsdX18.div(totalVaultSharesX18).intoInt256().toInt128();
self.unrealizedDebtUsdPerVaultShare = newUnrealizedDebtUsdX18.div(totalVaultSharesX18).intoInt256().toInt128();
}

Impact

This affects the credit delegation as we can see below, incorrectly calculating the correct conversion rate between assets and shares.

// update the last distributed debt, credit and reward values to the vault's credit delegation to the
// given market id, in order to keep next calculations consistent
creditDelegation.updateVaultLastDistributedValues(
sd59x18(market.realizedDebtUsdPerVaultShare),
sd59x18(market.unrealizedDebtUsdPerVaultShare),
ud60x18(market.usdcCreditPerVaultShare),
ud60x18(market.wethRewardPerVaultShare)
);

Tools Used

Recommendations

Scale up the debts per vault share 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.