58301 sc critical accounting issue in liquidation logic after force repay we charge repayment fee even if collateral balanc cannot account for it

Submitted on Nov 1st 2025 at 04:12:30 UTC by @damdam0249 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58301

  • Report Type: Smart Contract

  • Report severity: Critical

  • 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

Description

Brief/Intro

There’s an accounting issue in the liquidation logic that occurs after a force repay. When a user’s account doesn’t have enough collateral left to cover the liquidator fee, the protocol still transfers the full fee to the liquidator using the Alchemist contract’s own balance. In short, the protocol ends up paying liquidators out of its own funds instead of the debtor’s.

Vulnerability Details

/// @dev Handles repayment fee calculation and account deduction
/// @param accountId The tokenId of the account to force a repayment on.
/// @param repaidAmountInYield The amount of debt repaid in yield tokens.
/// @return fee The fee in yield tokens to be sent to the liquidator.
function _resolveRepaymentFee(uint256 accountId, uint256 repaidAmountInYield) internal returns (uint256 fee) {
    Account storage account = _accounts[accountId];
    // calculate repayment fee and deduct from account

    fee = repaidAmountInYield * repaymentFee / BPS;
    emit balancebefore(account.collateralBalance, fee);

@see>>> account.collateralBalance -= fee > account.collateralBalance ? account.collateralBalance : fee;

@see>>> emit RepaymentFee(accountId, repaidAmountInYield, msg.sender, fee);
@see>>> return fee;  

The issue arises when the user’s collateralBalance is less than the expected feeInYield. Even though a balance check is performed, the contract does not reduce or cap the fee based on what the account can actually cover.

In cases where the user’s collateral is already partially depleted (for example, after a force repayment), the remaining collateral may be insufficient to pay the full fee. Despite that, the protocol still proceeds to transfer the entire fee to the liquidator — meaning the excess amount comes from the Alchemist contract’s own balance instead of the user’s account.

This results in the protocol subsidising liquidators using protocol funds.

Code References

Summary of the Flow

Force repay reduces the user’s collateral balance.

Liquidation logic then calculates a repayment fee.

Even if the user’s collateral is insufficient, the full fee is still transferred out.

The contract’s own MYT balance covers the shortfall.

Impact Details

As a result, protocol funds (MYT tokens) will gradually be reduced over time as liquidators receive more than the amount available in the user’s account.

References

Add any relevant links to documentation or code

Proof of Concept

Proof of Concept

Test will revert because of token balance

Was this helpful?