58626 sc critical repayment fee overpayment in liquidation repay only path

Submitted on Nov 3rd 2025 at 17:34:21 UTC by @jayx for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58626

  • 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

Description

Brief/Intro

The _liquidate() function's repay-only liquidation path contains a critical vulnerability where the repayment fee transferred to the liquidator exceeds the fee actually debited from the borrower's collateral. Specifically, _resolveRepaymentFee() deducts \min(fee, collateral) from the position but returns the full nominal fee, which is then transferred unconditionally to the liquidator. This creates a direct theft vector where liquidators systematically receive unearned tokens from the protocol's reserves, resulting in protocol insolvency over time as repeated liquidations compound losses.

Vulnerability Details

Root Cause

The vulnerability exists in the asymmetric handling of repayment fees between the account deduction and liquidator payment:

In _resolveRepaymentFee():

In _liquidate() repay-only path:

The Mismatch

The critical issue is that:

  1. Account deduction: Only \min(fee, collateral) is removed from the position

  2. Fee return: The function returns the full nominal fee

  3. Liquidator payment: The full nominal fee is transferred to the liquidator without verification

This violates the protocol's own directive: "the fee should only come from account collateral, otherwise only the debited amount should be transferred."

Code Asymmetry

Repay-only path (vulnerable):

Partial liquidation path (protected):

Exploitation Scenario

  1. Borrower has 100 MYT collateral and 80 MYT debt

  2. Earmarked debt (60 MYT) is created via Transmuter

  3. Price crash makes position undercollateralized

  4. Repayment fee is configured at 100% (10,000 BPS)

  5. Liquidation is triggered:

    • _forceRepay repays 60 MYT debt to Transmuter

    • _resolveRepaymentFee calculates: fee = 60 *10000/10000 = 60 MYT

    • Account collateral decreases by: min(60, 100) = 60 MYT

    • But liquidator receives: 60 MYT (the full nominal fee)

    • Result: Liquidator gets paid correctly, but ONLY because collateral was sufficient

  6. Critical case - Lower collateral scenario:

    • Same setup but borrower only has 20 MYT collateral

    • Fee calculation: 60 MYT

    • Account deduction: min(60, 20) = 20 MYT

    • Liquidator receives: 60 MYT

    • Overpayment: 40 MYT comes from protocol reserves

Impact Details

Direct Financial Impact

Per-Liquidation Loss:

  • If nominalFee > min(nominalFee, collateralBalance), the protocol loses tokens

  • Overpayment per liquidation: max(0, nominalFee - collateralBalance)

Cumulative Loss:

  • Each undercollateralized liquidation triggers the overpayment

  • With sustained market volatility and active liquidations, cumulative losses scale linearly with liquidation volume

  • Example: 1,000 liquidations × 50 MYT average overpayment = 50,000 MYT lost

Affected Parties

  1. Protocol: Suffers direct token depletion from reserves

  2. Legitimate liquidators: Indirectly benefit if they liquidate undercollateralized positions (though this is not their fault)

  3. Other borrowers: Protocol insolvency from cumulative losses could prevent collateral withdrawals or protocol operations

Attack Complexity

Low: The vulnerability requires:

  1. A position with earmarked debt (automatic via Transmuter)

  2. Market conditions that cause undercollateralization (happens naturally)

  3. Calling the public liquidate() function (no special access needed)

An attacker could trigger this repeatedly by:

  • Creating multiple positions with earmarked debt

  • Monitoring for price drops that cause undercollateralization

  • Calling liquidate() to extract overpaid fees

References

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

Proof of Concept

Proof of Concept

Was this helpful?