Part 2

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

Decimal precision issue with `totalAssets()`

Summary

The getAmountOfAssetOut() and getTotalCreditCapacityUsd() functions incorrectly handle decimal precision when calculating vault assets in USD by not normalizing the index token's decimals to 18 decimals before multiplication.

Vulnerability Details

The issue occurs in the following code where totalAssets() returns a value in the index token's native decimals (which can be less than 18) but is directly multiplied with indexPriceX18 (18 decimals):

File: StabilityBranch.sol
097: function getAmountOfAssetOut(
098: uint128 vaultId,
099: UD60x18 usdAmountInX18,
100: UD60x18 indexPriceX18
101: )
102: public
103: view
104: returns (UD60x18 amountOutX18)
105: {
106: // fetch the vault's storage pointer
107: Vault.Data storage vault = Vault.load(vaultId);
108:
109: // fetch the vault's total assets in USD; if the vault is empty
110: // revert here to prevent panic from subsequent divide by zero
111: UD60x18 vaultAssetsUsdX18 = ud60x18(IERC4626(vault.indexToken).totalAssets()).mul(indexPriceX18);
112: if (vaultAssetsUsdX18.isZero()) revert Errors.InsufficientVaultBalance(vaultId, 0, 0);
113:
114: // we use the vault's net sum of all debt types coming from its connected markets to determine the swap rate
115: SD59x18 vaultDebtUsdX18 = vault.getTotalDebt();
116:
117: // calculate the premium or discount that may be applied to the vault asset's index price
118: // note: if no premium or discount needs to be applied, the premiumDiscountFactorX18 will be
119: // 1e18 (UD60x18 one value)
120: UD60x18 premiumDiscountFactorX18 =
121: UsdTokenSwapConfig.load().getPremiumDiscountFactor(vaultAssetsUsdX18, vaultDebtUsdX18);
122:
123: // get amounts out taking into consideration the CL price and the premium/discount
124: amountOutX18 = usdAmountInX18.div(indexPriceX18).mul(premiumDiscountFactorX18);
125: }
File: Vault.sol
207: function getTotalCreditCapacityUsd(Data storage self) internal view returns (SD59x18 creditCapacityUsdX18) {
208: // load the collateral configuration storage pointer
209: Collateral.Data storage collateral = self.collateral;
210:
211: // fetch the zlp vault's total assets amount
212: UD60x18 totalAssetsX18 = ud60x18(IERC4626(self.indexToken).totalAssets());
213:
214: // calculate the total assets value in usd terms
215: UD60x18 totalAssetsUsdX18 = collateral.getAdjustedPrice().mul(totalAssetsX18);
216:
217: // calculate the vault's credit capacity in usd terms
218: creditCapacityUsdX18 = totalAssetsUsdX18.intoSD59x18().sub(getTotalDebt(self));
219: }

These functions should convert the index token's totalAssets() value to 18 decimals before using it in the calculation.

Impact

The incorrect decimal handling leads to wrong calculation of asset output amounts during swaps.

Recommendations

Convert the index token's totalAssets() value to 18 decimals before multiplying with indexPriceX18 like getVaultCreditCapacity().

Updates

Lead Judging Commences

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

`totalAssets()` is not properly scaled to ZAROS precision

Support

FAQs

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