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 10 months ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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

Give us feedback!