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 V3
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:
Account deduction: Only \min(fee, collateral) is removed from the position
Fee return: The function returns the full nominal fee
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
Borrower has 100 MYT collateral and 80 MYT debt
Earmarked debt (60 MYT) is created via Transmuter
Price crash makes position undercollateralized
Repayment fee is configured at 100% (10,000 BPS)
Liquidation is triggered:
_forceRepayrepays 60 MYT debt to Transmuter_resolveRepaymentFeecalculates: fee = 60 *10000/10000 = 60 MYTAccount 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
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
Protocol: Suffers direct token depletion from reserves
Legitimate liquidators: Indirectly benefit if they liquidate undercollateralized positions (though this is not their fault)
Other borrowers: Protocol insolvency from cumulative losses could prevent collateral withdrawals or protocol operations
Attack Complexity
Low: The vulnerability requires:
A position with earmarked debt (automatic via Transmuter)
Market conditions that cause undercollateralization (happens naturally)
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?