58464 sc critical repayment fee paid from protocol funds when user collateral is depleted
Submitted on Nov 2nd 2025 at 14:06:02 UTC by @auditagent for Audit Comp | Alchemix V3
Report ID: #58464
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Protocol insolvency
Description
Brief/Intro
_resolveRepaymentFeecalculates the full fee(repaidAmount * repaymentFee / BPS)and transfers that amount to the liquidator.The collateral is only debited up to their remaining balance.
Liquidator receives full repayment fee from pool even when user's collateral couldn't cover it.
The docs claims that the Liquidator Fee Vault only covers fees when the user's collateral can’t pay the liquidator. https://keenanlukeom.github.io/alchemix-v3-docs/user/concepts/liquidations. However, this is not how the protocol behaves currently
Vulnerability Details
_liquidate()calls_forceRepay()to clear earmarked debt using the user's collateral.If the user's debt is fully cleared or position becomes healthy after repayment, the function takes an early-return path that pays a repayment fee.
_resolveRepaymentFee()computes the fee and deducts only what's available from user collateral:
_liquidate()then transfers the full returned fee:
The docs mention expected behavior:
Should the user’s collateral not be sufficient on its own to pay a liquidator, there is a separate fee vault that may be funded by any entity (including the DAO) that may be drawn from to pay liquidators.
However currently the returned feeInYield is transferred to the liquidator unconditionally. There is no check to verify the user's collateral was sufficient.
Impact Details
Each earmark-only liquidation on an undercollateralized position drains protocol reserves equal to the fee shortfall
References
https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol#L900
https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol#L840
https://keenanlukeom.github.io/alchemix-v3-docs/user/concepts/liquidations/
Proof of Concept
Proof of Concept
Add the following PoC in src/test/AlchemistV3.t.sol and run using forge test --match-test Liquidate_EarmarkedRepaymentFeeShortfall_ComesFromPool
Was this helpful?