58306 sc critical repayment fee not adjusted for insufficient collateral
Submitted on Nov 1st 2025 at 06:28:12 UTC by @JoeMama for Audit Comp | Alchemix V3
Report ID: #58306
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
During liquidation, _forceRepay will try to reduce the debt. If the debt is cleared or the debt ratio becomes healthy again, the repayment fee for the caller is recalculated based on the new debt. However, this fee could be larger than the remaining collateral balance, meaning the collateral cannot fully cover the fee.
Vulnerability Details
The problem lies in _resolveRepaymentFee. It calculates the repayment fee from the debt and tries to deduct it from the collateral balance. However, even if the collateral isn’t enough to cover the full fee, the function still returns the total fee amount.
function _resolveRepaymentFee(uint256 accountId, uint256 repaidAmountInYield) internal returns (uint256 fee) {
Account storage account = _accounts[accountId];
// calculate repayment fee and deduct from account
fee = repaidAmountInYield * repaymentFee / BPS; // 1%
account.collateralBalance -= fee > account.collateralBalance ? account.collateralBalance : fee;
emit RepaymentFee(accountId, repaidAmountInYield, msg.sender, fee);
return fee;
}Impact Details
If the full fee cannot be deducted from the collateral, the collateral balance becomes insufficient to cover it. As a result, other users may end up paying part of the fee.
Link to Proof of Concept
https://gist.github.com/hexens-joe/564f4927c57da0d0dacb776c4a5a5733
Proof of Concept
Proof of Concept
please run the gist with forge test --mt testRepaymentFeeTakenFromAlchemist -vv
During this poc, console log was added to the relevant _resolveRepaymentFee function
The outputs:
Meaning the fee was more than collateralBalance, but still returned the full fee amount to be send to the caller.
Was this helpful?