58288 sc critical incorrect fee payment logic leads to underpayment
Submitted on Nov 1st 2025 at 00:35:05 UTC by @Petrus for Audit Comp | Alchemix V3
Report ID: #58288
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 _resolveRepaymentFee () in AlchemistV3 computes the full repayment fee but only deducts the capped amount from the account's collateral balance while returning, emitting, and enabling transfer of the uncapped full fee, causing per-account accounting overstatements and potential over-withdrawals.
Vulnerability Details
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;
@> account.collateralBalance -= fee > account.collateralBalance ? account.collateralBalance : fee; // Caps subtraction
emit RepaymentFee(accountId, repaidAmountInYield, msg.sender, fee); // Full fee
return fee; // Full fee transferred in caller
} The function calculates the full fee based on repaidAmountInYield but deducts only the minimum of fee and account.collateralBalance from the per-account collateralBalance, then emits and returns the uncapped fee for transfer in the caller, allowing the full amount to be transferred from the contract's total balance without fully deducting from the account's tracked balance.
Soln
To fix the mismatched accounting issue in _resolveRepaymentFee by ensuring the returned, emitted, and transferred fee matches the actual amount deducted from the account's collateral balance (capping it to available funds), compute and use an actualFee variable consistently to prevent over-transfers and per-account balance overstatements.
Impact Details
Per-account collateral accounting becomes overstated relative to actual outflows, enabling over-withdrawals from the liquidated account and diverging summed account balances from the global token holdings.
References
https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L900
Proof of Concept
Proof of Concept/
Run the test in AlchemistV3.t.sol
Run the test with: forge test --match-test testResolveRepaymentFeeOverstatesAccountCollateralEnablingDilutionAndWithdrawalFailures -vvvv
Was this helpful?