56975 sc high liquidation fee trapping in alchemistv3

Submitted on Oct 22nd 2025 at 11:01:12 UTC by @Paludo0x for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56975

  • 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

Description

Brief/Intro

In AlchemistV3::liquidate() the liquidator’s feeInYield is carved out of the seized collateral amountLiquidated but its payout is conditionally gated by account.collateralBalance >= feeInYield after the seizure.

If the gate fails, the fee is neither sent to the liquidator nor forwarded to the Transmuter, leaving these tokens locked in the AlchemistV3 contract.

With no recovery function, this results in permanent freezing of unclaimed royalties (liquidator fees).

Vulnerability Details

This is the vulnerability flow in AlchemixV3::_doLiquidation():

  1. Seize the borrower's collateral

  1. Send NET to the Transmuter: (gross - fee)

  1. Pay the liquidator FEE ONLY IF enough residual collateral remains

These are the keypoints that make implementation wrong:

  • Step (2) reserves feeInYield by sending only (amountLiquidated - feeInYield) to the Transmuter.

  • Step (3) may skip sending the fee if the post-seizure accounting residual is < feeInYield.

  • There is no fallback path to re-route the fee anywhere. The reserved amount remains in the AlchemistV3 contract balance, permanently frozen.

The branch is real, but its reachability depends on parameters. In debt units, fee trapping requires:

With m = minimumCollateralization and fee fraction phi = liquidatorFee / 10_000, this reduces to the threshold:

For the defaults in this repo (m ≈ 1.111…), the threshold is ≈ 90.91% (≈ 9091 bps). With the default liquidatorFee = 300 bps (3%), the inequality does not hold, so the problematic branch is not reachable under normal settings. It becomes reachable only with extreme fee configurations.

Impact Details

The impact is High: Permanent freezing of unclaimed royalties.

  • Permanent freezing: The unpaid liquidator fee is never sent and cannot be reclaimed on-chain (no skim/sweep/recover path), leaving the fee permanently frozen in AlchemistV3.

  • Incentive erosion: Liquidators may be underpaid exactly when positions are near the threshold, reducing liquidation participation and increasing systemic risk.

  1. When the gate fails, send all seized to the Transmuter.

  2. Implement a governance function sweepTrappedFees

Proof of Concept

Proof of Concept

What PoC tests show:

  • test_FeeTrapping_Condition_Unreachable_With_Default_Fee: Uses calculateLiquidation to sample undercollateralized states and shows that, with default fee, collateral ≥ debtToBurn + 2*fee always holds; the branch cannot trigger in practice.

  • test_FeeTrapping_Condition_Becomes_Possible_When_Fee_Above_Threshold: Temporarily raises the fee above the threshold and demonstrates collateral < debtToBurn + 2*fee can hold, proving the branch exists but is extreme-only.

Was this helpful?