Part 2

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

Incorrect logic leads to denial of service from the protocol

_recalculateConnectedMarketsState() was called in recalculateVaultsCreditCapacity() and recalculateVaultsCreditCapacity() was called in the following

  • deposit

  • stake

  • unStake

  • redeem

Example vault A,B,C delegated these valueS to market
vauilt

// Initial Setup
Vaults:
A: 500,000 USDC deposited (price $1) = $500.000 value
B: 30ETH deposited (price $2000) = $60.000 value
C: 10,000 USDT deposited (price $1) = $100.000 value
// Each vault has credit delegation weights to different markets
Vault A credit delegations:
- Market 1: 40%
- Market 2: 30%
- Market 3: 30%
Vault B credit delegations:
- Market 1: 50%
- Market 2: 50%
Vault C credit delegations:
- Market 2: 60%
- Market 3: 40%
// Calculate credit capacity per market
Market 1 credit capacity:
Market 1:
- Vault A: $500,000 * 40% = $200,000
- Vault B: $60,000 * 50% = $30,000
Total = $230,000
Market 2:
- Vault A: $500,000 * 30% = $150,000
- Vault B: $60,000 * 50% = $30,000
- Vault C: $100,000 * 60% = $60,000
Total = $240,000
Market 3:
- Vault A: $500,000 * 30% = $150,000
- Vault C: $100,000 * 40% = $40,000
Total = $190,000
Market 1:
creditDeposits = {
USDC: 200,000 * $1 * 0.95 = $190,000
WETH: 15 ETH * $2000 * 0.90 = $27,000
}
creditDepositsValueUsdX18 = $217,000
Market 2:
creditDeposits = {
USDC: 150,000 * $1 * 0.95 = $142,500
WETH: 15 ETH * $2000 * 0.90 = $27,000
USDT: 60,000 * $1 * 0.95 = $57,000
}
creditDepositsValueUsdX18 = $226,500
Market 3:
creditDeposits = {
USDC: 150,000 * $1 * 0.95 = $142,500
USDT: 40,000 * $1 * 0.95 = $38,000
}
creditDepositsValueUsdX18 = $180,500

Lets Assume netUsdTokenIssuance are these value provided

Then the realizedDebtUsdX18 will return these values below too.

Market 1:
```solidity
creditDepositsValueUsdX18 = $217,000
netUsdTokenIssuance = $100,000 (tokens minted)
realizedDebtUsdX18 = $217,000 + $100,000 = $317,000

Market 2:

creditDepositsValueUsdX18 = $226,500
netUsdTokenIssuance = $150,000 (tokens minted)
realizedDebtUsdX18 = $226,500 + $150,000 = $376,500

Market 3:

creditDepositsValueUsdX18 = $180,500
netUsdTokenIssuance = $80,000 (tokens minted)
realizedDebtUsdX18 = $180,500 + $80,000 = $260,500

so getRealizedDebtUsd()
will return

  • Market 1: = $317,000

  • Market 2: = $376,500

  • Market 3: = $260,500

Now when _recalculateConnectedMarketsState() calls it , it returns these value

recalculateVaultsCreditCapacity() later call _recalculateConnectedMarketsState()

At the end of the function you see this

// update the vault's credit delegations
(, SD59x18 vaultNewCreditCapacityUsdX18) =
_updateCreditDelegations(self, updatedConnectedMarketsIdsCache, false);

Then what is updatedConnectedMarketsIdsCache and how it was gotten

(
uint128[] memory updatedConnectedMarketsIdsCache,
SD59x18 vaultTotalRealizedDebtChangeUsdX18,
SD59x18 vaultTotalUnrealizedDebtChangeUsdX18,
UD60x18 vaultTotalUsdcCreditChangeX18,
UD60x18 vaultTotalWethRewardChangeX18
) = _recalculateConnectedMarketsState(self, connectedMarketsIdsCache, true);

so vaultTotalRealizedDebtChangeUsdX18 will now be the total per vault

Now remember there is this at the end of recalculateVaultsCreditCapacity()

// update the vault's credit delegations
(, SD59x18 vaultNewCreditCapacityUsdX18) =
_updateCreditDelegations(self, updatedConnectedMarketsIdsCache, false);

_updateCreditDelegations

// stores the vault's total credit capacity to be returned
vaultCreditCapacityUsdX18 = getTotalCreditCapacityUsd(self);

getTotalCreditCapacityUsd(self); will return creditCapacityUsdX18

// calculate the vault's credit capacity in usd terms
creditCapacityUsdX18 = totalAssetsUsdX18.intoSD59x18().sub(getTotalDebt(self));

Here totalAssetsX18 is is using the AdjustedPrice() and is subtracting from the getTotalDebt()

function getTotalCreditCapacityUsd(Data storage self) internal view returns (SD59x18 creditCapacityUsdX18) {
// load the collateral configuration storage pointer
Collateral.Data storage collateral = self.collateral;
// fetch the zlp vault's total assets amount
UD60x18 totalAssetsX18 = ud60x18(IERC4626(self.indexToken).totalAssets());
// calculate the total assets value in usd terms
UD60x18 totalAssetsUsdX18 = collateral.getAdjustedPrice().mul(totalAssetsX18);
// calculate the vault's credit capacity in usd terms
creditCapacityUsdX18 = totalAssetsUsdX18.intoSD59x18().sub(getTotalDebt(self));
}

Here getTotalDebt is adding

function getTotalDebt(Data storage self) internal view returns (SD59x18 totalDebtUsdX18) {
totalDebtUsdX18 = getUnsettledRealizedDebt(self).add(sd59x18(self.marketsUnrealizedDebtUsd));
}

now after getting these total dept it will be more higher than the normal total dept , this will result to faster

impact

now auto deveraging faster vault will stop delegating causing denial of service from the protocol

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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