58524 sc high when liquidating there are cases where the fee is not paid to the liquidator

Submitted on Nov 3rd 2025 at 01:16:50 UTC by @bigbear1229 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58524

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Permanent freezing of unclaimed royalties

    • Since the Fee is not paid to the liquidator, liquidations may not proceed, which can disrupt the normal operation of the system.

Description

Brief/Intro

There are cases where the Fee is not paid to the liquidator during liquidation.

Vulnerability Details

In the _doLiquidation() function of AlchemistV3.sol, there is a part that pays the Fee to the liquidator.

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
        account.collateralBalance = account.collateralBalance > amountLiquidated ? account.collateralBalance - amountLiquidated : 0;
        _subDebt(accountId, debtToBurn);

        // send liquidation amount - fee to transmuter
        TokenUtils.safeTransfer(myt, transmuter, amountLiquidated - feeInYield);

        // send base fee to liquidator if available
        if (feeInYield > 0 && account.collateralBalance >= feeInYield) {
            TokenUtils.safeTransfer(myt, msg.sender, feeInYield);
        }

        ...
    }

In the _doLiquidation() function, the amountLiquidated value is obtained from the calculateLiquidation() function. Looking at calculateLiquidation(), we can see that the grossCollateralToSeize value includes the Fee.

Looking at line 871 of AlchemistV3.sol, we can see that the amountLiquidated value, which already includes the Fee, has been subtracted from account.collateralBalance.

However, when paying the Fee to the liquidator, there is a check to see if the account.collateralBalance value is greater than the Fee that needs to be paid.

Since the fee has already been subtracted from account.collateralBalance, this check becomes unnecessary. Ultimately, if account.collateralBalance is less than feeInYield, the fee cannot be paid.

Impact Details

Since the Fee is not paid to the liquidator, liquidations may not proceed, which can disrupt the normal operation of the system.

##Recommended mitigation steps

Proof of Concept

Proof of Concept

To identify cases where the fee is not paid to the liquidator, we set the liquidatorFee in AlchemistV3 to a high value and demonstrated the issue. This issue can be verified by adding the following test_CheckPairAddress() function to the AlchemistV3.t.sol file.

Was this helpful?