In _doLiquidation, the liquidator fee is computed and included in the amount of collateral to seize. The code deducts the full seized amount from the user, transfers the net seized minus fee to the transmuter, and then tries to transfer the fee to the liquidator only if the victim’s remaining collateral balance is at least the fee. Because the victim’s balance was already reduced by the seized amount (including the fee), the post-seizure remaining balance is often smaller than the fee, so the conditional fails and the liquidator gets nothing. _doLiquidation removes the entire seized amount from the account before forwarding the fee. The protocol keeps the fee trapped, liquidators are disincentivized from acting, bad debt can grow unchecked, and trapped fees represent hidden liabilities
Vulnerability Details
check code excerpt here https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L867-#L880
// compute amountsamountLiquidated =convertDebtTokensToYield(liquidationAmount);feeInYield =convertDebtTokensToYield(baseFee);// update user balance and debtaccount.collateralBalance = account.collateralBalance > amountLiquidated ? account.collateralBalance - amountLiquidated :0;_subDebt(accountId, debtToBurn);// send liquidation amount - fee to transmuterTokenUtils.safeTransfer(myt, transmuter, amountLiquidated - feeInYield);// send base fee to liquidator if availableif(feeInYield >0&& account.collateralBalance >= feeInYield){ TokenUtils.safeTransfer(myt,msg.sender, feeInYield);}
Why this is wrong:
calculateLiquidation(...) returns grossCollateralToSeize = debtToBurn + fee (i.e., the fee is included in the seized collateral).
The code reduces account.collateralBalance by amountLiquidated (the full seized amount, which includes the fee). After this reduction, the fee portion of the seized collateral is not represented in the account’s remaining balance.
The conditional account.collateralBalance >= feeInYield asks whether the victim’s remaining balance is at least the fee. That is the wrong invariant: the correct check should not be against the victim’s post-seizure balance because the fee was already extracted as part of the seizure. In nearly all cases where a liquidation consumes most or all of the collateral, the post-seizure balance will be < fee and the fee transfer will be skipped, even though the fee was already taken from the user and sits in the contract.
Net effect: fee is seized but not forwarded, it stays in contract balances as an unforwarded protocol-held token
Impact Details
Permanent freezing of unclaimed yield: fee amounts taken from user collateral remain in contract and are not forwarded → effectively frozen until manual recovery.
Protocol insolvency: trapped fees accumulate as hidden protocol-held liabilities and reduce available funds; if many liquidations occur with trapped fees, the protocol’s ability to meet obligations deteriorates; under stress, this can cause insolvency