58234 sc critical there is a problem related ot repayment fee overpayment can lead to protocol insolvency
Submitted on Oct 31st 2025 at 16:09:38 UTC by @XDZIBECX for Audit Comp | Alchemix V3
Report ID: #58234
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Protocol insolvency
Description
Brief/Intro
there is a problem in the_resolveRepaymentFee() function, this function is calculates the full repayment fee and returns that amount, and when it's deducting from the user's account, it only takes what's actually available. and If the account doesn't have enough collateral to cover the full fee, the function caps the deduction at whatever balance is remains. the problem is that the liquidators get paid the full calculated fee amount, even though the protocol only deducted the partial or zero amount from the account. and this mismatch its can causes the protocol to pay liquidators from its own reserves to make up the difference, and this is leaking funds with every undercollateralized liquidation as result this is lead to insolvency where the protocol cannot honor user withdrawals is show this issue in the poc check the test is show this issue .
Vulnerability Details
root of the bug is the mismatch between what the fucntion is deducts from an account and what it reports as deducted. in _resolveRepaymentFee it's deducts min(nominal_fee, account.collateralBalance) from the account's collateral balance, capping the deduction at available funds, butit's returns the uncapped nominal_fee value.When _forceRepay() calls this function and receives the returned fee value, it uses that amount to transfer MYT tokens to the liquidator and this is happen without realizing that less or even zero, was actually deducted from the account. and this is mmeans that whenever an account has insufficient collateral to cover the full repayment fee, the protocol pays the liquidator from its general reserves rather than from the liquidated account's collateral, and this is directly leaking protocol funds with each such liquidation and reduce the collateral backing for all user deposits here where this bug is came from ---> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L900C1-L907C6 :
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;
emit RepaymentFee(accountId, repaidAmountInYield, msg.sender, fee);
return fee; << here is Returns nominal fee, not actual deducted amount
}the bug is triggered during the liquidation when the fucntion _forceRepay() is consumes all or most of an account's collateral to repay their debt by transferring MYT to the transmuter. because after this transfer depletes the account's collateral balance to zero or near-zero, the fucntion _resolveRepaymentFee() is called to calculate the liquidator's fee as 10% of the repaid amount, and is attempts to deduct it from the account's collateral (which now has little or nothing left, so it deducts zero or a tiny amount), but then returns the full nominal fee amount. then the the _forceRepay transfers this full nominal fee to the liquidator even though little or nothing was actually deducted from the victim's account. So the difference between what was paid out and what was deducted comes directly from the protocol's general MYT reserves, and this is cause a leak that can exploite repeatedly to drain the protocol ,
here where the liquidate() fucntion is calls the _forceRepay --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L819C1-L822C10 :
the function _forceRepay() is depletes the account collateral from this line --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L764C1-L782C6
so the account collateral is now 0 or very low after these deductions, the
_liquidateis calls the_resolveRepaymentFeehere where the bug is happen --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L823C9-L829C1
the same as here --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L837C8-L842C10 the fucntion _resolveRepaymentFee() it's returns the nominal fee despite the insufficient collateral from here --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L896C1-L908C1 :
so when liquidating an undercollateralized account with 100e18 collateral, the _forceRepay() first consumes *all 100e18 to repay the debt (sent to transmuter), ans this is leave the the account with 0 collateral. then the _resolveRepaymentFee() is calculates a 50e18 liquidation fee (as 50% of repaid amount), and is attempts to deduct it from the now-empty account (deducts nothing), but is returns the full 50e18 anyway, and the protocol then pays this 50e18 to the liquidator from its general MYT reserves (not from the account). so the the protocol loses 150e18 total (100e18 legitimate + 50e18 leaked) even though the account only had 100e18, and _mytSharesDeposited is never updated to reflect this loss. so this is can exploit repeatedly and as result this will drains the protocol reserves and causes insolvency because the protocol owes more to depositors than it actually holds.
Impact Details
This vulnerability it's can allows attackers to steal funds from the protocol's reserves, and this can happen when an undercollateralized account gets liquidated, the protocol first takes all the account's collateral (lets say 100 tokens) to repay debt. then it calculates a liquidation fee (let's say 50 tokens at 50% fee rate) to pay the liquidator. The bug is that even though the account is already empty, the protocol still pays the liquidator the full 50 tokens from its own reserves and the money that doesn't belong to that account. The attacker can create bad positions, and liquidate them, and take this extra payment each time. the protocol's accounting doesn't track these leaked funds, it thinks it has more money than it actually does. so after this happen and many attacks, the protocol runs out of real funds while still owing users their deposits. so When users try to withdraw, there won't be enough tokens to pay them back, then the protocol becomes insolvent and cannot honor withdrawal requests. this bug is can leads to protocol insolvency and loss of all users' funds it's need to be fixed i show a scenario that confirm this check the test
References
i use all line in the vulnerability details check them
Proof of Concept
Proof of Concept
here is a test show this bug, copy past this test in the contract AlchemistV3.t.sol an run forge test --match-test test_RepaymentFee_Overpayment_Bug -vvvv
the result are long that why i past this part of them only :
Was this helpful?