58354 sc high forcerepay does not decrement mytsharesdeposited causing a temporal blocking of new deposits

Submitted on Nov 1st 2025 at 14:08:17 UTC by @Pataroff for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58354

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Temporary freezing of funds for at least 1 hour

Description

Brief/Intro

Unlike repay(), the _forceRepay() function does not decrement _mytSharesDeposited after transferring MYT tokens to the Transmuter and fee receiver.

As a result, _mytSharesDeposited is overstated relative to the actual MYT balance held by the contract, preventing new deposits from succeeding until manual intervention.

Vulnerability Details

During liquidation, _forceRepay() uses a user’s collateral to repay outstanding debt and transfer funds to the Transmuter and protocol fee receiver. While the user’s per-account state is updated correctly, the global _mytSharesDeposited variable is not decremented, leaving it stale:

    function _forceRepay(uint256 accountId, uint256 amount) internal returns (uint256) {
        ...
        // Repay debt from earmarked amount of debt first
        uint256 earmarkToRemove = credit > account.earmarked ? account.earmarked : credit;
        account.earmarked -= earmarkToRemove;

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

        uint256 protocolFeeTotal = creditToYield * protocolFee / BPS;

        emit ForceRepay(accountId, amount, creditToYield, protocolFeeTotal);

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

        if (creditToYield > 0) {
            // Transfer the repaid tokens from the account to the transmuter.
            TokenUtils.safeTransfer(myt, address(transmuter), creditToYield);
        }

@> //@audit-missing _mytSharesDeposited -= creditToYield

        return creditToYield;
    }

As a consequence, subsequent deposits will fail if the deposit cap was reached since we enforce the deposit cap check against the _mytSharesDeposited variable:

Impact Details

References

AlchemistV3.sol#L738-L782arrow-up-right

AlchemistV3.sol#L369arrow-up-right

Proof of Concept

Proof of Concept

Was this helpful?