Part 2

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

Incorrect Decimal Offset Handling

Summary

totalAssetsMinusVaultDebt (Vault’s credit capacity) is inaccurate due to incorrect decimal scaling during offset addition.

Vulnerability Details

function getVaultCreditCapacity(uint128 vaultId) public view returns (uint256) {
// fetch storage slot for vault by id
Vault.Data storage vault = Vault.loadExisting(vaultId);
// fetch the vault's total assets in 18 dec
SD59x18 totalAssetsX18 =
vault.collateral.convertTokenAmountToSd59x18(IERC4626(vault.indexToken).totalAssets().toInt256());
// we use the vault's net sum of all debt types coming from its connected markets to determine the swap rate
SD59x18 vaultDebtUsdX18 = vault.getTotalDebt();
// get collateral asset price
UD60x18 assetPriceX18 = vault.collateral.getPrice();
// convert the vault debt value in USD to the equivalent amount of assets to be credited or debited
SD59x18 vaultDebtInAssetsX18 = vaultDebtUsdX18.div(assetPriceX18.intoSD59x18());
// get decimal offset
uint8 decimalOffset = Constants.SYSTEM_DECIMALS - vault.collateral.decimals;
// subtract the vault's debt from the total assets
// NOTE: we add 1 to the total assets to avoid division by zero.
// Add 10 ** decimalsOffset since when converting back from x18 to uint256, it would equal 1
// NOTE: credit is accounted as negative debt, so it would be added to the total assets
SD59x18 totalAssetsMinusVaultDebtX18 =
totalAssetsX18.add(sd59x18(int256(10 ** uint256(decimalOffset)))).sub(vaultDebtInAssetsX18);
// sd59x18 -> uint256
uint256 totalAssetsMinusVaultDebt = vault.collateral.convertSd59x18ToTokenAmount(totalAssetsMinusVaultDebtX18);
return totalAssetsMinusVaultDebt;
}

decimalOffset is computed as Constants.SYSTEM_DECIMALS - vault.collateral.decimals (e.g., 12 for a 6-decimal token like USDC).

10 ** decimalOffset (e.g., 10^12) is added to totalAssetsX18, treating it as an absolute value without scaling to the system's 18 decimals.

This Introduces a skew in the Vault’s net credit capacity calculation.

Also it Converts a token’s 1-unit offset (intended to avoid division by zero) into an exponentially larger value, corrupting asset/share conversion rates (getIndexTokenSwapRate, getVaultAssetSwapRate).

Example:

For a 6-decimal token (e.g., USDC):

decimalOffset = 18 - 6 = 12.

10^12 in token decimals ↔ 0.000001 USDC.

But adding 10^12 as-is in the system (18-decimal) scale treats it as 1e12 * 1e18, leading to an unintended 1e30 units inflation.

Impact

Incorrectly inflates/deflates the Vault's credit capacity. Users receive improper asset amounts during deposits/withdrawals.

Tools Used

Manual Review

Recommendations

Adjust the offset to be added in the token’s decimal scale before converting to the system’s 18 decimals

// Convert 1 token unit to 18 decimals before adding
uint256 offsetInTokenDecimals = 1 * 10 ** vault.collateral.decimals;
SD59x18 offsetX18 = vault.collateral.convertTokenAmountToSd59x18(offsetInTokenDecimals);
totalAssetsX18 = totalAssetsX18.add(offsetX18);
Updates

Lead Judging Commences

inallhonesty Lead Judge
5 months ago
inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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