Part 2

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

In `CreditDelegationBranch.settleVaultsDebt`, calculating an input amount and an output amount should be separated.

Summary

CreditDelegationBranch.calculateSwapAmount returns an expected output amount, but in the case of swapping the
vault asset to usdc, settleVaultsDebt uses the function to decide the input amount.

For example, suppose a uniswap v2 ETH/USDC pool and there are (10 ETH, 30000 USDC) in the pool, so the invariant is
xy = 300000.

  1. When a user sells 1 ETH, the invariant becomes (10 + 1) * (30000 - outputUSDC) = 300000, the output USDC is
    (30000 - 27272) = 2728.

  2. When a user buys 1 ETH, the invariant beomces (10 - 1) * (30000 + inputUSDC) = 300000, the input USDC is
    (33333 - 30000) = 3333.
    So it is not the same to calculate the input amount and the output amount.

Vulnerability Details

In CreditDelegationBranch.settleVaultsDebt, calculateSwapAmount is used to calculate the swap input amount of vault
asset.

if (ctx.vaultUnsettledRealizedDebtUsdX18.lt(SD59x18_ZERO)) {
// @audit assetIn is usdc, and assetOut is vaultAsset
ctx.swapAmount = calculateSwapAmount(
dexSwapStrategy.dexAdapter,
ctx.usdc,
ctx.vaultAsset,
usdcCollateralConfig.convertSd59x18ToTokenAmount(ctx.vaultUnsettledRealizedDebtUsdX18.abs())
);
// swap the vault's assets to usdc in order to cover the usd denominated debt partially or fully
// both input and output in native precision
ctx.usdcOut = _convertAssetsToUsdc(
vault.swapStrategy.usdcDexSwapStrategyId,
ctx.vaultAsset,
// @audit swapAmount is the input amount of vault asset
ctx.swapAmount,
vault.swapStrategy.usdcDexSwapPath,
address(this),
ctx.usdc
);

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/branches/CreditDelegationBranch.sol#L438-L454

However, calculateSwapAmount returns the expected output amount of assetOut.

amount = IDexAdapter(dexAdapter).getExpectedOutput(assetIn, assetOut, vaultUnsettledDebtUsdAbs);

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/branches/CreditDelegationBranch.sol#L558

This is same for the else block in the same function settleVaultsDebt.

ctx.usdcIn = calculateSwapAmount(
dexSwapStrategy.dexAdapter,
ctx.vaultAsset,
ctx.usdc,
usdcCollateralConfig.convertSd59x18ToTokenAmount(ctx.vaultUnsettledRealizedDebtUsdX18.abs())
);

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/branches/CreditDelegationBranch.sol#L483-L488

ctx.assetOutAmount = _convertUsdcToAssets(
vault.swapStrategy.assetDexSwapStrategyId,
ctx.vaultAsset,
ctx.usdcIn,
vault.swapStrategy.assetDexSwapPath,
vault.indexToken,
ctx.usdc
);

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/branches/CreditDelegationBranch.sol#L500C1-L507C19

Impact

If the input amount is calculated lower than the actual amount, the vault may not be able to settle all its debt.
If the input amount is calculated higher than the actual amount, settleVaultsDebt will revert.

Tools Used

Manual review

Recommendations

  1. Rename CreditDelegationBranch.calculateSwapAmount to calculateExpectedSwapOutput.

  2. Implement a separate CreditDelegationBranch.calculateSwapInputAmount function to calculate the input amount for the swap.

Updates

Lead Judging Commences

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

Support

FAQs

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