58768 sc high mytsharesdeposited is not updated during liquidations breaking core accounting

Submitted on Nov 4th 2025 at 12:38:53 UTC by @auditagent for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58768

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Protocol insolvency

Description

Brief/Intro

The _mytSharesDeposited state variable is intended to track the total amount of myt tokens held by the AlchemistV3 contract.

However, during the liquidation process, myt tokens are transferred out, but the _mytSharesDeposited variable is never decremented to reflect this outflow. This occurs in both the _forceRepay and _doLiquidation

Vulnerability Details

_mytSharesDeposited feeds both depositCap enforcement and TVL via _getTotalUnderlyingValue(). When MYT exits the contract without decrementing this counter, TVL and cap appear higher than reality.

function _forceRepay(uint256 accountId, uint256 amount) internal returns (uint256) {
    // ...
    if (account.collateralBalance > protocolFeeTotal) {
        account.collateralBalance -= protocolFeeTotal;
        // Transfer the protocol fee to the protocol fee receiver
        TokenUtils.safeTransfer(myt, protocolFeeReceiver, protocolFeeTotal); // myt transferred out
    }
    if (creditToYield > 0) {
        // Transfer the repaid tokens from the account to the transmuter.
        TokenUtils.safeTransfer(myt, address(transmuter), creditToYield); // myt transferredout
        // @audit: `_mytSharesDeposited` is not decremented for MYT transferred out
    }
    return creditToYield;
}

Root cause

  • In _forceRepay, MYT is sent to the transmuter and the protocol fee receiver, but _mytSharesDeposited is never decremented by those outflows.

  • In _doLiquidation, MYT is sent to the transmuter and to the liquidator, but _mytSharesDeposited is not decremented.

  • Other flows correctly update the counter.

Impact Details

  • _getTotalUnderlyingValue() overstates collateral.

References

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

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

Proof of Concept

Proof of Concept

Add the following test in src/test/AlchemistV3.t.sol and run using forge test --match-test test_mismatch

Was this helpful?