57122 sc critical mismatch between capped fee and returned fee in resolverepaymentfee

Submitted on Oct 23rd 2025 at 17:09:30 UTC by @farismaulana for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #57122

  • Report Type: Smart Contract

  • Report severity: Critical

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

  • Impacts:

    • Protocol insolvency

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

the fee handling when earmark repayment happening in liquidation is incorrectly sent the uncapped fee amount when it should be capped, making the fee sent that exceed account.collateralBalance comes from other’s collateral instead.

Vulnerability Details

    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;
@>      account.collateralBalance -= fee > account.collateralBalance ? account.collateralBalance : fee;
        emit RepaymentFee(accountId, repaidAmountInYield, msg.sender, fee);
@>      return fee;
    }

as we can see here, when the account collateral balance would be deducted by fee, it would cap the amount if the fee is greater than collateralBalance.

but there are no readjustment of fee after that, making the _resolveRepaymentFee returning the original fee amount regardless. the returned amount then used to pay the liquidator:

this can happen when earmark is large enough to force repay the full collateral position, meaning there a liquidation happening.

however if this happening, it would take more amount of MYT than what the liquidated MYT amount, as shown on the PoC below.

Impact Details

it is possible for a liquidated account paying the liquidation/repayment fees not only from their liquidated position, but also taking from the MYT contract balance directly which is owned by another possibly healthy positions.

this further worsening the protocol condition, making the actual collateral held lower than what is accounted. this can lead to protocol insolvency.

References

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

Proof of Concept

Proof of Concept

add this test into src/test/AlchemistV3.t.sol :

by running the test we can see the result:

MYT out from alchemist contract (sent for transmuter + fee for liquidator) are higher than the liquidated collateral position

Was this helpful?