57148 sc high mytsharesdeposited variable is not correctly updated during liquidations leading to wrong assumptions and incorrect bad debt calculation in the transmuter
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
_mytSharesDeposited variable represents the total amount of MYT tokens held in positions in the AlchemistV3 contract.
This variable is incremented when a user deposits, and decremented when a user withdraws. It is also decremented by the fee paid to the protocolFeeReceiver when a user repays his debt through the burn or repay functions.
The problem arises because during liquidations, this variable is not updated even though MYT tokens are sent outside of the contract. This happens in multiple places, in _liquidate, _forceRepay and in _doLiquidation functions.
This leads to _mytSharesDeposited variable being overestimated forever. The more liquidations / force repay happen, the more the variable will be overestimated.
_mytSharesDeposited variable is used to compute the total underlying value in getTotalUnderlyingValue function. This function is used by the Transmuter in claimRedemption function to compute the badDebtRatio in order to determine the actual scaled transmuted amount.
Because _mytSharesDeposited variable is overestimated, the bad debt ratio will be inaccurate and underestimated. Hence, the system might not be able to adapt and resolve a bad debt situation.
Vulnerability Details
The issue lies in the _liquidate, _forceRepay and _doLiquidation functions.
In _liquidate function, we can see 2 times the pattern:
In _forceRepay function:
And in _doLiquidation function:
Because _mytSharesDeposited is inflated as time passes and liquidations occur, the transmuter will be fed with an incorrect value in claimRedemption function:
denominator will be inflated, badDebtRatio will be underestimated, and a potential needed scaling won't be applied.
Impact Details
The impact of this issue is medium as it breaks internal accounting for _mytSharesDeposited, leading to incorrect bad debt calculation by the transmuter and impossibility for the transmuter to adapt and scale transmuted amount to normally resolve a bad dept situation.
References
Add any relevant links to documentation or code
Proof of Concept
Proof of Concept
This proof of concept highlights the path using _doLiquidation.
Please copy paste the following test in AlchemistV3.t.sol file:
This test will revert with the following error:
Indeed, _mytSharesDeposited is not 0 because it has not been decremented enough during liquidation.
feeInYield = _resolveRepaymentFee(accountId, repaidAmountInYield);
// @audit _mytSharesDeposited should be updated
TokenUtils.safeTransfer(myt, msg.sender, feeInYield);
return (repaidAmountInYield, feeInYield, 0);
if (account.collateralBalance > protocolFeeTotal) {
account.collateralBalance -= protocolFeeTotal;
// Transfer the protocol fee to the protocol fee receiver
// @audit _mytSharesDeposited should be updated
TokenUtils.safeTransfer(myt, protocolFeeReceiver, protocolFeeTotal);
}
if (creditToYield > 0) {
// Transfer the repaid tokens from the account to the transmuter.
// @audit _mytSharesDeposited should be updated
TokenUtils.safeTransfer(myt, address(transmuter), creditToYield);
}
// update user balance and debt
account.collateralBalance = account.collateralBalance > amountLiquidated ? account.collateralBalance - amountLiquidated : 0;
_subDebt(accountId, debtToBurn);
// send liquidation amount - fee to transmuter
// @audit _mytSharesDeposited should be updated
TokenUtils.safeTransfer(myt, transmuter, amountLiquidated - feeInYield);
// send base fee to liquidator if available
if (feeInYield > 0 && account.collateralBalance >= feeInYield) {
// @audit _mytSharesDeposited should be updated
TokenUtils.safeTransfer(myt, msg.sender, feeInYield);
}