58491 sc high mytsharesdeposited not reduced on liquidation leading to deposit cap bypass and potential insovency

Submitted on Nov 2nd 2025 at 17:36:26 UTC by @Bizarro for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58491

  • Report Type: Smart Contract

  • Report severity: High

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol

  • Impacts:

    • Smart contract unable to operate due to lack of token funds

    • Protocol insolvency

Description

Brief/Intro

In the _doLiquidation function, when a user is liquidated, their collateral balance is reduced but the global varibale _mytSharesDeposited is not decreased accordingly. This results in an inconsistency between the actual MYT collateral held by the contract and the reported global deposits which can lead to insolvency.

Vulnerability Details

The _doLiquidation function fails to decrease _mytSharesDeposited by the amount of collateral removed from the user during liquidation. This leads to an inflated view of total deposited MYT tokens in the protocol In _forceRepay also there is no decrease in _mytSharesDeposited after reducing collateralBalance

function _doLiquidation(uint256 accountId, uint256 collateralInUnderlying, uint256 repaidAmountInYield)
        internal
        returns (uint256 amountLiquidated, uint256 feeInYield, uint256 feeInUnderlying)
    {
        ---

        amountLiquidated = convertDebtTokensToYield(liquidationAmount);
        feeInYield = convertDebtTokensToYield(baseFee);

        // update user balance and debt
@>1        account.collateralBalance = account.collateralBalance > amountLiquidated ? account.collateralBalance - amountLiquidated : 0;
        _subDebt(accountId, debtToBurn);

        ---
    }

function _forceRepay(uint256 accountId, uint256 amount) internal returns (uint256) {

        ---

        creditToYield = creditToYield > account.collateralBalance ? account.collateralBalance : creditToYield;
@2>        account.collateralBalance -= creditToYield;

        if (account.collateralBalance > protocolFeeTotal) {
@3>            account.collateralBalance -= protocolFeeTotal;
            // Transfer the protocol fee to the protocol fee receiver
            TokenUtils.safeTransfer(myt, protocolFeeReceiver, protocolFeeTotal);
        }

        ---
    }
  • @1 -> In _doLiquidation the collateralBalance of the user is decreased but the _mytSharesDeposited is not decreased

  • @2, @3 -> In _forceRepay, user's collateralBalance is decreased but the _mytSharesDeposited is not decreased.

Adding to this, the transmuter also uses the _mytSharesDeposited amount to calculate the badDebtRatio which helps to determine if the user will get 1:1 for their alAsset deposited or not. If the badDebtRatio is greater than 1e18, then user will not get 1:1 for their tokens and the badDebtRatio is calculated as

Here the denominator is inflated because of the inflated _mytSharesDeposited amount leading to deflated badDebtRatio and causing protocol to pay 1:1 ratio of tokens to the redeemer even though the protocol should be transferring less amount of mytTokens.

This leads to protocol paying more that it should leading to insolvency as the 1:1 cannot be maintained because of alAssets being more than their collateral backing but still protocol has to pay 1:1 to the redeemer.

Impact Details

  1. Insolvency Risk: _getTotalUnderlyingValue will be incorrect as the _mytSharesDeposited will be inflated leading to insovency as the calculation will lead to a situation where it is unable to meet all user claims(alUSDC can be redeemed for 1:1 even when there isn’t enough backing)

  2. Deposit Cap Bypass: As the mytSharesDeposited is not updated, the protocol's deposit cap logic will be reached way before the actual myt shares in the contract.

References

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L852

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/Transmuter.sol#L219

Proof of Concept

paste this test in test/AlchemistV3.t.sol and forge test --mt test_mytSharesDeposited_not_Reduced -vvv

The function will revert with IllegalState() error.

Was this helpful?