Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
liquidator incentive failure: The _doLiquidation function checks whether to pay the liquidator fee against the victim's remaining collateral balance after seizure, rather than validating against the seized amount. When a liquidation nearly depletes the victim's position, the fee which was already included in the seized collateral is never transferred to the liquidator, remaining trapped in the Alchemist contract.
Vulnerability Details
Liquidator fee withheld when collateral depleted
Summary: In _doLiquidation (lines 867-895), the liquidation fee payment logic contains a critical flaw:
Line 867-868: amountLiquidated and feeInYield are calculated from calculateLiquidation, where grossCollateralToSeize = debtToBurn + fee (line 1292)
Line 871: The victim's collateral balance is reduced by the entire seized amount:
The bug: The check uses account.collateralBalanceafter deducting the full seizure (which already includes the fee). In scenarios where liquidation consumes most/all of the victim's collateral, the remaining balance falls below feeInYield, causing the condition to fail. The fee was already seized from the user and is held by the Alchemist contract (line 874 only sends amountLiquidated - feeInYield to the transmuter), but it's never transferred to the liquidator.
Impact Details
Liquidators receive no compensation despite performing economically rational liquidations, creating a systemic disincentive that leaves under-collateralized positions unaddressed. Accumulated trapped fees represent protocol insolvency, and the lack of liquidation during volatile periods can cascade into bad debt.
Recommended: The math in calculateLiquidation guarantees that grossCollateralToSeize = debtToBurn + fee (line 1292), and amountLiquidated is derived directly from grossCollateralToSeize. The seized collateral is in the contract's custody, so the fee is always payable. The balance check adds no safety and only introduces this bug.
References
-src/AlchemistV3.sol#L852-L895 — _doLiquidation function containing the liquidation fee payment vulnerability.
-src/AlchemistV3.sol#L867-L868 — Calculation of amountLiquidated and feeInYield from calculateLiquidation results.
-src/AlchemistV3.sol#L871 — Victim's collateral balance reduced by the entire seized amount (including fee) -src/AlchemistV3.sol#L874 — Transfer of seized collateral minus fee to the transmuter.
-src/AlchemistV3.sol#L877 — Buggy fee payment check using victim's remaining collateral balance after seizure.
-src/AlchemistV3.sol#L1244-L1295 — calculateLiquidation function that computes grossCollateralToSeize including the fee.
-src/AlchemistV3.sol#L1290 — grossCollateralToSeize = debtToBurn + fee calculation ensuring fee is part of seized amount